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