Compare commits

..

No commits in common. "e2649ce6ed10ffa352407d3bacb1e142ed473ef2" and "0843c774731ae98927c12fbb6b30dfbb4ddf06bb" have entirely different histories.

5 changed files with 82 additions and 84 deletions

4
Cargo.lock generated
View file

@ -1420,7 +1420,7 @@ dependencies = [
"security-framework", "security-framework",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1570,7 +1570,7 @@ dependencies = [
[[package]] [[package]]
name = "serenity" name = "serenity"
version = "0.12.5" version = "0.12.5"
source = "git+https://github.com/serenity-rs/serenity.git?branch=next#37b9f433ada8b9ccc5f93f04826403b175855f86" source = "git+https://github.com/serenity-rs/serenity.git?rev=refs%2Fheads%2Fnext#37b9f433ada8b9ccc5f93f04826403b175855f86"
dependencies = [ dependencies = [
"aformat", "aformat",
"arrayvec", "arrayvec",

View file

@ -5,7 +5,7 @@ edition = "2024"
[dependencies] [dependencies]
dotenv = "0.15.0" dotenv = "0.15.0"
serenity = { version = "0.12.5" } serenity = { git = "https://github.com/serenity-rs/serenity.git", rev = "refs/heads/next" , features = ["collector"] }
tokio = { version = "1.53.1", features = ["rt-multi-thread"] } tokio = { version = "1.53.1", features = ["rt-multi-thread"] }
tracing = "0.1.44" tracing = "0.1.44"
tracing-subscriber = "0.3.23" tracing-subscriber = "0.3.23"
@ -15,6 +15,3 @@ thiserror = "2.0.20"
nom-language = "0.1.0" nom-language = "0.1.0"
rand_xoshiro = "0.8.1" rand_xoshiro = "0.8.1"
rand = "0.10.2" rand = "0.10.2"
[patch.crates-io]
serenity = { git = "https://github.com/serenity-rs/serenity.git", branch = "next" }

View file

@ -1,6 +1,4 @@
use crate::limits::{ use crate::limits::{MAX_DICE_COUNT_PER_ROLL, MAX_DISPLAYED_DICE_PER_MESSAGE, RESULT_CULL_CHARACTER_THRESHOLD};
MAX_DICE_COUNT_PER_ROLL, MAX_DISPLAYED_DICE_PER_MESSAGE, RESULT_CULL_CHARACTER_THRESHOLD,
};
use anyhow::bail; use anyhow::bail;
use rand::{Rng, RngExt}; use rand::{Rng, RngExt};
use std::convert::Infallible; use std::convert::Infallible;
@ -118,23 +116,27 @@ impl Expression {
// Exploding dice are early, they add rolls // Exploding dice are early, they add rolls
if let Some(fragments) = x { if let Some(fragments) = x {
write!(w, "x")?; write!(w, "x")?;
let comparers = fragments let mut comparers = Vec::with_capacity(fragments.len());
.iter() for fragment in fragments {
.map(|fragment| fragment.comparer(rng, w)) comparers.push(fragment.comparer(rng, w)?);
.collect::<Result<Vec<_>, _>>()?; }
let mut i = 0; let mut i = 0;
while i < rolls.len() { while i < rolls.len() {
if i > MAX_DICE_COUNT_PER_ROLL { if i > MAX_DICE_COUNT_PER_ROLL {
bail!("Explosion added too many dice.") bail!("Explosion added too many dice.")
} }
if comparers.is_empty() { if comparers.len() == 0 {
// Explode on max size dice // Explode on max size dice
if rolls[i] == size { if rolls[i] == size {
rolls.push(rng.random_range(1..=size)); rolls.push(rng.random_range(1..=size));
} }
} else { } else {
if comparers.iter().any(|comparer| comparer(rolls[i] as f64)) { // Explode based on comparers provided
rolls.push(rng.random_range(1..=size)); for comparer in comparers.iter() {
if comparer(rolls[i] as f64) {
rolls.push(rng.random_range(1..=size));
break;
}
} }
} }
i += 1; i += 1;
@ -185,25 +187,25 @@ impl Expression {
let mut skip = false; let mut skip = false;
let roll = rolls[idx]; let roll = rolls[idx];
let rank = ranks[idx]; let rank = ranks[idx];
if let Some(kh) = kh if let Some(kh) = kh {
&& rolls.len() - 1 - rank >= kh if rolls.len() - 1 - rank >= kh {
{ skip = true;
skip = true; }
} }
if let Some(dh) = dh if let Some(dh) = dh {
&& rolls.len() - 1 - rank < dh if rolls.len() - 1 - rank < dh {
{ skip = true;
skip = true; }
} }
if let Some(kl) = kl if let Some(kl) = kl {
&& rank >= kl if rank >= kl {
{ skip = true;
skip = true; }
} }
if let Some(dl) = dl if let Some(dl) = dl {
&& rank < dl if rank < dl {
{ skip = true;
skip = true; }
} }
if !skip { if !skip {
result_set.push(roll as f64); result_set.push(roll as f64);
@ -298,19 +300,19 @@ impl Expression {
bail!("Invalid die size.") bail!("Invalid die size.")
} }
if x.is_some() { if let Some(_) = x {
bail!("Not implemented yet"); bail!("Not implemented yet");
} }
if kh.is_some() { if let Some(_) = kh {
bail!("Not implemented yet"); bail!("Not implemented yet");
}; };
if kl.is_some() { if let Some(_) = kl {
bail!("Not implemented yet"); bail!("Not implemented yet");
}; };
if dh.is_some() { if let Some(_) = dh {
bail!("Not implemented yet"); bail!("Not implemented yet");
}; };
if dl.is_some() { if let Some(_) = dl {
bail!("Not implemented yet"); bail!("Not implemented yet");
}; };
@ -452,15 +454,12 @@ pub(crate) enum DiscordMdWitnessError {
} }
impl DiscordMd { impl DiscordMd {
pub(crate) fn create_witness(md: Arc<RwLock<Self>>) -> DiscordMdWitness { pub(crate) fn create_witness(md: &Arc<RwLock<Self>>) -> DiscordMdWitness {
let buffer_idx = { let mut parent = md.write().unwrap();
let mut md = md.write().unwrap(); parent.buffers.push(String::new());
md.buffers.push(String::new());
md.buffers.len() - 1
};
DiscordMdWitness { DiscordMdWitness {
parent: md, parent: md.clone(),
buffer_idx, buffer_idx: parent.buffers.len() - 1,
result: None, result: None,
} }
} }
@ -470,6 +469,7 @@ impl DiscordMd {
} }
} }
impl DiscordMdWitness { impl DiscordMdWitness {
pub(crate) fn end(self) { pub(crate) fn end(self) {
let mut parent = self.parent.write().unwrap(); let mut parent = self.parent.write().unwrap();
@ -477,9 +477,10 @@ impl DiscordMdWitness {
parent.buffers[self.buffer_idx] = self parent.buffers[self.buffer_idx] = self
.result .result
.map(|result| format!("**{result} = …**")) .map(|result| format!("**{result} = …**"))
.unwrap_or_default(); .unwrap_or_else(|| String::new());
} else if let Some(result) = self.result { } else if let Some(result) = self.result {
parent.buffers[self.buffer_idx].insert_str(0, &format!("**{result}** = ")); parent.buffers[self.buffer_idx]
.insert_str(0, &format!("**{result}** = "));
} }
} }
} }
@ -509,6 +510,7 @@ impl Witness for DiscordMdWitness {
} }
} }
impl<'a> WitnessSet for DiscordMdWitnessSet<'a> { impl<'a> WitnessSet for DiscordMdWitnessSet<'a> {
type Ok = (); type Ok = ();
type Error = DiscordMdWitnessError; type Error = DiscordMdWitnessError;
@ -517,7 +519,10 @@ impl<'a> WitnessSet for DiscordMdWitnessSet<'a> {
let mut parent = self.witness.parent.write().unwrap(); let mut parent = self.witness.parent.write().unwrap();
if parent.dice_written >= MAX_DISPLAYED_DICE_PER_MESSAGE { if parent.dice_written >= MAX_DISPLAYED_DICE_PER_MESSAGE {
if !self.dice_elided { if !self.dice_elided {
write!(parent.buffers[self.witness.buffer_idx], "…")?; write!(
parent.buffers[self.witness.buffer_idx],
"…"
)?;
self.dice_elided = true; self.dice_elided = true;
} }
return Ok(()); return Ok(());

View file

@ -137,7 +137,8 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
} }
}) })
.unwrap_or(1) .unwrap_or(1)
.clamp(1, MAX_ROLL_REPEATS as i64) as usize; .min(MAX_ROLL_REPEATS as i64)
.max(1) as usize;
let private = options let private = options
.iter() .iter()
.find(|option| option.name == "private") .find(|option| option.name == "private")
@ -169,10 +170,9 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
None None
} }
}) })
.map(|s| s.parse::<u64>().context("Seed could not be parsed. Make sure it is a 64 bit integer between 0 and 2^64-1.")) .and_then(|s| s.parse::<u64>().ok());
.transpose()?;
let expression = parsing::parse(formula).context("Dice formula could not be parsed.")?; let expression = parsing::parse(&formula).context("Dice formula could not be parsed.")?;
let seed = fixed_seed.unwrap_or_else(|| rand::rng().next_u64()); let seed = fixed_seed.unwrap_or_else(|| rand::rng().next_u64());
let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(seed); let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(seed);
@ -193,13 +193,13 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
let surrounding_par_count = pre_paragraphs.len() + post_paragraphs.len(); let surrounding_par_count = pre_paragraphs.len() + post_paragraphs.len();
let roll_paragraphs = if repeat <= 40 - surrounding_par_count { let paragraphs = if repeat <= 40 - surrounding_par_count {
let discord_md = Arc::new(std::sync::RwLock::new(DiscordMd { let discord_md = Arc::new(std::sync::RwLock::new(DiscordMd {
buffers: Vec::with_capacity(repeat), buffers: Vec::with_capacity(repeat),
dice_written: 0, dice_written: 0,
})); }));
for _ in 0..repeat { for _ in 0..repeat {
let mut witness = DiscordMd::create_witness(discord_md.clone()); let mut witness = DiscordMd::create_witness(&discord_md);
let result = expression.evaluate(&mut rng, &mut witness, i64::MAX)?; let result = expression.evaluate(&mut rng, &mut witness, i64::MAX)?;
witness.witness_total_result(result)?; witness.witness_total_result(result)?;
witness.end(); witness.end();
@ -208,7 +208,7 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
discord_md.buffers discord_md.buffers
} else { } else {
let mut par = String::with_capacity(8 * repeat); let mut par = String::new();
for i in 0..repeat { for i in 0..repeat {
if i > 0 { if i > 0 {
write!(par, ", ")?; write!(par, ", ")?;
@ -222,11 +222,9 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
vec![par] vec![par]
}; };
let paragraphs = { pre_paragraphs.extend(paragraphs);
pre_paragraphs.extend(roll_paragraphs); pre_paragraphs.extend(post_paragraphs);
pre_paragraphs.extend(post_paragraphs); let paragraphs = pre_paragraphs;
pre_paragraphs
};
if paragraphs.iter().map(|s| s.chars().count()).sum::<usize>() > 4000 if paragraphs.iter().map(|s| s.chars().count()).sum::<usize>() > 4000
|| paragraphs.len() > 40 || paragraphs.len() > 40
@ -255,29 +253,27 @@ pub async fn roll(ctx: &Context, command: &CommandInteraction) -> anyhow::Result
Ok(()) Ok(())
} }
match inner(ctx, command).await { if let Ok(_) = inner(ctx, command).await {
Ok(_) => {} } else if let Err(err) = inner(ctx, command).await {
Err(err) => { warn!("Error encountered: {err:?}");
warn!("Error encountered: {err:?}"); command
command .create_response(
.create_response( &ctx.http,
&ctx.http, CreateInteractionResponse::Message(
CreateInteractionResponse::Message( CreateInteractionResponseMessage::new()
CreateInteractionResponseMessage::new() .components(vec![CreateComponent::Container(CreateContainer::new(
.components(vec![CreateComponent::Container(CreateContainer::new( vec![CreateContainerComponent::TextDisplay(
vec![CreateContainerComponent::TextDisplay( CreateTextDisplay::new(err.to_string()),
CreateTextDisplay::new(err.to_string()), )],
)], ))])
))]) // flags needs to be called before ephemeral for correct ordering
// flags needs to be called before ephemeral for correct ordering .flags(
.flags( MessageFlags::IS_COMPONENTS_V2 | MessageFlags::SUPPRESS_NOTIFICATIONS,
MessageFlags::IS_COMPONENTS_V2 | MessageFlags::SUPPRESS_NOTIFICATIONS, )
) .ephemeral(true),
.ephemeral(true), ),
), )
) .await?
.await?
}
}; };
Ok(()) Ok(())
} }

View file

@ -80,7 +80,7 @@ fn expr<'c, 'i>(
) )
.map(|(count, size, (kh, kl, dh, dl, x))| { .map(|(count, size, (kh, kl, dh, dl, x))| {
Dice(DiceFormula { Dice(DiceFormula {
count: count.map(Box::new), count: count.map(|n| Box::new(n)),
size: Box::new(size), size: Box::new(size),
kh: kh.map(|n| Box::new(n.unwrap_or(Const(1f64)))), kh: kh.map(|n| Box::new(n.unwrap_or(Const(1f64)))),
kl: kl.map(|n| Box::new(n.unwrap_or(Const(1f64)))), kl: kl.map(|n| Box::new(n.unwrap_or(Const(1f64)))),