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
18
# 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}"
  @authenticated = false
end

Instance Attribute Details

#authenticatedObject (readonly)

Returns the value of attribute authenticated.



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

def authenticated
  @authenticated
end

#idObject (readonly)

Returns the value of attribute id.



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

def id
  @id
end

#player_idObject

Returns the value of attribute player_id.



21
22
23
# File 'lib/game_2d/server_connection.rb', line 21

def player_id
  @player_id
end

#player_nameObject (readonly)

Returns the value of attribute player_name.



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

def player_name
  @player_name
end

Instance Method Details

#add_npc(npc, at_tick) ⇒ Object



81
82
83
# File 'lib/game_2d/server_connection.rb', line 81

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

#add_player(player, at_tick) ⇒ Object



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

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

#answer_handshake(handshake) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/game_2d/server_connection.rb', line 24

def answer_handshake(handshake)
  if authenticated?
    warn "#{self} cannot re-handshake"
    disconnect!
    return
  end
  @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



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/game_2d/server_connection.rb', line 42

def (b64_password_hash, b64_iv)
  if authenticated?
    warn "#{self} cannot re-login"
    disconnect!
    return
  end
  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]
      warn "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
  @authenticated = true

  @port.register_player @player_name, self
  player = @game.add_player(@player_name)
  @player_id = player.registry_id
  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



77
78
79
# File 'lib/game_2d/server_connection.rb', line 77

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

#authenticated?Boolean

Returns:

  • (Boolean)


22
# File 'lib/game_2d/server_connection.rb', line 22

def authenticated?; @authenticated; end

#closeObject



102
103
104
# File 'lib/game_2d/server_connection.rb', line 102

def close
  @game.send_player_gone player
end

#debug_packet(direction, hash) ⇒ Object



136
137
138
139
140
141
# File 'lib/game_2d/server_connection.rb', line 136

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



89
90
91
# File 'lib/game_2d/server_connection.rb', line 89

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

#disconnect!Object



143
144
145
# File 'lib/game_2d/server_connection.rb', line 143

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

#on_packet(data, channel) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/game_2d/server_connection.rb', line 106

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 ping = hash.delete(:ping)
    answer_ping ping
  elsif !authenticated?
    warn "Ignoring #{hash.inspect}, not authenticated"
  elsif hash.delete(:save)
    @game.save
  else
    hash[:player_name] = @player_name
    hash[:player_id] = @player_id
    @game.add_player_action hash
    # TODO: Validate
    @port.broadcast_player_action hash, channel
  end
end

#playerObject



73
74
75
# File 'lib/game_2d/server_connection.rb', line 73

def player
  @game[@player_id]
end

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



128
129
130
131
132
133
134
# File 'lib/game_2d/server_connection.rb', line 128

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

#to_sObject



147
148
149
# File 'lib/game_2d/server_connection.rb', line 147

def to_s
  "#{@player_name || '??'} ##{@id} from #{@remote_addr}"
end

#update_entities(entities, at_tick) ⇒ Object



93
94
95
# File 'lib/game_2d/server_connection.rb', line 93

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…



98
99
100
# File 'lib/game_2d/server_connection.rb', line 98

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