mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Persistence of auxiliary abilities.
This commit is contained in:
parent
d86692c4fe
commit
4c3771a1a0
@ -62,12 +62,11 @@ impl Default for ActiveAbilities {
|
||||
}
|
||||
|
||||
impl ActiveAbilities {
|
||||
pub fn new(_inv: Option<&Inventory>, _skill_set: Option<&SkillSet>) -> Self {
|
||||
// Maybe hook into loading saved variants when they exist here?
|
||||
// let mut pool = Self::default();
|
||||
// pool.auto_update(inv, skill_set);
|
||||
// pool
|
||||
Self::default()
|
||||
pub fn new(auxiliary_sets: HashMap<AuxiliaryKey, [AuxiliaryAbility; MAX_ABILITIES]>) -> Self {
|
||||
ActiveAbilities {
|
||||
auxiliary_sets,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_ability(
|
||||
|
@ -115,6 +115,7 @@ pub enum ServerEvent {
|
||||
comp::Inventory,
|
||||
Option<comp::Waypoint>,
|
||||
Vec<(comp::Pet, comp::Body, comp::Stats)>,
|
||||
comp::ActiveAbilities,
|
||||
),
|
||||
},
|
||||
ExitIngame {
|
||||
|
@ -69,7 +69,15 @@ pub fn create_character(
|
||||
entity,
|
||||
player_uuid,
|
||||
character_alias,
|
||||
(body, stats, skill_set, inventory, waypoint, Vec::new()),
|
||||
(
|
||||
body,
|
||||
stats,
|
||||
skill_set,
|
||||
inventory,
|
||||
waypoint,
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ pub fn handle_loaded_character_data(
|
||||
comp::Inventory,
|
||||
Option<comp::Waypoint>,
|
||||
Vec<(comp::Pet, comp::Body, comp::Stats)>,
|
||||
comp::ActiveAbilities,
|
||||
),
|
||||
) {
|
||||
server
|
||||
|
@ -202,6 +202,7 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
||||
Some(presence),
|
||||
Some(skill_set),
|
||||
Some(inventory),
|
||||
Some(active_abilities),
|
||||
Some(player_uid),
|
||||
Some(player_info),
|
||||
mut character_updater,
|
||||
@ -210,6 +211,9 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
||||
state.read_storage::<Presence>().get(entity),
|
||||
state.read_storage::<comp::SkillSet>().get(entity),
|
||||
state.read_storage::<comp::Inventory>().get(entity),
|
||||
state
|
||||
.read_storage::<comp::ability::ActiveAbilities>()
|
||||
.get(entity),
|
||||
state.read_storage::<Uid>().get(entity),
|
||||
state.read_storage::<comp::Player>().get(entity),
|
||||
state.ecs().fetch_mut::<CharacterUpdater>(),
|
||||
@ -251,7 +255,13 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
||||
|
||||
character_updater.add_pending_logout_update(
|
||||
char_id,
|
||||
(skill_set.clone(), inventory.clone(), pets, waypoint),
|
||||
(
|
||||
skill_set.clone(),
|
||||
inventory.clone(),
|
||||
pets,
|
||||
waypoint,
|
||||
active_abilities.clone(),
|
||||
),
|
||||
);
|
||||
},
|
||||
PresenceKind::Spectator => { /* Do nothing, spectators do not need persisting */ },
|
||||
|
12
server/src/migrations/V46__ability_sets.sql
Normal file
12
server/src/migrations/V46__ability_sets.sql
Normal file
@ -0,0 +1,12 @@
|
||||
-- Creates new ability_set table
|
||||
CREATE TABLE "ability_set" (
|
||||
"entity_id" INT NOT NULL,
|
||||
"ability_sets" TEXT NOT NULL,
|
||||
PRIMARY KEY("entity_id"),
|
||||
FOREIGN KEY("entity_id") REFERENCES "character"("character_id")
|
||||
);
|
||||
|
||||
-- Inserts starting ability sets for everyone
|
||||
INSERT INTO ability_set
|
||||
SELECT c.character_id, 'Empty'
|
||||
FROM character c
|
@ -8,10 +8,10 @@ extern crate rusqlite;
|
||||
|
||||
use super::{error::PersistenceError, models::*};
|
||||
use crate::{
|
||||
comp,
|
||||
comp::Inventory,
|
||||
comp::{self, Inventory},
|
||||
persistence::{
|
||||
character::conversions::{
|
||||
convert_active_abilities_from_database, convert_active_abilities_to_database,
|
||||
convert_body_from_database, convert_body_to_database_json,
|
||||
convert_character_from_database, convert_inventory_from_database_items,
|
||||
convert_items_to_database_items, convert_loadout_from_database_items,
|
||||
@ -234,6 +234,20 @@ pub fn load_character_data(
|
||||
})
|
||||
.collect::<Vec<(comp::Pet, comp::Body, comp::Stats)>>();
|
||||
|
||||
let mut stmt = connection.prepare_cached(
|
||||
"
|
||||
SELECT ability_sets
|
||||
FROM ability_set
|
||||
WHERE entity_id = ?1",
|
||||
)?;
|
||||
|
||||
let ability_set_data = stmt.query_row(&[char_id], |row| {
|
||||
Ok(AbilitySets {
|
||||
entity_id: char_id,
|
||||
ability_sets: row.get(0)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
convert_body_from_database(&body_data.variant, &body_data.body_data)?,
|
||||
convert_stats_from_database(character_data.alias),
|
||||
@ -246,6 +260,7 @@ pub fn load_character_data(
|
||||
)?,
|
||||
char_waypoint,
|
||||
pets,
|
||||
convert_active_abilities_from_database(&ability_set_data),
|
||||
))
|
||||
}
|
||||
|
||||
@ -327,14 +342,14 @@ pub fn create_character(
|
||||
uuid: &str,
|
||||
character_alias: &str,
|
||||
persisted_components: PersistedComponents,
|
||||
transactionn: &mut Transaction,
|
||||
transaction: &mut Transaction,
|
||||
) -> CharacterCreationResult {
|
||||
check_character_limit(uuid, transactionn)?;
|
||||
check_character_limit(uuid, transaction)?;
|
||||
|
||||
let (body, _stats, skill_set, inventory, waypoint, _) = persisted_components;
|
||||
let (body, _stats, skill_set, inventory, waypoint, _, active_abilities) = persisted_components;
|
||||
|
||||
// Fetch new entity IDs for character, inventory and loadout
|
||||
let mut new_entity_ids = get_new_entity_ids(transactionn, |next_id| next_id + 3)?;
|
||||
let mut new_entity_ids = get_new_entity_ids(transaction, |next_id| next_id + 3)?;
|
||||
|
||||
// Create pseudo-container items for character
|
||||
let character_id = new_entity_ids.next().unwrap();
|
||||
@ -365,7 +380,7 @@ pub fn create_character(
|
||||
},
|
||||
];
|
||||
|
||||
let mut stmt = transactionn.prepare_cached(
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
INSERT INTO item (item_id,
|
||||
parent_container_item_id,
|
||||
@ -386,7 +401,7 @@ pub fn create_character(
|
||||
}
|
||||
drop(stmt);
|
||||
|
||||
let mut stmt = transactionn.prepare_cached(
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
INSERT INTO body (body_id,
|
||||
variant,
|
||||
@ -402,7 +417,7 @@ pub fn create_character(
|
||||
])?;
|
||||
drop(stmt);
|
||||
|
||||
let mut stmt = transactionn.prepare_cached(
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
INSERT INTO character (character_id,
|
||||
player_uuid,
|
||||
@ -421,7 +436,7 @@ pub fn create_character(
|
||||
|
||||
let db_skill_groups = convert_skill_groups_to_database(character_id, skill_set.skill_groups());
|
||||
|
||||
let mut stmt = transactionn.prepare_cached(
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
INSERT INTO skill_group (entity_id,
|
||||
skill_group_kind,
|
||||
@ -444,10 +459,25 @@ pub fn create_character(
|
||||
}
|
||||
drop(stmt);
|
||||
|
||||
let ability_sets = convert_active_abilities_to_database(character_id, &active_abilities);
|
||||
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
INSERT INTO skill_group (entity_id,
|
||||
ability_sets)
|
||||
VALUES (?1, ?2)",
|
||||
)?;
|
||||
|
||||
stmt.execute(&[
|
||||
&character_id as &dyn ToSql,
|
||||
&ability_sets.ability_sets as &dyn ToSql,
|
||||
])?;
|
||||
drop(stmt);
|
||||
|
||||
// Insert default inventory and loadout item records
|
||||
let mut inserts = Vec::new();
|
||||
|
||||
get_new_entity_ids(transactionn, |mut next_id| {
|
||||
get_new_entity_ids(transaction, |mut next_id| {
|
||||
let inserts_ = convert_items_to_database_items(
|
||||
loadout_container_id,
|
||||
&inventory,
|
||||
@ -458,7 +488,7 @@ pub fn create_character(
|
||||
next_id
|
||||
})?;
|
||||
|
||||
let mut stmt = transactionn.prepare_cached(
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
INSERT INTO item (item_id,
|
||||
parent_container_item_id,
|
||||
@ -479,7 +509,7 @@ pub fn create_character(
|
||||
}
|
||||
drop(stmt);
|
||||
|
||||
load_character_list(uuid, transactionn).map(|list| (character_id, list))
|
||||
load_character_list(uuid, transaction).map(|list| (character_id, list))
|
||||
}
|
||||
|
||||
pub fn edit_character(
|
||||
@ -579,6 +609,17 @@ pub fn delete_character(
|
||||
delete_pets(transaction, char_id, Rc::new(pet_ids))?;
|
||||
}
|
||||
|
||||
// Delete ability sets
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
DELETE
|
||||
FROM ability_set
|
||||
WHERE entity_id = ?1",
|
||||
)?;
|
||||
|
||||
stmt.execute(&[&char_id])?;
|
||||
drop(stmt);
|
||||
|
||||
// Delete character
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
@ -892,6 +933,7 @@ pub fn update(
|
||||
inventory: comp::Inventory,
|
||||
pets: Vec<PetPersistenceData>,
|
||||
char_waypoint: Option<comp::Waypoint>,
|
||||
active_abilities: comp::ability::ActiveAbilities,
|
||||
transaction: &mut Transaction,
|
||||
) -> Result<(), PersistenceError> {
|
||||
// Run pet persistence
|
||||
@ -1035,5 +1077,27 @@ pub fn update(
|
||||
)));
|
||||
}
|
||||
|
||||
let ability_sets = convert_active_abilities_to_database(char_id, &active_abilities);
|
||||
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
UPDATE ability_set
|
||||
SET ability_sets = ?1
|
||||
WHERE entity_id = ?2
|
||||
",
|
||||
)?;
|
||||
|
||||
let ability_sets_count = stmt.execute(&[
|
||||
&ability_sets.ability_sets as &dyn ToSql,
|
||||
&char_id as &dyn ToSql,
|
||||
])?;
|
||||
|
||||
if ability_sets_count != 1 {
|
||||
return Err(PersistenceError::OtherError(format!(
|
||||
"Error updating ability_set table for char_id {}",
|
||||
char_id
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::persistence::{
|
||||
character::EntityId,
|
||||
models::{Character, Item, SkillGroup},
|
||||
models::{AbilitySets, Character, Item, SkillGroup},
|
||||
};
|
||||
|
||||
use crate::persistence::{
|
||||
@ -602,3 +602,29 @@ pub fn convert_skill_groups_to_database<'a, I: Iterator<Item = &'a skillset::Ski
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn convert_active_abilities_to_database(
|
||||
entity_id: CharacterId,
|
||||
active_abilities: &ability::ActiveAbilities,
|
||||
) -> AbilitySets {
|
||||
let ability_sets = active_abilities
|
||||
.auxiliary_sets
|
||||
.iter()
|
||||
.filter_map(|set| serde_json::to_string(&set).ok())
|
||||
.collect::<Vec<String>>();
|
||||
AbilitySets {
|
||||
entity_id,
|
||||
ability_sets: serde_json::to_string(&ability_sets).unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_active_abilities_from_database(
|
||||
ability_sets: &AbilitySets,
|
||||
) -> ability::ActiveAbilities {
|
||||
let ability_sets = core::iter::once(ability_sets)
|
||||
.flat_map(|sets| serde_json::from_str::<Vec<String>>(&sets.ability_sets))
|
||||
.flatten()
|
||||
.filter_map(|set| serde_json::from_str::<(ability::AuxiliaryKey, [ability::AuxiliaryAbility; ability::MAX_ABILITIES])>(&set).ok())
|
||||
.collect::<HashMap<ability::AuxiliaryKey, [ability::AuxiliaryAbility; ability::MAX_ABILITIES]>>();
|
||||
ability::ActiveAbilities::new(ability_sets)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub type CharacterUpdateData = (
|
||||
comp::Inventory,
|
||||
Vec<PetPersistenceData>,
|
||||
Option<comp::Waypoint>,
|
||||
comp::ability::ActiveAbilities,
|
||||
);
|
||||
|
||||
pub type PetPersistenceData = (comp::Pet, comp::Body, comp::Stats);
|
||||
@ -330,21 +331,25 @@ impl CharacterUpdater {
|
||||
&'a comp::Inventory,
|
||||
Vec<PetPersistenceData>,
|
||||
Option<&'a comp::Waypoint>,
|
||||
&'a comp::ability::ActiveAbilities,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
let updates = updates
|
||||
.map(|(character_id, skill_set, inventory, pets, waypoint)| {
|
||||
(
|
||||
character_id,
|
||||
.map(
|
||||
|(character_id, skill_set, inventory, pets, waypoint, active_abilities)| {
|
||||
(
|
||||
skill_set.clone(),
|
||||
inventory.clone(),
|
||||
pets,
|
||||
waypoint.cloned(),
|
||||
),
|
||||
)
|
||||
})
|
||||
character_id,
|
||||
(
|
||||
skill_set.clone(),
|
||||
inventory.clone(),
|
||||
pets,
|
||||
waypoint.cloned(),
|
||||
active_abilities.clone(),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
.chain(self.pending_logout_updates.drain())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -382,18 +387,19 @@ fn execute_batch_update(
|
||||
let mut transaction = connection.connection.transaction()?;
|
||||
transaction.set_drop_behavior(DropBehavior::Rollback);
|
||||
trace!("Transaction started for character batch update");
|
||||
updates
|
||||
.into_iter()
|
||||
.try_for_each(|(character_id, (stats, inventory, pets, waypoint))| {
|
||||
updates.into_iter().try_for_each(
|
||||
|(character_id, (stats, inventory, pets, waypoint, active_abilities))| {
|
||||
super::character::update(
|
||||
character_id,
|
||||
stats,
|
||||
inventory,
|
||||
pets,
|
||||
waypoint,
|
||||
active_abilities,
|
||||
&mut transaction,
|
||||
)
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
transaction.commit()?;
|
||||
|
||||
trace!("Commit for character batch update completed");
|
||||
|
@ -29,6 +29,7 @@ pub type PersistedComponents = (
|
||||
comp::Inventory,
|
||||
Option<comp::Waypoint>,
|
||||
Vec<PetPersistenceData>,
|
||||
comp::ActiveAbilities,
|
||||
);
|
||||
|
||||
pub type EditableComponents = (comp::Body,);
|
||||
|
@ -35,3 +35,8 @@ pub struct Pet {
|
||||
pub body_variant: String,
|
||||
pub body_data: String,
|
||||
}
|
||||
|
||||
pub struct AbilitySets {
|
||||
pub entity_id: i64,
|
||||
pub ability_sets: String,
|
||||
}
|
||||
|
@ -238,10 +238,8 @@ impl StateExt for State {
|
||||
.unwrap_or(0),
|
||||
))
|
||||
.with(stats)
|
||||
.with(comp::ActiveAbilities::new(
|
||||
Some(&inventory),
|
||||
Some(&skill_set),
|
||||
))
|
||||
// TODO: Figure out way to have this start with sane defaults
|
||||
.with(comp::ActiveAbilities::default())
|
||||
.with(skill_set)
|
||||
.maybe_with(health)
|
||||
.with(poise)
|
||||
@ -500,7 +498,7 @@ impl StateExt for State {
|
||||
}
|
||||
|
||||
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) {
|
||||
let (body, stats, skill_set, inventory, waypoint, pets) = components;
|
||||
let (body, stats, skill_set, inventory, waypoint, pets, active_abilities) = components;
|
||||
|
||||
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||
// Notify clients of a player list update
|
||||
@ -530,10 +528,7 @@ impl StateExt for State {
|
||||
self.write_component_ignore_entity_dead(entity, comp::Energy::new(body, energy_level));
|
||||
self.write_component_ignore_entity_dead(entity, comp::Poise::new(body));
|
||||
self.write_component_ignore_entity_dead(entity, stats);
|
||||
self.write_component_ignore_entity_dead(
|
||||
entity,
|
||||
comp::ActiveAbilities::new(Some(&inventory), Some(&skill_set)),
|
||||
);
|
||||
self.write_component_ignore_entity_dead(entity, active_abilities);
|
||||
self.write_component_ignore_entity_dead(entity, skill_set);
|
||||
self.write_component_ignore_entity_dead(entity, inventory);
|
||||
self.write_component_ignore_entity_dead(
|
||||
|
@ -2,7 +2,7 @@ use crate::{persistence::character_updater, presence::Presence, sys::SysSchedule
|
||||
use common::{
|
||||
comp::{
|
||||
pet::{is_tameable, Pet},
|
||||
Alignment, Body, Inventory, SkillSet, Stats, Waypoint,
|
||||
ActiveAbilities, Alignment, Body, Inventory, SkillSet, Stats, Waypoint,
|
||||
},
|
||||
uid::Uid,
|
||||
};
|
||||
@ -25,6 +25,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Waypoint>,
|
||||
ReadStorage<'a, Pet>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, ActiveAbilities>,
|
||||
WriteExpect<'a, character_updater::CharacterUpdater>,
|
||||
Write<'a, SysScheduler<Self>>,
|
||||
);
|
||||
@ -45,6 +46,7 @@ impl<'a> System<'a> for Sys {
|
||||
player_waypoints,
|
||||
pets,
|
||||
stats,
|
||||
active_abilities,
|
||||
mut updater,
|
||||
mut scheduler,
|
||||
): Self::SystemData,
|
||||
@ -57,11 +59,18 @@ impl<'a> System<'a> for Sys {
|
||||
&player_inventories,
|
||||
&uids,
|
||||
player_waypoints.maybe(),
|
||||
&active_abilities,
|
||||
)
|
||||
.join()
|
||||
.filter_map(
|
||||
|(presence, skill_set, inventory, player_uid, waypoint)| match presence.kind
|
||||
{
|
||||
|(
|
||||
presence,
|
||||
skill_set,
|
||||
inventory,
|
||||
player_uid,
|
||||
waypoint,
|
||||
active_abilities,
|
||||
)| match presence.kind {
|
||||
PresenceKind::Character(id) => {
|
||||
let pets = (&alignments, &bodies, &stats, &pets)
|
||||
.join()
|
||||
@ -78,7 +87,7 @@ impl<'a> System<'a> for Sys {
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some((id, skill_set, inventory, pets, waypoint))
|
||||
Some((id, skill_set, inventory, pets, waypoint, active_abilities))
|
||||
},
|
||||
PresenceKind::Spectator => None,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user