class Game
Attributes
tick[R]
Public Class Methods
new(args)
click to toggle source
# File lib/game_2d/game.rb, line 30 def initialize(args) all_storage = Storage.in_home_dir(args[:storage] || DEFAULT_STORAGE) @player_storage = all_storage.dir('players')['players'] @levels_storage = all_storage.dir('levels') level_storage = @levels_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 << Entity::Base.new(*@space.center) @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
Public Instance Methods
[](id)
click to toggle source
# File lib/game_2d/game.rb, line 168 def [](id) @space[id] end
_create_server_port(*args)
click to toggle source
# File lib/game_2d/game.rb, line 76 def _create_server_port(*args) ServerPort.new *args end
add_npcs(npcs_json)
click to toggle source
Answering request from client
# File lib/game_2d/game.rb, line 148 def add_npcs(npcs_json) npcs_json.each {|json| @space << Serializable.from_json(json) } end
add_player(player_name)
click to toggle source
# File lib/game_2d/game.rb, line 101 def add_player(player_name) if base = @space.available_base player = Entity::Gecko.new(player_name) player.x, player.y, player.a = base.x, base.y, base.a else player = Entity::Ghost.new(player_name) player.x, player.y = @space.center end @space << player each_player_conn do |c| c.add_player(player, @tick) unless c.player_name == player_name end player end
add_player_action(action)
click to toggle source
# File lib/game_2d/game.rb, line 180 def add_player_action(action) at_tick, player_name = action[:at_tick], action[:player_name] unless at_tick warn "Received update from #{player_name} without at_tick!" at_tick = @tick + 1 end if at_tick <= @tick warn "Received update from #{player_name} #{@tick + 1 - at_tick} ticks late" at_tick = @tick + 1 end @player_actions[at_tick] << action end
delete_entities(entities)
click to toggle source
# File lib/game_2d/game.rb, line 140 def delete_entities(entities) entities.each do |registry_id| @space.doom(@space[registry_id]) end @space.purge_doomed_entities end
each_player_conn() { |pc| ... }
click to toggle source
# File lib/game_2d/game.rb, line 131 def each_player_conn get_all_players.each {|p| pc = player_connection(p) and yield pc} end
get_all_npcs()
click to toggle source
# File lib/game_2d/game.rb, line 176 def get_all_npcs @space.npcs end
get_all_players()
click to toggle source
# File lib/game_2d/game.rb, line 172 def get_all_players @space.players end
player_connection(player)
click to toggle source
# File lib/game_2d/game.rb, line 127 def player_connection(player) player_name_connection(player.player_name) end
player_data(player_name)
click to toggle source
# File lib/game_2d/game.rb, line 92 def player_data(player_name) @player_storage[player_name] end
player_name_connection(player_name)
click to toggle source
# File lib/game_2d/game.rb, line 123 def player_name_connection(player_name) @port.player_name_connection(player_name) end
process_player_actions()
click to toggle source
# File lib/game_2d/game.rb, line 193 def process_player_actions if actions = @player_actions.delete(@tick) actions.each do |action| player_name = action.delete :player_name conn = player_name_connection(player_name) unless conn warn "No connection -- dropping move from #{player_name}" next end player_id = conn.player_id player = @space[player_id] unless player warn "No such player #{player_id} -- dropping move from #{player_name}" next end if (move = action[:move]) player.add_move move elsif (npcs = action[:add_npcs]) add_npcs npcs elsif (entities = action[:update_entities]) update_npcs entities elsif (entities = action[:delete_entities]) delete_entities entities elsif (entity_id = action[:snap_to_grid]) @space.snap_to_grid entity_id.to_sym else warn "IGNORING BAD DATA from #{player_name}: #{action.inspect}" end end end end
replace_player_entity(player_name, new_player_id)
click to toggle source
# File lib/game_2d/game.rb, line 117 def replace_player_entity(player_name, new_player_id) conn = player_name_connection(player_name) old = conn.player_id conn.player_id = new_player_id end
run()
click to toggle source
# File lib/game_2d/game.rb, line 288 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)) warn "Updates per second: #{@tick / (Time.now.to_r - run_start)}" if @profile end # times end # infinite loop end
save()
click to toggle source
# File lib/game_2d/game.rb, line 88 def save @space.save end
send_full_updates()
click to toggle source
New players always get a full update (with some additional information) Everyone else gets full registry dump every N ticks, where N == @registry_broadcast_every
# File lib/game_2d/game.rb, line 257 def send_full_updates # Set containing brand-new players' IDs # This is cleared after we read it new_players = @port.new_players each_player_conn do |pc| if new_players.include? pc.player_name response = { :you_are => pc.player_id, :world => { :world_name => world_name, :world_id => world_id, :highest_id => world_highest_id, :cell_width => world_cell_width, :cell_height => world_cell_height, }, :add_players => get_all_players, :add_npcs => get_all_npcs, :at_tick => tick, } pc.send_record response, true # answer login reliably elsif @registry_broadcast_every > 0 && (@tick % @registry_broadcast_every == 0) pc.send_record( { :registry => @space.all_registered, :highest_id => @space.highest_id, :at_tick => @tick, }, false, 1 ) end end end
send_player_gone(toast)
click to toggle source
# File lib/game_2d/game.rb, line 135 def send_player_gone(toast) @space.doom toast each_player_conn {|pc| pc.delete_entity toast, @tick } end
send_updated_entities(*entities)
click to toggle source
# File lib/game_2d/game.rb, line 164 def send_updated_entities(*entities) each_player_conn {|pc| pc.update_entities entities, @tick } end
store_player_data(player_name, data)
click to toggle source
# File lib/game_2d/game.rb, line 96 def store_player_data(player_name, data) @player_storage[player_name] = data @player_storage.save end
update()
click to toggle source
# File lib/game_2d/game.rb, line 225 def update @tick += 1 # This will: # 1) Queue up player actions for existing players # (create_npc included) # 2) Add new players in response to login 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 update won't be updated # themselves this tick @space.update # Do this at the end, so the update contains all the # latest and greatest news send_full_updates if @self_check @space.check_for_grid_corruption @space.check_for_registry_leaks end end
update_npcs(npcs_json)
click to toggle source
# File lib/game_2d/game.rb, line 152 def update_npcs(npcs_json) npcs_json.each do |json| id = json[:registry_id] if entity = @space[id] entity.update_from_json json entity.grab! else warn "Can't update #{id}, doesn't exist" end end end
world_cell_height()
click to toggle source
# File lib/game_2d/game.rb, line 86 def world_cell_height; @space.cell_height; end
world_cell_width()
click to toggle source
# File lib/game_2d/game.rb, line 85 def world_cell_width; @space.cell_width; end
world_highest_id()
click to toggle source
# File lib/game_2d/game.rb, line 84 def world_highest_id; @space.highest_id; end
world_id()
click to toggle source
# File lib/game_2d/game.rb, line 83 def world_id; @space.world_id; end
world_name()
click to toggle source
# File lib/game_2d/game.rb, line 82 def world_name; @space.world_name; end