add feature to make gradual taiko calc sync
This commit is contained in:
@@ -42,8 +42,16 @@ jobs:
|
||||
- name: Install nextest
|
||||
uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Run tests
|
||||
run: cargo nextest run --no-fail-fast --failure-output=immediate-final
|
||||
- name: Run tests on default features
|
||||
run: cargo nextest run --no-default-features --no-fail-fast --failure-output=immediate-final
|
||||
|
||||
- name: Run tests with sync feature
|
||||
run: >
|
||||
cargo nextest run
|
||||
--features sync
|
||||
--filter-expr 'test(util::sync::tests::share_gradual_taiko)'
|
||||
--filter-expr 'test(taiko::difficulty::gradual::tests::next_and_nth)'
|
||||
--no-fail-fast --failure-output=immediate-final
|
||||
|
||||
- name: Run doctests
|
||||
run: cargo test --doc
|
||||
run: cargo test --no-default-features --doc
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sync = []
|
||||
tracing = ["rosu-map/tracing"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -48,7 +48,7 @@ let stars = diff_attrs.stars();
|
||||
// Calculate performance attributes
|
||||
let perf_attrs = map.performance()
|
||||
// To speed up the calculation, we can use the previous attributes.
|
||||
// **Note** that this should only be done if the map, mode, mods,
|
||||
// **Note** that this should only be done if the map, mode, mods,
|
||||
// clock rate, and amount of passed objects stay the same.
|
||||
// Otherwise, the final attributes will be incorrect.
|
||||
.attributes(diff_attrs)
|
||||
@@ -79,7 +79,8 @@ TODO
|
||||
| Flag | Description | Dependencies
|
||||
| --------- | ----------- | ------------
|
||||
| `default` | No features |
|
||||
| `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is not enabled, errors will be ignored. | [`tracing`]
|
||||
| `sync` | Some gradual calculation types can only be shared across threads if this feature is enabled. This adds a performance penalty so only enable this if really needed. |
|
||||
| `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is **not** enabled, errors will be ignored. | [`tracing`]
|
||||
|
||||
### Bindings
|
||||
|
||||
|
||||
+3
-2
@@ -42,7 +42,7 @@
|
||||
//! // Calculate performance attributes
|
||||
//! let perf_attrs = map.performance()
|
||||
//! // To speed up the calculation, we can use the previous attributes.
|
||||
//! // **Note** that this should only be done if the map, mode, mods,
|
||||
//! // **Note** that this should only be done if the map, mode, mods,
|
||||
//! // clock rate, and amount of passed objects stay the same.
|
||||
//! // Otherwise, the final attributes will be incorrect.
|
||||
//! .attributes(diff_attrs)
|
||||
@@ -73,7 +73,8 @@
|
||||
//! | Flag | Description | Dependencies
|
||||
//! | --------- | ----------- | ------------
|
||||
//! | `default` | No features |
|
||||
//! | `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is not enabled, errors will be ignored. | [`tracing`]
|
||||
//! | `sync` | Some gradual calculation types can only be shared across threads if this feature is enabled. This adds a performance penalty so only enable this if really needed. |
|
||||
//! | `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is **not** enabled, errors will be ignored. | [`tracing`]
|
||||
//!
|
||||
//! ## Bindings
|
||||
//!
|
||||
|
||||
@@ -1,41 +1,39 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::{Rc, Weak},
|
||||
use crate::{
|
||||
taiko::difficulty::object::TaikoDifficultyObject,
|
||||
util::sync::{RefCount, Weak},
|
||||
};
|
||||
|
||||
use crate::taiko::difficulty::object::TaikoDifficultyObject;
|
||||
|
||||
use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AlternatingMonoPattern {
|
||||
pub mono_streaks: Vec<Rc<RefCell<MonoStreak>>>,
|
||||
pub parent: Option<Weak<RefCell<RepeatingHitPatterns>>>,
|
||||
pub mono_streaks: Vec<RefCount<MonoStreak>>,
|
||||
pub parent: Option<Weak<RepeatingHitPatterns>>,
|
||||
pub idx: usize,
|
||||
}
|
||||
|
||||
impl AlternatingMonoPattern {
|
||||
pub fn new() -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
pub fn new() -> RefCount<Self> {
|
||||
RefCount::new(Self {
|
||||
mono_streaks: Vec::new(),
|
||||
parent: None,
|
||||
idx: 0,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_repetition_of(&self, other: &Self) -> bool {
|
||||
self.has_identical_mono_len(other)
|
||||
&& self.mono_streaks.len() == other.mono_streaks.len()
|
||||
&& self.mono_streaks[0].borrow().hit_type() == other.mono_streaks[0].borrow().hit_type()
|
||||
&& self.mono_streaks[0].get().hit_type() == other.mono_streaks[0].get().hit_type()
|
||||
}
|
||||
|
||||
pub fn has_identical_mono_len(&self, other: &Self) -> bool {
|
||||
self.mono_streaks[0].borrow().run_len() == other.mono_streaks[0].borrow().run_len()
|
||||
self.mono_streaks[0].get().run_len() == other.mono_streaks[0].get().run_len()
|
||||
}
|
||||
|
||||
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
|
||||
self.mono_streaks
|
||||
.first()
|
||||
.and_then(|mono| mono.borrow().first_hit_object())
|
||||
.and_then(|mono| mono.get().first_hit_object())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
use crate::util::sync::{RefCount, Weak};
|
||||
|
||||
use self::{
|
||||
alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak,
|
||||
@@ -15,7 +12,7 @@ pub mod repeating_hit_patterns;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TaikoDifficultyColor {
|
||||
pub mono_streak: Option<Weak<RefCell<MonoStreak>>>,
|
||||
pub alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern>>>,
|
||||
pub repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns>>>,
|
||||
pub mono_streak: Option<Weak<MonoStreak>>,
|
||||
pub alternating_mono_pattern: Option<Weak<AlternatingMonoPattern>>,
|
||||
pub repeating_hit_patterns: Option<RefCount<RepeatingHitPatterns>>,
|
||||
}
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::{Rc, Weak},
|
||||
use crate::{
|
||||
taiko::{difficulty::object::TaikoDifficultyObject, object::HitType},
|
||||
util::sync::{RefCount, Weak},
|
||||
};
|
||||
|
||||
use crate::taiko::{difficulty::object::TaikoDifficultyObject, object::HitType};
|
||||
|
||||
use super::alternating_mono_pattern::AlternatingMonoPattern;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MonoStreak {
|
||||
pub hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject>>>,
|
||||
pub parent: Option<Weak<RefCell<AlternatingMonoPattern>>>,
|
||||
pub hit_objects: Vec<Weak<TaikoDifficultyObject>>,
|
||||
pub parent: Option<Weak<AlternatingMonoPattern>>,
|
||||
pub idx: usize,
|
||||
}
|
||||
|
||||
impl MonoStreak {
|
||||
pub fn new() -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
pub fn new() -> RefCount<Self> {
|
||||
RefCount::new(Self {
|
||||
hit_objects: Vec::new(),
|
||||
parent: None,
|
||||
idx: 0,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run_len(&self) -> usize {
|
||||
@@ -31,10 +29,10 @@ impl MonoStreak {
|
||||
self.hit_objects
|
||||
.first()
|
||||
.and_then(Weak::upgrade)
|
||||
.map(|h| h.borrow().base_hit_type)
|
||||
.map(|h| h.get().base_hit_type)
|
||||
}
|
||||
|
||||
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
|
||||
self.hit_objects.first().and_then(Weak::upgrade)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
collections::VecDeque,
|
||||
rc::Rc,
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::taiko::difficulty::object::TaikoDifficultyObjects;
|
||||
use crate::{
|
||||
taiko::difficulty::object::TaikoDifficultyObjects,
|
||||
util::sync::{Ref, RefCount},
|
||||
};
|
||||
|
||||
use super::{
|
||||
alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak,
|
||||
@@ -18,54 +17,54 @@ impl ColorDifficultyPreprocessor {
|
||||
let hit_patterns = Self::encode(hit_objects);
|
||||
|
||||
for repeating_hit_pattern in hit_patterns {
|
||||
if let Some(obj) = repeating_hit_pattern.borrow().first_hit_object() {
|
||||
obj.borrow_mut().color.repeating_hit_patterns =
|
||||
Some(Rc::clone(&repeating_hit_pattern));
|
||||
if let Some(obj) = repeating_hit_pattern.get().first_hit_object() {
|
||||
obj.get_mut().color.repeating_hit_patterns =
|
||||
Some(RefCount::clone(&repeating_hit_pattern));
|
||||
}
|
||||
|
||||
let mono_patterns = Ref::map(repeating_hit_pattern.borrow(), |repeating| {
|
||||
let mono_patterns = Ref::map(repeating_hit_pattern.get(), |repeating| {
|
||||
repeating.alternating_mono_patterns.as_slice()
|
||||
});
|
||||
|
||||
for (i, mono_pattern) in mono_patterns.iter().enumerate() {
|
||||
{
|
||||
let mut mono_pattern = mono_pattern.borrow_mut();
|
||||
mono_pattern.parent = Some(Rc::downgrade(&repeating_hit_pattern));
|
||||
let mut mono_pattern = mono_pattern.get_mut();
|
||||
mono_pattern.parent = Some(RefCount::downgrade(&repeating_hit_pattern));
|
||||
mono_pattern.idx = i;
|
||||
}
|
||||
|
||||
if let Some(obj) = mono_pattern.borrow().first_hit_object() {
|
||||
obj.borrow_mut().color.alternating_mono_pattern =
|
||||
Some(Rc::downgrade(mono_pattern));
|
||||
if let Some(obj) = mono_pattern.get().first_hit_object() {
|
||||
obj.get_mut().color.alternating_mono_pattern =
|
||||
Some(RefCount::downgrade(mono_pattern));
|
||||
}
|
||||
|
||||
let mono_streaks = Ref::map(mono_pattern.borrow(), |alternating| {
|
||||
let mono_streaks = Ref::map(mono_pattern.get(), |alternating| {
|
||||
alternating.mono_streaks.as_slice()
|
||||
});
|
||||
|
||||
for (j, mono_streak) in mono_streaks.iter().enumerate() {
|
||||
{
|
||||
let mut borrowed = mono_streak.borrow_mut();
|
||||
borrowed.parent = Some(Rc::downgrade(mono_pattern));
|
||||
let mut borrowed = mono_streak.get_mut();
|
||||
borrowed.parent = Some(RefCount::downgrade(mono_pattern));
|
||||
borrowed.idx = j;
|
||||
}
|
||||
|
||||
if let Some(obj) = mono_streak.borrow().first_hit_object() {
|
||||
obj.borrow_mut().color.mono_streak = Some(Rc::downgrade(mono_streak));
|
||||
if let Some(obj) = mono_streak.get().first_hit_object() {
|
||||
obj.get_mut().color.mono_streak = Some(RefCount::downgrade(mono_streak));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(data: &TaikoDifficultyObjects) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
|
||||
fn encode(data: &TaikoDifficultyObjects) -> Vec<RefCount<RepeatingHitPatterns>> {
|
||||
let mono_streaks = Self::encode_mono_streaks(data);
|
||||
let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks);
|
||||
|
||||
Self::encode_repeating_hit_patterns(alternating_mono_patterns)
|
||||
}
|
||||
|
||||
fn encode_mono_streaks(data: &TaikoDifficultyObjects) -> Vec<Rc<RefCell<MonoStreak>>> {
|
||||
fn encode_mono_streaks(data: &TaikoDifficultyObjects) -> Vec<RefCount<MonoStreak>> {
|
||||
let mut data_iter = data.objects.iter();
|
||||
|
||||
let Some(taiko_object) = data_iter.next() else {
|
||||
@@ -76,15 +75,15 @@ impl ColorDifficultyPreprocessor {
|
||||
let mut curr_mono_streak = mono_streaks.last();
|
||||
|
||||
if let Some(curr) = curr_mono_streak {
|
||||
curr.borrow_mut()
|
||||
curr.get_mut()
|
||||
.hit_objects
|
||||
.push(Rc::downgrade(taiko_object));
|
||||
.push(RefCount::downgrade(taiko_object));
|
||||
}
|
||||
|
||||
for taiko_object in data_iter {
|
||||
let condition = data
|
||||
.previous_note(&taiko_object.borrow(), 0)
|
||||
.filter(|prev| taiko_object.borrow().base_hit_type == prev.borrow().base_hit_type);
|
||||
.previous_note(&taiko_object.get(), 0)
|
||||
.filter(|prev| taiko_object.get().base_hit_type == prev.get().base_hit_type);
|
||||
|
||||
if condition.is_none() {
|
||||
mono_streaks.push(MonoStreak::new());
|
||||
@@ -92,9 +91,9 @@ impl ColorDifficultyPreprocessor {
|
||||
}
|
||||
|
||||
if let Some(curr) = curr_mono_streak {
|
||||
curr.borrow_mut()
|
||||
curr.get_mut()
|
||||
.hit_objects
|
||||
.push(Rc::downgrade(taiko_object));
|
||||
.push(RefCount::downgrade(taiko_object));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +101,8 @@ impl ColorDifficultyPreprocessor {
|
||||
}
|
||||
|
||||
fn encode_alternating_mono_pattern(
|
||||
data: Vec<Rc<RefCell<MonoStreak>>>,
|
||||
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> {
|
||||
data: Vec<RefCount<MonoStreak>>,
|
||||
) -> VecDeque<RefCount<AlternatingMonoPattern>> {
|
||||
let mut data = data.into_iter();
|
||||
|
||||
let Some(mono) = data.next() else {
|
||||
@@ -114,14 +113,14 @@ impl ColorDifficultyPreprocessor {
|
||||
mono_patterns.push_back(AlternatingMonoPattern::new());
|
||||
let mut curr_mono_pattern = mono_patterns.back();
|
||||
|
||||
let mut prev_run_len = mono.borrow().run_len();
|
||||
let mut prev_run_len = mono.get().run_len();
|
||||
|
||||
if let Some(curr) = curr_mono_pattern {
|
||||
curr.borrow_mut().mono_streaks.push(mono);
|
||||
curr.get_mut().mono_streaks.push(mono);
|
||||
}
|
||||
|
||||
for mono in data {
|
||||
let run_len = mono.borrow().run_len();
|
||||
let run_len = mono.get().run_len();
|
||||
|
||||
if run_len != prev_run_len {
|
||||
mono_patterns.push_back(AlternatingMonoPattern::new());
|
||||
@@ -131,7 +130,7 @@ impl ColorDifficultyPreprocessor {
|
||||
prev_run_len = run_len;
|
||||
|
||||
if let Some(curr_mono_pattern) = curr_mono_pattern {
|
||||
curr_mono_pattern.borrow_mut().mono_streaks.push(mono);
|
||||
curr_mono_pattern.get_mut().mono_streaks.push(mono);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,50 +138,50 @@ impl ColorDifficultyPreprocessor {
|
||||
}
|
||||
|
||||
fn encode_repeating_hit_patterns(
|
||||
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>,
|
||||
) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
|
||||
mut data: VecDeque<RefCount<AlternatingMonoPattern>>,
|
||||
) -> Vec<RefCount<RepeatingHitPatterns>> {
|
||||
let mut hit_patterns = Vec::new();
|
||||
let mut curr_hit_pattern = None;
|
||||
|
||||
while !data.is_empty() {
|
||||
let old = curr_hit_pattern.as_ref().map(Rc::downgrade);
|
||||
let old = curr_hit_pattern.as_ref().map(RefCount::downgrade);
|
||||
let curr_hit_pattern = &*curr_hit_pattern.insert(RepeatingHitPatterns::new(old));
|
||||
|
||||
let mut is_coupled = data.get(2).map_or(false, |other| {
|
||||
data[0].borrow().is_repetition_of(&other.borrow())
|
||||
});
|
||||
let mut is_coupled = data
|
||||
.get(2)
|
||||
.map_or(false, |other| data[0].get().is_repetition_of(&other.get()));
|
||||
|
||||
if is_coupled {
|
||||
while is_coupled {
|
||||
curr_hit_pattern
|
||||
.borrow_mut()
|
||||
.get_mut()
|
||||
.alternating_mono_patterns
|
||||
.push(data.pop_front().unwrap());
|
||||
|
||||
is_coupled = data.get(2).map_or(false, |other| {
|
||||
data[0].borrow().is_repetition_of(&other.borrow())
|
||||
});
|
||||
is_coupled = data
|
||||
.get(2)
|
||||
.map_or(false, |other| data[0].get().is_repetition_of(&other.get()));
|
||||
}
|
||||
|
||||
for front in data.drain(..2) {
|
||||
curr_hit_pattern
|
||||
.borrow_mut()
|
||||
.get_mut()
|
||||
.alternating_mono_patterns
|
||||
.push(front);
|
||||
}
|
||||
} else {
|
||||
curr_hit_pattern
|
||||
.borrow_mut()
|
||||
.get_mut()
|
||||
.alternating_mono_patterns
|
||||
.push(data.pop_front().unwrap());
|
||||
}
|
||||
|
||||
hit_patterns.push(Rc::clone(curr_hit_pattern));
|
||||
hit_patterns.push(RefCount::clone(curr_hit_pattern));
|
||||
}
|
||||
|
||||
hit_patterns
|
||||
.iter_mut()
|
||||
.for_each(|pattern| pattern.borrow_mut().find_repetition_interval());
|
||||
.for_each(|pattern| pattern.get_mut().find_repetition_interval());
|
||||
|
||||
hit_patterns
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
cmp,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
use std::cmp;
|
||||
|
||||
use crate::taiko::difficulty::object::TaikoDifficultyObject;
|
||||
use crate::{
|
||||
taiko::difficulty::object::TaikoDifficultyObject,
|
||||
util::sync::{RefCount, Weak},
|
||||
};
|
||||
|
||||
use super::alternating_mono_pattern::AlternatingMonoPattern;
|
||||
|
||||
@@ -12,18 +11,18 @@ const MAX_REPETITION_INTERVAL: usize = 16;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RepeatingHitPatterns {
|
||||
pub alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern>>>,
|
||||
pub prev: Option<Weak<RefCell<Self>>>,
|
||||
pub alternating_mono_patterns: Vec<RefCount<AlternatingMonoPattern>>,
|
||||
pub prev: Option<Weak<Self>>,
|
||||
pub repetition_interval: usize,
|
||||
}
|
||||
|
||||
impl RepeatingHitPatterns {
|
||||
pub fn new(prev: Option<Weak<RefCell<Self>>>) -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self {
|
||||
pub fn new(prev: Option<Weak<Self>>) -> RefCount<Self> {
|
||||
RefCount::new(Self {
|
||||
alternating_mono_patterns: Vec::new(),
|
||||
prev,
|
||||
repetition_interval: 0,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_repetition_interval(&mut self) {
|
||||
@@ -34,13 +33,13 @@ impl RepeatingHitPatterns {
|
||||
let mut interval = 1;
|
||||
|
||||
while interval < MAX_REPETITION_INTERVAL {
|
||||
if self.is_repetition_of(&other.borrow()) {
|
||||
if self.is_repetition_of(&other.get()) {
|
||||
self.repetition_interval = cmp::min(interval, MAX_REPETITION_INTERVAL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(next) = other.borrow().prev.as_ref().and_then(Weak::upgrade) else {
|
||||
let Some(next) = other.get().prev.as_ref().and_then(Weak::upgrade) else {
|
||||
break;
|
||||
};
|
||||
|
||||
@@ -60,16 +59,12 @@ impl RepeatingHitPatterns {
|
||||
.iter()
|
||||
.zip(other.alternating_mono_patterns.iter())
|
||||
.take(2)
|
||||
.all(|(self_pat, other_pat)| {
|
||||
self_pat
|
||||
.borrow()
|
||||
.has_identical_mono_len(&other_pat.borrow())
|
||||
})
|
||||
.all(|(self_pat, other_pat)| self_pat.get().has_identical_mono_len(&other_pat.get()))
|
||||
}
|
||||
|
||||
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
|
||||
self.alternating_mono_patterns
|
||||
.first()
|
||||
.and_then(|mono| mono.borrow().first_hit_object())
|
||||
.and_then(|mono| mono.get().first_hit_object())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::{cell::RefCell, mem, rc::Rc, slice::Iter};
|
||||
use std::{mem, slice::Iter};
|
||||
|
||||
use crate::{
|
||||
model::{beatmap::HitWindows, hit_object::HitObject},
|
||||
taiko::TaikoBeatmap,
|
||||
util::sync::RefCount,
|
||||
ModeDifficulty,
|
||||
};
|
||||
|
||||
@@ -52,7 +53,7 @@ pub struct TaikoGradualDifficulty {
|
||||
pub(crate) clock_rate: f64,
|
||||
attrs: TaikoDifficultyAttributes,
|
||||
diff_objects: TaikoDifficultyObjects,
|
||||
diff_objects_iter: Iter<'static, Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
diff_objects_iter: Iter<'static, RefCount<TaikoDifficultyObject>>,
|
||||
peaks: Peaks,
|
||||
total_hits: usize,
|
||||
first_combos: FirstTwoCombos,
|
||||
@@ -132,8 +133,8 @@ impl TaikoGradualDifficulty {
|
||||
}
|
||||
|
||||
fn extend_lifetime(
|
||||
iter: Iter<'_, Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
) -> Iter<'static, Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
iter: Iter<'_, RefCount<TaikoDifficultyObject>>,
|
||||
) -> Iter<'static, RefCount<TaikoDifficultyObject>> {
|
||||
// SAFETY: The underlying data will never be moved.
|
||||
unsafe { mem::transmute(iter) }
|
||||
}
|
||||
@@ -149,7 +150,7 @@ impl Iterator for TaikoGradualDifficulty {
|
||||
if self.idx >= 2 {
|
||||
loop {
|
||||
let curr = self.diff_objects_iter.next()?;
|
||||
let borrowed = curr.borrow();
|
||||
let borrowed = curr.get();
|
||||
PeaksSkill::new(&mut self.peaks, &self.diff_objects).process(&borrowed);
|
||||
|
||||
if borrowed.base_hit_type.is_hit() {
|
||||
@@ -236,7 +237,7 @@ impl Iterator for TaikoGradualDifficulty {
|
||||
for _ in 0..take {
|
||||
loop {
|
||||
let curr = self.diff_objects_iter.next()?;
|
||||
let borrowed = curr.borrow();
|
||||
let borrowed = curr.get();
|
||||
peaks.process(&borrowed);
|
||||
|
||||
if borrowed.base_hit_type.is_hit() {
|
||||
|
||||
@@ -98,7 +98,7 @@ impl DifficultyValues {
|
||||
let mut peaks = PeaksSkill::new(&mut peaks, &diff_objects);
|
||||
|
||||
for hit_object in diff_objects.iter().take(n_diff_objects) {
|
||||
peaks.process(&hit_object.borrow());
|
||||
peaks.process(&hit_object.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::{cell::RefCell, rc::Rc, slice::Iter};
|
||||
use std::slice::Iter;
|
||||
|
||||
use crate::{
|
||||
any::difficulty::object::IDifficultyObject,
|
||||
taiko::object::{HitType, TaikoObject},
|
||||
util::sync::RefCount,
|
||||
};
|
||||
|
||||
use super::{color::TaikoDifficultyColor, rhythm::HitObjectRhythm};
|
||||
@@ -27,7 +28,7 @@ impl TaikoDifficultyObject {
|
||||
clock_rate: f64,
|
||||
idx: usize,
|
||||
objects: &mut TaikoDifficultyObjects,
|
||||
) -> Rc<RefCell<Self>> {
|
||||
) -> RefCount<Self> {
|
||||
let delta_time = (hit_object.start_time - last_object.start_time) / clock_rate;
|
||||
let rhythm = closest_rhythm(delta_time, last_object, last_last_object, clock_rate);
|
||||
let color = TaikoDifficultyColor::default();
|
||||
@@ -47,7 +48,7 @@ impl TaikoDifficultyObject {
|
||||
HitType::NonHit => MonoIndex::None,
|
||||
};
|
||||
|
||||
let this = Rc::new(RefCell::new(Self {
|
||||
let this = RefCount::new(Self {
|
||||
idx,
|
||||
delta_time,
|
||||
start_time: hit_object.start_time / clock_rate,
|
||||
@@ -56,16 +57,16 @@ impl TaikoDifficultyObject {
|
||||
note_idx,
|
||||
rhythm,
|
||||
color,
|
||||
}));
|
||||
});
|
||||
|
||||
match hit_object.hit_type {
|
||||
HitType::Center => {
|
||||
objects.note_objects.push(Rc::clone(&this));
|
||||
objects.center_hit_objects.push(Rc::clone(&this));
|
||||
objects.note_objects.push(RefCount::clone(&this));
|
||||
objects.center_hit_objects.push(RefCount::clone(&this));
|
||||
}
|
||||
HitType::Rim => {
|
||||
objects.note_objects.push(Rc::clone(&this));
|
||||
objects.rim_hit_objects.push(Rc::clone(&this));
|
||||
objects.note_objects.push(RefCount::clone(&this));
|
||||
objects.rim_hit_objects.push(RefCount::clone(&this));
|
||||
}
|
||||
HitType::NonHit => {}
|
||||
}
|
||||
@@ -82,10 +83,10 @@ pub enum MonoIndex {
|
||||
}
|
||||
|
||||
pub struct TaikoDifficultyObjects {
|
||||
pub objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
pub center_hit_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
pub rim_hit_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
pub note_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
pub objects: Vec<RefCount<TaikoDifficultyObject>>,
|
||||
pub center_hit_objects: Vec<RefCount<TaikoDifficultyObject>>,
|
||||
pub rim_hit_objects: Vec<RefCount<TaikoDifficultyObject>>,
|
||||
pub note_objects: Vec<RefCount<TaikoDifficultyObject>>,
|
||||
}
|
||||
|
||||
impl TaikoDifficultyObjects {
|
||||
@@ -101,7 +102,7 @@ impl TaikoDifficultyObjects {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, hit_object: Rc<RefCell<TaikoDifficultyObject>>) {
|
||||
pub fn push(&mut self, hit_object: RefCount<TaikoDifficultyObject>) {
|
||||
self.objects.push(hit_object);
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ impl TaikoDifficultyObjects {
|
||||
self.objects.is_empty()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_, Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
pub fn iter(&self) -> Iter<'_, RefCount<TaikoDifficultyObject>> {
|
||||
self.objects.iter()
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ impl TaikoDifficultyObjects {
|
||||
&self,
|
||||
curr: &TaikoDifficultyObject,
|
||||
mut backwards_idx: usize,
|
||||
) -> Option<&Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
) -> Option<&RefCount<TaikoDifficultyObject>> {
|
||||
backwards_idx += 1;
|
||||
|
||||
match curr.mono_idx {
|
||||
@@ -135,7 +136,7 @@ impl TaikoDifficultyObjects {
|
||||
&self,
|
||||
curr: &TaikoDifficultyObject,
|
||||
backwards_idx: usize,
|
||||
) -> Option<&Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
) -> Option<&RefCount<TaikoDifficultyObject>> {
|
||||
curr.note_idx
|
||||
.checked_sub(backwards_idx + 1)
|
||||
.and_then(|idx| self.note_objects.get(idx))
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
f64::consts::E,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
use std::f64::consts::E;
|
||||
|
||||
use crate::{
|
||||
any::difficulty::{
|
||||
@@ -16,6 +12,7 @@ use crate::{
|
||||
},
|
||||
object::{TaikoDifficultyObject, TaikoDifficultyObjects},
|
||||
},
|
||||
util::sync::{RefCount, Weak},
|
||||
};
|
||||
|
||||
const SKILL_MULTIPLIER: f64 = 0.12;
|
||||
@@ -65,7 +62,7 @@ impl Skill<'_, Color> {
|
||||
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
let prev_start_time = curr
|
||||
.previous(0, &self.diff_objects.objects)
|
||||
.map_or(0.0, |prev| prev.borrow().start_time);
|
||||
.map_or(0.0, |prev| prev.get().start_time);
|
||||
|
||||
self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
|
||||
}
|
||||
@@ -113,8 +110,8 @@ impl ColorEvaluator {
|
||||
sigmoid * (height / 2.0) + middle
|
||||
}
|
||||
|
||||
fn evaluate_diff_of_mono_streak(mono_streak: &Rc<RefCell<MonoStreak>>) -> f64 {
|
||||
let mono_streak = mono_streak.borrow();
|
||||
fn evaluate_diff_of_mono_streak(mono_streak: &RefCount<MonoStreak>) -> f64 {
|
||||
let mono_streak = mono_streak.get();
|
||||
|
||||
let parent_eval = mono_streak
|
||||
.parent
|
||||
@@ -127,9 +124,9 @@ impl ColorEvaluator {
|
||||
}
|
||||
|
||||
fn evaluate_diff_of_alternating_mono_pattern(
|
||||
alternating_mono_pattern: &Rc<RefCell<AlternatingMonoPattern>>,
|
||||
alternating_mono_pattern: &RefCount<AlternatingMonoPattern>,
|
||||
) -> f64 {
|
||||
let alternating_mono_pattern = alternating_mono_pattern.borrow();
|
||||
let alternating_mono_pattern = alternating_mono_pattern.get();
|
||||
|
||||
let parent_eval = alternating_mono_pattern
|
||||
.parent
|
||||
@@ -142,9 +139,9 @@ impl ColorEvaluator {
|
||||
}
|
||||
|
||||
fn evaluate_diff_of_repeating_hit_patterns(
|
||||
repeating_hit_patterns: &Rc<RefCell<RepeatingHitPatterns>>,
|
||||
repeating_hit_patterns: &RefCount<RepeatingHitPatterns>,
|
||||
) -> f64 {
|
||||
let repetition_interval = repeating_hit_patterns.borrow().repetition_interval as f64;
|
||||
let repetition_interval = repeating_hit_patterns.get().repetition_interval as f64;
|
||||
|
||||
2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0))
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ impl Skill<'_, Rhythm> {
|
||||
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
let prev_start_time = curr
|
||||
.previous(0, &self.diff_objects.objects)
|
||||
.map_or(0.0, |prev| prev.borrow().start_time);
|
||||
.map_or(0.0, |prev| prev.get().start_time);
|
||||
|
||||
self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Skill<'_, Stamina> {
|
||||
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
let prev_start_time = curr
|
||||
.previous(0, &self.diff_objects.objects)
|
||||
.map_or(0.0, |prev| prev.borrow().start_time);
|
||||
.map_or(0.0, |prev| prev.get().start_time);
|
||||
|
||||
self.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ impl StaminaEvaluator {
|
||||
|
||||
if let Some(key_prev) = key_prev {
|
||||
// * Add a base strain to all objects
|
||||
0.5 + Self::speed_bonus(taiko_curr.start_time - key_prev.borrow().start_time)
|
||||
0.5 + Self::speed_bonus(taiko_curr.start_time - key_prev.get().start_time)
|
||||
} else {
|
||||
// * There is no previous hit object hit by the current key
|
||||
0.0
|
||||
|
||||
@@ -4,3 +4,4 @@ pub mod map_or_attrs;
|
||||
pub mod mods;
|
||||
pub mod random;
|
||||
pub mod sort;
|
||||
pub mod sync;
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
use std::fmt;
|
||||
|
||||
pub use inner::*;
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
mod inner {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
pub struct RefCount<T>(pub(super) Rc<RefCell<T>>);
|
||||
|
||||
pub struct Weak<T>(pub(super) std::rc::Weak<RefCell<T>>);
|
||||
|
||||
pub type Ref<'a, T> = std::cell::Ref<'a, T>;
|
||||
|
||||
pub type RefMut<'a, T> = std::cell::RefMut<'a, T>;
|
||||
|
||||
impl<T> RefCount<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self(Rc::new(RefCell::new(inner)))
|
||||
}
|
||||
|
||||
pub fn clone(this: &Self) -> Self {
|
||||
Self(Rc::clone(&this.0))
|
||||
}
|
||||
|
||||
pub fn downgrade(&self) -> Weak<T> {
|
||||
Weak(Rc::downgrade(&self.0))
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Ref<'_, T> {
|
||||
self.0.borrow()
|
||||
}
|
||||
|
||||
pub fn get_mut(&self) -> RefMut<'_, T> {
|
||||
self.0.borrow_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
mod inner {
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
ops,
|
||||
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
};
|
||||
|
||||
pub struct RefCount<T>(pub(super) Arc<RwLock<T>>);
|
||||
|
||||
pub struct Weak<T>(pub(super) std::sync::Weak<RwLock<T>>);
|
||||
|
||||
pub struct Ref<'a, T: ?Sized>(RwLockReadGuard<'a, T>);
|
||||
|
||||
pub type RefMut<'a, T> = RwLockWriteGuard<'a, T>;
|
||||
|
||||
impl<T> RefCount<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self(Arc::new(RwLock::new(inner)))
|
||||
}
|
||||
|
||||
pub fn clone(this: &Self) -> Self {
|
||||
Self(Arc::clone(&this.0))
|
||||
}
|
||||
|
||||
pub fn downgrade(&self) -> Weak<T> {
|
||||
Weak(Arc::downgrade(&self.0))
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Ref<'_, T> {
|
||||
Ref(self.0.read().unwrap())
|
||||
}
|
||||
|
||||
pub fn get_mut(&self) -> RefMut<'_, T> {
|
||||
self.0.write().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ref<'_, T> {
|
||||
pub fn map<U: ?Sized, F>(orig: Ref<'_, T>, f: F) -> RefWrap<'_, T, U, F>
|
||||
where
|
||||
F: Copy + FnOnce(&T) -> &U,
|
||||
{
|
||||
RefWrap {
|
||||
orig,
|
||||
access: f,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RefWrap<'a, T, U: ?Sized, F> {
|
||||
orig: Ref<'a, T>,
|
||||
access: F,
|
||||
_phantom: PhantomData<U>,
|
||||
}
|
||||
|
||||
impl<T, U: ?Sized, F> ops::Deref for RefWrap<'_, T, U, F>
|
||||
where
|
||||
F: Copy + FnOnce(&T) -> &U,
|
||||
{
|
||||
type Target = U;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
(self.access)(&self.orig)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> ops::Deref for Ref<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
ops::Deref::deref(&self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Weak<T> {
|
||||
pub fn upgrade(&self) -> Option<RefCount<T>> {
|
||||
self.0.upgrade().map(RefCount)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for RefCount<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Weak<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// ```compile_fail
|
||||
/// use rosu_pp::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
|
||||
///
|
||||
/// let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
||||
/// let difficulty = ModeDifficulty::new();
|
||||
/// let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||
///
|
||||
/// // Rc<RefCell<_>> cannot be shared across threads so compilation should fail
|
||||
/// std::thread::spawn(move || { let _ = gradual.next(); });
|
||||
/// ```
|
||||
#[cfg(not(feature = "sync"))]
|
||||
fn _share_gradual_taiko() {}
|
||||
|
||||
#[cfg(all(test, feature = "sync"))]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn share_gradual_taiko() {
|
||||
use crate::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
|
||||
|
||||
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
||||
let difficulty = ModeDifficulty::new();
|
||||
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||
|
||||
// Arc<RwLock<_>> *can* be shared across threads so this should compile
|
||||
std::thread::spawn(move || {
|
||||
let _ = gradual.next();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user