写点什么

Rust 元宇宙 14 —— 创建角色和同步

作者:Miracle
  • 2021 年 12 月 08 日
  • 本文字数:2182 字

    阅读完需:约 7 分钟

Rust 元宇宙 14 —— 创建角色和同步

上一章我们成功的将客户端连接到元宇宙服务器中,并实现了最简单的 登录协议 —— 在 http/https 头中附加客户端获得的认证 token,也就是 agent 服务支持 内部的 rpc 协议:

#[derive(Debug, Clone)]pub enum AgentRequest {    Login(String, crossbeam_channel::Sender<AgentMsg>),    TokenMsg(String, AgentMsg),    IDMsg(u32, AgentMsg),    Notify(NotifyType)}
复制代码


Login 协议附带了 Websocket 接收数据的 Sender,这样以后服务器的数据可以通过这个 Sender 发送到 保持这个 Websocket 的异步线程,并转发到连接上来的客户端,上一章包含了简单的转发代码。

客户端连接上服务器之后,有下面几种状态,适用不同的消息类型。

  • 未加载角色,角色没有进入游戏世界,没有获得 玩家的唯一 ID,这个时候需要使用基于 token 的 msg 比如说创建和选择角色。

  • 角色已加载并进入游戏世界,使用基于 ID 的 msg

  • 来自元宇宙的通知消息,Notify


一个最典型的 TokenMsg 是创建角色,为说明概念,我们这里仅设定角色 昵称,创建角色,随机设置位置,并让角色进入 0 号世界中,创建角色的代码如下:

   pub fn create(&mut self, token: &str, nick: &str)-> u32 {        let pos = Position{x: (rand::random::<i32>() % 1024) as f32, y: (rand::random::<i32>() % 1024) as f32};        let id = self.agents.add(token.into(), Player{nick: nick.into(), world: 0, pos});        self.save(token);        id    }
复制代码


加载和存储的代码我们已经在 12 章列出。


为了让客户端可以创建角色,我们扩充 AgentMsg 消息如下:

#[derive(Debug, Serialize, Deserialize, Clone)]pub enum AgentMsg {    LoginOk(u32, Player),    LoginFail(String),    Create(String),    Notify(NotifyType)}
复制代码


Create 使用昵称来创建角色,Notify 通知 客户端元宇宙的变化。


我们在客户端增加下面的代码实现命令行的处理:

let mut input = String::new();stdin().read_line(&mut input).unwrap();let words: Vec<&str> = input.split_ascii_whitespace().collect();if words[0].eq_ignore_ascii_case("create") {	  game.send(&AgentMsg::Create(words[1].into()));}
复制代码

将输入根据分隔符拆分为 &str,并使用 collect 收集为一个数组,可以看到 Rust 使用函数编程模式之后表达效率的极大提升,一行代码可以实现 C/C++需要数十行代码完成到功能。

let words: Vec<&str> = input.split_ascii_whitespace().collect();
复制代码

这样在客户端执行

create 就可以 将创建角色的协议发送到服务器端。


我们在服务端维持一个 token_id 的 tuple 保持 客户端的 状态

let mut token_id = (None, None);msg = socket.recv() => {    match msg {        Some(Ok(Message::Binary(buf)))=> {            let mut de = rmp_serde::Deserializer::from_read_ref(&buf);            let agent_msg: AgentMsg = Deserialize::deserialize(&mut de).unwrap();            if let Some(id) = token_id.1 {                rpc::run(&agent_tx, AgentRequest::IDMsg(id, agent_msg));            } else {                rpc::run(&agent_tx, AgentRequest::TokenMsg(token_id.0.clone().unwrap(), agent_msg));            }         },         Some(Err(e))=> {             println!("error -> {:?}", e);             break;         },         _=> {}     }}
复制代码


服务器保持 客户端 Websocket 的线程,从客户端接受到二进制的消息之后,首先使用 messagepack 解码,如果没有获得 id,则使用 AgentRequest::TokenMsg 转发这个客户端消息到 agent 服务进行处理,如果已经获得了 id,则使用 AgentRequest::IDMsg 转发到 agent 服务处理。


服务器 agent 服务相关处理的代码如下:

AgentRequest::Login(token, _tx)=> {                    if let Some((id, player)) = self.load(&token) {                        super::rpc::run(world, WorldRequest::Enter(id, player.pos.clone()));                        _tx.send(AgentMsg::LoginOk(id, player));                        self.id_txs.insert(id, _tx);                    } else {                        _tx.send(AgentMsg::LoginFail("token do not exist".into()));                        self.token_txs.insert(token.clone(), _tx);                    }                },                AgentRequest::TokenMsg(token, msg)=> {                    match msg {                        AgentMsg::Create(nick)=> {                            let id = self.create(&token, &nick);                            if let Some(tx) = self.token_txs.remove(&token) {                                tx.send(AgentMsg::LoginOk(id, self.agents[id].clone().unwrap())).unwrap();                                self.id_txs.insert(id, tx);                            }                        },                        _=> {}                    }                },
复制代码

如果是登录消息,获得 ID 或者 token 并将 发送消息到客户端的 tx 存放到 token 的 HashMap 或者 ID 的 BTreeMap 中。

如果是创建角色,则将创建后的 id 和角色信息发送到 客户端。

发布于: 2021 年 12 月 08 日阅读数: 48
用户头像

Miracle

关注

三十年资深码农 2019.10.25 加入

还未添加个人简介

评论

发布
暂无评论
Rust 元宇宙 14 —— 创建角色和同步