Class: ClientConnection
- Inherits:
-
Object
- Object
- ClientConnection
- Includes:
- Base64, Encryption
- Defined in:
- lib/game_2d/client_connection.rb
Overview
The client creates one of these. It is then used for all communication with the server.
Constant Summary collapse
- ACTION_DELAY =
We tell the server to execute all actions this many ticks in the future, to give the message time to propagate around the fleet
6
Instance Attribute Summary collapse
-
#engine ⇒ Object
Returns the value of attribute engine.
-
#player_name ⇒ Object
readonly
Returns the value of attribute player_name.
Instance Method Summary collapse
- #_create_connection(*args) ⇒ Object
- #debug_packet(direction, hash) ⇒ Object
- #disconnect ⇒ Object
-
#initialize(host, port, game, player_name, key_size, timeout = 2000) ⇒ ClientConnection
constructor
1/10 of a second.
- #login(server_public_key) ⇒ Object
- #on_close ⇒ Object
- #on_connect ⇒ Object
- #on_packet(data, channel) ⇒ Object
- #online? ⇒ Boolean
- #send_actions_at ⇒ Object
- #send_create_npc(npc) ⇒ Object
- #send_delete_entity(entity) ⇒ Object
- #send_move(player_id, move, args = {}) ⇒ Object
- #send_ping ⇒ Object
- #send_record(data, reliable = false) ⇒ Object
- #send_save ⇒ Object
- #send_snap_to_grid(entity) ⇒ Object
- #send_update_entity(entity) ⇒ Object
- #start(password_hash) ⇒ Object
- #update ⇒ Object
Methods included from Encryption
#decrypt, #encrypt, #key=, #make_cipher, #make_password_hash
Constructor Details
#initialize(host, port, game, player_name, key_size, timeout = 2000) ⇒ ClientConnection
1/10 of a second
23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/game_2d/client_connection.rb', line 23 def initialize(host, port, game, player_name, key_size, timeout=2000) # remote host address, remote host port, channels, download bandwidth, upload bandwidth @socket = _create_connection(host, port, 2, 0, 0) @host, @port, @game, @player_name, @key_size, @timeout = host, port, game, player_name, key_size, timeout @socket.on_connection(method(:on_connect)) @socket.on_disconnection(method(:on_close)) @socket.on_packet_receive(method(:on_packet)) @dh = @password_hash = nil end |
Instance Attribute Details
#engine ⇒ Object
Returns the value of attribute engine.
16 17 18 |
# File 'lib/game_2d/client_connection.rb', line 16 def engine @engine end |
#player_name ⇒ Object (readonly)
Returns the value of attribute player_name.
15 16 17 |
# File 'lib/game_2d/client_connection.rb', line 15 def player_name @player_name end |
Instance Method Details
#_create_connection(*args) ⇒ Object
50 51 52 |
# File 'lib/game_2d/client_connection.rb', line 50 def _create_connection(*args) ENet::Connection.new(*args) end |
#debug_packet(direction, hash) ⇒ Object
176 177 178 179 180 181 |
# File 'lib/game_2d/client_connection.rb', line 176 def debug_packet(direction, hash) return unless $debug_traffic at_tick = hash[:at_tick] || 'NO TICK' keys = hash.keys - [:at_tick] puts "#{direction} #{keys.join(', ')} <#{at_tick}>" end |
#disconnect ⇒ Object
188 |
# File 'lib/game_2d/client_connection.rb', line 188 def disconnect; @socket.disconnect(200) if online?; end |
#login(server_public_key) ⇒ Object
63 64 65 66 67 68 69 70 71 |
# File 'lib/game_2d/client_connection.rb', line 63 def login(server_public_key) self.key = @dh.compute_key(OpenSSL::BN.new server_public_key) data, iv = encrypt(@password_hash) @password_hash = nil send_record( :password_hash => strict_encode64(data), :iv => strict_encode64(iv) ) end |
#on_close ⇒ Object
73 74 75 76 |
# File 'lib/game_2d/client_connection.rb', line 73 def on_close puts "Client disconnected by server" @game.shutdown end |
#on_connect ⇒ Object
54 55 56 57 58 59 60 61 |
# File 'lib/game_2d/client_connection.rb', line 54 def on_connect @game. "Connected, logging in" send_record( { :handshake => { :player_name => @player_name, :dh_public_key => @dh.public_key.to_pem, :client_public_key => @dh.pub_key.to_s } }, true) # send handshake reliably end |
#on_packet(data, channel) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/game_2d/client_connection.rb', line 78 def on_packet(data, channel) hash = JSON.parse(data).fix_keys debug_packet('Received', hash) if pong = hash.delete(:pong) stop = Time.now.to_f puts "Ping took #{stop - pong[:start]} seconds" end if server_public_key = hash.delete(:server_public_key) login(server_public_key) return end # Leave :at_tick intact; add_delta will reuse it fail "No at_tick in #{hash.inspect}" unless at_tick = hash[:at_tick] if world = hash.delete(:world) @game. @engine.establish_world(world, at_tick) end you_are = hash.delete :you_are registry, highest_id = hash.delete(:registry), hash.delete(:highest_id) delta_keys = [ :add_players, :add_npcs, :delete_entities, :update_entities, :update_score, :move ] @engine.add_delta(hash) if delta_keys.any? {|k| hash.has_key? k} if you_are # The 'world' response includes deltas for add_players and add_npcs # Need to process those first, as one of the players is us @engine.apply_deltas(at_tick) if world @game.player_id = you_are end @engine.sync_registry(registry, highest_id, at_tick) if registry end |
#online? ⇒ Boolean
187 |
# File 'lib/game_2d/client_connection.rb', line 187 def online?; @socket.online?; end |
#send_actions_at ⇒ Object
119 120 121 |
# File 'lib/game_2d/client_connection.rb', line 119 def send_actions_at @engine.tick + ACTION_DELAY end |
#send_create_npc(npc) ⇒ Object
132 133 134 135 136 137 138 |
# File 'lib/game_2d/client_connection.rb', line 132 def send_create_npc(npc) return unless online? # :on_* hooks are for our own use; we don't send them remote_npc = npc.reject {|k,v| k.to_s.start_with? 'on_'} send_record :at_tick => send_actions_at, :add_npcs => [ remote_npc ] @engine.add_delta :at_tick => send_actions_at, :add_npcs => [ npc ] end |
#send_delete_entity(entity) ⇒ Object
147 148 149 150 151 152 |
# File 'lib/game_2d/client_connection.rb', line 147 def send_delete_entity(entity) return unless online? delta = { :delete_entities => [entity.registry_id], :at_tick => send_actions_at } send_record delta @engine.add_delta delta end |
#send_move(player_id, move, args = {}) ⇒ Object
123 124 125 126 127 128 129 130 |
# File 'lib/game_2d/client_connection.rb', line 123 def send_move(player_id, move, args={}) return unless move && online? args[:move] = move.to_s delta = { :at_tick => send_actions_at, :move => args } send_record delta delta[:player_id] = player_id @engine.add_delta delta end |
#send_ping ⇒ Object
165 166 167 |
# File 'lib/game_2d/client_connection.rb', line 165 def send_ping send_record :ping => { :start => Time.now.to_f } end |
#send_record(data, reliable = false) ⇒ Object
169 170 171 172 173 174 |
# File 'lib/game_2d/client_connection.rb', line 169 def send_record(data, reliable=false) return unless online? debug_packet('Sending', data) @socket.send_packet(data.to_json, reliable, 0) @socket.flush end |
#send_save ⇒ Object
161 162 163 |
# File 'lib/game_2d/client_connection.rb', line 161 def send_save send_record :save => true end |
#send_snap_to_grid(entity) ⇒ Object
154 155 156 157 158 159 |
# File 'lib/game_2d/client_connection.rb', line 154 def send_snap_to_grid(entity) return unless online? && entity delta = { :at_tick => send_actions_at, :snap_to_grid => entity.registry_id } send_record delta @engine.add_delta delta end |
#send_update_entity(entity) ⇒ Object
140 141 142 143 144 145 |
# File 'lib/game_2d/client_connection.rb', line 140 def send_update_entity(entity) return unless online? delta = { :update_entities => [entity], :at_tick => send_actions_at } send_record delta @engine.add_delta delta end |
#start(password_hash) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/game_2d/client_connection.rb', line 36 def start(password_hash) @password_hash = password_hash Thread.new do @game. "Establishing encryption (#{@key_size}-bit)..." @dh = OpenSSL::PKey::DH.new(@key_size) # Connect to server and kick off handshaking # We will create our player object only after we've been accepted by the server # and told our starting position @game. "Connecting to #{@host}:#{@port} as #{@player_name}" @socket.connect(@timeout) end end |
#update ⇒ Object
183 184 185 |
# File 'lib/game_2d/client_connection.rb', line 183 def update @socket.update(0) # non-blocking end |