Implement updating skills.
This commit is contained in:
parent
b467c32acb
commit
d1e29b40ed
|
@ -31,6 +31,7 @@ message Attributes {
|
||||||
int32 composure = 9;
|
int32 composure = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update an attribute's dot amount. TODO rename to AttributesUpdate.
|
||||||
message Attribute {
|
message Attribute {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
int32 value = 2;
|
int32 value = 2;
|
||||||
|
@ -44,11 +45,26 @@ message Skills {
|
||||||
repeated CofdSheet.Skill social_skills = 3;
|
repeated CofdSheet.Skill social_skills = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Full update of a single skill
|
||||||
message SkillUpdate {
|
message SkillUpdate {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
CofdSheet.Skill skill = 2;
|
CofdSheet.Skill skill = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Partial update of a single skill dot amount.
|
||||||
|
message SkillValueUpdate {
|
||||||
|
string name = 1;
|
||||||
|
int32 value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Partial update of only a skill's specializations. The
|
||||||
|
//specializations will be overwritten with the new values.
|
||||||
|
message SkillSpecializationsUpdate {
|
||||||
|
string name = 1;
|
||||||
|
repeated string specializations = 2;
|
||||||
|
}
|
||||||
|
|
||||||
//Add a Condition to a Chronicles of Darkness character sheet.
|
//Add a Condition to a Chronicles of Darkness character sheet.
|
||||||
message Condition {
|
message Condition {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
|
|
|
@ -6,6 +6,15 @@ use std::ops::Deref;
|
||||||
|
|
||||||
pub mod cofd;
|
pub mod cofd;
|
||||||
|
|
||||||
|
const CRATE_NAME: &'static str = env!("CARGO_BIN_NAME");
|
||||||
|
|
||||||
|
/// Convert an incoming protobuf content-type to the equivalent type
|
||||||
|
/// name produced by std::any::type_name(). Currently does NOT work
|
||||||
|
/// with nested types due to how prost generates the module names.
|
||||||
|
fn convert_to_rust_name(proto_type: &str) -> String {
|
||||||
|
format!("{}::{}", CRATE_NAME, proto_type.replace(".", "::"))
|
||||||
|
}
|
||||||
|
|
||||||
/// A struct wrapping a protobuf that allows it to be used as binary
|
/// A struct wrapping a protobuf that allows it to be used as binary
|
||||||
/// data submitted via POST using fetch API. Can automatically be
|
/// data submitted via POST using fetch API. Can automatically be
|
||||||
/// dereferenced into its wrapped type.
|
/// dereferenced into its wrapped type.
|
||||||
|
@ -23,8 +32,27 @@ where
|
||||||
{
|
{
|
||||||
type Error = crate::errors::Error;
|
type Error = crate::errors::Error;
|
||||||
|
|
||||||
async fn from_data(_req: &Request<'_>, data: Data) -> Outcome<Self, Error> {
|
async fn from_data(req: &Request<'_>, data: Data) -> Outcome<Self, Error> {
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
|
let content_type = req.content_type();
|
||||||
|
|
||||||
|
let is_protobuf = content_type
|
||||||
|
.map(|ct| ct.top() == "application" && ct.sub() == "x-protobuf")
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let message_type: Option<String> = content_type.and_then(|ct| {
|
||||||
|
ct.params()
|
||||||
|
.find(|&(name, _)| name == "messageType")
|
||||||
|
.map(|(_, message_type)| convert_to_rust_name(message_type))
|
||||||
|
});
|
||||||
|
|
||||||
|
if !is_protobuf {
|
||||||
|
return Outcome::Failure((Status::new(422, "invalid protobuf"), Error::InvalidInput));
|
||||||
|
}
|
||||||
|
|
||||||
|
if message_type.as_ref().map(String::as_str) != Some(std::any::type_name::<T>()) {
|
||||||
|
return Outcome::Forward(data);
|
||||||
|
}
|
||||||
|
|
||||||
let bytes: Vec<u8> = match data.open(2.mebibytes()).stream_to_vec().await {
|
let bytes: Vec<u8> = match data.open(2.mebibytes()).stream_to_vec().await {
|
||||||
Ok(read_bytes) => read_bytes,
|
Ok(read_bytes) => read_bytes,
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub(crate) fn routes() -> Vec<rocket::Route> {
|
||||||
cofd::update_attributes,
|
cofd::update_attributes,
|
||||||
cofd::update_attribute,
|
cofd::update_attribute,
|
||||||
cofd::update_skills,
|
cofd::update_skills,
|
||||||
|
cofd::update_skill_value,
|
||||||
cofd::add_condition,
|
cofd::add_condition,
|
||||||
cofd::remove_condition
|
cofd::remove_condition
|
||||||
]
|
]
|
||||||
|
@ -49,6 +50,35 @@ mod cofd {
|
||||||
use crate::models::proto::cofd::cofd_sheet::Skill;
|
use crate::models::proto::cofd::cofd_sheet::Skill;
|
||||||
use crate::models::proto::{cofd::api::*, cofd::*, Proto};
|
use crate::models::proto::{cofd::api::*, cofd::*, Proto};
|
||||||
|
|
||||||
|
fn find_skill_entry<'a>(
|
||||||
|
sheet: &'a mut CofdSheet,
|
||||||
|
skill_name: &'a str,
|
||||||
|
) -> Option<OccupiedEntry<'a, String, Skill>> {
|
||||||
|
let all_skills = vec![
|
||||||
|
&mut sheet.mental_skills,
|
||||||
|
&mut sheet.physical_skills,
|
||||||
|
&mut sheet.social_skills,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Search all skill lists for this value using "workaround" to
|
||||||
|
// break value from for loops.
|
||||||
|
let skill: Option<OccupiedEntry<_, _>> = 'l: loop {
|
||||||
|
for skill_map in all_skills {
|
||||||
|
if let Entry::Occupied(entry) = skill_map.entry(skill_name.to_owned()) {
|
||||||
|
break 'l Some(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break None;
|
||||||
|
};
|
||||||
|
|
||||||
|
skill
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_skill<'a>(sheet: &'a mut CofdSheet, skill_name: &'a str) -> Option<&'a mut Skill> {
|
||||||
|
find_skill_entry(sheet, skill_name).map(|entry| entry.into_mut())
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/cofd/<owner>/<character_id>/basic-info", data = "<info>")]
|
#[post("/cofd/<owner>/<character_id>/basic-info", data = "<info>")]
|
||||||
pub(super) fn update_basic_info<'a>(
|
pub(super) fn update_basic_info<'a>(
|
||||||
owner: String,
|
owner: String,
|
||||||
|
@ -101,7 +131,11 @@ mod cofd {
|
||||||
Ok("lol")
|
Ok("lol")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[patch("/cofd/<owner>/<character_id>/skills", data = "<skill_update>")]
|
#[patch(
|
||||||
|
"/cofd/<owner>/<character_id>/skills",
|
||||||
|
data = "<skill_update>",
|
||||||
|
rank = 1
|
||||||
|
)]
|
||||||
pub(super) async fn update_skills<'a>(
|
pub(super) async fn update_skills<'a>(
|
||||||
owner: String,
|
owner: String,
|
||||||
character_id: i32,
|
character_id: i32,
|
||||||
|
@ -111,28 +145,11 @@ mod cofd {
|
||||||
) -> Result<&'a str, Error> {
|
) -> Result<&'a str, Error> {
|
||||||
let mut character = load_character(&conn, logged_in_user, owner, character_id).await?;
|
let mut character = load_character(&conn, logged_in_user, owner, character_id).await?;
|
||||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||||
let skill: &Skill = skill_update.skill.as_ref().ok_or(Error::InvalidInput)?;
|
let updated_skill: &Skill = skill_update.skill.as_ref().ok_or(Error::InvalidInput)?;
|
||||||
|
let skill_entry = find_skill_entry(&mut sheet, &skill_update.name);
|
||||||
let all_skills = vec![
|
|
||||||
&mut sheet.mental_skills,
|
|
||||||
&mut sheet.physical_skills,
|
|
||||||
&mut sheet.social_skills,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Search all skill lists for this value using "workaround" to
|
|
||||||
// break value from for loops.
|
|
||||||
let skill_entry: Option<OccupiedEntry<_, _>> = 'l: loop {
|
|
||||||
for skill_map in all_skills {
|
|
||||||
if let Entry::Occupied(entry) = skill_map.entry(skill_update.name.clone()) {
|
|
||||||
break 'l Some(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break None;
|
|
||||||
};
|
|
||||||
|
|
||||||
skill_entry
|
skill_entry
|
||||||
.map(|mut entry| entry.insert(skill.clone()))
|
.map(|mut entry| entry.insert(updated_skill.clone()))
|
||||||
.ok_or(Error::InvalidInput)?;
|
.ok_or(Error::InvalidInput)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
@ -145,6 +162,33 @@ mod cofd {
|
||||||
Ok("lol")
|
Ok("lol")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[patch(
|
||||||
|
"/cofd/<owner>/<character_id>/skills",
|
||||||
|
data = "<value_update>",
|
||||||
|
rank = 2
|
||||||
|
)]
|
||||||
|
pub(super) async fn update_skill_value<'a>(
|
||||||
|
owner: String,
|
||||||
|
character_id: i32,
|
||||||
|
value_update: Proto<SkillValueUpdate>,
|
||||||
|
conn: TenebrousDbConn<'_>,
|
||||||
|
logged_in_user: Option<&User>,
|
||||||
|
) -> Result<&'a str, Error> {
|
||||||
|
let mut character = load_character(&conn, logged_in_user, owner, character_id).await?;
|
||||||
|
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||||
|
let skill: Option<&mut Skill> = find_skill(&mut sheet, &value_update.name);
|
||||||
|
|
||||||
|
skill
|
||||||
|
.map(|s| s.dots = value_update.value)
|
||||||
|
.ok_or(Error::InvalidInput)?;
|
||||||
|
|
||||||
|
println!("updated skill value",);
|
||||||
|
|
||||||
|
character.update_data(sheet)?;
|
||||||
|
conn.update_character_sheet(&character).await?;
|
||||||
|
Ok("lol")
|
||||||
|
}
|
||||||
|
|
||||||
#[put("/cofd/<owner>/<character_id>/conditions", data = "<info>")]
|
#[put("/cofd/<owner>/<character_id>/conditions", data = "<info>")]
|
||||||
pub(super) fn add_condition<'a>(
|
pub(super) fn add_condition<'a>(
|
||||||
owner: String,
|
owner: String,
|
||||||
|
|
|
@ -1,9 +1,21 @@
|
||||||
function makeAPI(root) {
|
function makeAPI(root) {
|
||||||
const Attribute = root.lookupType("models.proto.cofd.api.Attribute");
|
//Protobuf types
|
||||||
|
const AttributeType = 'models.proto.cofd.api.Attribute';
|
||||||
|
const Attribute = root.lookupType(AttributeType);
|
||||||
|
const SkillValueUpdateType = 'models.proto.cofd.api.SkillValueUpdate';
|
||||||
|
const SkillValueUpdate = root.lookupType(SkillValueUpdateType);
|
||||||
|
const SkillSpecializationUpdateType = 'models.proto.cofd.api.SkillSpecializationsUpdate';
|
||||||
|
const SkillSpecializationsUpdate = root.lookupType(SkillSpecializationUpdateType);
|
||||||
|
|
||||||
|
const protobufContentType = (messageType) =>
|
||||||
|
({ 'Content-Type': 'application/x-protobuf; messageType="' + messageType + '"' });
|
||||||
|
|
||||||
const attributesResource = (username, characterID) =>
|
const attributesResource = (username, characterID) =>
|
||||||
'/api/cofd/' + username + '/' + characterID + '/attributes';
|
'/api/cofd/' + username + '/' + characterID + '/attributes';
|
||||||
|
|
||||||
|
const skillResource = (username, characterID, skillName) =>
|
||||||
|
'/api/cofd/' + username + '/' + characterID + '/skills';
|
||||||
|
|
||||||
async function updateAttribute(params) {
|
async function updateAttribute(params) {
|
||||||
const { username, characterID, attribute, newValue } = params;
|
const { username, characterID, attribute, newValue } = params;
|
||||||
|
|
||||||
|
@ -16,6 +28,7 @@ function makeAPI(root) {
|
||||||
|
|
||||||
let resp = await fetch(resource, {
|
let resp = await fetch(resource, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
headers: { ... protobufContentType(AttributeType) },
|
||||||
body: Attribute.encode(req).finish()
|
body: Attribute.encode(req).finish()
|
||||||
}).then(async resp => {
|
}).then(async resp => {
|
||||||
console.log("resp is", await resp.text());
|
console.log("resp is", await resp.text());
|
||||||
|
@ -24,7 +37,29 @@ function makeAPI(root) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateSkillValue(params) {
|
||||||
|
const { username, characterID, skillName, newValue } = params;
|
||||||
|
|
||||||
|
let req = SkillValueUpdate.create({
|
||||||
|
name: skillName,
|
||||||
|
value: parseInt(newValue)
|
||||||
|
});
|
||||||
|
|
||||||
|
const resource = skillResource(username, characterID);
|
||||||
|
|
||||||
|
let resp = await fetch(resource, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { ... protobufContentType(SkillValueUpdateType) },
|
||||||
|
body: SkillValueUpdate.encode(req).finish()
|
||||||
|
}).then(async resp => {
|
||||||
|
console.log("resp is", await resp.text());
|
||||||
|
}).catch(async err => {
|
||||||
|
console.log("err is", err.text());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updateAttribute
|
updateAttribute,
|
||||||
|
updateSkillValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,37 @@
|
||||||
function setupAttributes() {
|
function setupAttributes() {
|
||||||
const attributeInputs = document.querySelectorAll('#attributes input[type="number"]');
|
const attributeInputs = document.querySelectorAll('#attributes input[type="number"]');
|
||||||
|
|
||||||
|
async function attributeHandler(event) {
|
||||||
|
console.log("updating attr");
|
||||||
|
const attribute = event.target.id;
|
||||||
|
const newValue = parseInt(event.target.value);
|
||||||
|
const params = { username: USERNAME, characterID: CHARACTER_ID, attribute, newValue };
|
||||||
|
await api.updateAttribute(params);
|
||||||
|
}
|
||||||
|
|
||||||
Array.from(attributeInputs).forEach(input => {
|
Array.from(attributeInputs).forEach(input => {
|
||||||
input.addEventListener('change', async function(event) {
|
input.addEventListener('change', attributeHandler);
|
||||||
console.log("updating attr");
|
});
|
||||||
const attribute = event.target.id;
|
}
|
||||||
const newValue = parseInt(event.target.value);
|
|
||||||
const params = { username: USERNAME, characterID: CHARACTER_ID, attribute, newValue };
|
function setupSkills() {
|
||||||
await api.updateAttribute(params);
|
const attributeInputs = document.querySelectorAll('#skills input[type="number"]');
|
||||||
});
|
|
||||||
|
async function skillValueHandler(event) {
|
||||||
|
console.log("updating skill value");
|
||||||
|
const skillName = event.target.id;
|
||||||
|
const newValue = parseInt(event.target.value);
|
||||||
|
const params = { username: USERNAME, characterID: CHARACTER_ID, skillName, newValue };
|
||||||
|
await api.updateSkillValue(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.from(attributeInputs).forEach(input => {
|
||||||
|
input.addEventListener('change', skillValueHandler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setupAttributes();
|
setupAttributes();
|
||||||
|
setupSkills();
|
||||||
})().catch(e => {
|
})().catch(e => {
|
||||||
alert(e);
|
alert(e);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue