【HLSDK系列】服务端 AddToFullPack 函数

服务端会给客户端发送一些数据,其中两大种类数据是 clientdata_t 和 entity_state_t 这里我们说说 entity_state_t 这个结构体。

你在丢在地上的枪、C4等等是服务端实体(edict_t),并且你能在客户端看到它们(废话),这些实体们是怎样发送到你的客户端的呢?

引擎不可能原原本本地把 edict_t 发送出去的,所以就有了 entity_state_t 这个结构体,它表示了一个可见实体所有必要的数据。

接上面:如果实体不可见,那何必发到客户端呢?:-)

所以 entity_state_t 只保存跟实体显示有关的数据,例如 origin、angles、model 这些,引擎只需要把这些数据发到客户端就行了。

引擎里有一个叫做 FullPack 的包(实际上就是数组),这个包里有全部需要发送给客户端的实体的 entity_state_t。

引擎会逐个检查服务端的所有实体,并且添加到包里,准备发送给客户端。

那引擎是不是默认就把能看见的实体都添加到包里了呢?并不是,因为引擎提供了一个接口让我们自己决定哪些实体可以被添加到包里!

你可以在 mp.dll 的源码里找到 AddToFullPack 这函数,可以看到这样的代码:

int AddToFullPack(struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet)
{
    if ((ent->v.effects == EF_NODRAW) && ent != host)
        return 0;    // 有EF_NODRAW这个标记的实体是不可见的,不添加到包里。

    if (!ent->v.modelindex || !STRING(ent->v.model))
        return 0;    // 没有模型的实体是不可见的,不添加到包里。

    if ((ent->v.flags & FL_SPECTATOR) && ent != host)
        return 0;    // 观察者也是不可见的,不添加到包里。

    // ...

    if (ent != host)
    {
        // 在可视范围(PVS)外的实体是看不到的,不添加到包里。
        if (!CheckEntityRecentlyInPVS(hostindex, e, gpGlobals->time))
        {
            if (!ENGINE_CHECK_VISIBILITY((const struct edict_s *)ent, pSet))
            {
                MarkEntityInPVS(hostindex, e, gpGlobals->time, true);
                return 0;
            }

            MarkEntityInPVS(hostindex, e, gpGlobals->time);
        }
    }

    // ...
}

参数 state 是将要添加到包里的 entity_state_t ,参数 ent 是正在处理的实体,参数 host 是包要发送到的那个玩家(的客户端)!

如果这个函数返回 0 (FALSE)引擎就不会把这个实体添加到包里,这个实体自然也就不会被发送到那个客户端(看不到)。

你甚至可以在这个函数里自定义需要发送的实体的数据!我们可以看到这样的代码:

int AddToFullPack(struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet)
{
    // ...

    state->number = e;
    state->entityType = ENTITY_NORMAL;

    state->animtime = (int)(1000.0 * ent->v.animtime) / 1000.0;

    memcpy(state->origin, ent->v.origin, 3 * sizeof(float));
    memcpy(state->angles, ent->v.angles, 3 * sizeof(float));
    memcpy(state->mins, ent->v.mins, 3 * sizeof(float));
    memcpy(state->maxs, ent->v.maxs, 3 * sizeof(float));
    memcpy(state->startpos, ent->v.startpos, 3 * sizeof(float));
    memcpy(state->endpos, ent->v.endpos, 3 * sizeof(float));

    state->impacttime = ent->v.impacttime;
    state->starttime = ent->v.starttime;
    state->modelindex = ent->v.modelindex;
    state->frame = ent->v.frame;

    // ...
}

你可以看到它从服务端实体(edict_t)抽出必要的数据填充到 entity_state_t 里!

最后返回 1(TRUE)引擎将会把我们填充好的 entity_state_t 添加到包里,发送给客户端。

这个函数真的很有用对吧,我们可以做一些有趣的功能,让一个实体可以被玩家A看到,玩家B却看不到!

我们只需要检查当前正在处理的实体(ent)是那个我们不让玩家B看到的实体,然后判断 host 是不是玩家B,如果是,就返回 0 不让这个实体发送给玩家B!

你甚至可以设置一个玩家(玩家也是实体)不让其它玩家看到!

注意,引擎里的包最多只能容纳 256 个实体!如果超出了这个数量,引擎将会忽略超出部分的实体!

时间: 2024-12-27 22:16:25

【HLSDK系列】服务端 AddToFullPack 函数的相关文章

【HLSDK系列】服务端 UpdateClientData 函数

