mirror of
https://gitlab.com/veloren/veloren
synced 2026-08-16 16:26:07 -04:00
Merge branch 'imbris/character-location' into 'master'
Use `Content` for character location names See merge request veloren/veloren!5412
This commit is contained in:
commit
c424a2a8fe
12 changed files with 102 additions and 74 deletions
|
|
@ -1,5 +1,6 @@
|
|||
hard_tabs = false
|
||||
edition = "2024"
|
||||
style_edition = "2024"
|
||||
hard_tabs = false
|
||||
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = true
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ pub struct Client {
|
|||
pending_invites: HashSet<Uid>,
|
||||
// The pending trade the client is involved in, and it's id
|
||||
pending_trade: Option<(TradeId, PendingTrade, Option<SitePrices>)>,
|
||||
waypoint: Option<String>,
|
||||
waypoint: Option<Content>,
|
||||
|
||||
network: Option<Network>,
|
||||
participant: Option<Participant>,
|
||||
|
|
@ -367,7 +367,7 @@ pub struct Client {
|
|||
/// additional state to handle UI.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CharacterList {
|
||||
pub characters: Vec<CharacterItem>,
|
||||
pub characters: Vec<CharacterItem<Content>>,
|
||||
pub loading: bool,
|
||||
}
|
||||
|
||||
|
|
@ -2315,7 +2315,7 @@ impl Client {
|
|||
}))
|
||||
}
|
||||
|
||||
pub fn waypoint(&self) -> &Option<String> { &self.waypoint }
|
||||
pub fn waypoint(&self) -> Option<&Content> { self.waypoint.as_ref() }
|
||||
|
||||
pub fn set_battle_mode(&mut self, battle_mode: BattleMode) {
|
||||
self.send_msg(ClientGeneral::SetBattleMode(battle_mode));
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ pub enum ServerGeneral {
|
|||
/// Result of loading character data
|
||||
CharacterDataLoadResult(Result<UpdateCharacterMetadata, String>),
|
||||
/// A list of characters belonging to the a authenticated player was sent
|
||||
CharacterListUpdate(Vec<CharacterItem>),
|
||||
CharacterListUpdate(Vec<CharacterItem<Content>>),
|
||||
/// An error occurred while creating or deleting a character
|
||||
CharacterActionError(String),
|
||||
/// A new character was created
|
||||
|
|
@ -304,7 +304,7 @@ pub enum InviteAnswer {
|
|||
/// not relevant to rendering.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Notification {
|
||||
WaypointSaved { location_name: String },
|
||||
WaypointSaved { location_name: Content },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -21,11 +21,10 @@ pub struct Character {
|
|||
/// Data needed to render a single character item in the character list
|
||||
/// presented during character selection.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct CharacterItem {
|
||||
pub struct CharacterItem<Location> {
|
||||
pub character: Character,
|
||||
pub body: comp::Body,
|
||||
pub hardcore: bool,
|
||||
pub inventory: Inventory,
|
||||
// this string changes between database representation and human readable name in server.tick
|
||||
pub location: Option<String>,
|
||||
pub location: Option<Location>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3878,6 +3878,8 @@ fn handle_set_waypoint(
|
|||
);
|
||||
|
||||
if let Some(location_name) = location_name {
|
||||
#[expect(deprecated, reason = "i18n location name")]
|
||||
let location_name = Content::legacy(location_name);
|
||||
server.notify_client(
|
||||
target,
|
||||
ServerGeneral::Notification(Notification::WaypointSaved { location_name }),
|
||||
|
|
|
|||
|
|
@ -759,22 +759,32 @@ impl Server {
|
|||
/// Get a reference to the Chat Cache
|
||||
pub fn chat_cache(&self) -> &ChatCache { &self.chat_cache }
|
||||
|
||||
fn parse_locations(&self, character_list_data: &mut [CharacterItem]) {
|
||||
character_list_data.iter_mut().for_each(|c| {
|
||||
let name = c
|
||||
.location
|
||||
.as_ref()
|
||||
.and_then(|s| {
|
||||
persistence::parse_waypoint(s)
|
||||
.ok()
|
||||
.and_then(|(waypoint, _)| waypoint.map(|w| w.get_pos()))
|
||||
})
|
||||
.and_then(|wpos| {
|
||||
self.world
|
||||
.get_location_name(self.index.as_index_ref(), wpos.xy().as_::<i32>())
|
||||
});
|
||||
c.location = name;
|
||||
});
|
||||
/// Converts positions to location names for a list of characters.
|
||||
fn get_location_names(
|
||||
&self,
|
||||
character_list: Vec<CharacterItem<Vec3<f32>>>,
|
||||
) -> Vec<CharacterItem<Content>> {
|
||||
character_list
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
#[expect(deprecated, reason = "i18n location name")]
|
||||
let name = c
|
||||
.location
|
||||
.as_ref()
|
||||
.and_then(|wpos| {
|
||||
self.world
|
||||
.get_location_name(self.index.as_index_ref(), wpos.xy().as_::<i32>())
|
||||
})
|
||||
.map(Content::legacy);
|
||||
CharacterItem {
|
||||
character: c.character,
|
||||
body: c.body,
|
||||
hardcore: c.hardcore,
|
||||
inventory: c.inventory,
|
||||
location: name,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Execute a single server tick, handle input and update the game state by
|
||||
|
|
@ -1061,24 +1071,22 @@ impl Server {
|
|||
CharacterUpdaterMessage::CharacterScreenResponse(response) => {
|
||||
match response.response_kind {
|
||||
CharacterScreenResponseKind::CharacterList(result) => match result {
|
||||
Ok(mut character_list_data) => {
|
||||
self.parse_locations(&mut character_list_data);
|
||||
self.notify_client(
|
||||
response.target_entity,
|
||||
ServerGeneral::CharacterListUpdate(character_list_data),
|
||||
)
|
||||
},
|
||||
Ok(list) => self.notify_client(
|
||||
response.target_entity,
|
||||
ServerGeneral::CharacterListUpdate(self.get_location_names(list)),
|
||||
),
|
||||
Err(error) => self.notify_client(
|
||||
response.target_entity,
|
||||
ServerGeneral::CharacterActionError(error.to_string()),
|
||||
),
|
||||
},
|
||||
CharacterScreenResponseKind::CharacterCreation(result) => match result {
|
||||
Ok((character_id, mut list)) => {
|
||||
self.parse_locations(&mut list);
|
||||
Ok((character_id, list)) => {
|
||||
self.notify_client(
|
||||
response.target_entity,
|
||||
ServerGeneral::CharacterListUpdate(list),
|
||||
ServerGeneral::CharacterListUpdate(
|
||||
self.get_location_names(list),
|
||||
),
|
||||
);
|
||||
self.notify_client(
|
||||
response.target_entity,
|
||||
|
|
@ -1091,11 +1099,12 @@ impl Server {
|
|||
),
|
||||
},
|
||||
CharacterScreenResponseKind::CharacterEdit(result) => match result {
|
||||
Ok((character_id, mut list)) => {
|
||||
self.parse_locations(&mut list);
|
||||
Ok((character_id, list)) => {
|
||||
self.notify_client(
|
||||
response.target_entity,
|
||||
ServerGeneral::CharacterListUpdate(list),
|
||||
ServerGeneral::CharacterListUpdate(
|
||||
self.get_location_names(list),
|
||||
),
|
||||
);
|
||||
self.notify_client(
|
||||
response.target_entity,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ extern crate rusqlite;
|
|||
|
||||
use super::{error::PersistenceError, models::*};
|
||||
use crate::{
|
||||
comp::{self, Inventory},
|
||||
comp::{self, Inventory, MapMarker, Waypoint},
|
||||
persistence::{
|
||||
EditableComponents, PersistedComponents,
|
||||
character::conversions::{
|
||||
|
|
@ -44,8 +44,6 @@ mod conversions;
|
|||
|
||||
pub(crate) type EntityId = i64;
|
||||
|
||||
pub(crate) use conversions::convert_waypoint_from_database_json as parse_waypoint;
|
||||
|
||||
const CHARACTER_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.character";
|
||||
const INVENTORY_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.inventory";
|
||||
const LOADOUT_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.loadout";
|
||||
|
|
@ -124,6 +122,24 @@ pub fn load_items(connection: &Connection, root: i64) -> Result<Vec<Item>, Persi
|
|||
Ok(items)
|
||||
}
|
||||
|
||||
fn convert_waypoint_or_warn(
|
||||
waypoint_json: Option<&str>,
|
||||
char_id: CharacterId,
|
||||
) -> (Option<Waypoint>, Option<MapMarker>) {
|
||||
match waypoint_json.map(convert_waypoint_from_database_json) {
|
||||
Some(Ok(w)) => w,
|
||||
Some(Err(e)) => {
|
||||
warn!(
|
||||
"Error reading waypoint from database for character ID
|
||||
{}, error: {}",
|
||||
char_id.0, e
|
||||
);
|
||||
(None, None)
|
||||
},
|
||||
None => (None, None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Load stored data for a character.
|
||||
///
|
||||
/// After first logging in, and after a character is selected, we fetch this
|
||||
|
|
@ -175,22 +191,8 @@ pub fn load_character_data(
|
|||
},
|
||||
)?;
|
||||
|
||||
let (char_waypoint, char_map_marker) = match character_data
|
||||
.waypoint
|
||||
.as_ref()
|
||||
.map(|x| convert_waypoint_from_database_json(x))
|
||||
{
|
||||
Some(Ok(w)) => w,
|
||||
Some(Err(e)) => {
|
||||
warn!(
|
||||
"Error reading waypoint from database for character ID
|
||||
{}, error: {}",
|
||||
char_id.0, e
|
||||
);
|
||||
(None, None)
|
||||
},
|
||||
None => (None, None),
|
||||
};
|
||||
let (char_waypoint, char_map_marker) =
|
||||
convert_waypoint_or_warn(character_data.waypoint.as_deref(), char_id);
|
||||
|
||||
let mut stmt = connection.prepare_cached(
|
||||
"
|
||||
|
|
@ -395,13 +397,19 @@ pub fn load_character_list(player_uuid_: &str, connection: &Connection) -> Chara
|
|||
|
||||
let (recipe_book, _) = convert_recipe_book_from_database_items(&recipe_book_items)?;
|
||||
|
||||
let (char_waypoint, _char_map_marker) = convert_waypoint_or_warn(
|
||||
character_data.waypoint.as_deref(),
|
||||
CharacterId(character_data.character_id),
|
||||
);
|
||||
let location = char_waypoint.map(|w| w.get_pos());
|
||||
|
||||
Ok(CharacterItem {
|
||||
character: char,
|
||||
body: char_body,
|
||||
hardcore: hardcore.is_some(),
|
||||
inventory: Inventory::with_loadout(loadout, char_body)
|
||||
.with_recipe_book(recipe_book),
|
||||
location: character_data.waypoint.as_ref().cloned(),
|
||||
location,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ use crossbeam_channel::{self, TryIter};
|
|||
use rusqlite::Connection;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use tracing::{debug, error};
|
||||
use vek::Vec3;
|
||||
|
||||
pub(crate) type CharacterListResult = Result<Vec<CharacterItem>, PersistenceError>;
|
||||
pub(crate) type CharacterCreationResult =
|
||||
Result<(CharacterId, Vec<CharacterItem>), PersistenceError>;
|
||||
pub(crate) type CharacterEditResult = Result<(CharacterId, Vec<CharacterItem>), PersistenceError>;
|
||||
type CharacterList = Vec<CharacterItem<Vec3<f32>>>;
|
||||
pub(crate) type CharacterListResult = Result<CharacterList, PersistenceError>;
|
||||
pub(crate) type CharacterCreationResult = Result<(CharacterId, CharacterList), PersistenceError>;
|
||||
pub(crate) type CharacterEditResult = Result<(CharacterId, CharacterList), PersistenceError>;
|
||||
pub(crate) type CharacterDataResult =
|
||||
Result<(PersistedComponents, UpdateCharacterMetadata), PersistenceError>;
|
||||
type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind);
|
||||
|
|
|
|||
|
|
@ -27,9 +27,6 @@ use std::{
|
|||
};
|
||||
use tracing::info;
|
||||
|
||||
// re-export waypoint parser for use to look up location names in character list
|
||||
pub(crate) use character::parse_waypoint;
|
||||
|
||||
/// A struct of the components that are persisted to the DB for each character
|
||||
#[derive(Debug)]
|
||||
pub struct PersistedComponents {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use crate::client::Client;
|
||||
use common::{
|
||||
comp::{CharacterState, PhysicsState, Player, Pos, Vel, Waypoint, WaypointArea},
|
||||
comp::{CharacterState, Content, PhysicsState, Player, Pos, Vel, Waypoint, WaypointArea},
|
||||
resources::Time,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
|
|
@ -83,6 +83,8 @@ impl<'a> System<'a> for Sys {
|
|||
);
|
||||
|
||||
if let Some(location_name) = location_name {
|
||||
#[expect(deprecated, reason = "i18n location name")]
|
||||
let location_name = Content::legacy(location_name);
|
||||
client.send_fallible(ServerGeneral::Notification(
|
||||
Notification::WaypointSaved { location_name },
|
||||
));
|
||||
|
|
|
|||
|
|
@ -1244,9 +1244,12 @@ impl Widget for Diary<'_> {
|
|||
CharacterStat::Waypoint => self
|
||||
.client
|
||||
.waypoint()
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "Unknown".to_string()),
|
||||
.map(|c| self.localized_strings.get_content(c))
|
||||
.unwrap_or_else(|| {
|
||||
self.localized_strings
|
||||
.get_msg("char_selection-uncanny_valley")
|
||||
.into_owned()
|
||||
}),
|
||||
CharacterStat::Hitpoints => format!("{}", self.health.maximum() as u32),
|
||||
CharacterStat::Energy => format!("{}", self.energy.maximum() as u32),
|
||||
CharacterStat::Poise => format!("{}", self.poise.maximum() as u32),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ use common::{
|
|||
terrain::TerrainChunkSize,
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use common_i18n::Content;
|
||||
use common_net::msg::world_msg::SiteId;
|
||||
use i18n::{Localization, LocalizationHandle};
|
||||
use rand::{RngExt, rng};
|
||||
|
|
@ -667,9 +668,9 @@ impl Controls {
|
|||
i18n.get_msg(
|
||||
"char_selection-uncanny_valley",
|
||||
)
|
||||
.to_string()
|
||||
.into_owned()
|
||||
},
|
||||
|s| s.clone(),
|
||||
|c| i18n.get_content(c),
|
||||
))
|
||||
.into(),
|
||||
]),
|
||||
|
|
@ -1760,7 +1761,12 @@ impl Controls {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
events: &mut Vec<Event>,
|
||||
characters: &[CharacterItem<Content>],
|
||||
) {
|
||||
match message {
|
||||
Message::Back => {
|
||||
if matches!(&self.mode, Mode::CreateOrEdit { .. }) {
|
||||
|
|
@ -2050,7 +2056,7 @@ impl Controls {
|
|||
/// Get the character to display
|
||||
pub fn display_body_inventory<'a>(
|
||||
&'a self,
|
||||
characters: &'a [CharacterItem],
|
||||
characters: &'a [CharacterItem<Content>],
|
||||
) -> Option<(comp::Body, &'a Inventory)> {
|
||||
match &self.mode {
|
||||
Mode::Select { .. } => self
|
||||
|
|
@ -2133,7 +2139,7 @@ impl CharSelectionUi {
|
|||
|
||||
pub fn display_body_inventory<'a>(
|
||||
&'a self,
|
||||
characters: &'a [CharacterItem],
|
||||
characters: &'a [CharacterItem<Content>],
|
||||
) -> Option<(comp::Body, &'a Inventory)> {
|
||||
self.controls.display_body_inventory(characters)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue