写点什么

Rust 元宇宙 15 —— 细节和重构

作者:Miracle
  • 2021 年 12 月 09 日
  • 本文字数:1872 字

    阅读完需:约 6 分钟

还记得在第十二章,我们实现了自己的 IDMap,同时可以使用 字符串的 token 和 u32 的 ID 来访问玩家的数据结构,但是我们的 IDMap 有一个小问题,没有办法通过 u32 的 ID 获得 token,解决这问题有一个最简单的方法,就是在 存放 Player 信息的 Vector 里面,存放一份 token 的拷贝,但是这样就会增加存储 token 的开销了,因为在 HashMap 中我们已经有了一份 token 的字符串数据。

一个简单的办法是在 Vector 存放 token 的引用而不是 token 的拷贝,但是这个简单的方法在 Rust 中没那么简单,因为一旦涉及到引用,Rust 就会问你,你的引用是一直有效吗,还是在一段时间有效,引用的生命周期跟谁的生命周期是一样的,是 IDMap,还是 IDMap 里面的 HashMap,具体的代码我在这就不列出来了,因为对于 Rust 初学者,这些带有 'a 或者 '_ 的代码具有强力劝退作用。

基于不再重复发明轮子的原则,我们在 Rust 的宝库 crates.io 中搜索 indexmap

Ok,这个拥有三千多万次下载的库就是我们想要的东西。

在 Cargo.toml 里面增加一行

我们改造 agent 服务如下:

pub struct Manager {    agents: IndexMap<String, Player>,    db: sled::Db,    token_txs: HashMap<String, crossbeam_channel::Sender<AgentMsg>>,    id_txs: BTreeMap<u32, crossbeam_channel::Sender<AgentMsg>>,}
复制代码


从数据库中加载 player:

    pub fn load(&mut self, token: &str)-> Option<(u32, Player)> {        if let Some((id, _, p)) = self.agents.get_full(token) {            return Some((id as u32, p.clone()));        }        if let Ok(Some(buf)) = self.db.get(token.as_bytes()) {            let mut de = rmp_serde::Deserializer::from_read_ref(&buf);            let player: Player = Deserialize::deserialize(&mut de).unwrap();            let (id, _) = self.agents.insert_full(token.into(), player.clone());            Some((id as u32, player))        } else {            None        }    }
复制代码


创建一个新玩家的代码:

 pub fn create(&mut self, token: &str, nick: &str)-> (u32, Player) {        let pos = Position{x: (rand::random::<i32>() % 1024) as f32, y: (rand::random::<i32>() % 1024) as f32};        let player = Player{nick: nick.into(), world: 0, pos};          //以后我们会调用一个专门的创建函数        let (id, _) = self.agents.insert_full(token.into(), player.clone());        self.save_token(token);        (id as u32, player)    }
复制代码

其中保存的函数有基于 token 和 基于 id 两种,以及共同的底层函数 save

 fn save(&self, token: &str, player: &Player) {        let mut buf = Vec::new();        if player.serialize(&mut Serializer::new(&mut buf)).is_ok() {            self.db.insert(token.to_string(), buf).unwrap();        }    }
pub fn save_id(&self, id: u32) { if let Some((token, player)) = self.agents.get_index(id as usize) { self.save(token, player); } }
pub fn save_token(&self, token: &str) { if let Some(player) = self.agents.get(token) { self.save(token, player); } }
复制代码

我们之前的脚本,是基于 world,实际上,首先需要有角色,才有 ID,才能加入和离开游戏世界,所以我们实现下面的脚本:

let mut engine = rhai::Engine::new();let (script_tx, script_rx) = crossbeam_channel::bounded(1024);let e = script_tx.clone();let c = agent_caller.clone();engine.register_fn("login", move |token: String| {    rpc::run(&c, AgentRequest::Login(token, e.clone()));});
复制代码

注意,我们 使用 script_tx,script_rx 来发送和接受脚本的消息,以后可以实现一个 AI 的引擎,使用 这两个端口来跟元宇宙的其他服务通讯。

agent_caller 是 agent 服务的调用端口,脚本注册一个 login 函数,就是 登录一个 token 的玩家到元宇宙世界中。

运行服务器,在命令行中测试脚本:

可以看到 返回了 登录失败的信息,因为我们还没有这个 token 对应的角色。

我们再注册一个函数,创建角色:

  let c = agent_caller.clone();  engine.register_fn("create", move |token: String, nick: String| {      rpc::run(&c, AgentRequest::TokenMsg(token, AgentMsg::Create(nick)));  });
复制代码

再次执行:

可以看到,我们创建了一个 昵称为 myname 的角色进入了元宇宙中。

发布于: 1 小时前阅读数: 31
用户头像

Miracle

关注

三十年资深码农 2019.10.25 加入

还未添加个人简介

评论

发布
暂无评论
Rust 元宇宙 15 —— 细节和重构