Class: ServerConnection

Inherits:
Object
  • Object
show all
Includes:
Base64, Encryption
Defined in:
lib/game_2d/server_connection.rb

Overview

An instance of this class is created by ServerPort whenever an incoming connection is accepted.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Encryption

#decrypt, #encrypt, #key=, #make_cipher, #make_password_hash

Constructor Details

#initialize(port, game, server, id, remote_addr) ⇒ ServerConnection

Returns a new instance of ServerConnection.



14
15
16
17
# File 'lib/game_2d/server_connection.rb', line 14

def initialize(port, game, server, id, remote_addr)
  @port, @game, @server, @id, @remote_addr = port, game, server, id, remote_addr
  puts "ServerConnection: New connection #{id} from #{remote_addr}"
end

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



19
20
21
# File 'lib/game_2d/server_connection.rb', line 19

def id
  @id
end

#player_idObject (readonly)

Returns the value of attribute player_id.



19
20
21
# File 'lib/game_2d/server_connection.rb', line 19

def player_id
  @player_id
end

Instance Method Details

#add_npc(npc, at_tick) ⇒ Object



67
68
69
# File 'lib/game_2d/server_connection.rb', line 67

def add_npc(npc, at_tick)
  send_record :add_npcs => [ npc ], :at_tick => at_tick
end

#add_player(player, at_tick) ⇒ Object



71
72
73
# File 'lib/game_2d/server_connection.rb', line 71

def add_player(player, at_tick)
  send_record :add_players => [ player ], :at_tick => at_tick
end

#answer_handshake(handshake) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/game_2d/server_connection.rb', line 21

def answer_handshake(handshake)
  @player_name = handshake[:player_name]
  dh_public_key = handshake[:dh_public_key]
  client_public_key = handshake[:client_public_key]
  dh = OpenSSL::PKey::DH.new(dh_public_key)
  dh.generate_key!
  self.key = dh.compute_key(OpenSSL::BN.new client_public_key)
  response = {
    :server_public_key => dh.pub_key.to_s
  }
  send_record response, true # answer reliably
end

#answer_login(b64_password_hash, b64_iv) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/game_2d/server_connection.rb', line 34

def (b64_password_hash, b64_iv)
  password_hash = decrypt(
    strict_decode64(b64_password_hash),
    strict_decode64(b64_iv))
  player_data = @game.player_data(@player_name)
  if player_data
    unless password_hash == player_data[:password_hash]
      $stderr.puts "Wrong password for #{@player_name} (#{password_hash} != #{player_data[:password_hash]})"
      disconnect!
      return
    end
  else # new player
    @game.store_player_data @player_name, :password_hash => password_hash
  end

  player = @game.add_player(@player_name)
  @player_id = player.registry_id
  @port.register_player @player_id, self
  puts "#{player} logs in from #{@remote_addr} at <#{@game.tick}>"

  # We don't send the registry here.  The Game will do it after
  # all logins have been processed and the update has completed.
  # Otherwise, we're sending an incomplete frame.
end

#answer_ping(ping) ⇒ Object



63
64
65
# File 'lib/game_2d/server_connection.rb', line 63

def answer_ping(ping)
  send_record :pong => ping
end

#closeObject



88
89
90
91
92
93
94
# File 'lib/game_2d/server_connection.rb', line 88

def close
  return unless @player_id
  @port.deregister_player @player_id
  toast = player
  puts "#{toast} -- #{@remote_addr} disconnected at <#{@game.tick}>"
  @game.delete_entity toast
end

#debug_packet(direction, hash) ⇒ Object



126
127
128
129
130
131
# File 'lib/game_2d/server_connection.rb', line 126

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

#delete_entity(entity, at_tick) ⇒ Object



75
76
77
# File 'lib/game_2d/server_connection.rb', line 75

def delete_entity(entity, at_tick)
  send_record :delete_entities => [ entity.registry_id ], :at_tick => at_tick
end

#disconnect!Object



133
134
135
# File 'lib/game_2d/server_connection.rb', line 133

def disconnect!
  @server.disconnect_client(@id)
end

#on_packet(data, channel) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/game_2d/server_connection.rb', line 96

def on_packet(data, channel)
  hash = JSON.parse(data).fix_keys
  debug_packet('Received', hash)
  if handshake = hash.delete(:handshake)
    answer_handshake(handshake)
  elsif password_hash = hash.delete(:password_hash)
    (password_hash, hash.delete(:iv))
  elsif hash.delete(:save)
    @game.save
  elsif ping = hash.delete(:ping)
    answer_ping ping
  else
    if hash[:player_id] = @player_id
      @game.add_player_action hash
      # TODO: Validate
      @port.broadcast_player_action hash, channel
    else
      $stderr.puts "Ignoring move #{hash.inspect}, no player_id for this connection"
    end
  end
end

#playerObject



59
60
61
# File 'lib/game_2d/server_connection.rb', line 59

def player
  @game[@player_id]
end

#send_record(hash, reliable = false, channel = 0) ⇒ Object



118
119
120
121
122
123
124
# File 'lib/game_2d/server_connection.rb', line 118

def send_record(hash, reliable=false, channel=0)
  debug_packet('Sending', hash)
  send_str = hash.to_json
  # Send data to the client (client ID, data, reliable or not, channel ID)
  @server.send_packet(@id, send_str, reliable, channel)
  @server.flush
end

#update_entities(entities, at_tick) ⇒ Object



79
80
81
# File 'lib/game_2d/server_connection.rb', line 79

def update_entities(entities, at_tick)
  send_record :update_entities => entities, :at_tick => at_tick
end

#update_score(player, at_tick) ⇒ Object

Not called yet…



84
85
86
# File 'lib/game_2d/server_connection.rb', line 84

def update_score(player, at_tick)
  send_record :update_score => { player.registry_id => player.score }, :at_tick => at_tick
end