Class: Game

Inherits:
Object
  • Object
show all
Defined in:
lib/game_2d/game.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ Game

Returns a new instance of Game.


29
30
31
32
33
34
35
36
37
38
39
40
41
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
# File 'lib/game_2d/game.rb', line 29

def initialize(args)
  @storage = Storage.in_home_dir(args[:storage] || DEFAULT_STORAGE).dir('server')
  level_storage = @storage[args[:level]]

  if level_storage.empty?
    @space = GameSpace.new(self).establish_world(
      args[:level],
      nil, # level ID
      args[:width] || WORLD_WIDTH,
      args[:height] || WORLD_HEIGHT)
    @space.storage = level_storage
  else
    @space = GameSpace.load(self, level_storage)
  end

  @tick = -1
  @player_actions = Hash.new {|h,tick| h[tick] = Array.new}

  @self_check, @profile, @registry_broadcast_every = args.values_at(
  :self_check, :profile, :registry_broadcast_every)
  @registry_broadcast_every ||= DEFAULT_REGISTRY_BROADCAST_EVERY

  # This should never happen.  It can only happen client-side because a
  # registry update may create an entity before we get around to it in,
  # say, add_npc
  def @space.fire_duplicate_id(old_entity, new_entity)
    raise "#{old_entity} and #{new_entity} have same ID!"
  end

  # This should never happen.  It can only happen client-side because a
  # registry update may delete an entity before we get around to it in
  # purge_doomed_entities
  def @space.fire_entity_not_found(entity)
    raise "Object #{entity} not in registry"
  end

  @port = _create_server_port(self,
    args[:port] || DEFAULT_PORT,
    args[:max_clients] || MAX_CLIENTS)
end

Instance Attribute Details

#tickObject (readonly)

Returns the value of attribute tick.


74
75
76
# File 'lib/game_2d/game.rb', line 74

def tick
  @tick
end

Class Method Details

.fire_duplicate_id(old_entity, new_entity) ⇒ Object

This should never happen. It can only happen client-side because a registry update may create an entity before we get around to it in, say, add_npc


54
55
56
# File 'lib/game_2d/game.rb', line 54

def @space.fire_duplicate_id(old_entity, new_entity)
  raise "#{old_entity} and #{new_entity} have same ID!"
end

.fire_entity_not_found(entity) ⇒ Object

This should never happen. It can only happen client-side because a registry update may delete an entity before we get around to it in purge_doomed_entities


61
62
63
# File 'lib/game_2d/game.rb', line 61

def @space.fire_entity_not_found(entity)
  raise "Object #{entity} not in registry"
end

Instance Method Details

#[](id) ⇒ Object


126
127
128
# File 'lib/game_2d/game.rb', line 126

def [](id)
  @space[id]
end

#_create_server_port(*args) ⇒ Object


70
71
72
# File 'lib/game_2d/game.rb', line 70

def _create_server_port(*args)
  ServerPort.new *args
end

#add_npc(npc) ⇒ Object


116
117
118
119
120
# File 'lib/game_2d/game.rb', line 116

def add_npc(npc)
  @space << npc or return
  puts "Created #{npc}"
  @space.players.each {|p| player_connection(p).add_npc npc, @tick }
end

#add_player(player_name) ⇒ Object


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

def add_player(player_name)
  player = Player.new(player_name)
  player.x = (@space.width - Entity::WIDTH) / 2
  player.y = (@space.height - Entity::HEIGHT) / 2
  other_players = @space.players.dup
  @space << player
  other_players.each {|p| player_connection(p).add_player(player, @tick) }
  player
end

#add_player_action(player_id, action) ⇒ Object


138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/game_2d/game.rb', line 138

def add_player_action(player_id, action)
  at_tick = action[:at_tick]
  unless at_tick
    $stderr.puts "Received update from #{player_id} without at_tick!"
    at_tick = @tick + 1
  end
  if at_tick <= @tick
    $stderr.puts "Received update from #{player_id} #{@tick + 1 - at_tick} ticks late"
    at_tick = @tick + 1
  end
  @player_actions[at_tick] << [player_id, action]
