5 个版本 (3 个重大更新)
0.4.0 | 2022 年 2 月 7 日 |
---|---|
0.3.0 | 2022 年 2 月 7 日 |
0.1.1 | 2021 年 10 月 20 日 |
0.1.0 | 2021 年 10 月 20 日 |
0.0.0 | 2021 年 10 月 20 日 |
#667 in 网络编程
105KB
2K SLoC
evoke - 简易网络代码
Evoke 提供了构建块,以将网络功能添加到游戏引擎中。
Evoke 支持
- 具有权威服务器的客户端-服务器模型
客户端-服务器
对于客户端-服务器模型,Evoke 会自动从服务器到客户端进行状态复制(带有 delta 压缩)以及从客户端到服务器的命令复制。
和命令复制。
Evoke 对游戏中使用的组件没有假设。
用户需要在服务器中注册 server::Descriptor
和在客户端中注册 client::Descriptor
以便复制的组件。对于可比较相等性和可序列化的组件,存在泛型实现。
针对 服务器 和 客户端。
核心
Evoke 的核心提供了非常抽象的客户端和服务器会话,支持发送和接收类似 Connect
、AddPlayer
、SendInput
、Update
等具有泛型负载的命令。Evoke 的核心作为单独的 crate evoke-core
提供,并从该 crate 重新导出为 evoke::core
与 evoke
crate(此 crate)不同,evoke-core
不依赖于 edict
,并且可以在任何游戏引擎中使用,即使是用除 Rust 之外的语言编写的,如果打包成 FFI 准备的库。
使用方法
要开始使用 Evoke,只需在服务器和客户端上配置和运行 ServerSystem
和 ClientSystem
。
服务器
要配置 ServerSystem
,提供组件复制的描述符和 RemotePlayer
实现的描述符。
/// Information associated with player.
#[derive(serde::Deserialize)]
struct MyPlayerInfo;
/// Player input serializable representation.
#[derive(serde::Deserialize)]
struct MyPlayerInput;
/// This type drives player lifecycle and input processing.
struct MyRemotePlayer;
impl evoke::server::RemotePlayer for MyRemotePlayer {
type Info = MyPlayerInfo;
type Input = MyPlayerInput;
fn accept(info: MyPlayerInfo, pid: evoke::PlayerId, world: &mut edict::World) -> eyre::Result<Self> {
// Decide here whether accept new player based on `info` provided.
// `Ok` signals that player is accepted.
// `Err` signals that player is rejected.
Ok(MyRemotePlayer)
}
fn apply_input(&mut self, entity: edict::EntityId, world: &mut edict::World, pack: MyPlayerInput) {
// Input is associated with provided entity.
// This code should transform input and put it where other systems would be able to consume it properly.
// Usually it do the reverse of [`client::LocalPlayer::replicate`].
}
}
/// Component that is own descriptor.
#[derive(Clone, Copy, PartialEq, serde::Serialize)]
pub struct MyComponent;
/// Prepare channel listener.
let listener = tokio::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, 12523)).await?;
/// Build server system.
let mut server = evoke::server::ServerSystem::builder()
.with_descriptor::<MyComponent>()
.with_player::<MyRemotePlayer>()
.build(listener);
let mut world = edict::World::new();
let scope = scoped_arena::Scope::new();
// game loop
loop {
//
// Game loop tick
//
// Run server every tick.
server.run(&mut world, &scope);
}
客户端
要配置 ClientSystem
,请提供组件复制描述符以及 LocalPlayer
的实现。
/// Information associated with player.
#[derive(serde::Serialize)]
struct MyPlayerInfo;
/// Player input serializable representation.
#[derive(serde::Serialize)]
struct MyPlayerInput;
/// This type drives player lifecycle and input processing.
struct MyLocalPlayer;
impl<'a> evoke::client::LocalPlayerPack<'a> for MyLocalPlayer {
type Pack = &'a MyPlayerInput;
}
impl evoke::client::LocalPlayer for MyLocalPlayer {
type Query = &'static MyPlayerInput;
fn replicate<'a>(item: &'a MyPlayerInput, _scope: &'a scoped_arena::Scope<'_>) -> &'a MyPlayerInput {
item
}
}
/// Component that is own descriptor.
#[derive(Clone, Copy, PartialEq, serde::Deserialize)]
pub struct MyComponent;
/// Build client system.
let mut client = evoke::client::ClientSystem::builder()
.with_descriptor::<MyComponent>()
.with_player::<MyLocalPlayer>()
.build();
let mut world = edict::World::new();
let scope = scoped_arena::Scope::new();
client.connect((std::net::Ipv4Addr::LOCALHOST, 12523), &scope).await?;
let player_id: evoke::PlayerId = client.add_player(&MyPlayerInfo, &scope).await?;
// The player can controls all entities to which server attaches same `PlayerId` as component.
// game loop
loop {
//
// Game loop tick
//
// Run client every tick.
client.run(&mut world, &scope);
}
许可证
以下任一许可证下授权:
- Apache License, Version 2.0, (license/APACHE 或 https://apache.ac.cn/licenses/LICENSE2.0)
- MIT 许可证 (license/MIT 或 http://opensource.org/licenses/MIT)
根据您的选择。
贡献
除非您明确声明,否则您有意提交的、根据 Apache-2.0 许可证定义的、旨在包含在本作品中的任何贡献,将按上述方式双授权,不附加任何额外的条款或条件。
依赖项
~3–16MB
~154K SLoC