写点什么

Rust 元宇宙 13 —— 客户端连接

作者:Miracle
  • 2021 年 12 月 05 日
  • 本文字数:2049 字

    阅读完需:约 7 分钟

Rust 元宇宙 13 —— 客户端连接

上一章中,我们为了连接进入元宇宙的客户端准备了一个服务器的 IDMap,以及基于 MessagePack 的持续化方法,用来存放元宇宙里面的玩家数据,现在我们开始一点点构建这个连接到元宇宙的客户端,以及服务端对应的支持。

我们使用基于消息的服务来处理服务器端的不同功能,比如说前面所有关于 临近角色 列表的部分,我们放在 world 服务中,使用 一个 本地的服务间 RPC 来跨跨线程调用,简单的 rpc 代码如下:

pub type Callback<R> = crossbeam_channel::Sender<R>;pub type ReqType<T, R> = (T, Option<Callback<R>>);pub type Caller<T, R> = crossbeam_channel::Sender<ReqType<T, R>>;pub type Handler<T, R> = crossbeam_channel::Receiver<ReqType<T, R>>;
pub fn run<T, R>(caller: &Caller<T, R>, req: T) { caller.send((req, None)).unwrap();}
pub fn call<T, R>(caller: &Caller<T, R>, req: T)-> Option<R> { let (tx, rx) = crossbeam_channel::bounded(1); caller.send((req, Some(tx))).unwrap(); rx.recv().ok()}
pub fn handle<T, R, F: FnMut(T, Option<Callback<R>>)-> bool>(h: &Handler<T, R>, mills: u64, f: &mut F)-> bool { while let Ok((msg, tx)) = h.try_recv() { if f(msg, tx) { return true; } } if let Ok((msg, tx)) = h.recv_timeout(std::time::Duration::from_millis(mills)) { if f(msg, tx) { return true; } } false}
复制代码

run 是远程执行一个请求,不需要回答,call 是远程执行一个请求 类型为 T,并等待返回值 类型为 R 的结果。

handle 是处理请求的基本函数,我们采用同步的方式处理,先看看队列里面有没有请求,如果有则全部处理,然后超时指定时间等待请求,这样在处理消息的同时,有机会定时处理一些事物。

除了 world 作为世界服务以外,我们使用 agent 作为玩家的代理服务,

agent 处理消息的代码如下:

pub fn deal(&mut self, handler: &super::rpc::Handler<AgentRequest, AgentResponse>)-> bool {        let mut agent_tx: Option<crossbeam_channel::Sender<AgentMsg>> = None;        super::rpc::handle(handler, 50, &mut |msg, _| {            match msg {                AgentRequest::Login(token, _tx)=> {                    agent_tx = Some(_tx);                    if let Some((id, player)) = self.load(&token) {                        agent_tx.as_ref().map(|tx| tx.send(AgentMsg::LoginOk(id, player)) );                    } else {                        agent_tx.as_ref().map(|tx| tx.send(AgentMsg::LoginFail("token do not exist".into())) );                    }                },                AgentRequest::Create(token, nick)=> {                    //tx.map(|tx| tx.send(WorldResponse::Roles(self.get_region_roles(&(x, y)))) );                },                _=> {}            }            false        })    }
复制代码

注意,我们要为每个玩家保留一个发送信息的端口 agent_tx,这样 客户端的 Websocket 连接建立之后,我们就可以使用这个端口将 登录消息,世界同步消息、以及其他一切玩家相关的消息发送到客户端。


Websocket 对应的连接处理代码如下:

#[derive(Debug, Clone)]pub enum AgentRequest {    Login(String, crossbeam_channel::Sender<AgentMsg>),    Create(String, String),    Logout(String),}
async fn ws_handler(ws: WebSocketUpgrade, auth: Option<TypedHeader<headers::Authorization<headers::authorization::Bearer>>>, Extension(agent_tx): Extension<rpc::Caller<AgentRequest, AgentResponse>>,) -> impl IntoResponse {
let (tx, rx) = crossbeam_channel::bounded(1024); if let Some(TypedHeader(a)) = auth { println!("`{:?}` connected", a.0.token()); rpc::run(&agent_tx, AgentRequest::Login(a.0.token().into(), tx)); }
复制代码

websocket 连接建立后,创建 一个 rx,tx 对,传递给 agent 服务。

当接受到 来自 rx 的消息(可能来自于 agent 服务,也可能来自以后的脚本、NFT 和元宇宙的其他服务)之后,我们使用 MessagePack 编码,并发送到连接上来的客户端。

  while let Ok(msg) = rx.try_recv() {      let mut buf = Vec::new();      if msg.serialize(&mut Serializer::new(&mut buf)).is_ok() {          if !socket.send(Message::Binary(buf)).await.is_ok() {              break;          }      }  }
复制代码


客户端使用下面的代码解码:

	OwnedMessage::Binary(buf)=> {			let mut de = rmp_serde::Deserializer::from_read_ref(&buf);			let msg: AgentMsg = Deserialize::deserialize(&mut de).unwrap();			println!("{:?}", msg);	}
复制代码


当我们执行客户端,将会看到下面的结果:

这是我们预期的结果,因为我们还没有创建 元宇宙的角色。

发布于: 3 小时前阅读数: 19
用户头像

Miracle

关注

三十年资深码农 2019.10.25 加入

还未添加个人简介

评论

发布
暂无评论
Rust 元宇宙 13 —— 客户端连接