3个不稳定版本

0.2.0 2022年2月7日
0.1.1 2021年10月20日
0.1.0 2021年10月20日

#17 in #netcode


用于 evoke

MIT/Apache

30KB
783

evoke - 简单网络代码

crates docs actions MIT/Apache loc

Evoke提供了构建块,以将网络功能添加到游戏引擎中。

Evoke支持

  • 带有授权服务器的客户端-服务器模型

客户端-服务器

对于客户端-服务器模型,Evoke自动从服务器到客户端执行状态复制,并使用增量压缩,以及从客户端到服务器的命令复制。

Evoke不对游戏中使用的组件做出假设。
用户需要在服务器和客户端中注册需要复制的组件的 server::Descriptorclient::Descriptor,对于可比较相等和可序列化的组件有泛型实现。
对于 服务器客户端

核心

Evoke的核心提供非常抽象的客户端和服务器会话,支持发送和接收命令,如ConnectAddPlayerSendInputUpdate等,具有泛型负载。Evoke的核心作为单独的crate evoke-core提供,并从该crate作为evoke::core导出。

与crate(本文件)evoke-core不同,它不依赖于edict,并且可以用于任何游戏引擎,即使是用除Rust以外的语言编写的,只要打包成FFI库。

使用方法

要开始使用Evoke,只需在服务器和客户端上分别配置并运行ServerSystemClientSystem

服务器

要配置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.0许可证定义的,您有意提交并包含在作品中的任何贡献,都将双重授权,如上所述,没有任何附加条款或条件。

依赖项

~1-13MB
~106K SLoC