Inital work on gamesaves/replays

This commit is contained in:
2021-01-15 21:25:55 -06:00
parent f6f2de1433
commit 37bab6ae3d
6 changed files with 80 additions and 16 deletions

4
.gitignore vendored
View File

@@ -1,5 +1,5 @@
data/settings.json data/settings.json
data/replays/*.replay data/replays/*.dat
data/saves/*.save data/saves/*.dat
doc/ doc/
.yardoc/ .yardoc/

View File

@@ -8,6 +8,7 @@ end
require "json" require "json"
require "socket" require "socket"
require "digest/sha2"
require "nokogiri" require "nokogiri"
@@ -20,6 +21,7 @@ require_relative "lib/camera"
require_relative "lib/setting" require_relative "lib/setting"
require_relative "lib/team_colors" require_relative "lib/team_colors"
require_relative "lib/constants" require_relative "lib/constants"
require_relative "lib/game_save"
require_relative "lib/states/boot" require_relative "lib/states/boot"
require_relative "lib/states/game" require_relative "lib/states/game"

View File

@@ -2,7 +2,7 @@ class IMICRTS
class Director class Director
attr_reader :current_tick, :map, :game, :players attr_reader :current_tick, :map, :game, :players
def initialize(game:, map:, players: [], networking_mode:, tick_rate: 10, local_game: true, replay: false) def initialize(game:, map:, players:, networking_mode:, tick_rate: 15, local_game: true, replay: false, game_save: false, gamesave_file: nil)
@game = game @game = game
@map = map @map = map
@players = players @players = players
@@ -11,6 +11,10 @@ class IMICRTS
@tick_rate = tick_rate @tick_rate = tick_rate
@local_game = local_game @local_game = local_game
@replay = replay @replay = replay
@game_save = game_save
@gamesave_file = gamesave_file ? gamesave_file : "#{IMICRTS::GAME_ROOT_PATH}/data/saves/savegame_#{Time.now.to_i}.dat"
@game_save_host = GameSave.new(mode: :write, gamesave_file: @gamesave_file, players: players, map_file: @map.map_file, gamesave: true)
@game_save_host.write_header
@last_tick_at = Gosu.milliseconds @last_tick_at = Gosu.milliseconds
@tick_time = 1000.0 / @tick_rate @tick_time = 1000.0 / @tick_rate
@@ -25,8 +29,8 @@ class IMICRTS
@replay @replay
end end
def add_player(player) def game_save?
@players << player @game_save
end end
def update def update
@@ -35,6 +39,7 @@ class IMICRTS
tick tick
@connection.update @connection.update
@game_save_host&.feed_tick(@current_tick) if @replay
end end
@players.each { |player| player.update } @players.each { |player| player.update }
@@ -58,6 +63,7 @@ class IMICRTS
order_args = o.deserialize(order_data[1..order_data.length - 1], self) order_args = o.deserialize(order_data[1..order_data.length - 1], self)
execute_order(o.id, *order_args) execute_order(o.id, *order_args)
@game_save_host&.write_order(order_data) unless @replay
player.orders.delete(order) player.orders.delete(order)
@@ -140,6 +146,7 @@ class IMICRTS
def finalize def finalize
@server&.stop @server&.stop
@connection&.finalize @connection&.finalize
@game_save_host&.finalize
end end
end end
end end

50
lib/game_save.rb Normal file
View File

@@ -0,0 +1,50 @@
class IMICRTS
class GameSave
def initialize(mode:, gamesave_file:, map_file: nil, players: nil, gamesave: false, player_id: nil)
@mode = mode
@gamesave_file = File.open(gamesave_file, "#{@mode == :write ? 'w' : 'r'}")
@map_file = map_file
@players = players
@gamesave = gamesave
@player_id = player_id
@version = IMICRTS::VERSION
end
def parse
#
end
def feed_tick(tick_id)
end
def write_header
player_data = @players.map do |player|
{
id: player.id,
name: player.name,
team: player.team,
spawnpoint: player.spawnpoint,
color: player.color&.gl,
bot: player.bot
}
end
s = %{#{@version}
#{@map_file}?#{Digest::SHA256.digest(File.read("#{IMICRTS::GAME_ROOT_PATH}/assets/#{@map_file}"))}
#{@gamesave ? "GAMESAVE?#{@player_id}" : "REPLAY"}
#{JSON.dump(player_data)}}
@gamesave_file.puts(s)
end
def write_order(raw_order)
@gamesave_file.puts(raw_order)
end
def finalize
@gamesave_file&.close
end
end
end

View File

@@ -1,8 +1,9 @@
class IMICRTS class IMICRTS
class Map class Map
attr_reader :tile_size, :tiles, :ores, :spawnpoints, :width, :height attr_reader :tile_size, :tiles, :ores, :spawnpoints, :width, :height, :map_file
def initialize(map_file:) def initialize(map_file:)
@map_file = map_file
@tiled_map = TiledMap.new(map_file) @tiled_map = TiledMap.new(map_file)
@width = @tiled_map.width @width = @tiled_map.width

View File

@@ -11,24 +11,26 @@ class IMICRTS
@options[:map] ||= Map.new(map_file: "maps/test_map.tmx") @options[:map] ||= Map.new(map_file: "maps/test_map.tmx")
@options[:local_player_id] ||= 0 @options[:local_player_id] ||= 0
@director = Director.new(game: self, map: @options[:map], networking_mode: @options[:networking_mode])
@options[:players] ||= [ @options[:players] ||= [
{ id: 0, team: 1, spawnpoint: @director.map.spawnpoints.last, color: :orange, bot: false }, { id: 0, name: "0xdeadbeef", team: 1, spawnpoint: "B", color: :orange, bot: false },
{ id: 1, team: 2, spawnpoint: @director.map.spawnpoints.first, color: :lightblue, bot: :brutal } { id: 1, name: "BrutalAI", team: 2, spawnpoint: "A", color: :lightblue, bot: :brutal }
] ]
@options[:players].each do |pl| players = @options[:players].map do |pl|
player = nil player = nil
visiblity_map = VisibilityMap.new(width: @director.map.width, height: @director.map.height, tile_size: @director.map.tile_size) visiblity_map = VisibilityMap.new(width: @options[:map].width, height: @options[:map].height, tile_size: @options[:map].tile_size)
unless pl[:bot] unless pl[:bot]
player = Player.new(id: pl[:id], spawnpoint: pl[:spawnpoint], team: pl[:team], color: TeamColors[pl[:color]], visiblity_map: visiblity_map) player = Player.new(id: pl[:id], name: pl[:name], spawnpoint: pl[:spawnpoint], team: pl[:team], color: TeamColors[pl[:color]], visiblity_map: visiblity_map)
else else
player = AIPlayer.new(id: pl[:id], spawnpoint: pl[:spawnpoint], team: pl[:team], color: TeamColors[pl[:color]], bot: pl[:bot], visiblity_map: visiblity_map) player = AIPlayer.new(id: pl[:id], name: pl[:name], spawnpoint: pl[:spawnpoint], team: pl[:team], color: TeamColors[pl[:color]], bot: pl[:bot], visiblity_map: visiblity_map)
end end
@player = player if player.id == @options[:local_player_id] @player = player if player.id == @options[:local_player_id]
@director.add_player(player)
player
end end
@director = Director.new(game: self, players: players, map: @options[:map], networking_mode: @options[:networking_mode])
@selected_entities = [] @selected_entities = []
@tool = set_tool(:entity_controller) @tool = set_tool(:entity_controller)
@overlays = [] @overlays = []
@@ -61,9 +63,11 @@ class IMICRTS
end end
@director.players.each do |player| @director.players.each do |player|
spawnpoint = @director.map.spawnpoints[player.spawnpoint.bytes.first - 65]
construction_yard = @director.spawn_entity( construction_yard = @director.spawn_entity(
player_id: player.id, name: :construction_yard, player_id: player.id, name: :construction_yard,
position: CyberarmEngine::Vector.new(player.spawnpoint.x, player.spawnpoint.y, ZOrder::BUILDING) position: CyberarmEngine::Vector.new(spawnpoint.x, spawnpoint.y, ZOrder::BUILDING)
) )
construction_yard.component(:structure).data.construction_progress = Entity.get(construction_yard.name).build_steps construction_yard.component(:structure).data.construction_progress = Entity.get(construction_yard.name).build_steps
construction_yard.component(:structure).data.construction_complete = true construction_yard.component(:structure).data.construction_complete = true
@@ -77,7 +81,7 @@ class IMICRTS
@director.spawn_entity( @director.spawn_entity(
player_id: player.id, name: :construction_worker, player_id: player.id, name: :construction_worker,
position: CyberarmEngine::Vector.new(player.spawnpoint.x - 64, player.spawnpoint.y + 64, ZOrder::GROUND_VEHICLE) position: CyberarmEngine::Vector.new(construction_yard.position.x - 64, construction_yard.position.y + 64, ZOrder::GROUND_VEHICLE)
) )
end end