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
|
- Sneaking lets you be closer to enemies without being detected
|
||||||
- Flight
|
- Flight
|
||||||
- Roll dodges melee attacks, and reduces the height of your hitbox
|
- Roll dodges melee attacks, and reduces the height of your hitbox
|
||||||
|
- Persistent waypoints (start from the last camp fire you visited)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -84,7 +84,13 @@ pub enum ServerEvent {
|
|||||||
},
|
},
|
||||||
UpdateCharacterData {
|
UpdateCharacterData {
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
components: (comp::Body, comp::Stats, comp::Inventory, comp::Loadout),
|
components: (
|
||||||
|
comp::Body,
|
||||||
|
comp::Stats,
|
||||||
|
comp::Inventory,
|
||||||
|
comp::Loadout,
|
||||||
|
Option<comp::Waypoint>,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
ExitIngame {
|
ExitIngame {
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
|
@ -23,11 +23,12 @@ pub fn create_character(
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
let inventory = Inventory::default();
|
let inventory = Inventory::default();
|
||||||
|
let waypoint = None;
|
||||||
|
|
||||||
character_loader.create_character(
|
character_loader.create_character(
|
||||||
entity,
|
entity,
|
||||||
player_uuid,
|
player_uuid,
|
||||||
character_alias,
|
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(
|
pub fn handle_loaded_character_data(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
entity: EcsEntity,
|
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
|
server
|
||||||
.state
|
.state
|
||||||
|
@ -156,7 +156,9 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
|||||||
.read_resource::<persistence::character_updater::CharacterUpdater>(),
|
.read_resource::<persistence::character_updater::CharacterUpdater>(),
|
||||||
) {
|
) {
|
||||||
if let PresenceKind::Character(character_id) = presences.kind {
|
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_character_from_database, convert_inventory_from_database_items,
|
||||||
convert_items_to_database_items, convert_loadout_from_database_items,
|
convert_items_to_database_items, convert_loadout_from_database_items,
|
||||||
convert_stats_from_database, convert_stats_to_database,
|
convert_stats_from_database, convert_stats_to_database,
|
||||||
|
convert_waypoint_to_database_json,
|
||||||
},
|
},
|
||||||
character_loader::{CharacterDataResult, CharacterListResult},
|
character_loader::{CharacterDataResult, CharacterListResult},
|
||||||
error::Error::DatabaseError,
|
error::Error::DatabaseError,
|
||||||
|
json_models::CharacterPosition,
|
||||||
PersistedComponents,
|
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 core::ops::Range;
|
||||||
use diesel::{prelude::*, sql_query, sql_types::BigInt};
|
use diesel::{prelude::*, sql_query, sql_types::BigInt};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -83,11 +88,22 @@ pub fn load_character_data(
|
|||||||
.filter(schema::body::dsl::body_id.eq(char_id))
|
.filter(schema::body::dsl::body_id.eq(char_id))
|
||||||
.first::<Body>(&*connection)?;
|
.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((
|
Ok((
|
||||||
convert_body_from_database(&char_body)?,
|
convert_body_from_database(&char_body)?,
|
||||||
convert_stats_from_database(&stats_data, character_data.alias),
|
convert_stats_from_database(&stats_data, character_data.alias),
|
||||||
convert_inventory_from_database_items(&inventory_items)?,
|
convert_inventory_from_database_items(&inventory_items)?,
|
||||||
convert_loadout_from_database_items(&loadout_items)?,
|
convert_loadout_from_database_items(&loadout_items)?,
|
||||||
|
waypoint,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +173,7 @@ pub fn create_character(
|
|||||||
|
|
||||||
use schema::{body, character, stats};
|
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
|
// Fetch new entity IDs for character, inventory and loadout
|
||||||
let mut new_entity_ids = get_new_entity_ids(connection, |next_id| next_id + 3)?;
|
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 character_id = new_entity_ids.next().unwrap();
|
||||||
let inventory_container_id = new_entity_ids.next().unwrap();
|
let inventory_container_id = new_entity_ids.next().unwrap();
|
||||||
let loadout_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![
|
let pseudo_containers = vec![
|
||||||
Item {
|
Item {
|
||||||
stack_size: 1,
|
stack_size: 1,
|
||||||
item_id: character_id,
|
item_id: character_id,
|
||||||
parent_container_item_id: WORLD_PSEUDO_CONTAINER_ID,
|
parent_container_item_id: WORLD_PSEUDO_CONTAINER_ID,
|
||||||
item_definition_id: CHARACTER_PSEUDO_CONTAINER_DEF_ID.to_owned(),
|
item_definition_id: CHARACTER_PSEUDO_CONTAINER_DEF_ID.to_owned(),
|
||||||
position: character_id.to_string(),
|
position: character_position,
|
||||||
},
|
},
|
||||||
Item {
|
Item {
|
||||||
stack_size: 1,
|
stack_size: 1,
|
||||||
@ -510,6 +530,7 @@ pub fn update(
|
|||||||
char_stats: comp::Stats,
|
char_stats: comp::Stats,
|
||||||
inventory: comp::Inventory,
|
inventory: comp::Inventory,
|
||||||
loadout: comp::Loadout,
|
loadout: comp::Loadout,
|
||||||
|
waypoint: Option<comp::Waypoint>,
|
||||||
connection: VelorenTransaction,
|
connection: VelorenTransaction,
|
||||||
) -> Result<Vec<Arc<common::comp::item::ItemId>>, Error> {
|
) -> Result<Vec<Arc<common::comp::item::ItemId>>, Error> {
|
||||||
use super::schema::{item::dsl::*, stats::dsl::*};
|
use super::schema::{item::dsl::*, stats::dsl::*};
|
||||||
@ -532,6 +553,22 @@ pub fn update(
|
|||||||
next_id
|
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.
|
// Next, delete any slots we aren't upserting.
|
||||||
trace!("Deleting items for character_id {}", char_id);
|
trace!("Deleting items for character_id {}", char_id);
|
||||||
let existing_items = parent_container_item_id
|
let existing_items = parent_container_item_id
|
||||||
|
@ -3,7 +3,10 @@ use crate::persistence::{
|
|||||||
models::{Body, Character, Item, Stats},
|
models::{Body, Character, Item, Stats},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::persistence::{error::Error, json_models::HumanoidBody};
|
use crate::persistence::{
|
||||||
|
error::Error,
|
||||||
|
json_models::{CharacterPosition, HumanoidBody},
|
||||||
|
};
|
||||||
use common::{
|
use common::{
|
||||||
character::CharacterId,
|
character::CharacterId,
|
||||||
comp::{Body as CompBody, *},
|
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)
|
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 {
|
pub fn convert_stats_to_database(character_id: CharacterId, stats: &common::comp::Stats) -> Stats {
|
||||||
Stats {
|
Stats {
|
||||||
stats_id: character_id,
|
stats_id: character_id,
|
||||||
|
@ -6,7 +6,12 @@ use crossbeam::channel;
|
|||||||
use std::{path::Path, sync::Arc};
|
use std::{path::Path, sync::Arc};
|
||||||
use tracing::{error, trace};
|
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
|
/// A unidirectional messaging resource for saving characters in a
|
||||||
/// background thread.
|
/// background thread.
|
||||||
@ -48,17 +53,23 @@ impl CharacterUpdater {
|
|||||||
&'a comp::Stats,
|
&'a comp::Stats,
|
||||||
&'a comp::Inventory,
|
&'a comp::Inventory,
|
||||||
&'a comp::Loadout,
|
&'a comp::Loadout,
|
||||||
|
Option<&'a comp::Waypoint>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
let updates = updates
|
let updates = updates
|
||||||
.map(|(character_id, stats, inventory, loadout)| {
|
.map(|(character_id, stats, inventory, loadout, waypoint)| {
|
||||||
(
|
(
|
||||||
character_id,
|
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) {
|
if let Err(e) = self.update_tx.as_ref().unwrap().send(updates) {
|
||||||
error!(?e, "Could not send stats updates");
|
error!(?e, "Could not send stats updates");
|
||||||
@ -72,8 +83,15 @@ impl CharacterUpdater {
|
|||||||
stats: &comp::Stats,
|
stats: &comp::Stats,
|
||||||
inventory: &comp::Inventory,
|
inventory: &comp::Inventory,
|
||||||
loadout: &comp::Loadout,
|
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();
|
let mut inserted_items = Vec::<Arc<ItemId>>::new();
|
||||||
|
|
||||||
if let Err(e) = connection.transaction::<_, super::error::Error, _>(|txn| {
|
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(
|
inserted_items.append(&mut super::character::update(
|
||||||
character_id,
|
character_id,
|
||||||
stats,
|
stats,
|
||||||
inventory,
|
inventory,
|
||||||
loadout,
|
loadout,
|
||||||
|
waypoint,
|
||||||
txn,
|
txn,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use common::comp;
|
use common::comp;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct HumanoidBody {
|
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;
|
use tracing::info;
|
||||||
|
|
||||||
/// A tuple of the components that are persisted to the DB for each character
|
/// 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
|
// 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
|
// 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) {
|
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) {
|
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||||
// Notify clients of a player list update
|
// Notify clients of a player list update
|
||||||
@ -270,11 +270,17 @@ impl StateExt for State {
|
|||||||
self.write_component(entity, stats);
|
self.write_component(entity, stats);
|
||||||
self.write_component(entity, inventory);
|
self.write_component(entity, inventory);
|
||||||
self.write_component(entity, loadout);
|
self.write_component(entity, loadout);
|
||||||
|
|
||||||
self.write_component(
|
self.write_component(
|
||||||
entity,
|
entity,
|
||||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::default()),
|
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},
|
sys::{SysScheduler, SysTimer},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Inventory, Loadout, Stats},
|
comp::{Inventory, Loadout, Stats, Waypoint},
|
||||||
msg::PresenceKind,
|
msg::PresenceKind,
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
@ -19,6 +19,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Stats>,
|
ReadStorage<'a, Stats>,
|
||||||
ReadStorage<'a, Inventory>,
|
ReadStorage<'a, Inventory>,
|
||||||
ReadStorage<'a, Loadout>,
|
ReadStorage<'a, Loadout>,
|
||||||
|
ReadStorage<'a, Waypoint>,
|
||||||
ReadExpect<'a, character_updater::CharacterUpdater>,
|
ReadExpect<'a, character_updater::CharacterUpdater>,
|
||||||
Write<'a, SysScheduler<Self>>,
|
Write<'a, SysScheduler<Self>>,
|
||||||
Write<'a, SysTimer<Self>>,
|
Write<'a, SysTimer<Self>>,
|
||||||
@ -31,6 +32,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
player_stats,
|
player_stats,
|
||||||
player_inventories,
|
player_inventories,
|
||||||
player_loadouts,
|
player_loadouts,
|
||||||
|
player_waypoint,
|
||||||
updater,
|
updater,
|
||||||
mut scheduler,
|
mut scheduler,
|
||||||
mut timer,
|
mut timer,
|
||||||
@ -45,11 +47,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
&player_stats,
|
&player_stats,
|
||||||
&player_inventories,
|
&player_inventories,
|
||||||
&player_loadouts,
|
&player_loadouts,
|
||||||
|
player_waypoint.maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter_map(
|
.filter_map(
|
||||||
|(presence, stats, inventory, loadout)| match presence.kind {
|
|(presence, stats, inventory, loadout, waypoint)| match presence.kind {
|
||||||
PresenceKind::Character(id) => Some((id, stats, inventory, loadout)),
|
PresenceKind::Character(id) => {
|
||||||
|
Some((id, stats, inventory, loadout, waypoint))
|
||||||
|
},
|
||||||
PresenceKind::Spectator => None,
|
PresenceKind::Spectator => None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user