veloren_server/persistence/
character_loader.rs1use crate::persistence::{
2 ConnectionMode, DatabaseSettings, PersistedComponents,
3 character::{load_character_data, load_character_list},
4 error::PersistenceError,
5 establish_connection,
6};
7use common::{
8 character::{CharacterId, CharacterItem},
9 event::UpdateCharacterMetadata,
10};
11use crossbeam_channel::{self, TryIter};
12use rusqlite::Connection;
13use std::sync::{Arc, RwLock};
14use tracing::{debug, error};
15use vek::Vec3;
16
17type CharacterList = Vec<CharacterItem<Vec3<f32>>>;
18pub(crate) type CharacterListResult = Result<CharacterList, PersistenceError>;
19pub(crate) type CharacterCreationResult = Result<(CharacterId, CharacterList), PersistenceError>;
20pub(crate) type CharacterEditResult = Result<(CharacterId, CharacterList), PersistenceError>;
21pub(crate) type CharacterDataResult =
22 Result<(PersistedComponents, UpdateCharacterMetadata), PersistenceError>;
23type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind);
24
25enum CharacterLoaderRequestKind {
27 LoadCharacterList {
28 player_uuid: String,
29 },
30 LoadCharacterData {
31 player_uuid: String,
32 character_id: CharacterId,
33 },
34}
35
36#[derive(Debug)]
37pub enum CharacterUpdaterMessage {
38 CharacterScreenResponse(CharacterScreenResponse),
39 DatabaseBatchCompletion(u64),
40}
41
42#[derive(Debug)]
45pub struct CharacterScreenResponse {
46 pub target_entity: specs::Entity,
47 pub response_kind: CharacterScreenResponseKind,
48}
49
50impl CharacterScreenResponse {
51 pub fn is_err(&self) -> bool {
52 matches!(
53 &self.response_kind,
54 CharacterScreenResponseKind::CharacterData(box Err(_))
55 | CharacterScreenResponseKind::CharacterList(Err(_))
56 | CharacterScreenResponseKind::CharacterCreation(Err(_))
57 )
58 }
59}
60
61#[derive(Debug)]
62pub enum CharacterScreenResponseKind {
63 CharacterList(CharacterListResult),
64 CharacterData(Box<CharacterDataResult>),
65 CharacterCreation(CharacterCreationResult),
66 CharacterEdit(CharacterEditResult),
67}
68
69pub struct CharacterLoader {
82 update_rx: crossbeam_channel::Receiver<CharacterUpdaterMessage>,
83 update_tx: crossbeam_channel::Sender<CharacterLoaderRequest>,
84}
85
86impl CharacterLoader {
87 pub fn new(settings: Arc<RwLock<DatabaseSettings>>) -> Result<Self, PersistenceError> {
88 let (update_tx, internal_rx) = crossbeam_channel::unbounded::<CharacterLoaderRequest>();
89 let (internal_tx, update_rx) = crossbeam_channel::unbounded::<CharacterUpdaterMessage>();
90
91 let builder = std::thread::Builder::new().name("persistence_loader".into());
92 builder
93 .spawn(move || {
94 let mut conn =
100 establish_connection(&settings.read().unwrap(), ConnectionMode::ReadOnly);
101
102 for request in internal_rx {
103 conn.update_log_mode(&settings);
104
105 let response = CharacterLoader::process_request(request, &conn);
106 debug!("Processing complete");
107 if let Err(e) = internal_tx.send(response) {
108 error!(?e, "Could not send character loader response");
109 }
110 debug!("Sent character loader response");
111 }
112 })
113 .unwrap();
114
115 Ok(Self {
116 update_rx,
117 update_tx,
118 })
119 }
120
121 fn process_request(
125 request: CharacterLoaderRequest,
126 connection: &Connection,
127 ) -> CharacterUpdaterMessage {
128 let (entity, kind) = request;
129 CharacterUpdaterMessage::CharacterScreenResponse(CharacterScreenResponse {
130 target_entity: entity,
131 response_kind: match kind {
132 CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => {
133 debug!(?player_uuid, "Loading character list");
134 CharacterScreenResponseKind::CharacterList(load_character_list(
135 &player_uuid,
136 connection,
137 ))
138 },
139 CharacterLoaderRequestKind::LoadCharacterData {
140 player_uuid,
141 character_id,
142 } => {
143 debug!(?player_uuid, ?character_id, "Loading character data");
144 let result = load_character_data(player_uuid, character_id, connection);
145 if result.is_err() {
146 error!(
147 ?result,
148 "Error loading character data for character_id: {}", character_id.0
149 );
150 }
151 CharacterScreenResponseKind::CharacterData(Box::new(result))
152 },
153 },
154 })
155 }
156
157 pub fn load_character_list(&self, entity: specs::Entity, player_uuid: String) {
160 debug!(?player_uuid, "Requesting character list");
161 if let Err(e) = self
162 .update_tx
163 .send((entity, CharacterLoaderRequestKind::LoadCharacterList {
164 player_uuid,
165 }))
166 {
167 error!(?e, "Could not send character list load request");
168 }
169 }
170
171 pub fn load_character_data(
173 &self,
174 entity: specs::Entity,
175 player_uuid: String,
176 character_id: CharacterId,
177 ) {
178 debug!(?character_id, ?player_uuid, "Requesting character data");
179 if let Err(e) =
180 self.update_tx
181 .send((entity, CharacterLoaderRequestKind::LoadCharacterData {
182 player_uuid,
183 character_id,
184 }))
185 {
186 error!(?e, "Could not send character data load request");
187 }
188 }
189
190 pub fn messages(&self) -> TryIter<'_, CharacterUpdaterMessage> { self.update_rx.try_iter() }
192}