-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(data_source): implement
DataSource
for bemaniutils
- Loading branch information
1 parent
65f1825
commit 6350ee7
Showing
11 changed files
with
254 additions
and
133 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,141 @@ | ||
use std::collections::HashMap; | ||
|
||
use super::DataSource; | ||
use crate::config::BemaniutilsConfig; | ||
use crate::model::{FullRecord, LevelStat}; | ||
use crate::Result; | ||
use crate::model::{FullRecord, LevelStat, self}; | ||
use crate::{Result, errors}; | ||
use mysql::prelude::*; | ||
use mysql::*; | ||
use serde::Deserialize; | ||
use rust_fuzzy_search::fuzzy_compare; | ||
|
||
pub struct BemaniutilsDataSource {} | ||
pub struct BemaniutilsDataSource { | ||
records: Vec<FullRecord>, | ||
} | ||
|
||
impl DataSource for BemaniutilsDataSource { | ||
/// Get records of music_ids | ||
fn get_record_by_id(&self, music_id: Vec<u16>) -> Vec<FullRecord> { | ||
vec![] | ||
self.records.iter().filter(|r| {music_id.contains(&r.music_id)}).cloned().collect() | ||
} | ||
/// Get records by name. The implementation is probably fuzzy search. | ||
fn get_record_by_name(&self, name: String) -> Vec<FullRecord> { | ||
vec![] | ||
self.records | ||
.iter() | ||
.filter(|r| {fuzzy_compare(&name.to_lowercase(), &r.music_name) > 0.5}) | ||
.cloned() | ||
.collect() | ||
} | ||
/// Get best 50 records of current user. | ||
fn get_best50_records(&self) -> Vec<FullRecord> { | ||
vec![] | ||
self.records.iter().take(50).cloned().collect() | ||
} | ||
/// Show how many CLEARs and GRADEs dose the user have at each type at the level. | ||
/// If `level` is `None`, return all level stats. | ||
fn get_level_stat(&self, level: Option<u8>) -> Vec<LevelStat> { | ||
vec![] | ||
} | ||
/// Show how many musics dose the user have played at the level. | ||
fn get_level_count(&self, level: u8) -> usize { | ||
0 | ||
let mut level_stat: HashMap<u8, LevelStat> = HashMap::new(); | ||
for r in self | ||
.records | ||
.iter() | ||
.filter(|r| match level { | ||
Some(l) => r.get_level() == l, | ||
None => true, | ||
}) | ||
{ | ||
let mut stat = LevelStat::new(r.get_level(), 0, 0, 0, 0, 0, 0, 0, 1); | ||
match r.get_clear_type() { | ||
model::ClearType::Complete => stat.incr_nc_num(1), | ||
model::ClearType::HardComplete => stat.incr_hc_num(1), | ||
model::ClearType::UltimateChain => stat.incr_uc_num(1), | ||
model::ClearType::PerfectUltimateChain => stat.incr_puc_num(1), | ||
_ => {} | ||
} | ||
match r.get_grade() { | ||
model::Grade::AAA => stat.incr_ta_num(1), | ||
model::Grade::AAAPlus => stat.incr_tap_num(1), | ||
model::Grade::S => stat.incr_s_num(1), | ||
_ => {} | ||
} | ||
if let Some(old_stat) = level_stat.get_mut(&r.get_level()) { | ||
*old_stat = *old_stat + stat; | ||
} else { | ||
level_stat.insert(r.get_level(), stat); | ||
} | ||
} | ||
let mut r = level_stat | ||
.iter() | ||
.map(|(_, &s)| s) | ||
.collect::<Vec<LevelStat>>(); | ||
r.sort_by_key(|&s| s.get_level()); | ||
r | ||
} | ||
} | ||
|
||
impl BemaniutilsDataSource { | ||
pub fn open(conf: BemaniutilsConfig) -> Result<Self> { | ||
println!("{:?}", conf); | ||
// read all need data when open | ||
let url = format!( | ||
"mysql://{}:{}@{}:{}/{}", | ||
conf.db_user, conf.db_password, conf.db_address, conf.db_port, conf.db_name | ||
); | ||
let pool = Pool::new(mysql::Opts::from_url(url.as_str()).unwrap())?; | ||
let mut conn = pool.get_conn()?; | ||
// get user id by username first | ||
let user_id: u16 = | ||
if let Some(id) = conn.exec_first("SELECT id FROM user WHERE username = ?", (conf.username,))? { | ||
id | ||
} else { | ||
return Err(errors::Error::OtherError("bemanitutils: username not found".to_string())); | ||
}; | ||
// get records by user id | ||
#[derive(Debug, Deserialize)] | ||
struct Records { | ||
songid: u16, | ||
name: String, | ||
chart: u8, | ||
points: u32, | ||
sdata: String, | ||
mdata: String, | ||
} | ||
|
||
let sql = | ||
"SELECT music.songid AS songid, music.name AS name, music.chart AS chart, score.points AS points, score.data AS sdata, music.data AS mdata \ | ||
FROM score, music \ | ||
WHERE score.userid = ? AND score.musicid = music.id AND music.game = 'sdvx' AND music.version = ?"; | ||
let result: Vec<Records> = conn.exec_map( | ||
sql, | ||
(user_id, conf.game_version,), | ||
|(songid, name, chart, points, sdata, mdata)| { | ||
Records { songid, name, chart, points, sdata, mdata } | ||
} | ||
)?; | ||
|
||
let mut full_records = result.into_iter().map(|r| { | ||
#[derive(Debug, Deserialize)] | ||
struct Mdata { difficulty: u8 } | ||
#[derive(Debug, Deserialize)] | ||
struct SData { grade: u16, clear_type: u16 } | ||
let mdata: Mdata = serde_json::from_str(r.mdata.as_str()).unwrap(); | ||
let sdata: SData = serde_json::from_str(r.sdata.as_str()).unwrap(); | ||
let grade = model::Grade::from(sdata.grade); | ||
let clear_type = model::ClearType::from(sdata.clear_type); | ||
|
||
FullRecord { | ||
music_id: r.songid, | ||
music_name: r.name, | ||
difficulty: model::Difficulty::from(r.chart), | ||
level: mdata.difficulty, | ||
score: r.points, | ||
grade: grade, | ||
clear_type:clear_type, | ||
volfoce: model::compute_volforce(mdata.difficulty, r.points, grade, clear_type), | ||
} | ||
}).collect::<Vec<FullRecord>>(); | ||
|
||
full_records.sort_by_key(|rec| rec.get_volforce()); | ||
|
||
println!("{} records loaded.", full_records.len()); | ||
println!("data loaded from Bemaniutils server database succeeded!"); | ||
Ok(Self {}) | ||
Ok(Self {records: full_records.into_iter().rev().collect()}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.