Skip to content

Commit

Permalink
Add a local character infos property to the render game module, which…
Browse files Browse the repository at this point in the history
… allows the render module to load (#39)

the skins and assets from local players quicker.
Additionally rename the continue_map_loading to continue_loading, and make the local skins & particles a requirement. (For spawning)
  • Loading branch information
Jupeyy authored Jan 7, 2025
1 parent 1647784 commit 6a2b366
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 132 deletions.
2 changes: 1 addition & 1 deletion game/api-render-game/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl RenderGameInterface for ApiRenderGame {
}

#[guest_func_call_from_host_auto(option)]
fn continue_map_loading(&mut self) -> Result<bool, String> {}
fn continue_loading(&mut self) -> Result<bool, String> {}

#[guest_func_call_from_host_auto(option)]
fn set_chat_commands(&mut self, chat_commands: ChatCommands) {}
Expand Down
20 changes: 20 additions & 0 deletions game/client-containers/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,26 @@ where
}
}

/// Returns `true` if the resource was loaded or failed to load (and will not be loaded),
/// `false` otherwise.
///
/// This call is non-blocking.
pub fn is_loaded_or_failed<Q>(&mut self, name: &Q) -> bool
where
Q: Borrow<ContainerKey>,
{
self.failed_tasks.contains(name.borrow()) || {
let item_res = self.items.get(name.borrow());
if item_res.is_none() {
// try to load the resource
self.get_or_default(name);
false
} else {
true
}
}
}

