1. 程式人生 > >【HLSDK系列】服務端 AddToFullPack 函數

【HLSDK系列】服務端 AddToFullPack 函數

客戶端 global 客戶 const start max pre 參數 glob

服務端會給客戶端發送一些數據,其中兩大種類數據是 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 個實體!如果超出了這個數量,引擎將會忽略超出部分的實體!

【HLSDK系列】服務端 AddToFullPack 函數