mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
parent
b11da85ff9
commit
b56919b123
@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Sneaking lets you be closer to enemies without being detected
|
||||
- Flight
|
||||
- Roll dodges melee attacks, and reduces the height of your hitbox
|
||||
- Persistent waypoints (start from the last camp fire you visited)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -84,7 +84,13 @@ pub enum ServerEvent {
|
||||
},
|
||||
UpdateCharacterData {
|
||||
entity: EcsEntity,
|
||||
components: (comp::Body, comp::Stats, comp::Inventory, comp::Loadout),
|
||||
components: (
|
||||
comp::Body,
|
||||
comp::Stats,
|
||||
comp::Inventory,
|
||||
comp::Loadout,
|
||||
Option<comp::Waypoint>,
|
||||
),
|
||||
},
|
||||
ExitIngame {
|
||||
entity: EcsEntity,
|
||||
|
@ -23,11 +23,12 @@ pub fn create_character(
|
||||
.build();
|
||||
|
||||
let inventory = Inventory::default();
|
||||
let waypoint = None;
|
||||
|
||||
character_loader.create_character(
|
||||
entity,
|
||||
player_uuid,
|
||||
character_alias,
|
||||
(body, stats, inventory, loadout),
|
||||
(body, stats, inventory, loadout, waypoint),
|
||||
);
|
||||
}
|
||||
|
@ -24,7 +24,13 @@ pub fn handle_initialize_character(
|
||||
pub fn handle_loaded_character_data(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
loaded_components: (comp::Body, comp::Stats, comp::Inventory, comp::Loadout),
|
||||
loaded_components: (
|
||||
comp::Body,
|
||||
comp::Stats,
|
||||
comp::Inventory,
|
||||
comp::Loadout,
|
||||
Option<comp::Waypoint>,
|
||||
),
|
||||
) {
|
||||
server
|
||||
.state
|
||||
|
@ -156,7 +156,9 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
||||
.read_resource::<persistence::character_updater::CharacterUpdater>(),
|
||||
) {
|
||||
if let PresenceKind::Character(character_id) = presences.kind {
|
||||
updater.update(character_id, stats, inventory, loadout);
|
||||
let waypoint_read = state.read_storage::<comp::Waypoint>();
|
||||
let waypoint = waypoint_read.get(entity);
|
||||
updater.update(character_id, stats, inventory, loadout, waypoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,13 +15,18 @@ use crate::{
|
||||
convert_character_from_database, convert_inventory_from_database_items,
|
||||
convert_items_to_database_items, convert_loadout_from_database_items,
|
||||
convert_stats_from_database, convert_stats_to_database,
|
||||
convert_waypoint_to_database_json,
|
||||
},
|
||||
character_loader::{CharacterDataResult, CharacterListResult},
|
||||
error::Error::DatabaseError,
|
||||
json_models::CharacterPosition,
|
||||
PersistedComponents,
|
||||
},
|
||||
};
|
||||
use common::character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER};
|
||||
use common::{
|
||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER},
|
||||
state::Time,
|
||||
};
|
||||
use core::ops::Range;
|
||||
use diesel::{prelude::*, sql_query, sql_types::BigInt};
|
||||
use std::sync::Arc;
|
||||
@ -83,11 +88,22 @@ pub fn load_character_data(
|
||||
.filter(schema::body::dsl::body_id.eq(char_id))
|
||||
.first::<Body>(&*connection)?;
|
||||
|
||||
let waypoint = item
|
||||
.filter(item_id.eq(char_id))
|
||||
.first::<Item>(&*connection)
|
||||
.ok()
|
||||
.and_then(|it: Item| {
|
||||
(serde_json::de::from_str::<CharacterPosition>(it.position.as_str()))
|
||||
.ok()
|
||||
.map(|charpos| comp::Waypoint::new(charpos.waypoint, Time(0.0)))
|
||||
});
|
||||
|
||||
Ok((
|
||||
convert_body_from_database(&char_body)?,
|
||||
convert_stats_from_database(&stats_data, character_data.alias),
|
||||
convert_inventory_from_database_items(&inventory_items)?,
|
||||
convert_loadout_from_database_items(&loadout_items)?,
|
||||
waypoint,
|
||||
))
|
||||
}
|
||||
|
||||
@ -157,7 +173,7 @@ pub fn create_character(
|
||||
|
||||
use schema::{body, character, stats};
|
||||
|
||||
let (body, stats, inventory, loadout) = persisted_components;
|
||||
let (body, stats, inventory, loadout, waypoint) = persisted_components;
|
||||
|
||||
// Fetch new entity IDs for character, inventory and loadout
|
||||
let mut new_entity_ids = get_new_entity_ids(connection, |next_id| next_id + 3)?;
|
||||
@ -166,13 +182,17 @@ pub fn create_character(
|
||||
let character_id = new_entity_ids.next().unwrap();
|
||||
let inventory_container_id = new_entity_ids.next().unwrap();
|
||||
let loadout_container_id = new_entity_ids.next().unwrap();
|
||||
// by default the character's position is the id in textual form
|
||||
let character_position = waypoint
|
||||
.and_then(|waypoint| serde_json::to_string(&waypoint.get_pos()).ok())
|
||||
.unwrap_or_else(|| character_id.to_string());
|
||||
let pseudo_containers = vec![
|
||||
Item {
|
||||
stack_size: 1,
|
||||
item_id: character_id,
|
||||
parent_container_item_id: WORLD_PSEUDO_CONTAINER_ID,
|
||||
item_definition_id: CHARACTER_PSEUDO_CONTAINER_DEF_ID.to_owned(),
|
||||
position: character_id.to_string(),
|
||||
position: character_position,
|
||||
},
|
||||
Item {
|
||||
stack_size: 1,
|
||||
@ -510,6 +530,7 @@ pub fn update(
|
||||
char_stats: comp::Stats,
|
||||
inventory: comp::Inventory,
|
||||
loadout: comp::Loadout,
|
||||
waypoint: Option<comp::Waypoint>,
|
||||
connection: VelorenTransaction,
|
||||
) -> Result<Vec<Arc<common::comp::item::ItemId>>, Error> {
|
||||
use super::schema::{item::dsl::*, stats::dsl::*};
|
||||
@ -532,6 +553,22 @@ pub fn update(
|
||||
next_id
|
||||
})?;
|
||||
|
||||
if let Some(waypoint) = waypoint {
|
||||
match convert_waypoint_to_database_json(&waypoint) {
|
||||
Ok(character_position) => {
|
||||
diesel::update(item.filter(item_id.eq(char_id)))
|
||||
.set(position.eq(character_position))
|
||||
.execute(&*connection)?;
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(Error::ConversionError(format!(
|
||||
"Error encoding waypoint: {:?}",
|
||||
err
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Next, delete any slots we aren't upserting.
|
||||
trace!("Deleting items for character_id {}", char_id);
|
||||
let existing_items = parent_container_item_id
|
||||
|
@ -3,7 +3,10 @@ use crate::persistence::{
|
||||
models::{Body, Character, Item, Stats},
|
||||
};
|
||||
|
||||
use crate::persistence::{error::Error, json_models::HumanoidBody};
|
||||
use crate::persistence::{
|
||||
error::Error,
|
||||
json_models::{CharacterPosition, HumanoidBody},
|
||||
};
|
||||
use common::{
|
||||
character::CharacterId,
|
||||
comp::{Body as CompBody, *},
|
||||
@ -165,6 +168,13 @@ pub fn convert_body_to_database_json(body: &CompBody) -> Result<String, Error> {
|
||||
serde_json::to_string(&json_model).map_err(Error::SerializationError)
|
||||
}
|
||||
|
||||
pub fn convert_waypoint_to_database_json(waypoint: &Waypoint) -> Result<String, Error> {
|
||||
let charpos = CharacterPosition {
|
||||
waypoint: waypoint.get_pos(),
|
||||
};
|
||||
serde_json::to_string(&charpos).map_err(Error::SerializationError)
|
||||
}
|
||||
|
||||
pub fn convert_stats_to_database(character_id: CharacterId, stats: &common::comp::Stats) -> Stats {
|
||||
Stats {
|
||||
stats_id: character_id,
|
||||
|
@ -6,7 +6,12 @@ use crossbeam::channel;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use tracing::{error, trace};
|
||||
|
||||
pub type CharacterUpdateData = (comp::Stats, comp::Inventory, comp::Loadout);
|
||||
pub type CharacterUpdateData = (
|
||||
comp::Stats,
|
||||
comp::Inventory,
|
||||
comp::Loadout,
|
||||
Option<comp::Waypoint>,
|
||||
);
|
||||
|
||||
/// A unidirectional messaging resource for saving characters in a
|
||||
/// background thread.
|
||||
@ -48,17 +53,23 @@ impl CharacterUpdater {
|
||||
&'a comp::Stats,
|
||||
&'a comp::Inventory,
|
||||
&'a comp::Loadout,
|
||||
Option<&'a comp::Waypoint>,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
let updates = updates
|
||||
.map(|(character_id, stats, inventory, loadout)| {
|
||||
.map(|(character_id, stats, inventory, loadout, waypoint)| {
|
||||
(
|
||||
character_id,
|
||||
(stats.clone(), inventory.clone(), loadout.clone()),
|
||||
(
|
||||
stats.clone(),
|
||||
inventory.clone(),
|
||||
loadout.clone(),
|
||||
waypoint.cloned(),
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(CharacterId, (comp::Stats, comp::Inventory, comp::Loadout))>>();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Err(e) = self.update_tx.as_ref().unwrap().send(updates) {
|
||||
error!(?e, "Could not send stats updates");
|
||||
@ -72,8 +83,15 @@ impl CharacterUpdater {
|
||||
stats: &comp::Stats,
|
||||
inventory: &comp::Inventory,
|
||||
loadout: &comp::Loadout,
|
||||
waypoint: Option<&comp::Waypoint>,
|
||||
) {
|
||||
self.batch_update(std::iter::once((character_id, stats, inventory, loadout)));
|
||||
self.batch_update(std::iter::once((
|
||||
character_id,
|
||||
stats,
|
||||
inventory,
|
||||
loadout,
|
||||
waypoint,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,12 +102,13 @@ fn execute_batch_update(
|
||||
let mut inserted_items = Vec::<Arc<ItemId>>::new();
|
||||
|
||||
if let Err(e) = connection.transaction::<_, super::error::Error, _>(|txn| {
|
||||
for (character_id, (stats, inventory, loadout)) in updates {
|
||||
for (character_id, (stats, inventory, loadout, waypoint)) in updates {
|
||||
inserted_items.append(&mut super::character::update(
|
||||
character_id,
|
||||
stats,
|
||||
inventory,
|
||||
loadout,
|
||||
waypoint,
|
||||
txn,
|
||||
)?);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use common::comp;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::Vec3;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HumanoidBody {
|
||||
@ -29,3 +30,8 @@ impl From<&comp::humanoid::Body> for HumanoidBody {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CharacterPosition {
|
||||
pub waypoint: Vec3<f32>,
|
||||
}
|
||||
|
@ -21,7 +21,13 @@ use std::{fs, path::Path};
|
||||
use tracing::info;
|
||||
|
||||
/// A tuple of the components that are persisted to the DB for each character
|
||||
pub type PersistedComponents = (comp::Body, comp::Stats, comp::Inventory, comp::Loadout);
|
||||
pub type PersistedComponents = (
|
||||
comp::Body,
|
||||
comp::Stats,
|
||||
comp::Inventory,
|
||||
comp::Loadout,
|
||||
Option<comp::Waypoint>,
|
||||
);
|
||||
|
||||
// See: https://docs.rs/diesel_migrations/1.4.0/diesel_migrations/macro.embed_migrations.html
|
||||
// This macro is called at build-time, and produces the necessary migration info
|
||||
|
@ -246,7 +246,7 @@ impl StateExt for State {
|
||||
}
|
||||
|
||||
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) {
|
||||
let (body, stats, inventory, loadout) = components;
|
||||
let (body, stats, inventory, loadout, waypoint) = components;
|
||||
|
||||
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||
// Notify clients of a player list update
|
||||
@ -270,11 +270,17 @@ impl StateExt for State {
|
||||
self.write_component(entity, stats);
|
||||
self.write_component(entity, inventory);
|
||||
self.write_component(entity, loadout);
|
||||
|
||||
self.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::default()),
|
||||
);
|
||||
|
||||
if let Some(waypoint) = waypoint {
|
||||
self.write_component(entity, waypoint);
|
||||
self.write_component(entity, comp::Pos(waypoint.get_pos()));
|
||||
self.write_component(entity, comp::Vel(Vec3::zero()));
|
||||
self.write_component(entity, comp::ForceUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
sys::{SysScheduler, SysTimer},
|
||||
};
|
||||
use common::{
|
||||
comp::{Inventory, Loadout, Stats},
|
||||
comp::{Inventory, Loadout, Stats, Waypoint},
|
||||
msg::PresenceKind,
|
||||
span,
|
||||
};
|
||||
@ -19,6 +19,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, Loadout>,
|
||||
ReadStorage<'a, Waypoint>,
|
||||
ReadExpect<'a, character_updater::CharacterUpdater>,
|
||||
Write<'a, SysScheduler<Self>>,
|
||||
Write<'a, SysTimer<Self>>,
|
||||
@ -31,6 +32,7 @@ impl<'a> System<'a> for Sys {
|
||||
player_stats,
|
||||
player_inventories,
|
||||
player_loadouts,
|
||||
player_waypoint,
|
||||
updater,
|
||||
mut scheduler,
|
||||
mut timer,
|
||||
@ -45,11 +47,14 @@ impl<'a> System<'a> for Sys {
|
||||
&player_stats,
|
||||
&player_inventories,
|
||||
&player_loadouts,
|
||||
player_waypoint.maybe(),
|
||||
)
|
||||
.join()
|
||||
.filter_map(
|
||||
|(presence, stats, inventory, loadout)| match presence.kind {
|
||||
PresenceKind::Character(id) => Some((id, stats, inventory, loadout)),
|
||||
|(presence, stats, inventory, loadout, waypoint)| match presence.kind {
|
||||
PresenceKind::Character(id) => {
|
||||
Some((id, stats, inventory, loadout, waypoint))
|
||||
},
|
||||
PresenceKind::Spectator => None,
|
||||
},
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user