1 个不稳定版本

0.3.0 2024 年 5 月 5 日

#323WebSocket


2 个包 使用

MIT 许可证

65KB
1.5K SLoC

Aper

GitHub Repo stars crates.io docs.rs wokflow state

Cartoonized face of an ape.

Aper 是一个用于使用 状态机 进行数据同步的 Rust 库。Aper 提供了用状态机表示常见数据结构的机制,以及一个不依赖于传输协议的协议,用于在网络中同步多个状态机的实例。

应用场景包括在共享状态下运行的实时多人游戏、希望增量双向共享状态更新的客户端-服务器应用程序以及多人回合制游戏。

什么是状态机?

对于 Aper 来说,状态机只是一个实现了 StateMachinestructenum,具有以下属性

  • 它定义了一个 StateMachine::Transition 类型,通过它可以描述对状态的所有可能更改。通常,这应该是一个 enum 类型,尽管不是必需的。
  • 它定义了一个 StateMachine::Conflict 类型,用于描述在应用转换时可能发生的冲突。对于不可能发生冲突的简单类型,您可以为此使用 NeverConflict
  • 所有状态更新都是确定性的:如果您克隆了一个 StateMachine 和一个 Transition,则将克隆的转换应用于克隆的状态的结果必须与将原始转换应用于原始状态的结果相同。

以下是一个实现计数的 StateMachine 的示例

use aper::{StateMachine, NeverConflict};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
struct Counter { value: i64 }

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
enum CounterTransition {
    Reset,
    Increment(i64),
    Decrement(i64),
}

impl StateMachine for Counter {
    type Transition = CounterTransition;
    type Conflict = NeverConflict;

    fn apply(&self, event: &CounterTransition) -> Result<Counter, NeverConflict> {
        match event {
            CounterTransition::Reset => Ok(Counter {value: 0}),
            CounterTransition::Increment(amount) => Ok(Counter {value: self.value + amount}),
            CounterTransition::Decrement(amount) => Ok(Counter {value: self.value - amount}),
        }
    }
}

为什么不使用 CRDT?

无冲突复制数据类型是一种非常巧妙的数据表示方式,用于表示在多个节点之间共享的数据。为了避免需要一个中央的“真相来源”,CRDTs要求更新操作(即状态转换)必须是交换律。这使得它们可以表示一系列常见的数据结构,但不允许你表示任意复杂的更新逻辑。通过依赖中央权威,状态机方法允许你实现具有任意更新逻辑的数据结构,例如在两个数据结构之间原子移动值,或棋盘游戏的规则。


Aper正在快速发展。请将此视为技术预览。查看1.0版本待解决的问题列表

依赖项

~3.5–5MB
~95K SLoC