Last active
July 13, 2025 02:06
-
-
Save twist84/8fd52529210279fd296247a96aab17e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
c_saved_film::c_saved_film() : | |
m_film_header(), | |
m_async_double_buffer(2) | |
{ | |
c_saved_film::reset_internal_state(); | |
} | |
bool c_saved_film::initialize(c_allocation_base* allocator) | |
{ | |
c_saved_film::reset_internal_state(); | |
bool storage_allocated = m_async_double_buffer.allocate_storage(allocator, k_saved_film_async_io_buffer_size); | |
if (!storage_allocated) | |
{ | |
event(_event_warning, "networking:saved_film: failed to allocate storage for film"); | |
} | |
return storage_allocated; | |
} | |
void c_saved_film::dispose(c_allocation_base* allocator) | |
{ | |
c_saved_film::close(); | |
m_async_double_buffer.release_storage(allocator); | |
} | |
// $TODO: check my work | |
bool c_saved_film::open_for_read(const char* filename, e_controller_index controller_index, bool disable_version_check) | |
{ | |
ASSERT(m_film_state == k_saved_film_state_none); | |
ASSERT(m_content_controller_index == k_no_controller); | |
if (controller_index < k_number_of_controllers) | |
{ | |
c_content_catalogue* content_catalogue = content_catalogue_get_interface(controller_index); | |
file_reference_create_from_path(&m_file_reference, filename, false); | |
int32 content_item_index = content_catalogue->content_item_try_and_get_from_file_reference(&m_file_reference); | |
if (content_item_index != NONE) | |
{ | |
if (!content_catalogue->content_item_mount(content_item_index, true)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to open content item for saved film read (file= '%s')", | |
filename); | |
event(_event_warning, "networking:saved_film: failed to open saved film '%s' for reading", | |
filename); | |
c_saved_film::close(); | |
return false; | |
} | |
m_content_controller_index = controller_index; | |
} | |
} | |
bool read_success = m_async_double_buffer.open_file(filename, _async_buffer_file_access_read, _async_buffer_disposition_open_existing); | |
if (!read_success) | |
{ | |
event(_event_warning, "networking:saved_film: async_double_buffer.open_file() failed for '%s'", | |
filename); | |
event(_event_warning, "networking:saved_film: failed to open saved film '%s' for reading", | |
filename); | |
c_saved_film::close(); | |
return false; | |
} | |
m_film_state = _saved_film_open_for_read; | |
read_success = c_saved_film::read_data(&m_film_header, k_saved_film_async_io_buffer_size); | |
if (!read_success) | |
{ | |
event(_event_warning, "networking:saved_film: failed to get saved film header for '%s'", | |
filename); | |
event(_event_warning, "networking:saved_film: failed to open saved film '%s' for reading", | |
filename); | |
c_saved_film::close(); | |
return false; | |
} | |
read_success = c_saved_film::header_valid(&m_film_header, disable_version_check); | |
if (!read_success) | |
{ | |
event(_event_warning, "networking:saved_film: film header not valid for '%s'", | |
filename); | |
event(_event_warning, "networking:saved_film: failed to open saved film '%s' for reading", | |
filename); | |
c_saved_film::close(); | |
return false; | |
} | |
m_current_film_offset = k_saved_film_async_io_buffer_size; | |
return read_success; | |
} | |
bool c_saved_film::read_update(s_saved_film_update* update_out) | |
{ | |
ASSERT(update_out); | |
ASSERT(m_film_state == _saved_film_open_for_read); | |
uns32 header = 0; | |
if (!c_saved_film::read_data(&header, sizeof(header))) | |
{ | |
event(_event_warning, "networking:saved_film: failed to read header"); | |
return false; | |
} | |
if (!c_saved_film::parse_update_header(header, update_out)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to parse header %d", | |
header); | |
return false; | |
} | |
return true; | |
} | |
bool c_saved_film::read_simulation_update(cnst s_saved_film_update* update, struct simulation_update* simulation_update_out) | |
{ | |
ASSERT(simulation_update_out); | |
ASSERT(update->update_type == _saved_film_update_type_simulation_update); | |
ASSERT(m_film_state==_saved_film_open_for_read); | |
if (update->update_size <= 0 || update->update_size > sizeof(s_blf_saved_film)) | |
{ | |
event(_event_error, "networking:saved_film: read bad update size %d", | |
update->update_size); | |
return false; | |
} | |
if (!c_saved_film::read_data(data, update_size)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to read update [size %d]", | |
update->update_size); | |
return false; | |
} | |
if (!simulation_update_read_from_buffer(simulation_update_out, update->update_size, data)) | |
{ | |
event(_event_warning, "networking:saved_film: read failed to decode update from buffer [size %d]", | |
update->update_size); | |
return false; | |
} | |
m_current_tick++; | |
return true; | |
} | |
bool c_saved_film::read_gamestate(const s_saved_film_update* update, void* compressed_game_state_buffer_out, int32 buffer_size, int32* compressed_game_state_size_out, int32* update_number_out) | |
{ | |
ASSERT(update); | |
ASSERT(compressed_game_state_buffer_out); | |
ASSERT(buffer_size >= k_game_state_maximum_compressed_size); | |
ASSERT(update->update_type == _saved_film_update_type_gamestate); | |
ASSERT(update->update_size == sizeof(s_saved_film_gamestate_header)); | |
ASSERT(buffer_size >= k_game_state_maximum_compressed_size); | |
ASSERT(m_film_state==_saved_film_open_for_read); | |
s_saved_film_gamestate_header saved_film_gamestate_header{}; | |
if (!c_saved_film::read_data(&saved_film_gamestate_header, sizeof(s_saved_film_gamestate_header))) | |
{ | |
event(_event_error, "networking:saved_film: failed to read game state header [header size %d]", | |
sizeof(s_saved_film_gamestate_header)); | |
return false; | |
} | |
if (saved_film_gamestate_header.compressed_size > buffer_size) | |
{ | |
event(_event_error, "networking:saved_film: read compressed game state size larger than buffer [%d > %d]", | |
saved_film_gamestate_header.compressed_size, | |
buffer_size); | |
return false; | |
} | |
if (!c_saved_film::read_data(compressed_game_state_buffer_out, saved_film_gamestate_header.compressed_size)) | |
{ | |
event(_event_error, "networking:saved_film: read failed to read compressed game state [size %d]", | |
saved_film_gamestate_header.compressed_size); | |
return false; | |
} | |
uns32 checksum = fast_checksum_buffer(NONE, compressed_game_state_buffer_out, saved_film_gamestate_header.compressed_size); | |
if (checksum != saved_film_gamestate_header.checksum) | |
{ | |
event(_event_error, "networking:saved_film: read compressed game state checksum mismatch [0x%08X != 0x%08X]", | |
checksum, | |
saved_film_gamestate_header.checksum); | |
return false; | |
} | |
*compressed_game_state_size_out = saved_film_gamestate_header.compressed_size; | |
*update_number_out = saved_film_gamestate_header.update_number; | |
return true; | |
} | |
int32 c_saved_film::get_position() | |
{ | |
ASSERT(m_film_state==_saved_film_open_for_read || m_film_state==_saved_film_open_for_write); | |
ASSERT(m_async_double_buffer.ready_to_read() || m_async_double_buffer.ready_to_write()); | |
return m_async_double_buffer.get_position(); | |
} | |
bool c_saved_film::set_position(int32 position) | |
{ | |
ASSERT(m_film_state==_saved_film_open_for_read || m_film_state==_saved_film_open_for_write); | |
ASSERT(m_async_double_buffer.ready_to_read() || m_async_double_buffer.ready_to_write()); | |
if (!m_async_double_buffer.set_position(position)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to set film position [%d]", | |
position); | |
return false; | |
} | |
return true; | |
} | |
//bool c_saved_film::open_for_write(const char* filename, const game_options* options, e_controller_index controller_index) | |
// $TODO: check my work | |
bool c_saved_film::write_simulation_update(const struct simulation_update* update) | |
{ | |
ASSERT(update); | |
ASSERT(m_film_state == _saved_film_open_for_write); | |
if (!simulation_update_write_to_buffer(update, sizeof(s_blf_saved_film), buffer, &out_update_length)) | |
{ | |
event(_event_error, "networking:saved_film: write failed to encode simulation update"); | |
event(_event_warning, "networking:saved_film: marking fatal error in saved film"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
if (update->update_size <= 0 || update->update_size > k_saved_film_update_size_max) | |
{ | |
event(_event_error, "networking:saved_film: write bad encoded size %d", | |
out_update_length); | |
event(_event_warning, "networking:saved_film: marking fatal error in saved film"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
int32 current_film_offset = m_current_film_offset; | |
int32 update_length = FLAG(k_saved_film_update_size_bits); | |
if (out_update_length < FLAG(k_saved_film_update_size_bits)) | |
{ | |
update_length = out_update_length; | |
} | |
if (out_update_length + (k_saved_film_async_io_buffer_size + sizeof(update_length)) + current_film_offset >= k_saved_film_maximum_size) | |
{ | |
event(_event_warning, "networking:saved_film: write would exceed max saved film size, closing", | |
out_update_length); | |
return false; | |
} | |
if (!c_saved_film::write_data(&update_length, sizeof(update_length))) | |
{ | |
event(_event_warning, "networking:saved_film: write failed to write header for update [size %d]", | |
out_update_length); | |
event(_event_warning, "networking:saved_film: marking fatal error in saved film"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
if (!c_saved_film::write_data(buffer, out_update_length)) | |
{ | |
event(_event_warning, "networking:saved_film: write failed to write encoded update [size %d]", | |
out_update_length); | |
event(_event_warning, "networking:saved_film: marking fatal error in saved film"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
m_current_tick++; | |
return true; | |
} | |
// $TODO: check my work | |
bool c_saved_film::write_gamestate() | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_write); | |
c_game_state_compressor* compressor = game_state_get_compressor(); | |
byte* compressed_game_state = NULL; | |
int32 compressed_size = 0; | |
if (!compressor->lock()) | |
{ | |
event(_event_error, "networking:saved_film: failed to lock game state compressor for write_gamestate"); | |
event(_event_warning, "networking:saved_film: failed to write gamestate, marking as fatal error"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
if (!c_saved_film::write_game_state_internal(0, compressed_game_state, compressed_size)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to write compressed gamestate [%d/%d]", | |
(uns32)compressed_game_state, | |
compressed_size); | |
compressor->unlock(); | |
event(_event_warning, "networking:saved_film: failed to write gamestate, marking as fatal error"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
compressor->unlock(); | |
return true; | |
} | |
// $TODO: check my work | |
bool c_saved_film::write_gamestate_from_buffer(int32 update_number, const void* gamestate, int32 gamestate_size) | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_write); | |
c_game_state_compressor* compressor = game_state_get_compressor(); | |
byte* compressed_game_state = NULL; | |
int32 compressed_size = 0; | |
if (!compressor->lock()) | |
{ | |
event(_event_error, "networking:saved_film: write lock game state compressor for write_gamestate_from_buffer"); | |
event(_event_warning, "networking:saved_film: failed to write gamestate from buffer, marking as fatal error"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
if (!compressor->game_state_get_compressed_from_buffer(_game_state_compression_level_fast, gamestate, gamestate_size, &compressed_game_state, &compressed_size)) | |
{ | |
event(_event_error, "networking:saved_film: write failed to compress game state"); | |
compressor->unlock(); | |
event(_event_warning, "networking:saved_film: failed to write gamestate from buffer, marking as fatal error"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
if (!c_saved_film::write_game_state_internal(update_number, compressed_game_state, compressed_size)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to write compressed gamestate from buffer [%d/%d]", | |
(uns32)compressed_game_state, | |
compressed_size); | |
compressor->unlock(); | |
event(_event_warning, "networking:saved_film: failed to write gamestate from buffer, marking as fatal error"); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
compressor->unlock(); | |
return true; | |
} | |
int32 c_saved_film::get_ticks_remaining() const | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_read); | |
return m_film_header.film_header.length_in_ticks - m_current_tick; | |
} | |
int32 c_saved_film::get_current_tick() const | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_read || m_film_state == _saved_film_open_for_write); | |
return m_current_tick; | |
} | |
int32 c_saved_film::get_length_in_ticks() const | |
{ | |
ASSERT(m_film_state==_saved_film_open_for_read); | |
return m_film_header.film_header.length_in_ticks; | |
} | |
e_saved_film_state c_saved_film::get_film_state() const | |
{ | |
return m_film_state; | |
} | |
game_options* c_saved_film::get_game_options() | |
{ | |
ASSERT(m_film_state!=k_saved_film_state_none); | |
return &m_film_header.film_header.options; | |
} | |
const s_blf_chunk_content_header* c_saved_film::get_film_content_header() const | |
{ | |
return &m_film_header.content_header; | |
} | |
bool c_saved_film::contains_gamestate() const | |
{ | |
return m_film_header.film_header.contains_gamestate; | |
} | |
bool c_saved_film::is_snippet() const | |
{ | |
return m_film_header.film_header.is_snippet; | |
} | |
int32 c_saved_film::get_snippet_start_tick() const | |
{ | |
if (!m_film_header.film_header.is_snippet) | |
{ | |
return NONE; | |
} | |
return m_film_header.film_header.snippet_start_tick; | |
} | |
bool c_saved_film::handle_revert(int32 file_position, int32 film_tick) | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_read); | |
if (!m_async_double_buffer.set_position(file_position)) | |
{ | |
event(_event_warning, "networking:saved_film: failed to set file position for revert %d [tick %d]", | |
file_position, | |
film_tick); | |
return false; | |
} | |
m_current_tick = film_tick; | |
return true; | |
} | |
bool c_saved_film::close() | |
{ | |
bool result = false; | |
bool open_for_write = false; | |
switch (m_film_state) | |
{ | |
case _saved_film_open_for_read: | |
{ | |
event(_event_message, "networking:saved_film: closing film"); | |
result = true; | |
} | |
break; | |
case _saved_film_open_for_write: | |
{ | |
if (!c_saved_film::write_finish()) | |
{ | |
event(_event_warning, "networking:saved_film: failed to finish writing the film, film is invalid"); | |
break; | |
} | |
if (m_fatal_error_encountered_during_write) | |
{ | |
break; | |
} | |
result = true; | |
open_for_write = true; | |
} | |
break; | |
} | |
m_async_double_buffer.close_file(); | |
if (open_for_write && result) | |
{ | |
char filename_buffer[256]{}; | |
file_reference_get_name(&m_file_reference, FLAG(_name_filename_bit), filename_buffer, sizeof(filename_buffer)); | |
c_static_string<256> destination_filename{}; | |
destination_filename.set(filename_buffer); | |
destination_filename.append(".film"); | |
file_rename(&m_file_reference, destination_filename.get_string()); | |
} | |
if (VALID_INDEX(m_content_controller_index, k_number_of_controllers)) | |
{ | |
c_content_catalogue* content_catalogue = content_catalogue_get_interface(m_content_controller_index); | |
if (content_catalogue->content_item_try_and_get_from_file_reference(&m_file_reference) == NONE) | |
{ | |
event(_event_warning, "networking:saved_film: on close failed to get content item for controller %d", | |
m_content_controller_index); | |
} | |
} | |
c_saved_film::reset_internal_state(); | |
return result; | |
} | |
// $TODO: check my work | |
bool c_saved_film::header_valid(const s_blf_saved_film* header, bool disable_version_check) const | |
{ | |
ASSERT(header); | |
if (header->film_data.header.chunk_size < 0 || header->film_data.header.chunk_size + k_maximum_game_state_header_size > k_saved_film_maximum_size) | |
{ | |
event(_event_warning, "networking:saved_film: film payload %d bytes is outside valid range (max %d)", | |
header->film_data.header.chunk_size, | |
k_saved_film_maximum_size); | |
return false; | |
} | |
if (!disable_version_check) | |
{ | |
int32 local_executable_type = 0; | |
int32 local_executable_version = 0; | |
int32 local_compatible_version = 0; | |
network_get_build_identifiers(&local_executable_type, &local_executable_version, &local_compatible_version); | |
int32 local_determinism_version = 0; | |
int32 local_determinism_compatible_version = 0; | |
game_get_determinism_versions(&local_determinism_version, &local_determinism_compatible_version); | |
if (header->film_header.build_compatibility.executable_type != local_executable_type) | |
{ | |
event(_event_warning, "networking:saved_film: executable type mismatch [%d!=%d], film not valid", | |
header->film_header.build_compatibility.executable_type, | |
local_executable_type); | |
return false; | |
} | |
if (!game_determinism_version_compatible(header->film_header.options.determinism_version)) | |
{ | |
event(_event_warning, "networking:saved_film: determinism version mismatch [film %d local %d/%d], film not valid", | |
header->film_header.options.determinism_version, | |
local_determinism_version, | |
local_determinism_compatible_version); | |
return false; | |
} | |
if (header->film_header.build_compatibility.network_executable_version != local_executable_version) | |
{ | |
event(_event_warning, "networking:saved_film: film executable version is different than local [%d != %d], relying on deterministic version", | |
header->film_header.build_compatibility.network_executable_version, | |
local_executable_version); | |
} | |
} | |
char error_string[512]{}; | |
if (!game_options_verify(&header->film_header.options, error_string, sizeof(error_string))) | |
{ | |
event(_event_warning, "networking:saved_film: film header has invalid game options (%s)", | |
error_string); | |
return false; | |
} | |
if (header->film_header.length_in_ticks <= 0) | |
{ | |
event(_event_warning, "networking:saved_film: film header has invalid length in ticks [%d]", | |
header->film_header.length_in_ticks); | |
return false; | |
} | |
if (header->film_header.is_snippet) | |
{ | |
if (!header->film_header.contains_gamestate) | |
{ | |
event(_event_warning, "networking:saved_film: film is marked as snippet but does not contain gamestate?"); | |
return false; | |
} | |
if (header->film_header.snippet_start_tick < 0) | |
{ | |
event(_event_warning, "networking:saved_film: film is marked as snippet but has bad snippet start tick %d", | |
header->film_header.snippet_start_tick); | |
return false; | |
} | |
} | |
return true; | |
} | |
bool c_saved_film::parse_update_header(uns32 header, s_saved_film_update* update_out) const | |
{ | |
e_saved_film_update_type update_type = (e_saved_film_update_type)(header >> k_saved_film_update_size_bits); | |
int32 update_size = (int32)(header & k_saved_film_update_size_max); | |
if (update_type > _saved_film_update_type_gamestate) | |
{ | |
event(_event_error, "networking:saved_film: header has bad update type %d", | |
update_type); | |
return false; | |
} | |
if (update->update_size <= 0 || update->update_size > k_saved_film_update_size_max) | |
{ | |
event(_event_error, "networking:saved_film: header has bad update size %d", | |
update_size); | |
return false; | |
} | |
update_out->update_type = update_type; | |
update_out->update_size = update_size; | |
return true; | |
} | |
uns32 c_saved_film::build_update_header(const s_saved_film_update* update) const | |
{ | |
ASSERT(update->update_size > 0 && update->update_size <= k_saved_film_update_size_max); | |
if (update->update_size > k_saved_film_update_size_max) | |
{ | |
return (update->update_type << k_saved_film_update_size_bits) | FLAG(k_saved_film_update_size_bits); | |
} | |
int32 update_size = 0; | |
if (update->update_size > 0) | |
{ | |
update_size = update->update_size; | |
} | |
return update_size | (update->update_type << k_saved_film_update_size_bits) | |
} | |
e_controller_index c_saved_film::get_cached_controller_index() const | |
{ | |
return m_content_controller_index; | |
} | |
s_file_reference* c_saved_film::get_file_reference() | |
{ | |
return &m_file_reference; | |
} | |
void c_saved_film::mark_film_as_snippet(int32 start_tick) | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_write); | |
ASSERT(start_tick != NONE); | |
ASSERT(start_tick >= 0); | |
m_film_header.film_header.snippet_start_tick = start_tick; | |
m_film_header.film_header.is_snippet = true; | |
} | |
int32 c_saved_film::get_map_signature_size() const | |
{ | |
return m_film_header.film_header.build_compatibility.map_signature_size; | |
} | |
const byte* c_saved_film::get_map_signature_bytes() const | |
{ | |
return m_film_header.film_header.build_compatibility.map_signature_bytes; | |
} | |
//void c_saved_film::copy_film(const char* source_file, const char* destination_file) | |
void c_saved_film::reset_internal_state() | |
{ | |
m_film_state = k_saved_film_state_none; | |
csmemset(&m_film_header, 0, sizeof(s_blf_saved_film)); | |
m_start_of_film_data_offset = 0; | |
m_current_film_offset = 0; | |
m_current_tick = 0; | |
m_async_double_buffer.initialize(); | |
csmemset(&m_file_reference, 0, sizeof(s_file_reference)); | |
m_content_controller_index = k_no_controller; | |
m_fatal_error_encountered_during_write = false; | |
m_finalizing_film = false; | |
} | |
bool c_saved_film::write_game_state_internal(int32 update_number, const void* compressed_game_state, int32 compressed_game_state_size) | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_write); | |
uns32 header = 0x4000000C; | |
if (!c_saved_film::write_data(&header, sizeof(header))) | |
{ | |
event(_event_warning, "networking:saved_film: write failed to write header for game state [compressed size %d]", | |
compressed_game_state_size); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
s_saved_film_gamestate_header saved_film_gamestate_header{}; | |
saved_film_gamestate_header.compressed_size = compressed_game_state_size; | |
saved_film_gamestate_header.checksum = fast_checksum_buffer(NONE, compressed_game_state, compressed_game_state_size); | |
saved_film_gamestate_header.update_number = update_number; | |
if (!c_saved_film::write_data(&saved_film_gamestate_header, sizeof(s_saved_film_gamestate_header))) | |
{ | |
event(_event_warning, "networking:saved_film: write failed to game state header [header size %d]", | |
sizeof(s_saved_film_gamestate_header)); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
if (!c_saved_film::write_data(compressed_game_state, compressed_game_state_size)) | |
{ | |
event(_event_warning, "networking:saved_film: write failed to write compressed game state [size %d]", | |
compressed_game_state_size); | |
m_fatal_error_encountered_during_write = true; | |
return false; | |
} | |
m_film_header.film_header.contains_gamestate = true; | |
return true; | |
} | |
bool c_saved_film::write_finish() | |
{ | |
ASSERT(m_film_state==_saved_film_open_for_write); | |
m_finalizing_film = true; | |
if (!async_usable()) | |
{ | |
event(_event_warning, "networking:saved_film: async not usable,won't be able to finish the write!"); | |
m_finalizing_film = false; | |
return false; | |
} | |
if (m_fatal_error_encountered_during_write) | |
{ | |
event(_event_warning, "networking:saved_film: finishing saved film write with an encountered fatal error"); | |
} | |
else | |
{ | |
ASSERT(m_current_film_offset == m_async_double_buffer.get_position()); | |
ASSERT(m_current_film_offset>=sizeof(s_blf_saved_film) && m_current_film_offset<=k_saved_film_maximum_size); | |
} | |
s_blf_chunk_end_of_file end_of_file{}; | |
{ | |
int32 current_film_offset = this->m_current_film_offset; | |
end_of_file.total_file_size = m_async_double_buffer.get_position(); | |
bool write_success = c_saved_film::write_data(&end_of_file, sizeof(end_of_file)); | |
if (!write_success) | |
{ | |
event(_event_error, "networking:saved_film: saved_film_write_finish failed to write footer"); | |
} | |
m_current_film_offset = current_film_offset; | |
} | |
int32 position = m_async_double_buffer.get_position(); | |
int32 current_film_offset = this->m_current_film_offset; | |
if (write_success) | |
{ | |
// $TODO: implement this | |
} | |
m_finalizing_film = false; | |
return write_success; | |
} | |
bool c_saved_film::read_data(void* data, int32 data_size) | |
{ | |
ASSERT(data); | |
ASSERT(data_size>0); | |
ASSERT(m_film_state==_saved_film_open_for_read); | |
ASSERT(m_async_double_buffer.ready_to_read()); | |
int32 bytes_read = 0; | |
m_async_double_buffer.read(data, data_size, &bytes_read); | |
if (bytes_read != data_size) | |
{ | |
event(_event_warning, "networking:saved_film: film async operation returned unexpected results. Wanted to read %d bytes, but async buffer read only %d.", | |
data_size, | |
bytes_read); | |
return false; | |
} | |
return true; | |
} | |
bool c_saved_film::write_data(const void* data, int32 data_size) | |
{ | |
ASSERT(m_film_state == _saved_film_open_for_write); | |
if (!m_finalizing_film && data_size + m_current_film_offset > k_saved_film_maximum_size) | |
{ | |
event(_event_warning, "networking:saved_film: the film exceeds the maximum size allowable. Trying to write %d bytes at offset %d (maximum film size %d)", | |
data_size, | |
m_current_film_offset, | |
k_saved_film_maximum_size); | |
return false; | |
} | |
int32 bytes_written = 0; | |
m_async_double_buffer.write(data, data_size, &bytes_written); | |
m_current_film_offset += bytes_written; | |
if (bytes_written != data_size) | |
{ | |
event(_event_warning, "networking:saved_film: film async operation returned unexpected results. Wanted to write %d bytes, but async buffer wrote only %d.", | |
data_size, | |
bytes_written); | |
return false; | |
} | |
return true; | |
} | |
c_saved_film::~c_saved_film() | |
{ | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
enum | |
{ | |
k_saved_film_update_size_bits = 30, | |
k_saved_film_update_size_max = FLAG(k_saved_film_update_size_bits) - 1 | |
} | |
enum | |
{ | |
k_saved_film_maximum_size = 0x6400000, | |
k_saved_film_async_io_buffer_size = 0x10000, | |
k_saved_film_maximum_map_signature_bytes = 0x3C, | |
}; | |
class c_saved_film | |
{ | |
public: | |
c_saved_film(); | |
bool initialize(c_allocation_base* allocator); | |
void dispose(c_allocation_base* allocator); | |
bool open_for_read(const char* filename, e_controller_index controller_index, bool disable_version_check); | |
bool read_update(s_saved_film_update* update_out); | |
bool read_simulation_update(const s_saved_film_update* update, struct simulation_update* simulation_update_out); | |
bool read_gamestate(const s_saved_film_update* update, void* compressed_game_state_buffer_out, int32 buffer_size, int32* compressed_game_state_size_out, int32* update_number_out); | |
int32 get_position(); | |
bool set_position(int32 position); | |
bool open_for_write(const char* filename, const game_options* options, e_controller_index controller_index); | |
bool write_simulation_update(const struct simulation_update* update); | |
bool write_gamestate(); | |
bool write_gamestate_from_buffer(int32 update_number, const void* gamestate, int32 gamestate_size); | |
int32 get_ticks_remaining() const; | |
int32 get_current_tick() const; | |
int32 get_length_in_ticks() const; | |
e_saved_film_state get_film_state() const; | |
game_options* get_game_options(); | |
const s_blf_chunk_content_header* get_film_content_header() const; | |
bool contains_gamestate() const; | |
bool is_snippet() const; | |
int32 get_snippet_start_tick() const; | |
bool handle_revert(int32 file_position, int32 film_tick); | |
bool close(); | |
bool header_valid(const s_blf_saved_film* header, bool disable_version_check) const; | |
bool parse_update_header(uns32 header, s_saved_film_update* update_out) const; | |
uns32 build_update_header(const s_saved_film_update* update) const; | |
e_controller_index get_cached_controller_index() const; | |
s_file_reference* get_file_reference(); | |
void mark_film_as_snippet(int32 start_tick); | |
int32 get_map_signature_size() const; | |
const byte* get_map_signature_bytes() const; | |
void copy_film(const char* source_file, const char* destination_file); | |
private: | |
void reset_internal_state(); | |
bool write_game_state_internal(int32 update_number, const void* compressed_game_state, int32 compressed_game_state_size); | |
bool write_finish(); | |
bool read_data(void* data, int32 data_size); | |
bool write_data(const void* data, int32 data_size); | |
public: | |
~c_saved_film(); | |
c_saved_film& operator=(const c_saved_film&) = default; | |
private: | |
e_saved_film_state m_film_state; | |
s_blf_saved_film m_film_header; | |
int32 m_start_of_film_data_offset; | |
int32 m_current_film_offset; | |
int32 m_current_tick; | |
c_async_stored_buffer_set<2> m_async_double_buffer; | |
s_file_reference m_file_reference; | |
e_controller_index m_content_controller_index; | |
bool m_fatal_error_encountered_during_write; | |
bool m_finalizing_film; | |
}; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace | |
{ | |
bool saved_film_manager_should_record_film_default = false; | |
} | |
bool saved_film_manager_should_record_film_default = false; | |
c_interlocked_long g_universal_saved_film_tick; | |
s_saved_film_manager_globals saved_film_manager_globals{}; | |
static int32 saved_film_pending_seek_film_tick = NONE; | |
c_physical_memory_allocation g_physical_memory_allocation; | |
static real32 saved_film_set_pending_playback_game_speed = -1.0f; | |
void saved_film_manager_abort_playback(e_saved_film_playback_abort_reason abort_reason) | |
{ | |
if (saved_film_manager_globals.playback_aborted) | |
{ | |
return; | |
} | |
ASSERT(game_is_playback()); | |
event(_event_warning, "networking:saved_film:manager: playback aborted due to error '%s'", | |
k_playback_abort_reason_names[abort_reason]); | |
saved_film_manager_globals.playback_aborted = true; | |
saved_film_manager_globals.playback_abort_reason = abort_reason; | |
} | |
bool saved_film_manager_authored_camera_locked_for_snippet() | |
{ | |
return game_in_progress() | |
&& game_playback_get() == _game_playback_film | |
&& saved_film_manager_globals.saved_film.m_film_state == _saved_film_open_for_read | |
&& !saved_film_manager_globals.film_ended | |
&& saved_film_snippet_recording_or_previewing(); | |
} | |
bool saved_film_manager_automatic_debug_saving_enabled() | |
{ | |
return saved_film_manager_globals.automatic_debug_saving_enabled; | |
} | |
void saved_film_manager_build_file_path_from_name(const char* film_name, e_saved_film_file_path_creation_purpose purpose, c_static_string<128>* film_path_out) | |
{ | |
ASSERT(film_name); | |
ASSERT(film_path_out); | |
switch (purpose) | |
{ | |
case _file_path_for_creation: | |
{ | |
const char* filename_prefix = ""; | |
const char* directory_path = autosave_queue_get_directory_path(); | |
if (autosave_queue_get_filename_prefix()) | |
{ | |
filename_prefix = autosave_queue_get_filename_prefix(); | |
} | |
film_path_out.print("%s\\%s_%s.film", directory_path, filename_prefix, film_name); | |
} | |
break; | |
case _file_path_for_creation_final: | |
{ | |
const char* filename_prefix = ""; | |
const char* directory_path = autosave_queue_get_directory_path(); | |
if (autosave_queue_get_filename_prefix()) | |
{ | |
filename_prefix = autosave_queue_get_filename_prefix(); | |
} | |
film_path_out.print("%s\\%s_%s.film", directory_path, filename_prefix, film_name); | |
} | |
break; | |
case _file_path_for_reading: | |
{ | |
film_path_out.print("%s.film", film_name); | |
} | |
break; | |
default: | |
{ | |
throw "unreachable"; | |
} | |
break; | |
} | |
} | |
bool saved_film_manager_can_revert(e_saved_film_revert_type desired_revert_type) | |
{ | |
return saved_film_history_ready_for_revert_or_reset() && saved_film_history_can_revert_by_type(desired_revert_type); | |
} | |
bool saved_film_manager_can_set_playback_control() | |
{ | |
if (saved_film_manager_globals.playback_locked) | |
{ | |
return false; | |
} | |
if (!game_is_authoritative_playback()) | |
{ | |
return false; | |
} | |
if (saved_film_manager_globals.film_ended) | |
{ | |
return false; | |
} | |
if (saved_film_manager_get_snippet_state() != _saved_film_snippet_state_none) | |
{ | |
return false; | |
} | |
if (saved_film_manager_globals.snippet_start_tick != NONE && | |
g_universal_saved_film_tick.peek() < saved_film_manager_globals.snippet_start_tick) | |
{ | |
return false; | |
} | |
return true; | |
} | |
void saved_film_manager_clear_playback_state() | |
{ | |
saved_film_manager_globals.authored_cam_set_for_user.clear(); | |
saved_film_manager_globals.valid_camera_mask = 0; | |
saved_film_manager_globals.desired_revert_index = NONE; | |
saved_film_manager_globals.snippet_start_tick = NONE; | |
saved_film_manager_globals.playback_game_speed = 1.0f; | |
saved_film_manager_globals.camera_updates.clear(); | |
saved_film_manager_globals.film_ended = false; | |
saved_film_manager_globals.film_ended_sound_disabled = false; | |
saved_film_manager_globals.playback_locked = false; | |
saved_film_manager_globals.desired_revert_type = _saved_film_revert_none; | |
saved_film_manager_globals.seek_film_tick = NONE; | |
saved_film_manager_globals.seek_and_stop = false; | |
g_universal_saved_film_tick.set(0); | |
saved_film_manager_globals.playback_aborted = false; | |
saved_film_manager_globals.playback_abort_reason = _saved_film_playback_abort_reason_none; | |
} | |
void saved_film_manager_close() | |
{ | |
if (saved_film_manager_globals.film_close_in_progress) | |
{ | |
event(_event_warning, "networking:saved_film:manager: film close in progress, not closing again"); | |
return; | |
} | |
saved_film_manager_globals.film_close_in_progress = true; | |
saved_film_manager_globals.saved_film.close(); | |
saved_film_manager_clear_playback_state(); | |
saved_film_manager_globals.pending_gamestate_load = false; | |
saved_film_manager_globals.gamestate_file_position = NONE; | |
saved_film_manager_globals.film_close_in_progress = false; | |
} | |
void saved_film_manager_commit_snippet_autoname(e_controller_index controller_index) | |
{ | |
e_saved_film_snippet_state snippet_state = saved_film_manager_get_snippet_state(); | |
if (snippet_state == _saved_film_snippet_state_none) | |
{ | |
event(_event_warning, "networking:saved_film:manager: snippts not available (can't commit by autoname)"); | |
return; | |
} | |
if (snippet_state != _saved_film_snippet_state_recorded_and_ready) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't commit snippet by autoname [current in state %d]", | |
snippet_state); | |
return; | |
} | |
if (!saved_film_snippet_commit_by_autoname(controller_index)) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_abort_snippet_failed_to_commit); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: committing snippet by autoname"); | |
} | |
void saved_film_manager_commit_snippet_keyboard(e_controller_index controller_index) | |
{ | |
e_saved_film_snippet_state snippet_state = saved_film_manager_get_snippet_state(); | |
if (snippet_state == _saved_film_snippet_state_none) | |
{ | |
event(_event_warning, "networking:saved_film:manager: snippts not available (can't commit by keyboard)"); | |
return; | |
} | |
if (snippet_state != _saved_film_snippet_state_recorded_and_ready) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't commit snippet by keyboard [current in state %d]", | |
snippet_state); | |
return; | |
} | |
if (!saved_film_snippet_commit_by_keyboard(controller_index)) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_abort_snippet_failed_to_commit); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: committing snippet by keyboard"); | |
} | |
void saved_film_manager_copy_film_to_debug_path() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
if (!saved_film_manager_globals.automatic_debug_saving_enabled || global_scenario->type > _scenario_type_multiplayer) | |
{ | |
return; | |
} | |
void* blf_saved_film = overlapped_malloc(sizeof(s_blf_saved_film)); | |
if (!blf_saved_film) | |
{ | |
event(_event_warning, "networking:saved_film:manager: failed to allocate copy buffer to save last saved film to the xbox dev kit drive!"); | |
return; | |
} | |
c_synchronized_long save_film_success = 0; | |
c_synchronized_long save_film_complete = 0; | |
if (saved_game_files_save_last_film_to_debugging_hard_drive( | |
_controller0, | |
blf_saved_film, | |
sizeof(s_blf_saved_film), | |
&save_film_success, | |
&save_film_complete) == NONE) | |
{ | |
event(_event_warning, "networking:saved_film:manager: failed to allocate async copy task to save last saved film to the xbox dev kit drive!"); | |
} | |
else | |
{ | |
internal_async_yield_until_done(&save_film_complete, false, false, __FILE__, __LINE__); | |
if (save_film_success.peek() != 1) | |
{ | |
event(_event_warning, "networking:saved_film:manager: failed to save last saved film to the xbox dev kit drive!"); | |
} | |
} | |
overlapped_free(blf_saved_film); | |
} | |
void saved_film_manager_create_film_directory() | |
{ | |
s_file_reference film_directory{}; | |
const char* directory_path = autosave_queue_get_directory_path(); | |
if (!file_reference_create_from_path(&film_directory, directory_path, true)) | |
{ | |
return; | |
} | |
if (file_exists(&film_directory)) | |
{ | |
return; | |
} | |
file_create_parent_directories_if_not_present(&film_directory); | |
} | |
void saved_film_manager_delete_current_snippet() | |
{ | |
if (!saved_film_manager_snippets_available()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: snippts not available (can't delete)"); | |
return; | |
} | |
e_saved_film_snippet_state current_state = saved_film_snippet_get_current_state(); | |
if (current_state != _saved_film_snippet_state_recorded_and_ready) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't delete snippet [current in state %d]", | |
current_state); | |
return; | |
} | |
if (!saved_film_snippet_delete()) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_abort_snippet_failed_to_delete); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: deleted snippet"); | |
} | |
void saved_film_manager_delete_on_level_load(bool delete_on_level_load) | |
{ | |
event(_event_error, "networking:saved_film:manager: this no longer does anything"); | |
} | |
void saved_film_manager_disable_version_checking(bool disable) | |
{ | |
saved_film_manager_globals.disable_version_checking = disable; | |
} | |
void saved_film_manager_dispose_from_old_map() | |
{ | |
if (game_is_ui_shell() || main_game_reset_in_progress()) | |
{ | |
return; | |
} | |
saved_film_manager_close(); | |
saved_film_history_dispose_from_saved_film_playback(); | |
saved_film_snippet_dispose_from_saved_film_playback(); | |
saved_film_manager_globals.screensaver_enabled = true; | |
} | |
void saved_film_manager_dispose() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
saved_film_manager_globals.saved_film.dispose(&g_physical_memory_allocation); | |
saved_film_manager_globals.saved_film_name.clear(); | |
saved_film_manager_globals.initialized = false; | |
c_saved_film_scratch_memory::get()->dispose(); | |
} | |
void saved_film_manager_end_film_internal() | |
{ | |
if (saved_film_manager_globals.film_ended) | |
{ | |
event(_event_warning, "networking:saved_film:manager: film ended already set, not ending the film again"); | |
return; | |
} | |
saved_film_manager_globals.film_ended = true; | |
saved_film_manager_globals.film_ended_system_milliseconds = system_milliseconds(); | |
if (game_is_authoritative_playback()) | |
{ | |
simulation_notify_saved_film_ended(); | |
} | |
} | |
bool saved_film_manager_film_is_ended(real32* out_seconds_ago) | |
{ | |
if (!game_in_progress() || !game_is_playback() || !saved_film_manager_globals.film_ended) | |
{ | |
return false; | |
} | |
if (out_seconds_ago) | |
{ | |
*out_seconds_ago = (float)system_milliseconds() * 0.001f; | |
} | |
return true; | |
} | |
bool saved_film_manager_film_valid(e_controller_index controller, const char* film_name) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
if (saved_film_manager_globals.saved_film.m_film_state != k_saved_film_state_none) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't validate film, as we have one open already [state %d]", | |
saved_film_manager_globals.saved_film.m_film_state); | |
return false; | |
} | |
bool result = saved_film_manager_open_film_for_reading(controller, film_name); | |
saved_film_manager_close(); | |
return result; | |
} | |
bool saved_film_manager_get_current_film_name(c_static_string<64>* film_name_out) | |
{ | |
ASSERT(film_name_out); | |
film_name_out->set(saved_film_manager_globals.saved_film_name.get_string()); | |
return true; | |
} | |
const game_options* saved_film_manager_get_current_game_options() | |
{ | |
if (saved_film_manager_globals.saved_film.m_film_state) | |
{ | |
return NULL; | |
} | |
return saved_film_manager_globals.saved_film.get_game_options(); | |
} | |
const s_saved_game_item_metadata* saved_film_manager_get_current_metadata() | |
{ | |
if (saved_film_manager_globals.saved_film.m_film_state) | |
{ | |
return NULL; | |
} | |
if (!saved_film_manager_globals.saved_film.m_film_header.content_header.metadata.is_valid()) | |
{ | |
return NULL; | |
} | |
return &saved_film_manager_globals.saved_film.m_film_header.content_header.metadata; | |
} | |
int32 saved_film_manager_get_current_tick_estimate() | |
{ | |
reutrn g_universal_saved_film_tick.peek(); | |
} | |
int32 saved_film_manager_get_current_tick() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.get_current_tick(); | |
} | |
void saved_film_manager_get_director_state(s_saved_film_manager_director_state* director_state_out) | |
{ | |
csmemset(director_state_out, 0, sizeof(s_saved_film_manager_director_state)); | |
for (int32 user_index = 0; user_index < 4; user_index++) | |
{ | |
if (!player_mapping_output_user_is_active(user_index)) | |
{ | |
continue; | |
} | |
c_director* director = director_get(user_index); | |
if (director->get_type() != _director_mode_saved_film) | |
{ | |
continue; | |
} | |
s_saved_film_manager_user_director_state* state = &director_state_out->user_director_states[user_index]; | |
c_saved_film_director* saved_film_director = (c_saved_film_director*)director; | |
user_director_state->observer_result = *observer_get_camera(user_index); | |
user_director_state->camera_mode = camera->get_type(); | |
user_director_state->desired_camera_mode = saved_film_director->m_desired_camera_mode; | |
user_director_state->valid = true; | |
user_director_state->camera_target_player_absolute_index = saved_film_director->get_watched_player(); | |
} | |
} | |
void saved_film_manager_get_hud_interface_state(s_saved_film_hud_interface_state* hud_state) | |
{ | |
csmemset(hud_state, 0, sizeof(s_saved_film_hud_interface_state)); | |
saved_film_history_get_hud_interface_state(hud_state); | |
saved_film_snippet_get_hud_interface_state(hud_state); | |
} | |
// $TODO: check my work | |
bool saved_film_manager_get_last_recorded_film(char* filepath, int32 maximum_characters, s_saved_game_item_metadata* out_optional_metadata) | |
{ | |
s_file_reference directory{}; | |
file_reference_create_from_path(&directory, autosave_queue_get_directory_path(), true); | |
s_find_file_data file_data{}; | |
find_files_start_with_search_spec(&file_data, 0, &directory, "*.film"); | |
s_file_reference current_file{}; | |
s_file_last_modification_date mod_date{}; | |
bool v7 = false; | |
bool v8 = true; | |
s_file_reference newest_file{}; | |
while (find_files_next(&file_data, ¤t_file, &mod_date)) | |
{ | |
if (!v8 && file_compare_last_modification_dates(&mod_date, &newest_file_mod_date) <= 0); | |
{ | |
continue; | |
} | |
newest_file = current_file; | |
v7 = true; | |
v8 = false; | |
} | |
find_files_end(&file_data); | |
if (!v7) | |
{ | |
return false; | |
} | |
file_reference_get_fullpath(&newest_file, filepath, maximum_characters); | |
if (out_optional_metadata) | |
{ | |
return saved_game_read_metadata_from_file(&newest_file, out_optional_metadata); | |
} | |
return true; | |
} | |
int32 saved_film_manager_get_length_in_ticks() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.get_length_in_ticks(); | |
} | |
real32 saved_film_manager_get_pending_playback_game_speed() | |
{ | |
return saved_film_set_pending_playback_game_speed; | |
} | |
real32 saved_film_manager_get_playback_game_speed() | |
{ | |
return saved_film_manager_globals.playback_game_speed; | |
} | |
int32 saved_film_manager_get_position() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.get_position(); | |
} | |
const char* saved_film_manager_get_recording_directory() | |
{ | |
return autosave_queue_get_directory_path(); | |
} | |
bool saved_film_manager_get_reproduction_enabled() | |
{ | |
return saved_film_manager_globals.reproduction_mode_enabled; | |
} | |
uns32 saved_film_manager_get_simulation_camera_update_mask() | |
{ | |
return saved_film_manager_globals.valid_camera_mask; | |
} | |
bool saved_film_manager_get_simulation_camera_updates(int32 camera_index, s_simulation_camera_update* simulation_camera_update_out) | |
{ | |
ASSERT(VALID_INDEX(camera_index, saved_film_manager_globals.camera_updates.get_count())); | |
if (!TEST_BIT(saved_film_manager_globals.valid_camera_mask, camera_index)) | |
{ | |
return false; | |
} | |
*simulation_camera_update_out = saved_film_manager_globals.camera_updates[camera_index]; | |
return true; | |
} | |
int32 saved_film_manager_get_snippet_start_tick() | |
{ | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
return NONE; | |
} | |
int32 snippet_start_tick = saved_film_manager_globals.snippet_start_tick; | |
if (!saved_film_manager_snippets_available()) | |
{ | |
return snippet_start_tick; | |
} | |
if (saved_film_snippet_get_current_state() == _saved_film_snippet_state_none) | |
{ | |
return snippet_start_tick; | |
} | |
int32 current_snippet_start_tick = NONE; | |
if (!saved_film_snippet_get_current_start_tick(¤t_snippet_start_tick)) | |
{ | |
return snippet_start_tick; | |
} | |
return current_snippet_start_tick; | |
} | |
e_saved_film_snippet_state saved_film_manager_get_snippet_state() | |
{ | |
if (!saved_film_manager_snippets_available()) | |
{ | |
return _saved_film_snippet_state_none; | |
} | |
return saved_film_snippet_get_current_state(); | |
} | |
int32 saved_film_manager_get_ticks_remaining() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
if (saved_film_manager_globals.saved_film.m_film_state) | |
{ | |
return NULL; | |
} | |
return saved_film_manager_globals.saved_film.get_ticks_remaining(); | |
} | |
void saved_film_manager_handle_camera_update(uns32 valid_camera_mask, const s_simulation_camera_update* camera_updates) | |
{ | |
saved_film_manager_globals.valid_camera_mask = valid_camera_mask; | |
saved_film_manager_globals.camera_updates.clear(); | |
if (TEST_BIT(valid_camera_mask, 0)) | |
{ | |
saved_film_manager_globals.camera_updates[0] = *camera_updates; | |
} | |
} | |
bool saved_film_manager_handle_revert(int32 saved_film_file_position, int32 film_tick) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
g_universal_saved_film_tick.set(film_tick); | |
if (!game_is_authoritative_playback()) | |
{ | |
return true; | |
} | |
return saved_film_manager_globals.saved_film.handle_revert(saved_film_file_position, film_tick) | |
} | |
bool saved_film_manager_has_pending_global_state_change() | |
{ | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
return false; | |
} | |
if (saved_film_manager_globals.pending_gamestate_load) | |
{ | |
return true; | |
} | |
return saved_film_manager_revert_desired(); | |
} | |
void saved_film_manager_initialize_for_new_map() | |
{ | |
if (game_is_ui_shell() || main_game_reset_in_progress()) | |
{ | |
return; | |
} | |
saved_film_manager_clear_playback_state(); | |
if (!game_is_playback()) | |
{ | |
return; | |
} | |
if (game_is_authoritative_playback()) | |
{ | |
int32 local_signature_size = 0; | |
const byte* local_signature_bytes = NULL; | |
if (!cache_file_get_content_signature(&local_signature_size, &local_signature_bytes)) | |
{ | |
event(_event_warning, "networking:saved_film:manager: signature MISMATCH, failed to get local signature"); | |
saved_film_manager_abort_playback(_saved_film_playback_map_signature_failed); | |
} | |
else | |
{ | |
int32 map_signature_size = saved_film_manager_globals.saved_film.m_film_header.film_header.build_compatibility.map_signature_size; | |
const byte* map_signature_bytes = saved_film_manager_globals.saved_film.m_film_header.film_header.build_compatibility.map_signature_bytes; | |
bool use_full_language_dependent_signature = !game_options_valid() || !game_is_campaign(); | |
if (!cache_file_content_signatures_match(local_signature_size, local_signature_bytes, map_signature_size, map_signature_bytes, use_full_language_dependent_signature)) | |
{ | |
event(_event_error, "networking:saved_film:manager: signature MISMATCH (%s), film signature (%s) does not match our local one (%s)", | |
use_full_language_dependent_signature ? "full language dependent" : "language neutral only", | |
cache_file_signature_summary(map_signature_size, map_signature_bytes).get_string(), | |
cache_file_signature_summary(local_signature_size, local_signature_bytes).get_string()); | |
saved_film_manager_abort_playback(_saved_film_playback_map_signature_failed); | |
} | |
else | |
{ | |
event(_event_message, "networking:saved_film:manager: signature match (%s), film signature matches our local one (%s)", | |
use_full_language_dependent_signature ? "full language dependent" : "language neutral only", | |
cache_file_signature_summary(local_signature_size, local_signature_bytes).get_string()); | |
} | |
} | |
} | |
saved_film_manager_globals.snippet_start_tick = game_options_get()->playback_start_tick; | |
saved_film_history_initialize_for_saved_film_playback(); | |
saved_film_snippet_initialize_for_saved_film_playback(); | |
saved_film_manager_globals.screensaver_enabled = false; | |
} | |
void saved_film_manager_initialize() | |
{ | |
ASSERT(!saved_film_manager_globals.initialized); | |
saved_film_manager_globals.saved_film.initialize(&g_physical_memory_allocation); | |
saved_film_manager_globals.saved_film_name.clear(); | |
saved_film_manager_globals.show_timestamp = true; | |
saved_film_manager_globals.disable_version_checking = false; | |
saved_film_manager_globals.automatic_debug_saving_enabled = false; | |
saved_film_manager_globals.pending_gamestate_load = false; | |
saved_film_manager_globals.gamestate_file_position = NONE; | |
saved_film_manager_globals.film_close_in_progress = false; | |
saved_film_manager_globals.ui_screen_active = false; | |
saved_film_manager_clear_playback_state(); | |
saved_film_manager_create_film_directory(); | |
saved_film_manager_globals.initialized = true; | |
c_saved_film_scratch_memory::get()->initialize(); | |
saved_film_history_initialize(); | |
saved_film_snippet_initialize(); | |
} | |
bool saved_film_manager_is_reading() | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.m_film_state == _saved_film_open_for_read; | |
} | |
//void saved_film_manager_load_pending_gamestate() | |
//bool saved_film_manager_load_pending_gamestate_to_compressor() | |
void saved_film_manager_memory_dispose() | |
{ | |
saved_film_snippet_memory_dispose(); | |
saved_film_history_memory_dispose(); | |
c_saved_film_scratch_memory::get()->memory_dispose(); | |
} | |
void saved_film_manager_memory_initialize(e_map_memory_configuration memory_configuration) | |
{ | |
if (!map_memory_configuration_is_saved_film(memory_configuration)) | |
{ | |
return; | |
} | |
c_saved_film_scratch_memory::get()->memory_initialize(); | |
saved_film_history_memory_initialize(); | |
saved_film_snippet_memory_initialize(); | |
} | |
void saved_film_manager_notify_gamestate_decompression_after_load_procs() | |
{ | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
return; | |
} | |
if (game_is_campaign()) | |
{ | |
game_state_save(); | |
} | |
determinism_debug_manager_set_file_position(0, 0); | |
{ | |
LOCAL_TAG_RESOURCE_SCOPE_LOCK; | |
players_finish_creation(); | |
} | |
saved_film_manager_globals.authored_cam_set_for_user.clear(); | |
for (int32 user_index = 0; user_index < 4; user_index++) | |
{ | |
if (!player_mapping_output_user_is_active(user_index)) | |
{ | |
continue; | |
} | |
c_director* director = director_get(user_index); | |
if (director->get_type() != _director_mode_saved_film) | |
{ | |
continue; | |
} | |
c_saved_film_director* saved_film_director = (c_saved_film_director *)director; | |
saved_film_director->notify_revert(); | |
} | |
} | |
void saved_film_manager_notify_gamestate_decompression_before_load_procs() | |
{ | |
if (!game_in_progress() || !game_is_playback() || !game_is_multiplayer()) | |
{ | |
return; | |
} | |
if (saved_film_manager_is_reading()) | |
{ | |
ASSERT(saved_film_manager_globals.pending_gamestate_load); | |
} | |
determinism_debug_manager_reset_for_core_load(); | |
saved_film_history_notify_initial_gamestate_loaded(); | |
} | |
void saved_film_manager_notify_gamestate_load(e_saved_film_game_state_load_source game_state_load_source) | |
{ | |
if (saved_film_manager_globals.saved_film.m_film_state != _saved_film_open_for_write) | |
{ | |
return; | |
} | |
if (game_state_load_source == _saved_film_game_state_load_source_core && saved_film_manager_globals.saved_film.get_current_tick() > 0) | |
{ | |
saved_film_manager_close(); | |
saved_film_manager_open_film_for_writing(saved_film_manager_globals.saved_film_name.get_string(), game_options_get()); | |
} | |
determinism_debug_manager_reset_for_core_load(); | |
if (saved_film_manager_globals.saved_film.get_current_tick()) | |
{ | |
event(_event_error, "networking:saved_film:manager: you are loading a core while a film is in the middle of being recorded, things are going to be bad!"); | |
} | |
else | |
{ | |
saved_film_manager_globals.saved_film.write_gamestate(); | |
} | |
} | |
void saved_film_manager_notify_out_of_sync() | |
{ | |
} | |
void saved_film_manager_notify_remote_end_film() | |
{ | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
event(_event_error, "networking:saved_film:manager: remote machine tried to end film but we are not running playback"); | |
return; | |
} | |
if (game_is_authoritative_playback()) | |
{ | |
event(_event_error, "networking:saved_film:manager: remote machine tried to end film but we are the authority!"); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: remote machine ended film"); | |
saved_film_manager_end_film_internal(); | |
} | |
void saved_film_manager_notify_reverted_gamestate_loaded(int32 history_record_index, int32 update_number, void* gamestate, int32 gamestate_size) | |
{ | |
event(_event_message, "networking:saved_film:manager: notified reverted [history %d update %d]", | |
history_record_index, | |
update_number); | |
if (saved_film_manager_globals.seek_film_tick != NONE && saved_film_snippet_get_current_state()) | |
{ | |
saved_film_snippet_finished_revert_for_seek(update_number, gamestate, gamestate_size); | |
} | |
simulation_notify_saved_film_revert(history_record_index, update_number); | |
} | |
void saved_film_manager_notify_snippet_preview_complete() | |
{ | |
event(_event_message, "networking:saved_film:manager: notified film snippet complete"); | |
ASSERT(game_is_authoritative_playback()); | |
saved_film_manager_globals.playback_locked = true; | |
saved_film_manager_globals.playback_game_speed = 0.0f; | |
} | |
bool saved_film_manager_open_film_for_reading(e_controller_index controller_index, const char* film_name) | |
{ | |
e_saved_film_state film_state = saved_film_manager_globals.saved_film.m_film_state; | |
if (film_state != k_saved_film_state_none) | |
{ | |
event(_event_error, "networking:saved_film:manager: can't open film for reading, current state is %d", | |
film_state); | |
return false; | |
} | |
c_static_string<128> saved_film_file_path{}; | |
ASSERT(saved_film_manager_globals.initialized); | |
event(_event_message, "networking:saved_film:manager: opening film %s for reading", | |
film_name); | |
if (csstrstr(film_name, "\\")) | |
{ | |
saved_film_file_path.set(film_name); | |
} | |
else | |
{ | |
saved_film_manager_build_file_path_from_name(film_name, _file_path_for_reading, &saved_film_file_path); | |
} | |
// $TODO: clean this up | |
c_debug_output_path debug_output_path{}; | |
bool valid = saved_film_manager_globals.saved_film.open_for_read(saved_film_file_path.get_string(), controller_index, saved_film_manager_globals.disable_version_checking); | |
if (valid || !csstrstr(film_name, "\\") | |
&& (saved_film_file_path.print("%s%s%s.film", debug_output_path.get_root(), "saved_films\\", film_name), | |
valid = saved_film_manager_globals.saved_film.open_for_read(saved_film_file_path.get_string(), controller_index, saved_film_manager_globals.disable_version_checking))) | |
{ | |
saved_film_manager_globals.saved_film_name.set(film_name); | |
saved_film_manager_globals.pending_gamestate_load = saved_film_manager_globals.saved_film.m_film_header.film_header.contains_gamestate; | |
saved_film_manager_globals.gamestate_file_position = saved_film_manager_globals.saved_film.get_position(); | |
} | |
else | |
{ | |
saved_film_manager_globals.saved_film_name.clear(); | |
saved_film_manager_globals.pending_gamestate_load = false; | |
saved_film_manager_globals.gamestate_file_position = NONE; | |
} | |
return valid; | |
} | |
bool saved_film_manager_open_film_for_writing(const char* film_name, const game_options* options) | |
{ | |
e_saved_film_state film_state = saved_film_manager_globals.saved_film.m_film_state; | |
if (film_state != k_saved_film_state_none) | |
{ | |
event(_event_error, "networking:saved_film:manager: can't open film for writing, current state is %d", | |
film_state); | |
return false; | |
} | |
c_static_string<128> saved_film_path{}; | |
ASSERT(saved_film_manager_globals.initialized); | |
event(_event_message, "networking:saved_film:manager: opening film %s for writing", | |
film_name); | |
saved_film_manager_build_file_path_from_name(film_name, _file_path_for_creation, &saved_film_file_path); | |
bool valid = saved_film_manager_globals.saved_film.open_for_write(saved_film_path.get_string(), options, controller_get_first_non_guest_signed_in_controller()); | |
if (valid) | |
{ | |
saved_film_manager_globals.saved_film_name.set(film_name); | |
} | |
else | |
{ | |
saved_film_manager_globals.saved_film_name.clear(); | |
} | |
return valid; | |
} | |
void saved_film_manager_perform_global_state_change() | |
{ | |
ASSERT(saved_film_manager_has_pending_global_state_change()); | |
event(_event_message, "networking:saved_film:manager: performing global state change"); | |
s_saved_film_manager_director_state director_state{}; | |
saved_film_manager_get_director_state(&director_state); | |
if (saved_film_manager_globals.pending_gamestate_load) | |
{ | |
saved_film_manager_load_pending_gamestate(); | |
saved_film_manager_globals.pending_gamestate_load = false; | |
} | |
bool director_state_set = false; | |
if (saved_film_manager_revert_desired()) | |
{ | |
saved_film_manager_perform_revert(&director_state_set); | |
} | |
if (!director_state_set) | |
{ | |
saved_film_manager_set_director_state(&director_state); | |
} | |
} | |
void saved_film_manager_perform_revert(bool* set_director_state_out) | |
{ | |
ASSERT(saved_film_manager_revert_desired()); | |
int32 current_tick_estimate = saved_film_manager_get_current_tick_estimate(); | |
if (saved_film_manager_globals.seek_film_tick == NONE || saved_film_manager_globals.seek_film_tick > current_tick_estimate) | |
{ | |
if (saved_film_manager_globals.desired_revert_type) | |
{ | |
event(_event_message, "networking:saved_film:manager: reverting by type %d", | |
saved_film_manager_globals.desired_revert_type); | |
saved_film_history_revert_by_type(saved_film_manager_globals.desired_revert_type); | |
} | |
else if (saved_film_manager_globals.desired_revert_index != NONE) | |
{ | |
event(_event_message, "networking:saved_film:manager: reverting by index %d", | |
saved_film_manager_globals.desired_revert_index); | |
if (saved_film_manager_globals.desired_revert_index == 0) | |
{ | |
saved_film_manager_globals.authored_cam_set_for_user.clear(); | |
} | |
if (!saved_film_history_revert_by_index(saved_film_manager_globals.desired_revert_index)) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_history_failed_to_revert_by_index); | |
} | |
} | |
} | |
else | |
{ | |
event(_event_message, "networking:saved_film:manager: reverting by film tick %d", | |
saved_film_manager_globals.seek_film_tick); | |
saved_film_history_revert_by_film_tick(saved_film_manager_globals.seek_film_tick); | |
if (saved_film_manager_snippets_available()) | |
{ | |
saved_film_snippets_notify_reverted_for_seek(set_director_state_out); | |
} | |
saved_film_manager_update_seeking(current_tick_estimate); | |
} | |
for (int32 user_index = 0; user_index < 4; user_index++) | |
{ | |
if (!player_mapping_output_user_is_active(user_index)) | |
{ | |
continue; | |
} | |
c_director* director = director_get(user_index); | |
if (director->get_type() != _director_mode_saved_film) | |
{ | |
continue; | |
} | |
c_saved_film_director* saved_film_director = (c_saved_film_director *)director; | |
saved_film_director->notify_revert(); | |
} | |
saved_film_manager_globals.desired_revert_type = _saved_film_revert_none; | |
saved_film_manager_globals.desired_revert_index = NONE; | |
} | |
void saved_film_manager_play_hs(int16 controller_index, const char* film_name) | |
{ | |
saved_film_manager_play((e_controller_index)controller_index, film_name); | |
} | |
void saved_film_manager_play_last_hs() | |
{ | |
char filename[64]{}; | |
if (saved_film_manager_get_last_recorded_film(filename, sizeof(filename), NULL)) | |
{ | |
saved_film_manager_play(k_no_controller, filename); | |
} | |
} | |
void saved_film_manager_play(e_controller_index controller_index, const char* film_name) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
network_life_cycle_end(); | |
simulation_end(_simulation_abort_reason_preparing_to_play_film); | |
saved_film_manager_set_playback_game_speed(1.0f); | |
saved_film_manager_close(); | |
if (!saved_film_manager_open_film_for_reading(controller_index, film_name)) | |
{ | |
event(_event_error, "networking:saved_film:manager: unable to read header from saved film '%s', film is invalid", | |
film_name); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: playing saved film '%s'", | |
film_name); | |
saved_film_manager_globals.saved_film_name.set(film_name); | |
game_options options{}; | |
options = *saved_film_manager_globals.saved_film.get_game_options(); | |
options.game_playback = _game_playback_film; | |
options.playback_length_in_ticks = saved_film_manager_get_length_in_ticks(); | |
int32 snippet_start_tick = NONE; | |
if (saved_film_manager_globals.saved_film.m_film_header.film_header.is_snippet) | |
{ | |
snippet_start_tick = saved_film_manager_globals.saved_film.m_film_header.film_header.snippet_start_tick; | |
} | |
options.playback_start_tick = snippet_start_tick; | |
options.record_saved_film = false; | |
game_options_validate(&options); | |
main_game_change(&options); | |
} | |
bool saved_film_manager_playback_aborted() | |
{ | |
return saved_film_manager_globals.playback_aborted; | |
} | |
void saved_film_manager_playback_lock_set(real32 playback_game_speed, bool locked) | |
{ | |
ASSERT(game_is_authoritative_playback()); | |
saved_film_manager_globals.playback_locked = locked; | |
saved_film_manager_globals.playback_game_speed = MAX(0.0f, MIN(30.0f, playback_game_speed)); | |
} | |
void saved_film_manager_preview_snippet_start() | |
{ | |
e_saved_film_snippet_state snippet_state = saved_film_manager_get_snippet_state(); | |
if (snippet_state == _saved_film_snippet_state_none) | |
{ | |
event(_event_warning, "networking:saved_film:manager: snippts not available (can't start preview)"); | |
return; | |
} | |
if (snippet_state != _saved_film_snippet_state_recorded_and_ready) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't preview snippet [current in state %d]", | |
snippet_state); | |
return; | |
} | |
if (!saved_film_snippet_preview_start()) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_abort_snippet_failed_to_start_preview); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: starting snippet preview"); | |
saved_film_manager_playback_lock_set(0.0f, true); | |
} | |
void saved_film_manager_preview_snippet_stop() | |
{ | |
e_saved_film_snippet_state snippet_state = saved_film_manager_get_snippet_state(); | |
if (snippet_state == _saved_film_snippet_state_none) | |
{ | |
event(_event_warning, "networking:saved_film:manager: snippts not available (can't stop preview)"); | |
return; | |
} | |
if (snippet_state != _saved_film_snippet_state_previewing) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't stop snippet preview [current in state %d]", | |
snippet_state); | |
return; | |
} | |
if (saved_film_manager_get_current_tick_estimate() <= saved_film_manager_get_snippet_start_tick()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't stop snippet preview [snippet has not actually started previewing]"); | |
return; | |
} | |
if (!saved_film_snippet_preview_stop()) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_abort_snippet_failed_to_stop_preview); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: stopping snippet preview"); | |
saved_film_manager_playback_lock_set(0.0f, true); | |
} | |
bool saved_film_manager_read_simulation_update(const s_saved_film_update* update, struct simulation_update* simulation_update_out) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.read_simulation_update(update, simulation_update_out); | |
} | |
bool saved_film_manager_read_update(s_saved_film_update* update_out) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.read_update(update_out); | |
} | |
void saved_film_manager_render_debug() | |
{ | |
} | |
void saved_film_manager_replay_film() | |
{ | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
event(_event_error, "networking:saved_film:manager: not running playback, can't replay film"); | |
return; | |
} | |
if (!game_is_authoritative_playback()) | |
{ | |
event(_event_error, "networking:saved_film:manager: not the authority, can't request replay film!"); | |
return; | |
} | |
if (!saved_film_history_ready_for_revert_or_reset()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: history not ready for revert or reset, can't replay film"); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: replaying film by reverting to index 0"); | |
saved_film_manager_globals.desired_revert_index = 0; | |
saved_film_manager_playback_lock_set(1.0f, false); | |
} | |
void saved_film_manager_request_end_film() | |
{ | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
event(_event_error, "networking:saved_film:manager: tried to end film but we are not running playback"); | |
return; | |
} | |
if (!game_is_authoritative_playback()) | |
{ | |
event(_event_error, "networking:saved_film:manager: tried to end film but we are running remote playback and cannot end authoritatively"); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: local authority ended film"); | |
saved_film_manager_end_film_internal(); | |
} | |
void saved_film_manager_request_revert_by_index(int32 revert_index) | |
{ | |
event(_event_message, "networking:saved_film:manager: requesting revert by index %d", revert_index); | |
ASSERT(!saved_film_manager_globals.playback_locked); | |
ASSERT(game_is_playback()); | |
ASSERT(!game_is_authoritative_playback()); | |
saved_film_manager_globals.desired_revert_index = revert_index; | |
} | |
void saved_film_manager_request_revert(e_saved_film_revert_type desired_revert_type) | |
{ | |
ASSERT(desired_revert_type != _saved_film_revert_none); | |
if (!saved_film_manager_can_set_playback_control()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't set playback control, can't request revert"); | |
return; | |
} | |
if (!saved_film_history_can_revert_by_type(desired_revert_type)) | |
{ | |
event(_event_warning, "networking:saved_film:manager: revert type %d unavailable", | |
desired_revert_type); | |
return; | |
} | |
event(_event_message, "networking:saved_film:manager: requesting revert by type (%d)", | |
desired_revert_type); | |
saved_film_manager_globals.desired_revert_type = desired_revert_type; | |
} | |
bool saved_film_manager_revert_desired() | |
{ | |
if (saved_film_manager_globals.film_ended || !simulation_in_progress()) | |
{ | |
return false; | |
} | |
if ((saved_film_manager_globals.seek_film_tick == NONE || saved_film_manager_globals.seek_film_tick > saved_film_manager_get_current_tick_estimate()) | |
&& saved_film_manager_globals.desired_revert_type == _saved_film_revert_none) | |
{ | |
return saved_film_manager_globals.desired_revert_index != NONE; | |
} | |
return true; | |
} | |
bool saved_film_manager_rewind_and_seek_to_film_tick(int32 film_tick, bool seek_and_stop) | |
{ | |
ASSERT(film_tick >= 0); | |
if (!saved_film_manager_snippets_available()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: film snippets unavailable, can't queue seek"); | |
return false; | |
} | |
if (saved_film_manager_has_pending_global_state_change()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: already have global state change pending, can't queue seek"); | |
return false; | |
} | |
int32 current_tick_estimate = saved_film_manager_get_current_tick_estimate() | |
if (film_tick > current_tick_estimate) | |
{ | |
event(_event_warning, "networking:saved_film:manager: attempting to rewind and seek to a tick in the future? [%d > %d]", | |
film_tick, | |
current_tick_estimate); | |
return false; | |
} | |
event(_event_message, "networking:saved_film:manager: seeking to film tick %d (seek and stop %s)", | |
film_tick, | |
seek_and_stop ? "TRUE" : "FALSE"); | |
saved_film_manager_playback_lock_set(1.0f, true); | |
saved_film_manager_globals.seek_film_tick = film_tick; | |
saved_film_manager_globals.seek_and_stop = seek_and_stop; | |
return true; | |
} | |
void saved_film_manager_seek_to_film_tick_hs(int32 film_tick) | |
{ | |
if (film_tick < 0) | |
{ | |
event(_event_warning, "networking:saved_film:manager: invalid film tick %d, can't seek", | |
film_tick); | |
return; | |
} | |
if (saved_film_manager_has_pending_global_state_change()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: already have global state change queued, can't seek"); | |
return; | |
} | |
saved_film_pending_seek_film_tick = film_tick; | |
} | |
bool saved_film_manager_seeking(int32* seek_time_available_out) | |
{ | |
ASSERT(seek_time_available_out); | |
if (!game_in_progress() | |
|| !game_is_playback() | |
|| saved_film_manager_globals.saved_film.m_film_state | |
|| saved_film_manager_globals.seek_film_tick == NONE | |
|| saved_film_manager_globals.seek_film_tick < saved_film_manager_get_current_tick_estimate()) | |
{ | |
return false; | |
} | |
*seek_time_available_out = saved_film_manager_globals.seek_film_tick - saved_film_manager_get_current_tick_estimate(); | |
return true; | |
} | |
// $TODO: check my work | |
void saved_film_manager_set_director_state(const s_saved_film_manager_director_state* director_state) | |
{ | |
LOCAL_TAG_RESOURCE_SCOPE_LOCK; | |
for (int32 user_index = 0; user_index < 4; user_index++) | |
{ | |
if (!player_mapping_output_user_is_active(user_index)) | |
{ | |
continue; | |
} | |
c_director* director = director_get(user_index); | |
if (director->get_type() != _director_mode_saved_film) | |
{ | |
continue; | |
} | |
s_saved_film_manager_user_director_state* user_director_state = &director_state->user_director_states[user_index]; | |
if (!user_director_state->valid) | |
{ | |
continue; | |
} | |
c_saved_film_director* saved_film_director = (c_saved_film_director *)director; | |
observer_obsolete_position(user_index); | |
saved_film_director->force_set_camera_mode(user_director_state->camera_mode, 0.0f); | |
saved_film_director->m_desired_camera_mode = user_director_state->desired_camera_mode; | |
saved_film_director->set_watched_player(NONE); | |
c_camera* camera = saved_film_director->get_camera(); | |
camera->set_target(NONE); | |
if (user_director_state->camera_target_player_absolute_index != NONE) | |
{ | |
player_datum* player = DATUM_GET(player_data, player_datum, user_director_state->camera_target_player_absolute_index); | |
if (player && !TEST_BIT(m_iterator.m_datum->flags, _player_left_game_bit)) | |
{ | |
int32 player_index = player_index_from_absolute_player_index(user_director_state->camera_target_player_absolute_index); | |
saved_film_director->set_watched_player(player_index); | |
if (!saved_film_director->in_free_camera_mode()) | |
{ | |
int32 unit_index = player->unit_index; | |
if (unit_index == NONE) | |
{ | |
unit_index = player->dead_unit_index; | |
} | |
camera->set_target(unit_index) | |
} | |
} | |
} | |
camera->set_position(&user_director_state->observer_result.position); | |
camera->set_forward(&user_director_state->observer_result.forward); | |
} | |
} | |
bool saved_film_manager_set_pending_playback_game_speed(real32 game_speed) | |
{ | |
saved_film_set_pending_playback_game_speed = game_speed; | |
return true; | |
} | |
bool saved_film_manager_set_playback_game_speed(real32 game_speed) | |
{ | |
if (!saved_film_manager_can_set_playback_control()) | |
{ | |
event(_event_warning, "networking:saved_film:manager: can't set playback control, can't set game speed"); | |
return false; | |
} | |
saved_film_manager_globals.playback_game_speed = MAX(0.0f, MIN(30.0f, game_speed)); | |
} | |
bool saved_film_manager_set_position(int32 position) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
return saved_film_manager_globals.saved_film.set_position(position); | |
} | |
void saved_film_manager_set_reproduction_enabled(bool reproduction_enabled) | |
{ | |
if (saved_film_manager_globals.reproduction_mode_enabled == reproduction_enabled) | |
{ | |
return; | |
} | |
saved_film_manager_globals.reproduction_mode_enabled = reproduction_enabled; | |
event(_event_message, "networking:saved_film:manager: set reproduction mode (%s)", | |
reproduction_enabled ? "enabled" : "disabled"); | |
if (game_in_progress() && game_is_playback()) | |
{ | |
event(_event_message, "networking:saved_film:manager: reproduction mode changed, forcing recreation of players' output users"); | |
for (int32 user_index = 0; user_index < 4; user_index++) | |
{ | |
player_mapping_attach_output_user(user_index, NONE); | |
} | |
players_finish_creation(); | |
} | |
} | |
void saved_film_manager_should_record_film_default_set(bool b) | |
{ | |
saved_film_manager_should_record_film_default = b; | |
} | |
bool saved_film_manager_should_record_film(const game_options* options) | |
{ | |
ASSERT(options); | |
if (options->game_mode == _game_mode_campaign) | |
{ | |
s_level_datum level_data{}; | |
if (levels_try_and_get_campaign_map(options->map_id, &level_data)) | |
{ | |
return level_data.flags.test(_level_allows_saved_films); | |
} | |
} | |
else if (options->game_mode == _game_mode_multiplayer) | |
{ | |
return true; | |
} | |
return saved_film_manager_should_record_film_default; | |
} | |
void saved_film_manager_show_timestamp(bool show_timestamp) | |
{ | |
saved_film_manager_globals.show_timestamp = show_timestamp; | |
} | |
bool saved_film_manager_snippets_available() | |
{ | |
return game_in_progress() | |
&& game_playback_get() == _game_playback_film | |
&& saved_film_manager_globals.saved_film.m_film_state == _saved_film_open_for_read | |
&& !saved_film_manager_globals.film_ended; | |
} | |
//void saved_film_manager_start_recording_snippet() | |
//void saved_film_manager_stop_recording_snippet() | |
bool saved_film_manager_timestamp_enabled_internal() | |
{ | |
return saved_film_manager_globals.show_timestamp; | |
} | |
void saved_film_manager_toggle_automatic_debug_saving(bool enable) | |
{ | |
saved_film_manager_globals.automatic_debug_saving_enabled = enable; | |
} | |
void saved_film_manager_update_after_simulation_update(const struct simulation_update* update, const s_simulation_update_metadata* metadata) | |
{ | |
saved_film_history_update_after_simulation_update(update, metadata); | |
if (!game_in_progress() || !game_is_playback()) | |
{ | |
return; | |
} | |
g_universal_saved_film_tick.set(metadata->saved_film_tick); | |
saved_film_manager_globals.valid_camera_mask = update->valid_camera_update_mask; | |
saved_film_manager_globals.camera_updates.clear(); | |
if (TEST_BIT(update->valid_camera_update_mask, 0)) | |
{ | |
saved_film_manager_globals.camera_updates = update->camera_updates[0]; | |
} | |
if (game_is_authoritative_playback()) | |
{ | |
saved_film_manager_update_seeking(metadata->saved_film_tick); | |
} | |
if (!saved_film_manager_snippets_available()) | |
{ | |
return; | |
} | |
if (!saved_film_snippet_update_after_simulation_update(update, metadata)) | |
{ | |
saved_film_manager_abort_playback(_saved_film_playback_abort_snippet_failed_to_update_after_simulation); | |
} | |
} | |
void saved_film_manager_update_before_simulation_update() | |
{ | |
saved_film_history_update_before_simulation_update(saved_film_manager_get_snippet_state() != _saved_film_snippet_state_none); | |
} | |
void saved_film_manager_update_seeking(int32 current_film_tick) | |
{ | |
if (!game_in_progress() | |
|| !game_is_playback() | |
|| saved_film_manager_globals.saved_film.m_film_state != _saved_film_open_for_read | |
|| saved_film_manager_globals.seek_film_tick == NONE) | |
{ | |
return; | |
} | |
if (current_film_tick < saved_film_manager_globals.seek_film_tick) | |
{ | |
e_game_playback_type game_playback = game_playback_get(); | |
if (game_playback == _game_playback_film) | |
{ | |
saved_film_manager_playback_lock_set(3.0f, game_playback); | |
return; | |
} | |
saved_film_manager_playback_lock_set(2.0f, true); | |
return; | |
} | |
if (current_film_tick != saved_film_manager_globals.seek_film_tick | |
&& current_film_tick != saved_film_manager_globals.seek_film_tick - 1) | |
{ | |
return; | |
} | |
saved_film_manager_playback_lock_set(saved_film_manager_globals.seek_and_stop ? 0.0f : 1.0f, false); | |
saved_film_manager_globals.seek_film_tick = NONE; | |
saved_film_manager_globals.seek_and_stop = false; | |
} | |
void saved_film_manager_update_snippet_authored_cameras() | |
{ | |
if (saved_film_manager_globals.snippet_start_tick == NONE) | |
{ | |
return; | |
} | |
for (int32 user_index = 0; user_index < 4; user_index++) | |
{ | |
if (!player_mapping_output_user_is_active(user_index)) | |
{ | |
continue; | |
} | |
c_director* director = director_get(user_index); | |
if (director->get_type() != _director_mode_saved_film) | |
{ | |
continue; | |
} | |
if (saved_film_manager_globals.authored_cam_set_for_user.test(user_index)) | |
{ | |
continue; | |
} | |
event(_event_message, "networking:saved_film:manager: setting user %d camera to authored for snippet playback", | |
user_index); | |
director->set_camera_mode(_camera_mode_authored, 0.0f); | |
saved_film_manager_globals.authored_cam_set_for_user.set(user_index, true) | |
} | |
} | |
//void saved_film_manager_update_ui_screens() | |
//void saved_film_manager_update() | |
//int32 saved_film_manager_upload_start(int32 maximum_file_count, s_file_reference* out_file_list) | |
bool saved_film_manager_write_simulation_update(const struct simulation_update* update) | |
{ | |
ASSERT(saved_film_manager_globals.initialized); | |
if (saved_film_manager_globals.saved_film.m_film_state != _saved_film_open_for_write) | |
{ | |
event(_event_error, "networking:saved_film:manager: film not open for write, can't write simulation update"); | |
return false; | |
} | |
if (!saved_film_manager_globals.saved_film.write_simulation_update(update)) | |
{ | |
event(_event_warning, "networking:saved_film:manager: failed to write simulation update %d to film", update->update_number); | |
return false; | |
} | |
g_universal_saved_film_tick.set(saved_film_manager_get_current_tick()); | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
enum e_saved_film_category | |
{ | |
_saved_film_category_none = 0, | |
_saved_film_category_recent_films, | |
_saved_film_category_film_clips, | |
_saved_film_category_campaign, | |
_saved_film_category_multiplayer, | |
_saved_film_category_editor, | |
_saved_film_category_invalid, | |
k_saved_film_category_count, | |
k_saved_film_category_bits = 4, | |
}; | |
enum e_saved_film_file_path_creation_purpose | |
{ | |
_file_path_for_creation = 0, | |
_file_path_for_creation_final = 1, | |
_file_path_for_reading = 2, | |
}; | |
enum e_saved_film_game_state_load_source | |
{ | |
_saved_film_game_state_load_source_core = 0, | |
_saved_film_game_state_load_source_storage, | |
k_saved_film_game_state_load_source | |
}; | |
enum e_saved_film_playback_abort_reason | |
{ | |
_saved_film_playback_abort_reason_none = 0, | |
_saved_film_playback_abort_simulation_failed_to_read, | |
_saved_film_playback_abort_snippet_failed_to_update, | |
_saved_film_playback_abort_snippet_failed_to_update_after_simulation, | |
_saved_film_playback_abort_snippet_failed_to_start_recording, | |
_saved_film_playback_abort_snippet_failed_to_stop_recording, | |
_saved_film_playback_abort_snippet_failed_to_start_preview, | |
_saved_film_playback_abort_snippet_failed_to_stop_preview, | |
_saved_film_playback_abort_snippet_failed_to_delete, | |
_saved_film_playback_abort_snippet_failed_to_commit, | |
_saved_film_playback_history_failed_to_write, | |
_saved_film_playback_history_failed_to_write_record, | |
_saved_film_playback_history_failed_to_revert, | |
_saved_film_playback_history_failed_to_revert_by_index, | |
_saved_film_playback_failed_to_load_gamestate_for_party, | |
_saved_film_playback_map_signature_failed, | |
k_saved_film_playback_abort_reason_count | |
}; | |
enum e_saved_film_revert_type | |
{ | |
_saved_film_revert_none = 0, | |
_saved_film_revert_backwards = 1, | |
_saved_film_revert_forwards = 2, | |
}; | |
enum e_saved_film_snippet_state | |
{ | |
_saved_film_snippet_state_none = 0, | |
_saved_film_snippet_state_recording_waiting_for_seek, | |
_saved_film_snippet_state_recording_waiting_for_start, | |
_saved_film_snippet_state_recording, | |
_saved_film_snippet_state_recorded_and_ready, | |
_saved_film_snippet_state_previewing_waiting_for_seek, | |
_saved_film_snippet_state_previewing, | |
_saved_film_snippet_state_commiting_invoking_title_keyboard, | |
_saved_film_snippet_state_commiting_waiting_title_keyboard, | |
_saved_film_snippet_state_commiting_invoking_description_keyboard, | |
_saved_film_snippet_state_commiting_waiting_description_keyboard, | |
_saved_film_snippet_state_commiting_initiate_creation, | |
_saved_film_snippet_state_commiting_wait_for_creation, | |
_saved_film_snippet_state_commiting_initiate_metadata_update, | |
_saved_film_snippet_state_commiting_wait_for_metadata_update, | |
_saved_film_snippet_state_resetting, | |
k_number_of_saved_film_snippet_states | |
}; | |
enum e_saved_film_state | |
{ | |
_saved_film_open_for_read = 0, | |
_saved_film_open_for_write, | |
k_saved_film_state_count, | |
k_saved_film_state_none = -1 | |
}; | |
enum e_saved_film_update_type | |
{ | |
_saved_film_update_type_simulation_update = 0, | |
_saved_film_update_type_gamestate, | |
k_saved_film_update_type_count, | |
k_saved_film_update_type_none = -1, | |
}; | |
struct s_saved_film_manager_user_director_state | |
{ | |
s_observer_result observer_result; | |
e_camera_mode camera_mode; | |
e_camera_mode desired_camera_mode; | |
int32 camera_target_player_absolute_index; | |
bool valid; | |
}; | |
struct s_saved_film_manager_director_state | |
{ | |
c_static_array<s_saved_film_manager_user_director_state,4> user_director_states; | |
}; | |
struct s_saved_film_hud_interface_state | |
{ | |
real32 duration_in_seconds; | |
real32 marker_position_in_seconds; | |
int32 number_of_chapters_available; | |
real32 buffered_theta; | |
real32 current_position_theta; | |
real32 recording_start_theta; | |
bool recording; | |
c_static_array<real32, 10> chapter_mark_theta; | |
}; | |
struct s_saved_film_update | |
{ | |
e_saved_film_update_type update_type; | |
int32 update_size; | |
}; | |
class c_saved_film; | |
struct s_saved_film_manager_globals | |
{ | |
c_static_string<64> saved_film_name; | |
c_saved_film saved_film; | |
bool pending_gamestate_load; | |
int32 gamestate_file_position; | |
bool film_close_in_progress; | |
bool reproduction_mode_enabled; | |
real32 playback_game_speed; | |
bool playback_locked; | |
bool show_timestamp; | |
bool film_ended; | |
bool film_ended_sound_disabled; | |
int32 film_ended_system_milliseconds; | |
int32 seek_film_tick; | |
bool seek_and_stop; | |
e_saved_film_revert_type desired_revert_type; | |
int32 desired_revert_index; | |
int32 snippet_start_tick; | |
c_static_flags_no_init<4> authored_cam_set_for_user; | |
uns32 valid_camera_mask; | |
c_static_array<s_simulation_camera_update, 1> camera_updates; | |
bool playback_aborted; | |
e_saved_film_playback_abort_reason playback_abort_reason; | |
bool disable_version_checking; | |
bool automatic_debug_saving_enabled; | |
bool ui_screen_active; | |
bool screensaver_enabled; | |
bool initialized; | |
}; | |
static const real32 k_saved_film_ended_fade_time_seconds = 4.0f; | |
namespace | |
{ | |
extern bool saved_film_manager_should_record_film_default; | |
} | |
extern bool saved_film_manager_should_record_film_default; | |
extern c_interlocked_long g_universal_saved_film_tick; | |
extern s_saved_film_manager_globals saved_film_manager_globals; | |
extern void saved_film_manager_abort_playback(e_saved_film_playback_abort_reason abort_reason); | |
extern bool saved_film_manager_authored_camera_locked_for_snippet(); | |
extern bool saved_film_manager_automatic_debug_saving_enabled(); | |
extern void saved_film_manager_build_file_path_from_name(const char* film_name, e_saved_film_file_path_creation_purpose purpose, c_static_string<128>* film_path_out); | |
extern bool saved_film_manager_can_revert(e_saved_film_revert_type desired_revert_type); | |
extern bool saved_film_manager_can_set_playback_control(); | |
extern void saved_film_manager_clear_playback_state(); | |
extern void saved_film_manager_close(); | |
extern void saved_film_manager_commit_snippet_autoname(e_controller_index controller_index); | |
extern void saved_film_manager_commit_snippet_keyboard(e_controller_index controller_index); | |
extern void saved_film_manager_copy_film_to_debug_path(); | |
extern void saved_film_manager_create_film_directory(); | |
extern void saved_film_manager_delete_current_snippet(); | |
extern void saved_film_manager_delete_on_level_load(bool delete_on_level_load); | |
extern void saved_film_manager_disable_version_checking(bool disable); | |
extern void saved_film_manager_dispose_from_old_map(); | |
extern void saved_film_manager_dispose(); | |
extern void saved_film_manager_end_film_internal(); | |
extern bool saved_film_manager_film_is_ended(real32* out_seconds_ago); | |
extern bool saved_film_manager_film_valid(e_controller_index controller, const char* film_name); | |
extern bool saved_film_manager_get_current_film_name(c_static_string<64>* film_name_out); | |
extern const game_options* saved_film_manager_get_current_game_options(); | |
extern const s_saved_game_item_metadata* saved_film_manager_get_current_metadata(); | |
extern int32 saved_film_manager_get_current_tick_estimate(); | |
extern int32 saved_film_manager_get_current_tick(); | |
extern void saved_film_manager_get_director_state(s_saved_film_manager_director_state* director_state_out); | |
extern void saved_film_manager_get_hud_interface_state(s_saved_film_hud_interface_state* hud_state); | |
extern bool saved_film_manager_get_last_recorded_film(char* filepath, int32 maximum_characters, s_saved_game_item_metadata* out_optional_metadata); | |
extern int32 saved_film_manager_get_length_in_ticks(); | |
extern real32 saved_film_manager_get_pending_playback_game_speed(); | |
extern real32 saved_film_manager_get_playback_game_speed(); | |
extern int32 saved_film_manager_get_position(); | |
extern const char* saved_film_manager_get_recording_directory(); | |
extern bool saved_film_manager_get_reproduction_enabled(); | |
extern uns32 saved_film_manager_get_simulation_camera_update_mask(); | |
extern bool saved_film_manager_get_simulation_camera_updates(int32 camera_index, s_simulation_camera_update* simulation_camera_update_out); | |
extern int32 saved_film_manager_get_snippet_start_tick(); | |
extern e_saved_film_snippet_state saved_film_manager_get_snippet_state(); | |
extern int32 saved_film_manager_get_ticks_remaining(); | |
extern void saved_film_manager_handle_camera_update(uns32 valid_camera_mask, const s_simulation_camera_update* camera_updates); | |
extern bool saved_film_manager_handle_revert(int32 saved_film_file_position, int32 film_tick); | |
extern bool saved_film_manager_has_pending_global_state_change(); | |
extern void saved_film_manager_initialize_for_new_map(); | |
extern void saved_film_manager_initialize(); | |
extern bool saved_film_manager_is_reading(); | |
extern void saved_film_manager_load_pending_gamestate(); | |
extern bool saved_film_manager_load_pending_gamestate_to_compressor(); | |
extern void saved_film_manager_memory_dispose(); | |
extern void saved_film_manager_memory_initialize(e_map_memory_configuration memory_configuration); | |
extern void saved_film_manager_notify_gamestate_decompression_after_load_procs(); | |
extern void saved_film_manager_notify_gamestate_decompression_before_load_procs(); | |
extern void saved_film_manager_notify_gamestate_load(e_saved_film_game_state_load_source game_state_load_source); | |
extern void saved_film_manager_notify_out_of_sync(); | |
extern void saved_film_manager_notify_remote_end_film(); | |
extern void saved_film_manager_notify_reverted_gamestate_loaded(int32 history_record_index, int32 update_number, void* gamestate, int32 gamestate_size); | |
extern void saved_film_manager_notify_snippet_preview_complete(); | |
extern bool saved_film_manager_open_film_for_reading(e_controller_index controller_index, const char* film_name); | |
extern bool saved_film_manager_open_film_for_writing(const char* film_name, const game_options* options); | |
extern void saved_film_manager_perform_global_state_change(); | |
extern void saved_film_manager_perform_revert(bool* set_director_state_out); | |
extern void saved_film_manager_play_hs(int16 controller_index, const char* film_name); | |
extern void saved_film_manager_play_last_hs(); | |
extern void saved_film_manager_play(e_controller_index controller_index, const char* film_name); | |
extern bool saved_film_manager_playback_aborted(); | |
extern void saved_film_manager_playback_lock_set(real32 playback_game_speed, bool locked); | |
extern void saved_film_manager_preview_snippet_start(); | |
extern void saved_film_manager_preview_snippet_stop(); | |
extern bool saved_film_manager_read_simulation_update(const s_saved_film_update* update, struct simulation_update* simulation_update_out); | |
extern bool saved_film_manager_read_update(s_saved_film_update* update_out); | |
extern void saved_film_manager_render_debug(); | |
extern void saved_film_manager_replay_film(); | |
extern void saved_film_manager_request_end_film(); | |
extern void saved_film_manager_request_revert_by_index(int32 revert_index); | |
extern void saved_film_manager_request_revert(e_saved_film_revert_type desired_revert_type); | |
extern bool saved_film_manager_revert_desired(); | |
extern bool saved_film_manager_rewind_and_seek_to_film_tick(int32 film_tick, bool seek_and_stop); | |
extern void saved_film_manager_seek_to_film_tick_hs(int32 film_tick); | |
extern bool saved_film_manager_seeking(int32* seek_time_available_out); | |
extern void saved_film_manager_set_director_state(const s_saved_film_manager_director_state* director_state); | |
extern bool saved_film_manager_set_pending_playback_game_speed(real32 game_speed); | |
extern bool saved_film_manager_set_playback_game_speed(real32 game_speed); | |
extern bool saved_film_manager_set_position(int32 position); | |
extern void saved_film_manager_set_reproduction_enabled(bool reproduction_enabled); | |
extern void saved_film_manager_should_record_film_default_set(bool b); | |
extern bool saved_film_manager_should_record_film(const game_options* options); | |
extern void saved_film_manager_show_timestamp(bool show_timestamp); | |
extern bool saved_film_manager_snippets_available(); | |
extern void saved_film_manager_start_recording_snippet(); | |
extern void saved_film_manager_stop_recording_snippet(); | |
extern bool saved_film_manager_timestamp_enabled_internal(); | |
extern void saved_film_manager_toggle_automatic_debug_saving(bool enable); | |
extern void saved_film_manager_update_after_simulation_update(const struct simulation_update* update, const s_simulation_update_metadata* metadata); | |
extern void saved_film_manager_update_before_simulation_update(); | |
extern void saved_film_manager_update_seeking(int32 current_film_tick); | |
extern void saved_film_manager_update_snippet_authored_cameras(); | |
extern void saved_film_manager_update_ui_screens(); | |
extern void saved_film_manager_update(); | |
extern int32 saved_film_manager_upload_start(int32 maximum_file_count, s_file_reference* out_file_list); | |
extern bool saved_film_manager_write_simulation_update(const struct simulation_update* update); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void shell_update_launch() | |
{ | |
if (setup_theater_playback("saved_films\\test_film.film")) | |
{ | |
launch_game_when_ready(); | |
} | |
} | |
bool launch_game_when_ready() | |
{ | |
e_session_game_start_error out_error = _session_game_start_error_none; | |
uns32 out_player_error_mask = 0; | |
if (user_interface_get_session_game_start_status(&out_error, &out_player_error_mask) != _session_game_start_status_ready) | |
{ | |
return false; | |
} | |
user_interface_squad_start_countdown_timer(first_controller(), 1, 0); | |
return true; | |
} | |
bool setup_theater_playback(const char* theater_save_film_file_name) | |
{ | |
user_interface_squad_set_ui_game_mode(_gui_game_setup_mode_theater); | |
s_file_reference film_file{}; | |
file_reference_create_from_path(&film_file, theater_save_film_file_name, false); | |
uns32 error_code = 0; | |
if (!file_exists(&film_file) || !file_open(&film_file, FLAG(_permission_read_bit), &error_code) || error_code != 0) | |
{ | |
return false; | |
} | |
s_blf_saved_film film_header{}; | |
if (!file_get_size(&film_file, &file_size)) | |
{ | |
file_close(&film_file); | |
return false; | |
} | |
if (!file_read(&film_file, file_size >= sizeof(s_blf_saved_film) ? sizeof(s_blf_saved_film) : file_size, true, &film_header)) | |
{ | |
file_close(&film_file); | |
return false; | |
} | |
set_squad_session_film(theater_save_film_file_name, &film_header); | |
file_close(&film_file); | |
return true; | |
} | |
bool set_squad_session_film(const char* filename, s_blf_saved_film* film_blf) | |
{ | |
int32 tick_length = 0; | |
int32 start_tick = 0; | |
bool was_valid = false; | |
bool result = false; | |
if (!film_blf->copy_to_and_validate(&film_blf->film_header.options, &tick_length, &start_tick, &was_valid) || !was_valid) | |
{ | |
return false; | |
} | |
const s_saved_game_item_metadata* metadata = &film_blf->content_header.metadata; | |
const game_options* options = &film_blf->film_header.options; | |
s_saved_film_description description{}; | |
description.category = _saved_film_category_recent_films; | |
description.campaign_id = _campaign_id_none; | |
description.map_id = metadata->map_id; | |
description.difficulty = _campaign_difficulty_level_easy; | |
description.length_seconds = metadata->length_seconds; | |
if (!user_interface_squad_set_film(&description)) | |
{ | |
return false; | |
} | |
if (options->campaign_id == _campaign_id_none) | |
{ | |
bool game_variant_set = user_interface_squad_set_game_variant(&options->multiplayer_variant); | |
if (!game_variant_set || options->map_variant.get_map_id() == _map_id_none) | |
{ | |
if (!game_variant_set || options->map_id == _map_id_none) | |
{ | |
return false; | |
} | |
c_map_variant default_map_variant{}; | |
default_map_variant.create_default(options->map_id); | |
return user_interface_squad_set_multiplayer_map(&default_map_variant); | |
} | |
return user_interface_squad_set_multiplayer_map(&options->map_variant); | |
} | |
// $TODO: campaign logic | |
return false; | |
} | |
Comments are disabled for this gist.