首先说明下,这个函数是写在 mp.dll 里的. 服务器会给每个客户端发送一些数据,其中两大数据种类就是 clientdata_t 和 entity_state_t 这里要说的是 clientdata_t 这个结构. 结构体 clientdata_t 是服务器发送给玩家的客户端的一组数据,每个玩家的状态不同,所以发送给不同玩家的客户端的 clientdata_t 自然也不同. 我们知道玩家就是一个实体(edict_t),我们在实体的 pev 或者 CBasePlayer 里保存了许多必要的数据,

TCP连接建立系列 — 服务端接收ACK段(一)

http://blog.csdn.net/zhangskd/article/details/17923917 分类: Linux TCP/IP Linux Kernel 2014-01-07 09:46 2311人阅读 评论(2) 收藏 举报 TCPIPlinux内核 目录(?)[+] 本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 函数路径 以下是第三次握手时,服务端接收到ACK后的处理路径. 接收

React(0.13) 服务端渲染的两个函数

1.React.renderToString 函数,  参数是组件,返回一个字符串 <!DOCTYPE html> <html> <head> <title>React JS</title> <script src="../build_0.13/react.js"></script> <script src="../build_0.13/JSXTransformer.js"&g

【HLSDK系列】HL引擎入门篇

如果你打算拿HL的源码(也就是HLSDK)来改出一个自己的游戏,那你就非常有必要理解一些HL引擎的工作方式. HL引擎分成两个部分,服务端和客户端.服务端管理所有玩家的状态和游戏规则,客户端负责显示UI和特效-之类的. 我们一般不对引擎本身(hw.dll sw.dll swds.dll)做什么手脚,因为我们没有源码,但是HL引擎开放了一些服务端和客户端的接口, 让我们可以定制自己的服务端和客户端,做成自己的游戏. 服务端接口和客户端接口都作为一个单独的DLL模块,我们可以自己修改这两个DLL,因

python socket编程 ,tcp,udp服务端客户端创建

转自http://blog.csdn.net/rebelqsp/article/details/22109925 Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发. 下面讲的是Socket模块功能 1.Socket 类型 套接字格式: socket(family,type[,protocal]) 使用给定的地址族.套接字类型.协议编号

Linux下的TCP/IP编程----进程及多进程服务端

在之前的学习中我们的服务端同一时间只能为一个客户端提供服务,即使是将accept()函数包含在循环中,也只能是为多个客户端依次提供服务,并没有并发服务的能力,这显然是不合理的.通过多进程的使用,我们可以很便捷的实现服务端的多进程,这样就可以同时为多个客户端提供服务. 首先我们要理解程序,进程,进程ID,僵尸进程,线程的概念. 程序:广泛的说就是为了达到某一目的二规定的途径,在编程中具体的就是为了实现某一功能而编写的代码实体,是静态的. 进程:程序的一次动态执行就是一个进程,它是占用了一定内存空间

python实现一个客户端与服务端的通信

函数介绍 Socket对象方法: 服务端: 函数 描述 .bind() 绑定地址关键字,AF_INET下以元组的形式表示地址.常用bind((host,port)) .listen() 监听TCP,可以挂起的最大连接数,该值至少为1,一般设为5即可 .accept() 被动接受TCP客户端的连接 客户端: 函数 描述 .connect() 初始化服务器连接 .connect_ex() 是对connect()函数的扩展,当出错时返回出错码,不报异常 其它函数: 函数 描述 .recv() 接收数据

【HLSDK系列】服务端实体 edict_t 和 控制类

我们来了解一下引擎是怎么管理实体的吧!我们这里就说说服务端的实体(edict_t) 服务端用 edict_t 这个结构体来保存一个实体,可以说一个 edict_t 就是一个 服务端实体,下文简称实体. 我们在 mp.dll 的源码里经常看到的那些 CBaseXXX 又和 edict_t 有什么关系呢? 引擎只管理小部分实体的功能,更多功能需要我们自己写代码去实现,这里就引入了 实体控制类 这个东西(就是那些 CBaseXXX),类就是C++的那个类.下文简称控制类. 接下来我们就分析 edict

[Axis2与Eclipse整合开发Web Service系列之二] Top-Down方式,通过WSDL逆向生成服务端(续)

前言 本篇是承接上一篇: [Axis2与Eclipse整合开发Web Service系列之二] Top-Down方式,通过WSDL逆向生成服务端 在上一篇粗略地介绍了如何使用Top-Down的方式创建一个web service .  但是对于如何部署及调用,以及一些细节的部分基本上没有介绍. 应某些博友的要求, 也适逢自己有空, 接下来就详细介绍一下整个部分如何进行. 环境准备 JDK 肯定要安装了, 这个就不多讲了. 1. eclipse  3.5.2 对eclipse 版本的要求其实不是很严