end

#create_npc(json) ⇒ Object

Answering request from client


112
113
114
# File 'lib/game_2d/game.rb', line 112

def create_npc(json)
  add_npc(Serializable.from_json(json, :GENERATE_ID))
end

#delete_entity(entity) ⇒ Object


104
105
106
107
108
109
# File 'lib/game_2d/game.rb', line 104

def delete_entity(entity)
  puts "Deleting #{entity}"
  @space.doom entity
  @space.purge_doomed_entities
  @space.players.each {|player| player_connection(player).delete_entity entity, @tick }
end

#get_all_npcsObject


134
135
136
# File 'lib/game_2d/game.rb', line 134

def get_all_npcs
  @space.npcs
end

#get_all_playersObject


130
131
132
# File 'lib/game_2d/game.rb', line 130

def get_all_players
  @space.players
end

#player_connection(player) ⇒ Object


100
101
102
# File 'lib/game_2d/game.rb', line 100

def player_connection(player)
  player_id_connection(player.registry_id)
end

#player_id_connection(player_id) ⇒ Object


96
97
98
# File 'lib/game_2d/game.rb', line 96

def player_id_connection(player_id)
  @port.player_connection(player_id)
end

#process_player_actionsObject


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/game_2d/game.rb', line 151

def process_player_actions
  if actions = @player_actions.delete(@tick)
    actions.each do |player_id, action|
      player = @space[player_id]
      unless player
        $stderr.puts "No such player #{player_id} -- dropping move"
        next
      end
      if (move = action[:move])
        player.add_move move
      elsif (npc = action[:create_npc])
        create_npc npc
      else
        $stderr.puts "IGNORING BAD DATA from #{player}: #{action.inspect}"
      end
    end
  end
end

#runObject


199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/game_2d/game.rb', line 199

def run
  run_start = Time.now.to_r
  loop do
    TICKS_PER_SECOND.times do |n|
      update

      # This results in something approaching TICKS_PER_SECOND
      @port.update_until(run_start + Rational(@tick, TICKS_PER_SECOND))

      $stderr.puts "Updates per second: #{@tick / (Time.now.to_r - run_start)}" if @profile
    end # times
  end # infinite loop
end

#saveObject


82
83
84
# File 'lib/game_2d/game.rb', line 82

def save
  @space.save
end

#send_updated_entities(*entities) ⇒ Object


122
123
124
# File 'lib/game_2d/game.rb', line 122

def send_updated_entities(*entities)
  @space.players.each {|p| player_connection(p).update_entities entities, @tick }
end

#updateObject


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/game_2d/game.rb', line 170

def update
  @tick += 1

  # This will:
  # 1) Queue up player actions for existing players
  #    (create_npc included)
  # 2) Add new players in response to handshake messages
  # 3) Remove players in response to disconnections
  @port.update

  # This will execute player moves, and create NPCs
  process_player_actions

  # Objects that exist by now will be updated
  # Objects created during this tick won't be updated this tick
  @space.update

  @port.broadcast(
    :registry => @space.all_registered,
    :highest_id => @space.highest_id,
    :at_tick => @tick
  ) if @registry_broadcast_every > 0 && (@tick % @registry_broadcast_every == 0)

  if @self_check
    @space.check_for_grid_corruption
    @space.check_for_registry_leaks
  end
end

#world_cell_heightObject


80
# File 'lib/game_2d/game.rb', line 80

def world_cell_height; @space.cell_height; end

#world_cell_widthObject


79
# File 'lib/game_2d/game.rb', line 79

def world_cell_width; @space.cell_width; end

#world_highest_idObject


78
# File 'lib/game_2d/game.rb', line 78

def world_highest_id; @space.highest_id; end

#world_idObject


77
# File 'lib/game_2d/game.rb', line 77

def world_id; @space.world_id; end

#world_nameObject


76
# File 'lib/game_2d/game.rb', line 76

def world_name; @space.world_name; end