#服务器 #命令 #响应 #API #角色 #摇摆 #颤抖

sf-api

一个简单的API,用于向Shakes & Fidget服务器发送命令并将响应解析为角色

5个版本

0.1.3 2024年4月20日
0.1.2 2024年2月28日
0.1.1 2024年2月19日
0.1.0 2024年2月1日
0.0.1 2024年1月19日

文本处理类别中排名第1306

MIT 许可证

295KB
7K SLoC

S&F API 🧙🏽‍♂️

crates.io Build Status Licence

概述

这是一个正在进行的非官方API,用于与Shakes & Fidget服务器通信。

使用此API的基本示例可能是

    // The session is what manages any data relevant to communicating with the 
    // server accross commands
    let mut session = CharacterSession::new(
        "username",
        "password",
        ServerConnection::new("f1.sfgame.net").unwrap(),
    );

    // The login response will contain information about our character and the 
    // server
    let login_respone = session.login().await.unwrap();

    // The game state is what we can use to look at all the data from the 
    // server in a more comprehensible way. 
    let mut game_state = GameState::new(login_respone).unwrap();

    // Now we are all set to do whatever we want.
    let best_description = "I love sushi!".to_string();

    // Just like above we get a response. This time however, it will only 
    // contain a partial response with things, that might have changed
    let response = session
        .send_command(&Command::SetDescription {
            description: best_description.clone(),
        })
        .await
        .unwrap();

    // As such, we should use update on our existing game state
    game_state.update(response).unwrap();

    // Lets make sure the server actually did what we told him
    assert!(game_state.character.description == best_description);

    println!("YAY, it worked! 🎉🍣🍣🍣🎉");

如果您使用的是单点登录的S&F账户,可以像这样使用它

    let account = SFAccount::login(
        "username".to_string(),
        "password".to_string()
    ).await.unwrap();

    for mut session in account.characters().await.unwrap().into_iter().flatten()
    {
        // You can use the sessions, that the account returns like
        // a normal (logged out) session now
        let response = session.login().await.unwrap();
        let mut game_state = GameState::new(response).unwrap();
    }

安装

您只需在您的Rust项目中运行以下命令

cargo add sf-api

指南

在您的账户被封禁之前,请注意以下几点

  1. 永远不要无限循环地运行发送命令到服务器的任何操作而不加延迟。这会立即使您的账户/ip在监控流量和系统的危险区域排名第一位。根据您的连接和账户数量,这可能会被分类为DDOS攻击。只需在某个地方放置异步睡眠并在设置的数量后退出。已警告您
  2. 正常网络客户端会定期向服务器发送更新命令。这是向服务器发出信号,表明您的账户仍然存在,并且应该更新您在公会屏幕上的最后活动时间。请注意,登录本身不会自动更新此时间。正如您所猜测的,如果账户始终活跃但没有发送任何更新命令,那就很奇怪了,因此偶尔发送它们。
  3. 此库不会检查您的角色应该有权访问哪些API调用。如果您尝试装备尚未解锁的同伴或尚未解锁的堡垒宝箱中的角色,那将是您的责任。命令枚举通过强制通过Rust类型系统进行有效输入来防止您自我伤害,但任何在此之上的逻辑在性能/复杂性/错误方面都是不值得的。
  4. 类似于前一点,除了处理服务器错误外,解析响应时不会检查响应是否是您请求的正确响应。如果服务器向您发送一个荣誉室响应,当您发送完成任务命令时,这将是服务器错误(我从未见过),将会被默默忽略。在您尝试在接下来的12小时内循环开始新的任务之前,您应该检查一下您的命令是否成功。特别是跨越时区的时间敏感信息可能会让您感到惊讶。
  5. 原始S&F API从1开始索引而不是0。这非常不直观且容易出错,如果您只想使用现有Vec/Array的索引作为命令输入。因此,我决定隐藏这个事实,并手动在命令中增加提供的索引1。这也使得无法提供过低的输入,但遗憾的是Rust的类型系统无法为值提供上限,所以您仍然可以提供过大的值。请尽量不要这样做,因为这将是任何官方版本中都不存在的错误,并可能导致开发团队对您的账户感兴趣,如果他们想调查以前未见过的错误。

性能

您不需要关心性能,因为您不应该在需要考虑这个问题的规模上运行这个库。忽略这个事实,这个库在构建时考虑了高可扩展性和低资源消耗。在我的机器上解析登录游戏状态将小于1毫秒,之后的完整更新将小于100微秒。

我已经通过重用之前的容器来尽量减少分配。此外,HashMap<&str,&str>主要被[T;K]替换,其中U as usize用于索引vec(主要通过某些get函数大量抽象)。

响应只是原始HTML响应体,包含一个~HashMap<&str,&str>,该HashMap引用了它。这是完全安全的,每个请求节省数百次分配,并使所有内容都集中在一个地方,便于缓存。

所有内容都解析成期望的确切数据类型,这也捕获了与只是i64化每个int相比奇怪的或意外的错误。请注意,我不期望永远维护这个(我首先不玩这个游戏),所以很多这样的内容都显示为日志警告,并默认为某些值,而不是返回错误。这样,您就不会因为某处蘑菇的价格为负而陷入困境。请随意在杂项函数中将warn!改为panic!来改变这种行为。

Rust功能

这个crate支持serde(反)序列化角色状态和S&F账户(sso),位于相应的功能标志之后。请注意,sso内部依赖于serde crate通过json与服务器通信。

设计

该API已被设计为保持会话和角色状态分离。为什么?主要是因为我可以序列化/反序列化响应并“回放”它们进行独特的事情,并减少服务器请求。

此外,这种架构在您有多个想要使用会话的东西时更容易使用,因为它们不需要等待更新完成就可以再次发送。

请注意,我认为这并不完美。特别是对于基本用例,这比有帮助还让人烦恼。我可能在某个时候添加一个统一的版本。

大约有 ~500 个属性需要解析。这是一笔如此巨大的数据量,所以我选择了直接在 GameState 中提供这些字段的原始访问权限,而不是为所有这些提供 get() 函数。这意味着如果你想,你可以创建带有可变引用的无效游戏状态,但这在我看来,那将是你的错误,而不是我的。

我可能考虑在将来使用类似 derive_getters 的东西,但我并不真正关心这个问题,而且它会增加编译时间,所以现在你只能访问字段。

依赖关系

~8–21MB
~333K SLoC