0.1.0 |
|
---|
#2 in #fantastic
1KB
evoke - 简易网络代码
Evoke 提供了将网络功能添加到游戏引擎的构建块。
Evoke 支持
- 具有授权服务器的客户端-服务器模型
客户端-服务器
对于客户端-服务器模型,Evoke 自动执行从服务器到客户端的状态复制,并进行 delta 压缩
以及从客户端到服务器的命令复制。
Evoke 对游戏中使用的组件不假设任何东西。
用户需要在服务器中注册 server::Descriptor
以及在客户端中注册 client::Descriptor
以复制所需的组件。对于可比较的组件和可序列化的组件,提供了泛型实现。
对于 服务器 和 客户端。
核心
Evoke 的核心提供了非常抽象的客户端和服务器会话,支持发送和接收类似于 Connect
、AddPlayer
、SendInput
、Update
等命令,具有泛型有效载荷。Evoke 的核心作为单独的 crate evoke-core
提供,并从这个 crate 作为 evoke::core
与 evoke
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 许可协议第 2 版 (license/APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可协议 (license/MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确说明,否则根据 Apache-2.0 许可证定义的,任何有意提交以包含在作品中的贡献,都将如上所述双重许可,不附加任何额外的条款或条件。