mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'xvar/preventdatalossfromdeserialization' into 'master'
Removed de-serialization failure tolerance to prevent player inventory/loadout wipes (#659) See merge request veloren/veloren!1173
This commit is contained in:
commit
10ba532815
@ -0,0 +1 @@
|
||||
-- Nothing to undo since up.sql only creates missing inventory/loadout records
|
@ -0,0 +1,15 @@
|
||||
-- This migration was added in conjunction with a change that no longer creates missing inventory/loadout records
|
||||
-- for characters that don't have one, to prevent such characters from being unable to log in.
|
||||
|
||||
-- Create a default loadout for any characters that don't have one
|
||||
INSERT INTO loadout (character_id, items)
|
||||
SELECT c."id", "{""active_item"":{""item"":{""name"":""Battered Sword"",""description"":""Two-Hand Sword\n\nPower: 2-10\n\nHeld together by Rust and hope.\n\n<Right-Click to use>"",""kind"":{""Tool"":{""kind"":{""Sword"":""BasicSword""},""equip_time_millis"":300}}},""ability1"":{""TripleStrike"":{""base_damage"":5,""needs_timing"":false}},""ability2"":{""DashMelee"":{""energy_cost"":700,""buildup_duration"":{""secs"":0,""nanos"":500000000},""recover_duration"":{""secs"":0,""nanos"":500000000},""base_damage"":10}},""ability3"":null,""block_ability"":""BasicBlock"",""dodge_ability"":""Roll""},""second_item"":null,""shoulder"":null,""chest"":{""name"":""Rugged Shirt"",""description"":""Chest\n\nArmor: 0\n\nSmells like Adventure.\n\n<Right-Click to use>"",""kind"":{""Armor"":{""kind"":{""Chest"":""Rugged0""},""stats"":20}}},""belt"":null,""hand"":null,""pants"":{""name"":""Rugged Commoner's Pants"",""description"":""Legs\n\nArmor: 0\n\nThey remind you of the old days.\n\n<Right-Click to use>"",""kind"":{""Armor"":{""kind"":{""Pants"":""Rugged0""},""stats"":20}}},""foot"":{""name"":""Worn out Sandals"",""description"":""Feet\n\nArmor: 0\n\nLoyal companions.\n\n<Right-Click to use>"",""kind"":{""Armor"":{""kind"":{""Foot"":""Sandal0""},""stats"":20}}},""back"":null,""ring"":null,""neck"":null,""lantern"":{""name"":""Black Lantern"",""description"":""Used by city guards."",""kind"":{""Lantern"":{""kind"":""Black0"",""color"":{""r"":255,""g"":190,""b"":75},""strength_thousandths"":3000,""flicker_thousandths"":300}}},""head"":null,""tabard"":null}"
|
||||
FROM character c
|
||||
WHERE (SELECT COUNT(1) FROM loadout WHERE character_id = c.id) = 0;
|
||||
|
||||
|
||||
-- Create a default inventory for any characters that don't have one
|
||||
INSERT INTO inventory (character_id, items)
|
||||
SELECT c."id", "{""slots"":[{""name"":""Dwarven Cheese"",""description"":""Restores 15 Health\n\nAromatic and nutritious\n\n<Right-Click to use>"",""kind"":{""Consumable"":{""kind"":""Cheese"",""effect"":{""Health"":{""amount"":15,""cause"":""Item""}},""amount"":1}}},{""name"":""Apple"",""description"":""Restores 20 Health\n\nRed and juicy\n\n<Right-Click to use>"",""kind"":{""Consumable"":{""kind"":""Apple"",""effect"":{""Health"":{""amount"":20,""cause"":""Item""}},""amount"":1}}},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],""amount"":2}"
|
||||
FROM character c
|
||||
WHERE (SELECT COUNT(1) FROM inventory WHERE character_id = c.id) = 0;
|
@ -236,71 +236,35 @@ impl Drop for CharacterLoader {
|
||||
fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> CharacterDataResult {
|
||||
let connection = establish_connection(db_dir);
|
||||
|
||||
let (character_data, body_data, stats_data, maybe_inventory, maybe_loadout) =
|
||||
schema::character::dsl::character
|
||||
.filter(schema::character::id.eq(character_id))
|
||||
.filter(schema::character::player_uuid.eq(player_uuid))
|
||||
.inner_join(schema::body::table)
|
||||
.inner_join(schema::stats::table)
|
||||
.left_join(schema::inventory::table)
|
||||
.left_join(schema::loadout::table)
|
||||
.first::<(Character, Body, Stats, Option<Inventory>, Option<Loadout>)>(&connection)?;
|
||||
let result = schema::character::dsl::character
|
||||
.filter(schema::character::id.eq(character_id))
|
||||
.filter(schema::character::player_uuid.eq(player_uuid))
|
||||
.inner_join(schema::body::table)
|
||||
.inner_join(schema::stats::table)
|
||||
.inner_join(schema::inventory::table)
|
||||
.inner_join(schema::loadout::table)
|
||||
.first::<(Character, Body, Stats, Inventory, Loadout)>(&connection);
|
||||
|
||||
Ok((
|
||||
comp::Body::from(&body_data),
|
||||
comp::Stats::from(StatsJoinData {
|
||||
alias: &character_data.alias,
|
||||
body: &comp::Body::from(&body_data),
|
||||
stats: &stats_data,
|
||||
}),
|
||||
maybe_inventory.map_or_else(
|
||||
|| {
|
||||
// If no inventory record was found for the character, create it now
|
||||
let row = Inventory::from((character_data.id, comp::Inventory::default()));
|
||||
|
||||
if let Err(error) = diesel::insert_into(schema::inventory::table)
|
||||
.values(&row)
|
||||
.execute(&connection)
|
||||
{
|
||||
warn!(
|
||||
"Failed to create an inventory record for character {}: {}",
|
||||
&character_data.id, error
|
||||
)
|
||||
}
|
||||
|
||||
comp::Inventory::default()
|
||||
},
|
||||
comp::Inventory::from,
|
||||
),
|
||||
maybe_loadout.map_or_else(
|
||||
|| {
|
||||
// Create if no record was found
|
||||
let default_loadout = LoadoutBuilder::new()
|
||||
.defaults()
|
||||
.active_item(LoadoutBuilder::default_item_config_from_str(
|
||||
character_data.tool.as_deref(),
|
||||
))
|
||||
.build();
|
||||
|
||||
let row = NewLoadout::from((character_data.id, &default_loadout));
|
||||
|
||||
if let Err(e) = diesel::insert_into(schema::loadout::table)
|
||||
.values(&row)
|
||||
.execute(&connection)
|
||||
{
|
||||
let char_id = character_data.id;
|
||||
warn!(
|
||||
?e,
|
||||
?char_id,
|
||||
"Failed to create an loadout record for character",
|
||||
)
|
||||
}
|
||||
|
||||
default_loadout
|
||||
},
|
||||
|data| comp::Loadout::from(&data),
|
||||
),
|
||||
))
|
||||
match result {
|
||||
Ok((character_data, body_data, stats_data, inventory, loadout)) => Ok((
|
||||
comp::Body::from(&body_data),
|
||||
comp::Stats::from(StatsJoinData {
|
||||
alias: &character_data.alias,
|
||||
body: &comp::Body::from(&body_data),
|
||||
stats: &stats_data,
|
||||
}),
|
||||
comp::Inventory::from(inventory),
|
||||
comp::Loadout::from(&loadout),
|
||||
)),
|
||||
Err(e) => {
|
||||
error!(
|
||||
?e,
|
||||
?character_id,
|
||||
"Failed to load character data for character"
|
||||
);
|
||||
Err(Error::CharacterDataError)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a list of characters belonging to the player. This data is a small
|
||||
@ -311,40 +275,36 @@ fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> Ch
|
||||
/// stats, body, etc...) the character is skipped, and no entry will be
|
||||
/// returned.
|
||||
fn load_character_list(player_uuid: &str, db_dir: &str) -> CharacterListResult {
|
||||
let data = schema::character::dsl::character
|
||||
let result = schema::character::dsl::character
|
||||
.filter(schema::character::player_uuid.eq(player_uuid))
|
||||
.order(schema::character::id.desc())
|
||||
.inner_join(schema::body::table)
|
||||
.inner_join(schema::stats::table)
|
||||
.left_join(schema::loadout::table)
|
||||
.load::<(Character, Body, Stats, Option<Loadout>)>(&establish_connection(db_dir))?;
|
||||
.inner_join(schema::loadout::table)
|
||||
.load::<(Character, Body, Stats, Loadout)>(&establish_connection(db_dir));
|
||||
|
||||
Ok(data
|
||||
.iter()
|
||||
.map(|(character_data, body_data, stats_data, maybe_loadout)| {
|
||||
let character = CharacterData::from(character_data);
|
||||
let body = comp::Body::from(body_data);
|
||||
let level = stats_data.level as usize;
|
||||
let loadout = maybe_loadout.as_ref().map_or_else(
|
||||
|| {
|
||||
LoadoutBuilder::new()
|
||||
.defaults()
|
||||
.active_item(LoadoutBuilder::default_item_config_from_str(
|
||||
character.tool.as_deref(),
|
||||
))
|
||||
.build()
|
||||
},
|
||||
comp::Loadout::from,
|
||||
);
|
||||
match result {
|
||||
Ok(data) => Ok(data
|
||||
.iter()
|
||||
.map(|(character_data, body_data, stats_data, loadout)| {
|
||||
let character = CharacterData::from(character_data);
|
||||
let body = comp::Body::from(body_data);
|
||||
let level = stats_data.level as usize;
|
||||
let loadout = comp::Loadout::from(loadout);
|
||||
|
||||
CharacterItem {
|
||||
character,
|
||||
body,
|
||||
level,
|
||||
loadout,
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
CharacterItem {
|
||||
character,
|
||||
body,
|
||||
level,
|
||||
loadout,
|
||||
}
|
||||
})
|
||||
.collect()),
|
||||
Err(e) => {
|
||||
error!(?e, ?player_uuid, "Failed to load character list for player");
|
||||
Err(Error::CharacterDataError)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new character with provided comp::Character and comp::Body data.
|
||||
|
@ -2,7 +2,7 @@ extern crate serde_json;
|
||||
|
||||
use super::schema::{body, character, inventory, loadout, stats};
|
||||
use crate::comp;
|
||||
use common::{character::Character as CharacterData, LoadoutBuilder};
|
||||
use common::character::Character as CharacterData;
|
||||
use diesel::sql_types::Text;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
@ -212,14 +212,7 @@ where
|
||||
bytes: Option<&<DB as diesel::backend::Backend>::RawValue>,
|
||||
) -> diesel::deserialize::Result<Self> {
|
||||
let t = String::from_sql(bytes)?;
|
||||
|
||||
match serde_json::from_str(&t) {
|
||||
Ok(data) => Ok(Self(data)),
|
||||
Err(e) => {
|
||||
warn!(?e, "Failed to deserialise inventory data");
|
||||
Ok(Self(comp::Inventory::default()))
|
||||
},
|
||||
}
|
||||
serde_json::from_str(&t).map_err(Box::from)
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,23 +291,7 @@ where
|
||||
bytes: Option<&<DB as diesel::backend::Backend>::RawValue>,
|
||||
) -> diesel::deserialize::Result<Self> {
|
||||
let t = String::from_sql(bytes)?;
|
||||
|
||||
match serde_json::from_str(&t) {
|
||||
Ok(data) => Ok(Self(data)),
|
||||
Err(e) => {
|
||||
warn!(?e, "Failed to deserialise loadout data");
|
||||
|
||||
// We don't have a weapon reference here, so we default to sword
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.defaults()
|
||||
.active_item(LoadoutBuilder::default_item_config_from_str(Some(
|
||||
"common.items.weapons.sword.starter_sword",
|
||||
)))
|
||||
.build();
|
||||
|
||||
Ok(Self(loadout))
|
||||
},
|
||||
}
|
||||
serde_json::from_str(&t).map_err(Box::from)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user