Skip to content

Commit

Permalink
feat: Introduce Creature Role (#49)
Browse files Browse the repository at this point in the history
* Query the new db fields (actions, skills, scales)
* Return percentage associated with each role
* Filter by role
* Random gen by role
* Divide response data (and Creature) in fields to have a modular and lightweight response
* Update dependencies
  • Loading branch information
RakuJa authored May 5, 2024
1 parent 1c6514c commit e175cec
Show file tree
Hide file tree
Showing 45 changed files with 1,555 additions and 262 deletions.
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["RakuJa"]

# Compiler info
edition = "2021"
rust-version = "1.75.0"
rust-version = "1.77.1"

description = "Beyond Your Bestiary Explorer (BYBE) is a web service that provides tools to help Pathfinder 2e Game Masters."
readme = "README.md"
Expand All @@ -29,17 +29,19 @@ validator = {version="0.16.1", features = ["derive"]}
utoipa = { version = "4.2.0", features = ["actix_extras"] }
utoipa-swagger-ui = { version = "6.0.0", features = ["actix-web"] }

sqlx = { version = "0.7.3", features = ["runtime-async-std", "sqlite"] }
sqlx = { version = "0.7.4", features = ["runtime-async-std", "sqlite"] }
mini-moka = "0.10.3"

anyhow = "1.0.80"
anyhow = "1.0.81"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
strum = {version="0.26.1", features = ["derive"]}
serde_json = "1.0.115"
strum = {version="0.26.2", features = ["derive"]}
rand = "0.9.0-alpha.0"
counter = "0.5.7"
dotenvy = "0.15.7"
regex = "1.10.4"

env_logger = "0.11.2"
env_logger = "0.11.3"
log = "0.4.21"
maplit = "1.0.2"
num-traits = "0.2.18"
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Stage 1: Build the Rust project
FROM rust:1.75-alpine as builder
FROM rust:1-alpine as builder

# Set the working directory in the container
WORKDIR /app
Expand Down
4 changes: 2 additions & 2 deletions src/db/db_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ pub fn from_db_data_to_filter_cache(

if !fields_values_cache
.list_of_sources
.contains(&curr_creature.core_data.source)
.contains(&curr_creature.core_data.publication_info.source)
{
fields_values_cache
.list_of_sources
.push(curr_creature.core_data.source.clone());
.push(curr_creature.core_data.publication_info.source.clone());
}

if !fields_values_cache.list_of_alignments.contains(&alignment) {
Expand Down
149 changes: 147 additions & 2 deletions src/db/db_communicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,34 @@ use crate::models::db::raw_sense::RawSense;
use crate::models::db::raw_speed::RawSpeed;
use crate::models::db::raw_trait::RawTrait;
use crate::models::db::raw_weakness::RawWeakness;
use crate::models::items::action::Action;
use crate::models::items::skill::Skill;
use crate::models::items::spell::Spell;
use crate::models::items::weapon::Weapon;
use crate::models::scales_struct::ability_scales::AbilityScales;
use crate::models::scales_struct::ac_scales::AcScales;
use crate::models::scales_struct::area_dmg_scales::AreaDmgScales;
use crate::models::scales_struct::creature_scales::CreatureScales;
use crate::models::scales_struct::hp_scales::HpScales;
use crate::models::scales_struct::item_scales::ItemScales;
use crate::models::scales_struct::perception_scales::PerceptionScales;
use crate::models::scales_struct::res_weak_scales::ResWeakScales;
use crate::models::scales_struct::saving_throw_scales::SavingThrowScales;
use crate::models::scales_struct::skill_scales::SkillScales;
use crate::models::scales_struct::spell_dc_and_atk_scales::SpellDcAndAtkScales;
use crate::models::scales_struct::strike_bonus_scales::StrikeBonusScales;
use crate::models::scales_struct::strike_dmg_scales::StrikeDmgScales;
use anyhow::Result;
use regex::Regex;
use sqlx::{Error, Pool, Sqlite};

async fn from_raw_vec_to_creature(conn: &Pool<Sqlite>, raw_vec: Vec<RawCreature>) -> Vec<Creature> {
async fn from_raw_vec_to_creature(
conn: &Pool<Sqlite>,
scales: &CreatureScales,
raw_vec: Vec<RawCreature>,
) -> Vec<Creature> {
let mut creature_list = Vec::new();
let scales_dmg_regex = Regex::new(r"\((\d+)\)").ok().unwrap();
for el in raw_vec {
let immunities = get_creature_immunities(conn, el.id)
.await
Expand All @@ -32,17 +53,23 @@ async fn from_raw_vec_to_creature(conn: &Pool<Sqlite>, raw_vec: Vec<RawCreature>
.unwrap_or_default();
let spells = get_creature_spells(conn, el.id).await.unwrap_or_default();
let weapons = get_creature_weapons(conn, el.id).await.unwrap_or_default();
let actions = get_creature_actions(conn, el.id).await.unwrap_or_default();
let skills = get_creature_skills(conn, el.id).await.unwrap_or_default();
creature_list.push(Creature::from((
el,
traits,
weapons,
spells,
actions,
skills,
immunities,
languages,
resistances,
senses,
speeds,
weaknesses,
scales,
&scales_dmg_regex,
)));
}
creature_list
Expand Down Expand Up @@ -129,6 +156,26 @@ async fn get_creature_weapons(conn: &Pool<Sqlite>, creature_id: i64) -> Result<V
.await?)
}

async fn get_creature_actions(conn: &Pool<Sqlite>, creature_id: i64) -> Result<Vec<Action>> {
Ok(sqlx::query_as!(
Action,
"SELECT * FROM ACTION_TABLE WHERE creature_id == ($1)",
creature_id
)
.fetch_all(conn)
.await?)
}

async fn get_creature_skills(conn: &Pool<Sqlite>, creature_id: i64) -> Result<Vec<Skill>> {
Ok(sqlx::query_as!(
Skill,
"SELECT name, description, modifier, proficiency FROM SKILL_TABLE WHERE creature_id == ($1)",
creature_id
)
.fetch_all(conn)
.await?)
}

async fn get_creature_spells(conn: &Pool<Sqlite>, creature_id: i64) -> Result<Vec<Spell>> {
Ok(sqlx::query_as!(
Spell,
Expand All @@ -139,12 +186,110 @@ async fn get_creature_spells(conn: &Pool<Sqlite>, creature_id: i64) -> Result<Ve
.await?)
}

pub async fn fetch_creatures(conn: &Pool<Sqlite>) -> Result<Vec<Creature>, Error> {
pub async fn fetch_creatures(
conn: &Pool<Sqlite>,
creature_scales: &CreatureScales,
) -> Result<Vec<Creature>, Error> {
Ok(from_raw_vec_to_creature(
conn,
creature_scales,
sqlx::query_as!(RawCreature, "SELECT * FROM CREATURE_TABLE ORDER BY name")
.fetch_all(conn)
.await?,
)
.await)
}

pub async fn fetch_creature_scales(conn: &Pool<Sqlite>) -> Result<CreatureScales> {
Ok(CreatureScales {
ability_scales: sqlx::query_as!(AbilityScales, "SELECT * FROM ABILITY_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
ac_scales: sqlx::query_as!(AcScales, "SELECT * FROM AC_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
area_dmg_scales: sqlx::query_as!(AreaDmgScales, "SELECT * FROM AREA_DAMAGE_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
hp_scales: sqlx::query_as!(HpScales, "SELECT * FROM HP_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
item_scales: sqlx::query_as!(ItemScales, "SELECT * FROM ITEM_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.cr_level.clone(), n))
.collect(),
perception_scales: sqlx::query_as!(
PerceptionScales,
"SELECT * FROM PERCEPTION_SCALES_TABLE",
)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
res_weak_scales: sqlx::query_as!(ResWeakScales, "SELECT * FROM RES_WEAK_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
saving_throw_scales: sqlx::query_as!(
SavingThrowScales,
"SELECT * FROM SAVING_THROW_SCALES_TABLE",
)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
skill_scales: sqlx::query_as!(SkillScales, "SELECT * FROM SKILL_SCALES_TABLE",)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
spell_dc_and_atk_scales: sqlx::query_as!(
SpellDcAndAtkScales,
"SELECT * FROM SPELL_DC_AND_ATTACK_SCALES_TABLE",
)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
strike_bonus_scales: sqlx::query_as!(
StrikeBonusScales,
"SELECT * FROM STRIKE_BONUS_SCALES_TABLE",
)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
strike_dmg_scales: sqlx::query_as!(
StrikeDmgScales,
"SELECT * FROM STRIKE_DAMAGE_SCALES_TABLE",
)
.fetch_all(conn)
.await?
.into_iter()
.map(|n| (n.level as i8, n))
.collect(),
})
}

// TODO: Restructure creature fetch to use transaction
15 changes: 13 additions & 2 deletions src/db/db_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use crate::services::url_calculator::add_boolean_query;
use crate::AppState;
use anyhow::Result;

const ACCURACY_THRESHOLD: i64 = 50;

fn hp_increase_by_level() -> HashMap<i8, u16> {
hashmap! { 1 => 10, 2=> 15, 5=> 20, 20=> 30 }
}
Expand Down Expand Up @@ -170,6 +172,14 @@ fn fetch_creatures_passing_single_filter(
filter_vec.contains(creature.core_data.is_spell_caster.to_string().as_str())
})
.collect(),
CreatureFilter::CreatureRoles => cr_iterator
.filter(|creature| {
creature.info.roles.iter().any(|(role, accuracy)| {
accuracy >= &ACCURACY_THRESHOLD
&& filter_vec.contains(role.to_string().as_str())
})
})
.collect(),
}
}

Expand All @@ -189,7 +199,6 @@ pub async fn get_keys(app_state: &AppState, field: CreatureField) -> Vec<String>
CreatureField::Alignment => runtime_fields_values.list_of_alignments,
CreatureField::Level => runtime_fields_values.list_of_levels,
CreatureField::CreatureTypes => runtime_fields_values.list_of_creature_types,

_ => vec![],
};
x.sort();
Expand All @@ -213,7 +222,9 @@ async fn fetch_creatures(app_state: &AppState, variant: CreatureVariant) -> Opti
let index = &CreatureVariant::to_cache_index(&variant);
if let Some(creatures) = cache.get(index) {
return Some(creatures);
} else if let Ok(creatures) = db_communicator::fetch_creatures(&app_state.conn).await {
} else if let Ok(creatures) =
db_communicator::fetch_creatures(&app_state.conn, &app_state.creature_scales).await
{
cache.insert(0, creatures.clone());
let mut weak_creatures = Vec::new();
let mut elite_creatures = Vec::new();
Expand Down
7 changes: 7 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod routes;

use crate::db::db_cache::RuntimeFieldsValues;
use crate::models::creature::Creature;
use crate::models::scales_struct::creature_scales::CreatureScales;
use crate::routes::{bestiary, encounter, health};
use actix_cors::Cors;
use actix_web::http::header::{CacheControl, CacheDirective};
Expand All @@ -26,6 +27,7 @@ pub struct AppState {
conn: Pool<Sqlite>,
creature_cache: Cache<i32, Vec<Creature>>,
runtime_fields_cache: Cache<i32, RuntimeFieldsValues>,
creature_scales: CreatureScales,
}

#[utoipa::path(get, path = "/")]
Expand Down Expand Up @@ -87,6 +89,10 @@ async fn main() -> std::io::Result<()> {
// Create the cache.
.build();

let creature_scales = db::db_communicator::fetch_creature_scales(&pool)
.await
.expect("Could not establish valid connection with the database.. Startup failed");

log::info!(
"starting HTTP server at http://{}:{}",
service_ip.as_str(),
Expand Down Expand Up @@ -127,6 +133,7 @@ async fn main() -> std::io::Result<()> {
conn: pool.clone(),
creature_cache: cr_cache.clone(),
runtime_fields_cache: fields_cache.clone(),
creature_scales: creature_scales.clone(),
}))
})
.bind((get_service_ip(), get_service_port()))?
Expand Down
Loading

0 comments on commit e175cec

Please sign in to comment.