2个不稳定版本
0.1.0 | 2024年1月2日 |
---|---|
0.0.1 | 2024年1月2日 |
#19 在 #server-connection
3KB
Bevy Simplenet Events
为处理网络连接提供基于事件的API,构建在 bevy_simplenet
之上。
此crate需要nightly rust。
使用说明
- 客户端连接事件、客户端/服务器消息事件和服务器响应可以在多个系统中使用事件读取器进行迭代。客户端请求可以使用
ServerRequestSource
在一个系统中清空。 - 单个类型的事件'通道'是FIFO,但是不同的事件通道之间不会相互同步。此crate不适用于需要所有客户端/服务器输出全局FIFO排序的用户(直接使用
bevy_simplenet
而不是)。 - 我们假设用户的连接事件处理器在调度
First
中的RefreshSet
之后和其它事件处理器之前调度。 - 事件必须在服务器和客户端上以相同的顺序注册。
同步保证
此crate的API具有高度的倾向性,以方便精确处理重连。
我们每帧在调度 First
中的 RefreshSet
更新客户端和服务器状态。所有旧事件都被清除,并插入新事件。如果用户的连接事件处理器按预期在其他事件处理器之前调度,那么我们保证以下内容
客户端
ClientMessageReader
只会从当前连接会话读取服务器消息。旧消息(在最后一次断开连接之前)将被丢弃。ClientResponseReader
仅会发出在当前连接会话中接收到的响应,包括ServerResponse::Response
或ServerResponse::Ack
。所有其他响应都会以响应失败变体(拒绝/发送失败/响应丢失)之一失败。请注意,我们保证对于每个发送的客户请求都会发出某种响应。- 如果最新的
ClientReport::Connected
还没有被ClientConnectionReader
至少读取一次(TODO:存在上游竞争条件),或者如果客户端未连接,则客户端消息/请求将无法发送或出错。可以使用从EventClient::send
返回的MessageSignal
监控消息状态,并且可以使用从EventClient::request
返回的RequestSignal
监控请求状态,或者您也可以等待结果作为事件发出。我们包括这个保证以减少客户端在处理连接事件时基于过时客户端状态发送消息的可能性。
服务器
ServerMessageReader
和ServerRequestSource
仅从客户端当前的连接会话中读取客户端消息和请求。旧消息(来自最后一次断开之前的消息)将被丢弃。- 如果针对该客户端的最新
ServerReport::Connected
还没有被ServerConnectionReader
至少读取一次(TODO:存在上游竞争条件),或者如果客户端未连接,则服务器向客户端发送的消息将无法发送或出错。我们包括这个保证以减少服务器在处理连接事件时基于过时服务器状态发送消息的可能性。请注意,从旧连接会话中发送的响应总是无法发送到新会话。
性能
这个包比 bevy_simplenet
效率低。
- 为了启用临时事件类型,事件被序列化和反序列化两次。
- 客户端和服务器有额外的间接引用和复制,以将消息从内部客户端/服务器传输到用户。
- 事件通过引用而不是值暴露(除客户端请求外,客户端请求在服务器上按值清空)。
创建通道
共享
准备消息类型和实现 EventPack
的通道标签。
#[derive(SimplenetEvent, Serialize, Deserialize)]
struct DemoMsg1(usize);
#[derive(SimplenetEvent, Serialize, Deserialize)]
struct DemoMsg2(usize);
#[derive(SimplenetEvent, Serialize, Deserialize)]
struct DemoRequest(usize);
#[derive(SimplenetEvent, Serialize, Deserialize)]
struct DemoResponse(usize);
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
struct DemoConnectMsg(String);
#[derive(Debug, Clone)]
struct DemoChannel;
impl EventPack for DemoChannel
{
type ConnectMsg = DemoConnectMsg;
}
准备事件设置函数。这应该在服务器和客户端应用上调用。
fn event_setup(app: &mut App)
{
app
.register_simplenet_client_message::<DemoChannel, DemoMsg1>()
.register_simplenet_client_message::<DemoChannel, DemoMsg2>()
.register_simplenet_server_message::<DemoChannel, DemoMsg1>()
.register_simplenet_server_message::<DemoChannel, DemoMsg2>()
.register_simplenet_request_response::<DemoChannel, DemoRequest, DemoResponse>()
;
}
服务器
准备服务器工厂。
type DemoServerReport = bevy_simplenet::ServerReport<DemoConnectMsg>;
fn demo_server_factory() -> bevy_simplenet::ServerFactory<EventWrapper<DemoChannel>>
{
bevy_simplenet::ServerFactory::<EventWrapper<DemoChannel>>::new("test")
}
准备服务器设置函数(示例)。
fn setup_server(app: &mut App) -> url::Url
{
let server = demo_server_factory().new_server(
enfync::builtin::native::TokioHandle::adopt_or_default(),
"127.0.0.1:0",
bevy_simplenet::AcceptorConfig::Default,
bevy_simplenet::Authenticator::None,
bevy_simplenet::ServerConfig::default(),
);
let url = server.url();
app.insert_simplenet_server(server);
event_setup(app);
url
}
客户端
准备客户端工厂。
fn demo_client_factory() -> bevy_simplenet::ClientFactory<EventWrapper<DemoChannel>>
{
bevy_simplenet::ClientFactory::<EventWrapper<DemoChannel>>::new("test")
}
准备客户端设置函数(示例)。
fn setup_client(app: &mut App, url: url::Url, client_id: SessionId, connect_msg: DemoConnectMsg)
{
let client = demo_client_factory().new_client(
enfync::builtin::Handle::adopt_or_default(),
url,
bevy_simplenet::AuthRequest::None{ client_id },
bevy_simplenet::ClientConfig::default(),
connect_msg
);
app.insert_simplenet_client(client);
event_setup(app);
}
客户端中的连接处理
每个tick必须先处理客户端连接报告,然后再处理所有其他客户端事件。
fn handle_client_connection_reports(reader: ClientConnectionReader<DemoChannel>)
{
for connection in reader.iter()
{
match connection
{
bevy_simplenet::ClientReport::Connected => todo!(),
bevy_simplenet::ClientReport::Disconnected => todo!(),
bevy_simplenet::ClientReport::ClosedByServer(_) => todo!(),
bevy_simplenet::ClientReport::ClosedBySelf => todo!(),
bevy_simplenet::ClientReport::IsDead(_) => todo!(),
}
}
}
服务器中的连接处理
每个tick必须先处理服务器连接报告,然后再处理所有其他服务器事件。
fn handle_server_connection_reports(reader: ServerConnectionReader<DemoChannel>)
{
for (session_id, connection) in reader.iter()
{
match connection
{
bevy_simplenet::ServerReport::<DemoConnectMsg>::Connected(_, _) => todo!(),
bevy_simplenet::ServerReport::<DemoConnectMsg>::Disconnected => todo!(),
}
}
}
从客户端发送
任何已注册的消息类型都可以发送。
fn send_client_message(client: EventClient<DemoChannel>)
{
client.send(DemoMsg1(42));
client.send(DemoMsg2(24));
}
从服务器发送
任何已注册的消息类型都可以发送。
fn send_server_message(In(session_id): In<SessionId>, server: EventServer<DemoChannel>)
{
server.send(session_id, DemoMsg1(42)).unwrap();
server.send(session_id, DemoMsg2(24)).unwrap();
}
服务器上的读取
客户端消息
fn read_client_messages(reader: ServerMessageReader<DemoChannel, DemoMsg1>)
{
for (session_id, message) in reader.iter()
{
todo!()
}
}
客户端请求
清空请求源会消耗所有请求,因为我们期望你处理请求令牌。
fn read_client_requests(source: ServerRequestSource<DemoChannel, DemoRequest1, DemoResponse1>)
{
for (token, request) in source.drain()
{
todo!()
}
}
客户端上的读取
服务器消息
fn read_server_messages(reader: ClientMessageReader<DemoChannel, DemoMsg1>)
{
for message in reader.iter()
{
todo!()
}
}
服务器响应
fn read_server_responses(reader: ClientResponseReader<DemoChannel, DemoRequest1, DemoResponse1>)
{
for response in reader.iter()
{
match response
{
ServerResponse::Response(response, _) => todo!(),
ServerResponse::Ack(_) => todo!(),
ServerResponse::Reject(_) => todo!(),
ServerResponse::SendFailed(_) => todo!(),
ServerResponse::ResponseLost(_) => todo!(),
}
}
}
Bevy 兼容性
bevy | bevy_simplenet_events |
---|---|
0.13 | v0.3.0 - master |
0.12 | v0.1.0 - v0.2.0 |
依赖项
~265–710KB
~17K SLoC