3个不稳定版本
0.2.0 | 2022年2月7日 |
---|---|
0.1.1 | 2021年10月20日 |
0.1.0 | 2021年10月20日 |
#17 in #netcode
用于 evoke
30KB
783 行
evoke - 简单网络代码
Evoke提供了构建块,以将网络功能添加到游戏引擎中。
Evoke支持
- 带有授权服务器的客户端-服务器模型
客户端-服务器
对于客户端-服务器模型,Evoke自动从服务器到客户端执行状态复制,并使用增量压缩,以及从客户端到服务器的命令复制。
。
Evoke不对游戏中使用的组件做出假设。
用户需要在服务器和客户端中注册需要复制的组件的 server::Descriptor
和 client::Descriptor
,对于可比较相等和可序列化的组件有泛型实现。
对于 服务器 和 客户端。
核心
Evoke的核心提供非常抽象的客户端和服务器会话,支持发送和接收命令,如Connect
、AddPlayer
、SendInput
、Update
等,具有泛型负载。Evoke的核心作为单独的crate evoke-core
提供,并从该crate作为evoke::core
导出。
与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 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证 (license/MIT 或 http://opensource.org/licenses/MIT)
。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义的,您有意提交并包含在作品中的任何贡献,都将双重授权,如上所述,没有任何附加条款或条件。
依赖项
~1-13MB
~106K SLoC