Skip to main content

root/
lib.rs

1mod arrow;
2mod directory;
3mod mount;
4mod node;
5pub use arrow::query::{Query, SearchDescriptionResult};
6//以后要把内存中的 handler task Sender Receiver 和 数据分开处理
7pub use mount::{Mount, Root};
8
9use std::cell::RefCell;
10use std::sync::LazyLock;
11
12use anyhow::{Result, anyhow};
13use dynamic::{Dynamic, MsgPack, MsgUnpack, Type};
14use rand::RngExt;
15
16use tokio::sync::mpsc;
17
18pub type Msg<T> = (T, Option<mpsc::Sender<T>>);
19pub type MsgSender<T> = mpsc::Sender<Msg<T>>;
20pub type MsgReceiver<T> = mpsc::Receiver<Msg<T>>;
21
22pub fn tx_rx<T: Send>() -> (MsgSender<T>, MsgReceiver<T>) {
23    let (tx, rx) = mpsc::channel(1024);
24    (tx, rx)
25}
26
27pub fn block_on_async<F, T>(f: F) -> T
28where
29    F: FnOnce() -> std::pin::Pin<Box<dyn Future<Output = T> + Send>> + 'static + Send,
30    T: Send + 'static,
31{
32    if tokio::runtime::Handle::try_current().is_ok() {
33        let (tx, rx) = tokio::sync::oneshot::channel();
34        tokio::task::spawn(async move {
35            let result = f().await;
36            let _ = tx.send(result);
37        });
38        tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(async { rx.await.unwrap() }))
39    } else {
40        let rt = tokio::runtime::Runtime::new().unwrap();
41        rt.block_on(f())
42    }
43}
44
45use smol_str::SmolStr;
46pub fn start_task<F>(info: Dynamic, f: F) -> Dynamic
47where
48    F: FnOnce() -> std::pin::Pin<Box<dyn Future<Output = Result<()>> + Send>> + 'static + Send,
49{
50    let id = uuid::Uuid::new_v4().to_string();
51    let task = SmolStr::new(format!("local/tasks/{}", id));
52    let r = if tokio::runtime::Handle::try_current().is_ok() {
53        Object::Task(tokio::task::spawn(async move { f().await }), info)
54    } else {
55        Object::ThreadTask(
56            std::thread::spawn(move || {
57                let rt = tokio::runtime::Runtime::new().unwrap();
58                rt.block_on(f())
59            }),
60            info,
61        )
62    };
63    let _ = add(&task, r);
64    log::info!("start task {:?}", task);
65    id.into()
66}
67
68#[macro_export]
69macro_rules! sync_await {
70    ($future:expr) => {
71        $crate::block_on_async(move || Box::pin($future))
72    };
73}
74
75pub fn send<T: Send + 'static>(tx: &MsgSender<T>, msg: T) -> Result<()> {
76    tx.try_send((msg, None)).map_err(|e| anyhow!("发送失败: {}", e))
77}
78
79pub fn call<T: Send + 'static>(tx: &MsgSender<T>, msg: T) -> Result<T> {
80    let (reply_tx, mut reply_rx) = mpsc::channel::<T>(1024);
81    tx.try_send((msg, Some(reply_tx))).map_err(|e| anyhow!("发送失败: {}", e))?;
82    reply_rx.try_recv().map_err(|e| anyhow!("接收回复失败: {}", e))
83}
84
85use tokio::task::JoinHandle;
86
87use crate::node::Node;
88#[derive(Debug)]
89pub enum Object {
90    Value(Dynamic),                                           //基本的值
91    Native(fn(Dynamic) -> Dynamic),                           //函数处理对象
92    Func(i64, Type),                                          //裸指针
93    Tx(MsgSender<Dynamic>, Dynamic),                          //包括 Tx 信息
94    Task(JoinHandle<Result<()>>, Dynamic),                    //异步任务
95    ThreadTask(std::thread::JoinHandle<Result<()>>, Dynamic), //同步任务
96}
97
98impl Into<Object> for Dynamic {
99    fn into(self) -> Object {
100        Object::Value(self)
101    }
102}
103
104impl Object {
105    pub fn value(&self) -> Dynamic {
106        match self {
107            Self::Value(v) => v.clone(),
108            Self::Task(_, info) => info.clone(),
109            Self::ThreadTask(_, info) => info.clone(),
110            Self::Tx(_, info) => info.clone(),
111            _ => Dynamic::Null,
112        }
113    }
114}
115
116impl Default for Object {
117    fn default() -> Self {
118        Self::Value(Dynamic::Null)
119    }
120}
121
122impl MsgPack for Object {
123    fn encode(&self, buf: &mut Vec<u8>) {
124        match self {
125            Self::Value(v) => v.encode(buf),
126            _ => {}
127        }
128    }
129}
130
131impl MsgUnpack for Object {
132    fn decode(buf: &[u8]) -> Result<(Self, usize)> {
133        let (v, len) = Dynamic::decode(buf)?;
134        Ok((Self::Value(v), len))
135    }
136}
137
138static ROOT: LazyLock<Root<Object>> = LazyLock::new(|| {
139    let root = Root::<Object>::new();
140    if let Ok((mount, name)) = root.get_mount("local/fight") {
141        mount.add(name, Object::Native(fight));
142    }
143    root
144});
145
146thread_local! {
147    static SEND_STACK: RefCell<Vec<String>> = RefCell::new(Vec::new());
148}
149
150fn builtin_native(name: &str) -> Option<fn(Dynamic) -> Dynamic> {
151    match name {
152        "fight" | "native.fight" | "native::fight" => Some(fight),
153        _ => None,
154    }
155}
156
157fn get_value(input: &Dynamic, keys: &[&str]) -> Option<Dynamic> {
158    keys.iter().find_map(|key| input.get_dynamic(key))
159}
160
161fn get_string(input: &Dynamic, keys: &[&str]) -> Option<String> {
162    get_value(input, keys).map(|value| value.as_str().to_string())
163}
164
165fn get_i64(input: &Dynamic, keys: &[&str]) -> Option<i64> {
166    get_value(input, keys).and_then(|value| value.as_int())
167}
168
169fn get_f64(input: &Dynamic, keys: &[&str]) -> Option<f64> {
170    get_value(input, keys).and_then(|value| value.as_float())
171}
172
173fn get_range(input: &Dynamic, range_keys: &[&str], min_keys: &[&str], max_keys: &[&str], default_min: i64, default_max: i64) -> (i64, i64) {
174    if let Some(range) = get_value(input, range_keys) {
175        let min = range.get_idx(0).and_then(|v| v.as_int()).unwrap_or(default_min);
176        let max = range.get_idx(1).and_then(|v| v.as_int()).unwrap_or(default_max);
177        return normalize_range(min, max, default_min, default_max);
178    }
179
180    let min = get_i64(input, min_keys).unwrap_or(default_min);
181    let max = get_i64(input, max_keys).unwrap_or(default_max);
182    normalize_range(min, max, default_min, default_max)
183}
184
185fn normalize_range(min: i64, max: i64, default_min: i64, default_max: i64) -> (i64, i64) {
186    let min = min.max(0);
187    let max = max.max(0);
188    if min == 0 && max == 0 {
189        (default_min, default_max)
190    } else if min <= max {
191        (min, max)
192    } else {
193        (max, min)
194    }
195}
196
197fn role_name(role: &Dynamic) -> String {
198    get_string(role, &["name", "名称"]).unwrap_or_else(|| "未命名".to_string())
199}
200
201fn role_hp(role: &Dynamic) -> i64 {
202    get_i64(role, &["hp", "health", "生命"]).unwrap_or(0).max(0)
203}
204
205fn role_max_hp(role: &Dynamic) -> i64 {
206    get_i64(role, &["max_hp", "max_health", "最大生命"]).unwrap_or_else(|| role_hp(role)).max(1)
207}
208
209fn role_speed(role: &Dynamic) -> i64 {
210    get_i64(role, &["speed", "initiative", "速度"]).unwrap_or(0)
211}
212
213fn role_attack_range(role: &Dynamic) -> (i64, i64) {
214    get_range(role, &["attack", "攻击"], &["attack_min", "min_attack", "攻击下限"], &["attack_max", "max_attack", "攻击上限"], 8, 15)
215}
216
217fn role_defense_range(role: &Dynamic) -> (i64, i64) {
218    get_range(role, &["defense", "防御"], &["defense_min", "min_defense", "防御下限"], &["defense_max", "max_defense", "防御上限"], 1, 4)
219}
220
221fn role_block_chance(role: &Dynamic) -> f64 {
222    get_f64(role, &["block_chance", "block", "格挡几率"]).unwrap_or(0.15).clamp(0.0, 0.95)
223}
224
225fn role_block_ratio(role: &Dynamic) -> f64 {
226    get_f64(role, &["block_ratio", "格挡减伤"]).unwrap_or(0.5).clamp(0.0, 1.0)
227}
228
229fn role_crit_chance(role: &Dynamic) -> f64 {
230    get_f64(role, &["crit_chance", "crit", "暴击几率"]).unwrap_or(0.1).clamp(0.0, 1.0)
231}
232
233fn role_crit_ratio(role: &Dynamic) -> f64 {
234    get_f64(role, &["crit_ratio", "暴击倍率"]).unwrap_or(1.5).max(1.0)
235}
236
237fn set_i64(role: &Dynamic, key: &str, value: i64) {
238    role.set_dynamic(key.into(), value);
239}
240
241fn set_bool(role: &Dynamic, key: &str, value: bool) {
242    role.set_dynamic(key.into(), value);
243}
244
245fn normalize_role(input: &Dynamic, side: &'static str, default_name: &str) -> Dynamic {
246    let role = if input.is_map() { input.deep_clone() } else { Dynamic::map(Default::default()) };
247    let name = get_string(&role, &["name", "名称"]).unwrap_or_else(|| default_name.to_string());
248    let hp = get_i64(&role, &["hp", "health", "生命"]).unwrap_or(100).max(1);
249    let (attack_min, attack_max) = role_attack_range(&role);
250    let (defense_min, defense_max) = role_defense_range(&role);
251
252    role.set_dynamic("name".into(), name);
253    role.set_dynamic("side".into(), side);
254    set_i64(&role, "hp", hp);
255    set_i64(&role, "max_hp", role_max_hp(&role).max(hp));
256    set_i64(&role, "attack_min", attack_min);
257    set_i64(&role, "attack_max", attack_max);
258    set_i64(&role, "defense_min", defense_min);
259    set_i64(&role, "defense_max", defense_max);
260    role.set_dynamic("block_chance".into(), role_block_chance(&role));
261    role.set_dynamic("block_ratio".into(), role_block_ratio(&role));
262    role.set_dynamic("crit_chance".into(), role_crit_chance(&role));
263    role.set_dynamic("crit_ratio".into(), role_crit_ratio(&role));
264    set_i64(&role, "speed", role_speed(&role));
265    set_bool(&role, "alive", hp > 0);
266    role
267}
268
269fn make_fight_record(entries: [(&str, Dynamic); 10]) -> Dynamic {
270    let mut map = std::collections::BTreeMap::<SmolStr, Dynamic>::new();
271    for (key, value) in entries {
272        map.insert(key.into(), value);
273    }
274    Dynamic::map(map)
275}
276
277fn fight(msg: Dynamic) -> Dynamic {
278    let left_input = msg.get_dynamic("left").or_else(|| msg.get_dynamic("a")).unwrap_or(Dynamic::Null);
279    let right_input = msg.get_dynamic("right").or_else(|| msg.get_dynamic("b")).unwrap_or(Dynamic::Null);
280    if !left_input.is_map() || !right_input.is_map() {
281        let mut error = std::collections::BTreeMap::new();
282        error.insert("error".into(), "fight expects {left: {...}, right: {...}}".into());
283        return Dynamic::map(error);
284    }
285
286    let left = normalize_role(&left_input, "left", "左侧");
287    let right = normalize_role(&right_input, "right", "右侧");
288    let max_rounds = get_i64(&msg, &["max_rounds", "最大回合"]).unwrap_or(50).clamp(1, 500) as usize;
289
290    let mut rng = rand::rng();
291    let mut process = Vec::new();
292    let mut records = Vec::new();
293    let mut left_turn = if role_speed(&left) == role_speed(&right) { rng.random_bool(0.5) } else { role_speed(&left) > role_speed(&right) };
294
295    let mut round_count = 0usize;
296    while role_hp(&left) > 0 && role_hp(&right) > 0 && round_count < max_rounds {
297        round_count += 1;
298        let (attacker, defender) = if left_turn { (&left, &right) } else { (&right, &left) };
299
300        let (attack_min, attack_max) = role_attack_range(attacker);
301        let (defense_min, defense_max) = role_defense_range(defender);
302        let attack_roll = rng.random_range(attack_min..=attack_max);
303        let defense_roll = rng.random_range(defense_min..=defense_max);
304        let blocked = rng.random_bool(role_block_chance(defender));
305        let crit = rng.random_bool(role_crit_chance(attacker));
306
307        let mut damage = (attack_roll - defense_roll).max(1);
308        if crit {
309            damage = ((damage as f64) * role_crit_ratio(attacker)).round() as i64;
310        }
311        if blocked {
312            damage = ((damage as f64) * (1.0 - role_block_ratio(defender))).round() as i64;
313        }
314        damage = damage.max(if blocked { 0 } else { 1 });
315
316        let defender_hp = (role_hp(defender) - damage).max(0);
317        let defender_max_hp = role_max_hp(defender);
318        set_i64(defender, "hp", defender_hp);
319        set_bool(defender, "alive", defender_hp > 0);
320
321        let line = format!(
322            "第{}回合 {} 攻击 {},攻击 {},防御 {},{}{}造成 {} 点伤害,{} 剩余 {} / {}",
323            round_count,
324            role_name(attacker),
325            role_name(defender),
326            attack_roll,
327            defense_roll,
328            if crit { "触发暴击," } else { "" },
329            if blocked { "被格挡后," } else { "" },
330            damage,
331            role_name(defender),
332            defender_hp,
333            defender_max_hp,
334        );
335        process.push(line.clone().into());
336        records.push(make_fight_record([
337            ("round", (round_count as i64).into()),
338            ("attacker", role_name(attacker).into()),
339            ("attacker_side", get_string(attacker, &["side"]).unwrap_or_default().into()),
340            ("defender", role_name(defender).into()),
341            ("defender_side", get_string(defender, &["side"]).unwrap_or_default().into()),
342            ("attack_roll", attack_roll.into()),
343            ("defense_roll", defense_roll.into()),
344            ("blocked", blocked.into()),
345            ("critical", crit.into()),
346            ("damage", damage.into()),
347        ]));
348        if let Some(last) = records.last() {
349            last.insert("defender_hp", defender_hp);
350            last.insert("text", line);
351        }
352
353        left_turn = !left_turn;
354    }
355
356    let left_hp = role_hp(&left);
357    let right_hp = role_hp(&right);
358    let left_name = role_name(&left);
359    let right_name = role_name(&right);
360    let winner = if left_hp == right_hp {
361        "draw".to_string()
362    } else if left_hp > right_hp {
363        left_name.clone()
364    } else {
365        right_name.clone()
366    };
367    let loser = if winner == "draw" {
368        String::new()
369    } else if winner == left_name {
370        right_name
371    } else {
372        left_name
373    };
374
375    let mut result = std::collections::BTreeMap::new();
376    result.insert("winner".into(), winner.into());
377    result.insert("loser".into(), loser.into());
378    result.insert("draw".into(), (left_hp == right_hp).into());
379    result.insert("round_count".into(), (round_count as i64).into());
380    result.insert("process".into(), Dynamic::list(process));
381    result.insert("records".into(), Dynamic::list(records));
382    result.insert("left".into(), left);
383    result.insert("right".into(), right);
384    Dynamic::map(result)
385}
386
387pub fn mount_memory(name: &str) -> bool {
388    ROOT.mount_memory(name)
389}
390
391pub fn mount_redis(name: &str, url: &str) -> Result<bool> {
392    ROOT.mount_redis(name, url)
393}
394
395pub fn get_mount<'a>(name: &'a str) -> Result<(Mount<Object>, &'a str)> {
396    ROOT.get_mount(name)
397}
398
399pub fn add(name: &str, obj: Object) -> Result<bool> {
400    let (m, name) = get_mount(name)?;
401    Ok(m.add(name, obj))
402}
403
404pub fn add_native(name: &str, native_name: &str) -> Result<bool> {
405    let Some(handler) = builtin_native(native_name) else {
406        return Ok(false);
407    };
408    let (m, name) = get_mount(name)?;
409    Ok(m.add(name, Object::Native(handler)))
410}
411
412pub fn add_value<T: Into<Dynamic>>(name: &str, val: T) -> Result<bool> {
413    let (m, name) = get_mount(name)?;
414    Ok(m.add(name, Object::Value(val.into())))
415}
416
417pub fn get(name: &str) -> Result<Dynamic> {
418    let (m, name) = get_mount(name)?;
419    m.get(name, |obj| obj.value())
420}
421
422pub fn dir(name: &str) -> Result<Dynamic> {
423    let mount_name = name.split_once('/').map(|(n, _)| n).unwrap_or(name);
424    let (m, name) = get_mount(name)?;
425    m.dir(name).map(|names| if matches!(m, Mount::Redis { .. }) { names.into_iter().map(|n| format!("{}/{}", mount_name, n).into()).collect::<Vec<SmolStr>>().into() } else { names.into() })
426}
427
428pub fn contains(name: &str) -> bool {
429    if let Ok((m, name)) = get_mount(name) { m.contains(name) } else { false }
430}
431
432pub fn remove(name: &str) -> Result<Dynamic> {
433    let (m, name) = get_mount(name)?;
434    match m.remove(name) {
435        Ok(Object::Value(v)) => Ok(v),
436        _ => Err(anyhow!("没有删除对象")),
437    }
438}
439
440pub fn add_list(name: &str) -> Result<()> {
441    let (m, name) = get_mount(name)?;
442    m.add_list(name);
443    Ok(())
444}
445
446pub fn push(name: &str, value: Dynamic) -> Result<usize> {
447    let (m, name) = get_mount(name)?;
448    m.push(name, Object::Value(value))
449}
450
451pub fn add_map(name: &str) -> Result<()> {
452    let (m, name) = get_mount(name)?;
453    m.add_map(name);
454    Ok(())
455}
456
457pub fn insert(name: &str, key: &str, value: Dynamic) -> Result<()> {
458    let (m, name) = get_mount(name)?;
459    let _ = m.insert(name, key, Object::Value(value));
460    Ok(())
461}
462
463pub fn get_key(name: &str, key: &str) -> Result<Dynamic> {
464    let (m, name) = get_mount(name)?;
465    m.get_key(name, key, |obj| obj.value())
466}
467
468use redis::Commands;
469pub fn get_list(name: &str) -> Result<Vec<Dynamic>> {
470    let (m, name) = get_mount(name)?;
471    match m {
472        Mount::Memory(m) => m
473            .read_sync(name, |_, v| match v {
474                Node::List(l) => l.iter().map(|(_, item)| item.value()).collect(),
475                _ => Vec::new(),
476            })
477            .ok_or(anyhow!("未发现 {}", name)),
478        Mount::Redis { client, rl: _ } => {
479            let mut conn = client.get_connection()?;
480            let items: Vec<Vec<u8>> = conn.lrange(name, 0, -1)?;
481            let items: Vec<Dynamic> = items.into_iter().map(|buf| Dynamic::decode(buf.as_slice()).map(|(v, _)| v).unwrap_or(Dynamic::Null)).collect();
482            Ok(items)
483        }
484    }
485}
486
487#[derive(Debug)]
488enum MyFn {
489    Null,
490    Sender(MsgSender<Dynamic>),
491    Native(fn(Dynamic) -> Dynamic),
492    Script(i64, Type),
493}
494
495impl MyFn {
496    fn call(&self, msg: Dynamic) -> Result<Dynamic> {
497        match self {
498            MyFn::Sender(tx) => call(tx, msg),
499            MyFn::Native(f) => Ok(f(msg)),
500            MyFn::Script(ptr, ty) => dynamic::call_fn(*ptr, ty.clone(), Box::new(msg)).map(|r| r.as_ref().clone()),
501            MyFn::Null => Ok(Dynamic::Null),
502        }
503    }
504}
505
506impl From<&Object> for MyFn {
507    fn from(obj: &Object) -> Self {
508        match obj {
509            Object::Tx(tx, _) => MyFn::Sender(tx.clone()),
510            Object::Task(t, _) => {
511                t.abort();
512                MyFn::Null
513            }
514            Object::Func(ptr, ty) => MyFn::Script(*ptr, ty.clone()),
515            Object::Native(f) => MyFn::Native(*f),
516            _ => MyFn::Null,
517        }
518    }
519}
520
521pub fn send_msg(name: &str, msg: Dynamic) -> Result<Dynamic> {
522    let entered = SEND_STACK.with(|stack| {
523        let mut stack = stack.borrow_mut();
524        if stack.len() >= 64 {
525            return Err(anyhow!("root::send call depth exceeded at {}", name));
526        }
527        if stack.iter().any(|item| item == name) {
528            return Err(anyhow!("root::send recursive path detected: {}", name));
529        }
530        stack.push(name.to_string());
531        Ok(())
532    });
533    entered?;
534    let result = (|| {
535        let (m, name) = get_mount(name)?;
536        let f: MyFn = m.get(name, |obj| obj.into())?;
537        f.call(msg)
538    })();
539    SEND_STACK.with(|stack| {
540        let _ = stack.borrow_mut().pop();
541    });
542    result
543}
544
545pub fn send_idx_msg(name: &str, idx: usize, msg: Dynamic) -> Result<Dynamic> {
546    let stack_name = format!("{}[{}]", name, idx);
547    let entered = SEND_STACK.with(|stack| {
548        let mut stack = stack.borrow_mut();
549        if stack.len() >= 64 {
550            return Err(anyhow!("root::send_idx call depth exceeded at {}", stack_name));
551        }
552        if stack.iter().any(|item| item == &stack_name) {
553            return Err(anyhow!("root::send_idx recursive path detected: {}", stack_name));
554        }
555        stack.push(stack_name);
556        Ok(())
557    });
558    entered?;
559    let result = (|| {
560        let (m, name) = get_mount(name)?;
561        let f: MyFn = m.get_idx(name, idx, |obj| obj.into())?;
562        f.call(msg)
563    })();
564    SEND_STACK.with(|stack| {
565        let _ = stack.borrow_mut().pop();
566    });
567    result
568}
569
570#[cfg(test)]
571mod tests {
572    use super::*;
573
574    fn fight_request() -> Dynamic {
575        let left = Dynamic::map(Default::default());
576        left.insert("name", "战士");
577        left.insert("hp", 30);
578        left.insert("attack_min", 8);
579        left.insert("attack_max", 12);
580        left.insert("defense_min", 1);
581        left.insert("defense_max", 3);
582        left.insert("block_chance", 0.2);
583
584        let right = Dynamic::map(Default::default());
585        right.insert("name", "盗贼");
586        right.insert("hp", 24);
587        right.insert("attack_min", 6);
588        right.insert("attack_max", 10);
589        right.insert("defense_min", 0);
590        right.insert("defense_max", 2);
591        right.insert("block_chance", 0.1);
592
593        let msg = Dynamic::map(Default::default());
594        msg.insert("left", left);
595        msg.insert("right", right);
596        msg
597    }
598
599    #[test]
600    fn add_native_registers_builtin_fight_handler() {
601        assert!(add_native("local/test/fight_handler", "native.fight").unwrap());
602        let result = send_msg("local/test/fight_handler", fight_request()).unwrap();
603        assert!(result.is_map());
604        assert!(result.get_dynamic("winner").is_some());
605        assert!(result.get_dynamic("process").is_some_and(|v| v.is_list()));
606    }
607
608    #[test]
609    fn local_fight_handler_is_available_by_default() {
610        assert!(contains("local/fight"));
611        let result = send_msg("local/fight", fight_request()).unwrap();
612        let rounds = result.get_dynamic("round_count").and_then(|v| v.as_int()).unwrap_or_default();
613        assert!(rounds > 0);
614    }
615}