/// Blocking wait for the item to be finished.
///
/// This is only useful for programs that don't run
Expand Down
7 changes: 6 additions & 1 deletion game/client-demo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use demo::{
ChunkHeader, DemoEvent, DemoEvents, DemoHeader, DemoHeaderExt, DemoSnapshot, DemoTail,
};
use egui::{FontDefinitions, Rect};
use game_base::{assets_url::HTTP_RESOURCE_URL, game_types::intra_tick_time_to_ratio};
use game_config::config::{ConfigGame, ConfigMap, ConfigRender, ConfigSoundRender};
use game_interface::{interface::GameStateInterface, types::game::GameTickType};
use graphics::{
Expand All @@ -50,7 +51,6 @@ use pool::datatypes::{
};
use pool::mt_datatypes::PoolCow as MtPoolCow;
use serde::de::DeserializeOwned;
use game_base::{assets_url::HTTP_RESOURCE_URL, game_types::intra_tick_time_to_ratio};
use sound::{
commands::{SceneAirMode, SoundSceneCreateProps},
sound::SoundManager,
Expand Down Expand Up @@ -787,6 +787,7 @@ impl DemoViewerImpl {
map_hash: ext.map_hash,
game_options: ext.game_options.clone(),
required_resources: ext.required_resources.clone(),
client_local_infos: ext.client_local_infos.clone(),
physics_module: ext.physics_mod.clone(),
render_module: ext.render_mod.clone(),
physics_group_name: ext.physics_group_name.clone(),
Expand Down Expand Up @@ -1202,6 +1203,10 @@ impl DemoViewer {
.header_ext
.required_resources
.clone(),
client_local_infos: demo_container
.header_ext
.client_local_infos
.clone(),
},
)
};
Expand Down
2 changes: 1 addition & 1 deletion game/client-map/src/client_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ impl ClientMapLoading {
}
}
GameLoading::Game(mut load_game) => {
match load_game.continue_map_loading() {
match load_game.continue_loading() {
Ok(loaded) => {
if loaded {
match (
Expand Down
153 changes: 100 additions & 53 deletions game/client-render-game/src/render_game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ use game_interface::{
},
interface::MAX_PHYSICS_GROUP_NAME_LEN,
types::{
character_info::NetworkCharacterInfo,
flag::FlagType,
game::GameTickType,
id_types::{CharacterId, PlayerId, StageId},
Expand Down Expand Up @@ -173,6 +174,8 @@ impl RenderModTy {
}
}

pub type ClientLocalInfos = Vec<NetworkCharacterInfo>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RenderGameCreateOptions {
pub physics_group_name: NetworkReducedAsciiString<MAX_PHYSICS_GROUP_NAME_LEN>,
Expand All @@ -189,6 +192,15 @@ pub struct RenderGameCreateOptions {
/// mod to download & prepare the required resources as soon
/// as possible, but generally is optional.
pub required_resources: RequiredResources,
/// The initial client local infos.
///
/// These don't need to come from the server,
/// instead they can be the infos that come from
/// the config values.
///
/// The implementation can use this information to speed up
/// loading of the resources _likely_ to be used.
pub client_local_infos: ClientLocalInfos,
}

#[derive(Default, Serialize, Deserialize)]
Expand Down Expand Up @@ -418,6 +430,9 @@ pub struct RenderGame {
canvas_handle: GraphicsCanvasHandle,
backend_handle: GraphicsBackendHandle,

// props
client_local_infos: ClientLocalInfos,

// helpers
helper: Pool<Vec<RenderPlayerHelper>>,

Expand Down Expand Up @@ -453,7 +468,7 @@ impl RenderGame {
Some("downloaded".as_ref()),
));

let containers = load_containers(
let mut containers = load_containers(
io,
thread_pool,
props.resource_http_download_url,
Expand Down Expand Up @@ -481,19 +496,28 @@ impl RenderGame {
let motd = MotdRender::new(graphics, &creator);
let spectator_selection = SpectatorSelectionRender::new(graphics, &creator);

let mut map_vote_thumbnails_container = load_thumbnail_container(
io.clone(),
thread_pool.clone(),
DEFAULT_THUMBNAIL_CONTAINER_PATH,
"map-votes-thumbnail",
graphics,
sound,
scene.clone(),
props.resource_download_server,
);

Self::update_containers_impl(
&mut containers,
&mut map_vote_thumbnails_container,
cur_time,
props.client_local_infos.iter(),
);

Ok(Self {
// containers
containers,
map_vote_thumbnails_container: load_thumbnail_container(
io.clone(),
thread_pool.clone(),
DEFAULT_THUMBNAIL_CONTAINER_PATH,
"map-votes-thumbnail",
graphics,
sound,
scene.clone(),
props.resource_download_server,
),
map_vote_thumbnails_container,

// components
players,
Expand All @@ -520,6 +544,8 @@ impl RenderGame {
canvas_handle: graphics.canvas_handle.clone(),
backend_handle: graphics.backend_handle.clone(),

client_local_infos: props.client_local_infos,

helper: Pool::with_capacity(1),

world_sound_scene: scene,
Expand Down Expand Up @@ -1061,7 +1087,7 @@ pub trait RenderGameInterface {
cur_time: &Duration,
input: RenderGameInput,
) -> RenderGameResult;
fn continue_map_loading(&mut self) -> Result<bool, String>;
fn continue_loading(&mut self) -> Result<bool, String>;
fn set_chat_commands(&mut self, chat_commands: ChatCommands);
/// Clear all rendering state (like particles, sounds etc.)
fn clear_render_state(&mut self);
Expand Down Expand Up @@ -2405,108 +2431,128 @@ impl RenderGame {
}
}

fn update_containers(
&mut self,
fn check_required_containers_loaded(&mut self) -> bool {
let loaded = self.client_local_infos.iter().all(|i| {
self.containers.skin_container.is_loaded_or_failed(&i.skin)
&& self
.containers
.particles_container
.is_loaded_or_failed(&i.particles)
});
// since we don't need the property anymore after they are loaded
// clear it to save memory & performance.
if loaded {
self.client_local_infos = Default::default();
}
loaded
}

fn update_containers_impl<'a, F>(
containers: &mut RenderGameContainers,
map_vote_thumbnails_container: &mut ThumbnailContainer,
cur_time: &Duration,
character_infos: &PoolFxLinkedHashMap<CharacterId, CharacterInfo>,
) {
self.containers.skin_container.update(
character_infos: F,
) where
F: Iterator<Item = &'a NetworkCharacterInfo> + Clone,
{
containers.skin_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos.values().map(|info| info.info.skin.borrow()),
character_infos.clone().map(|info| info.skin.borrow()),
None,
);
self.containers.weapon_container.update(
containers.weapon_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos
.values()
.map(|info| info.info.weapon.borrow()),
character_infos.clone().map(|info| info.weapon.borrow()),
None,
);
self.containers.hook_container.update(
containers.hook_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos.values().map(|info| info.info.hook.borrow()),
character_infos.clone().map(|info| info.hook.borrow()),
None,
);
self.containers.ctf_container.update(
containers.ctf_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos.values().map(|info| info.info.ctf.borrow()),
character_infos.clone().map(|info| info.ctf.borrow()),
None,
);
self.containers.ninja_container.update(
containers.ninja_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos
.values()
.map(|info| info.info.ninja.borrow()),
character_infos.clone().map(|info| info.ninja.borrow()),
None,
);
self.containers.freeze_container.update(
containers.freeze_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos
.values()
.map(|info| info.info.freeze.borrow()),
character_infos.clone().map(|info| info.freeze.borrow()),
None,
);
self.containers.entities_container.update(
containers.entities_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos
.values()
.map(|info| info.info.entities.borrow()),
character_infos.clone().map(|info| info.entities.borrow()),
None,
);
self.containers.hud_container.update(
containers.hud_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos.values().map(|info| info.info.hud.borrow()),
character_infos.clone().map(|info| info.hud.borrow()),
None,
);
self.containers.emoticons_container.update(
containers.emoticons_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos
.values()
.map(|info| info.info.emoticons.borrow()),
character_infos.clone().map(|info| info.emoticons.borrow()),
None,
);
self.containers.particles_container.update(
containers.particles_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos
.values()
.map(|info| info.info.particles.borrow()),
character_infos.clone().map(|info| info.particles.borrow()),
None,
);
self.containers.game_container.update(
containers.game_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
character_infos.values().map(|info| info.info.game.borrow()),
character_infos.map(|info| info.game.borrow()),
None,
);
self.map_vote_thumbnails_container.update(
map_vote_thumbnails_container.update(
cur_time,
&Duration::from_secs(5),
&Duration::from_secs(1),
[].into_iter(),
None,
);
}

fn update_containers(
&mut self,
cur_time: &Duration,
character_infos: &PoolFxLinkedHashMap<CharacterId, CharacterInfo>,
) {
Self::update_containers_impl(
&mut self.containers,
&mut self.map_vote_thumbnails_container,
cur_time,
character_infos.values().map(|i| &***i.info),
);
}
}

impl RenderGameInterface for RenderGame {
Expand Down Expand Up @@ -2712,10 +2758,11 @@ impl RenderGameInterface for RenderGame {
res
}

fn continue_map_loading(&mut self) -> Result<bool, String> {
fn continue_loading(&mut self) -> Result<bool, String> {
let containers_loaded = self.check_required_containers_loaded();
self.map
.continue_loading()
.map(|m| m.is_some())
.map(|m| m.is_some() && containers_loaded)
.map_err(|err| err.to_string())
}

Expand Down
Loading

0 comments on commit 6a2b366

Please sign in to comment.