﻿# Боремся с хедкрабами в исходниках Source SDK

GameDev тернист и неисповедим\. Как и любой проект, он проходит испытания кровью и потом, сражаясь с тварями, тьмой порождёнными: барнаклы, пиявки, муравьиные львы\. И это ещё не сказочка, это только смазочка\. Больше всего стоит остерегаться жуков хедкрабов\. Да, это те самые баги в коде\. Если их вовремя не убить монтировкой, то ваша участь — стать отвратительным кадавром\. Предлагаем читателю побывать в роли Гордона Фримена, погрузиться в недры Source SDK и побороться с хедкрабами\. В качестве монтировки же выступит PVS\-Studio\.

![1281_Source_SDK_ru/image1.png](https://import.viva64.com/docx/blog/1281_Source_SDK_ru/image1.png)

## О проекте

Перед тем как пересечь межпространственные врата, наш дорогой Гордон, изучим небольшую справку о мире, который предстоит посетить\.

[Source SDK](https://developer.valvesoftware.com/wiki/Source_SDK_Base_2013) — это необходимые зависимости для модов, позволяющие создавать собственный контент для игр на движке [Source](https://halflife.fandom.com/ru/wiki/Source) Engine, разработанный корпорацией Valve\. При помощи этого инструментария можно создавать кастомные карты, моды, изменять выражение лица NPC или моделей персонажей\.

Мы поистине рады тому, что разработчики исправили ошибки с нашей прошлой проверки\. Если интересно, с ней можно ознакомится в этой [статье](https://pvs-studio.ru/ru/blog/posts/cpp/0229/)\. Но проект Source SDK не стоит на месте, растёт и развивается, как и [PVS\-Studio](https://pvs-studio.ru/ru/docs/manual/0010/), с помощью которого была проведена проверка исходного кода\.

Думаю, важно сказать, что повторная проверка приурочена к обновлению движка, в котором было добавлено немало изменений: прирост производительности игры благодаря возможности использования более чем 4 Гб оперативной памяти, корректное масштабирование элементов VGUI на более высоких разрешениях, таких как 4K\. Но с последним крупным обновлением в Source SDK был добавлен код Team Fortress 2\. Это позволит моддерам изменять TF2 под свои нужды, начиная от небольших предметов и заканчивая созданием новых игр на основе TF2\.

Проект собирался из исходного кода по следующей [инструкции](https://github.com/ValveSoftware/source-sdk-2013)\. Хочется отметить, что разработчики постарались сделать сборку максимально простой и понятной\. Коммит, на котором был собран проект для проверки — [68c8b82](https://github.com/ValveSoftware/source-sdk-2013/tree/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e)\.

В статью вошли предупреждения только уровня High \(есть ещё Medium и Low\)\.

## Результаты проверки

## Ошибки при работе с массивами

### Выход за границу массива

```cpp
class DVariant
{
  ....
  union
  {
    ....
    float m_Vector[3];
    ....
  };
  ....
};

// This is passed into RecvProxy functions.
class CRecvProxyData
{
public:
  ....
  DVariant m_Value; // The value given to you to store.
  ....
};

void RecvProxy_QuaternionToQuaternion( const CRecvProxyData *pData, 
void *pStruct, void *pOut )
{
  const float *v = pData->m_Value.m_Vector;
  
  Assert( IsFinite( v[0] ) && IsFinite( v[1] ) &&
          IsFinite( v[2] ) && IsFinite( v[3] ) );
  ((float*)pOut)[0] = v[0];
  ((float*)pOut)[1] = v[1];
  ((float*)pOut)[2] = v[2];
  ((float*)pOut)[3] = v[3];
}
```

Предупреждение PVS\-Studio: [V557](https://pvs-studio.ru/ru/docs/warnings/v557/) Array overrun is possible\. The '3' index is pointing beyond array bound\. [dt\_recv\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/public/dt_recv.cpp#L369) 369, 373

Поле `m_Value` типа `DVariant` содержит внутри себя массив `m_Vector` с фиксированным размером из 3 элементов\. В тот момент, когда в функции `RecvProxy_QuaternionToQuaternion` пытаются получить данные у 4\-го элемента массива, поведение будет не определено\.

Проблема найдена, можем расходиться\. Однако это ещё не всё\. На самом деле декларация поля `m_Vector` выглядит несколько [хитрее](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/public/dt_common.h#L191-L195):

```cpp
union
{
  ....
#if 0 // We can't ship this since it changes the size of DTVariant 
      // to be 20 bytes instead of 16 and that breaks MODs!!!
  float m_Vector[4];
#else
  float m_Vector[3];
#endif
  ....
};
```

Судя из комментария, когда\-то размер вектора хотели увеличить до `4`, при этом изменив весь сопровождающий код\. Спустя какое\-то время разработчики осознали, что это сломает обратную совместимость с модами, и откатили правки\. Но, как оказалось, не во всех местах\.

### Неверное освобождение памяти массива

```cpp
class CCamoMaterialProxy : public CEntityMaterialProxy
{
public:
  CCamoMaterialProxy();
  virtual ~CCamoMaterialProxy();
  ....
private:
  ....
  void GenerateRandomPointsInNormalizedCube( void );
  ....
private:
  ....
  Vector *m_pointsInNormalizedBox; // [m_CamoPatternNumColors]
  ....
};

void CCamoMaterialProxy::GenerateRandomPointsInNormalizedCube( void )
{
  m_pointsInNormalizedBox = new Vector[m_CamoPatternNumColors];
  ....
}

CCamoMaterialProxy::~CCamoMaterialProxy()
{
  ....
  delete m_pointsInNormalizedBox;
}
```

Предупреждение PVS\-Studio: [V611](https://pvs-studio.ru/ru/docs/warnings/v611/) The memory was allocated using the 'operator new\[\]' but was released using the 'operator delete'\. The 'delete\[\] m\_pointsInNormalizedBox;' statement should be used instead\. Check lines: 164, 561\. [camomaterialproxy\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/client/camomaterialproxy.cpp#L164) 164

В конструкторе класса `CCamoMaterialProxy` динамическая память выделяется при помощи выражения `new[]` и затем удаляется в деструкторе при помощи выражения `delete`\. По стандарту поведение последней операции будет [не определено](https://timsong-cpp.github.io/cppwp/n4950/expr.delete#2)\. Для более подробной информации о том, почему массивы необходимо удалять через `delete[]`, лучше ознакомиться с нашей [статьёй](https://pvs-studio.ru/ru/blog/posts/cpp/0973/)\.

Исправленный код:

```cpp
CCamoMaterialProxy::~CCamoMaterialProxy()
{
  ....
  delete[] m_pointsInNormalizedBox;
}
```

### Использование неинициализированного массива

```cpp
KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate)
{
  // return the current key if a NULL subkey is asked for
  if (!keyName || !keyName[0]) // <=
    return this;

  // look for '/' characters deliminating sub fields
  char szBuf[256] = { 0 };
  const char *subStr = strchr(keyName, '/');
  const char *searchStr = keyName;           

  // pull out the substring if it exists
  if (subStr)
  {
    int size = Min( (int)(subStr - keyName + 1), 
  (int)V_ARRAYSIZE( szBuf ) );
    V_strncpy( szBuf, keyName, size );
    ....
  }
  ....
}

void LoadCmdLineFromFile( int &argc, char **&argv, 
                          const char *keyname,
                          const char *appname )
{
  ....
  if ( kv->LoadFromFile( g_pFileSystem, filename ) )
  {
    // Load the commandline arguments for this app
    KeyValues  *appKey  = kv->FindKey( keyname );
    ....
  }
  ....
}

int RunVVis( int argc, char **argv )
{
  char portalfile[1024];
  char source[1024];
  char mapFile[1024];
  double start, end;
  ....
  LoadCmdLineFromFile( argc, argv, source, "vvis" );
  ....
}
```

Предупреждение PVS\-Studio: [V614](https://pvs-studio.ru/ru/docs/warnings/v614/) \[CERT\-EXP53\-CPP\] Uninitialized buffer 'source' used\. Consider checking the third actual argument of the 'LoadCmdLineFromFile' function\. [vvis\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/utils/vvis/vvis.cpp#L1095C2-L1095C52) 1095

Анализатор обнаружил использование неинициализированной переменной `source`, что может привести к непредсказуемым результатам\. Пройдёмся по коду и посмотрим, что происходит\. Объявляется массив `source` типа `char[1024]` без инициализации, из\-за этого в ячейках массива будут хранится произвольные данные\. Далее `source` передаётся в функцию [`LoadCmdLineFromFile`](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/public/loadcmdline.cpp#L75), а та в свою очередь передаёт его в функцию [`FindKey`](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/tier1/KeyValues.cpp#L987)\. И уже здесь неинициализированный буфер будет прочтён, и поведение такой операции не определено\.

## Невероятно, но факт

### Same, same but different

```cpp
class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM
{
public:
  ....
  virtual bool CanHolster( void ) const { return TRUE; };
  ....
};

class CWeaponAR2 : public CHL2MPMachineGun
{
public:
  ....
  bool CanHolster( void );
  ....
};

bool CWeaponAR2::CanHolster( void )
{
  if ( m_bShotDelayed )
    return false;
  return BaseClass::CanHolster();
}
```

Предупреждение анализатора PVS\-Studio: [V762](https://pvs-studio.ru/ru/docs/warnings/v762/) It is possible a virtual function was overridden incorrectly\. See qualifiers of function 'CanHolster' in derived class 'C\_WeaponAR2' and base class 'C\_BaseCombatWeapon'\. [weapon\_ar2\.h](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/shared/hl2mp/weapon_ar2.h#L48) 48

Из текста предупреждения заметно, что проблема в ошибочном переопределении виртуальной функции\. Но что могло пойти не так?

Прежде чем разобраться в проблеме, немного вникнем в код\. Есть базовый класс `CBaseCombatWeapon`, у которого есть виртуальная функция `CanHolster`\. Также у нас есть дочерний класс `CWeaponAR2` с функцией\-членом `CanHolster`, который по задумке должен был переопределить виртуальную функцию из базового класса\.

Как видим, эти две функции с именем `CanHolster` имеют почти что одинаковые сигнатуры\. Единственное различие — квалификатор `const` после имени функции\.

Вспомним, что при [переопределении виртуальной функции](https://en.cppreference.com/w/cpp/language/virtual.html) сигнатура функции производного класса должна в точности соответствовать сигнатуре функции базового класса, а именно:

* имена функций в базовом и производном классах должны быть одинаковы;
* типы параметров функций должны быть одинаковы;
* квалификаторы нестатических функций в базовом и производном классах должны совпадать;
* возвращаемые типы и спецификации исключений функций в базовом и производном классах должны быть коварианты;
* \[дополнительно\] ссылочные квалификаторы функций должны быть идентичными\.

Так как одно из условий не выполняется, то в производный класс будет добавлена новая функция, которая перекроет имя из базового класса\. Чтобы исправить возникшую ситуацию, необходимо добавить квалификатор `const` после имени функции\-члена из дочернего класса\. И не забудем добавить спецификатор `override`, проверяющий, что есть функция из базового, которую переопределяем:

```cpp
class CWeaponAR2 : public CHL2MPMachineGun
{
public:
  ....
  bool CanHolster( void ) const override;
  ....
};
```

Неподалёку разместились ещё два класса, в которых содержится та же ошибка:

* V762 It is possible a virtual function was overridden incorrectly\. See qualifiers of function 'CanHolster' in derived class 'C\_WeaponPhysCannon' and base class 'C\_BaseCombatWeapon'\. weapon\_physcannon\.h 289
* V762 It is possible a virtual function was overridden incorrectly\. See qualifiers of function 'CanHolster' in derived class 'C\_WeaponRPG' and base class 'C\_BaseCombatWeapon'\. weapon\_rpg\.h 193

### Проверка на nullptr производится после использования указателя

```cpp
void CClientShadowMgr::RemoveAllShadowsFromReceiver( 
  IClientRenderable* pRenderable, ShadowReceiver_t type )
{
  // Don't bother if this renderable doesn't receive shadows
  if ( !pRenderable->ShouldReceiveProjectedTextures( // <=
       SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
    return;

  // Do different things depending on the receiver type
  switch( type )
  {
  case SHADOW_RECEIVER_BRUSH_MODEL:
    {
      model_t* pModel = const_cast<model_t*>(pRenderable->GetModel());
      shadowmgr->RemoveAllShadowsFromBrushModel( pModel );
    }
    break;

  case SHADOW_RECEIVER_STATIC_PROP:
    staticpropmgr->RemoveAllShadowsFromStaticProp(pRenderable);
    break;

  case SHADOW_RECEIVER_STUDIO_MODEL:
    if( pRenderable && pRenderable->GetModelInstance() != // <=
        MODEL_INSTANCE_INVALID )
    {
      shadowmgr->RemoveAllShadowsFromModel(pRenderable->GetModelInstance());
    }
    break;
  ...
}
```

Предупреждение PVS\-Studio: [V595](https://pvs-studio.ru/ru/docs/warnings/v595/) The 'pRenderable' pointer was utilized before it was verified against nullptr\. Check lines: 3535, 3553\. [clientshadowmgr\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/client/clientshadowmgr.cpp#L3535) 3535

В начале функции `RemoveAllShadowsFromReceiver` разыменовывается указатель `pRenderable`\. Но далее в кейсе `SHADOW_RECEIVER_STUDIO_MODEL` конструкции `switch` есть условие, которое проверяет этот указатель на валидность\. Разработчик предполагает, что он может быть нулевым\. Исходя из того, что этот указатель — параметр функции, логичным кажется всё же проверить его и сделать ранний возврат из функции:

```cpp
// Don't bother if this renderable doesn't receive shadows
if (   !pRenderable 
    || !pRenderable->ShouldReceiveProjectedTextures( 
          SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
  return;
```

Если же эта функция имеет неявный контракт "первый параметр всегда должен быть ненулевым", то проверку в `switch` стоит убрать\.

Таких предупреждений довольно много, и на них следует обратить внимание:

* V595 The 'pPlayer' pointer was utilized before it was verified against nullptr\. Check lines: 426, 442\. weapon\_frag\.cpp 426
* V595 The 'pPlayer' pointer was utilized before it was verified against nullptr\. Check lines: 146, 152\. weapon\_hl2mpbasebasebludgeon\.cpp 146
* V595 The 'pOwner' pointer was utilized before it was verified against nullptr\. Check lines: 1364, 1380\. weapon\_physcannon\.cpp 1364
* V595 The 'node' pointer was utilized before it was verified against nullptr\. Check lines: 2338, 2341\. nav\_generate\.cpp 2338
* V595 The 'adjNode' pointer was utilized before it was verified against nullptr\. Check lines: 2339, 2341\. nav\_generate\.cpp 2339
* V595 The 'm\_goal' pointer was utilized before it was verified against nullptr\. Check lines: 730, 744\. NextBotPathFollow\.cpp 730
* V595 The 'body' pointer was utilized before it was verified against nullptr\. Check lines: 1748, 1762\. NextBotPathFollow\.cpp 1748
* V595 The 'm\_pszString' pointer was utilized before it was verified against nullptr\. Check lines: 819, 827\. convar\.cpp 819
* V595 The 'pPortal\-\>nodes\[nOtherSideIndex\]' pointer was utilized before it was verified against nullptr\. Check lines: 712, 715\. vbsp\.cpp 712
* V595 The 'front' pointer was utilized before it was verified against nullptr\. Check lines: 397, 475, 477\. polylib\.cpp 475
* V595 The 'm\_pCommand' pointer was utilized before it was verified against nullptr\. Check lines: 251, 252\. consoledialog\.cpp 251
* V595 The '\_title' pointer was utilized before it was verified against nullptr\. Check lines: 1594, 1603\. Frame\.cpp 1594
* V595 The 'pActionSignalTarget' pointer was utilized before it was verified against nullptr\. Check lines: 636, 642\. perforcefilelistframe\.cpp 636
* V595 The '\_activeTab' pointer was utilized before it was verified against nullptr\. Check lines: 1063, 1087\. PropertySheet\.cpp 1063
* V595 The 'm\_pTree' pointer was utilized before it was verified against nullptr\. Check lines: 132, 144\. TreeViewListControl\.cpp 132
* V595 The 'pShaderShadow' pointer was utilized before it was verified against nullptr\. Check lines: 103, 334, 335\. example\_model\_dx9\_helper\.cpp 334

### Подозрительное преобразование

```cpp
typedef unsigned char byte;

inline void CBaseEntity::SetRenderColor( byte r, byte g, byte b, byte a )
{
  m_clrRender.Init( r, g, b, a );
}

void CGrenadeBugBait::BugBaitTouch( CBaseEntity *pOther )
{
  ....
  if ( pSporeExplosion )
  {
    ....
    pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f ); // <=
    ....
  }
  ....
}
```

Предупреждение PVS\-Studio: [V674](https://pvs-studio.ru/ru/docs/warnings/v674/) The literal '0\.5f' of 'float' type is being implicitly cast to 'unsigned char' type while calling the 'SetRenderColor' function\. Inspect the second argument\. [grenade\_bugbait\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/server/hl2/grenade_bugbait.cpp#L168) 168

Функция `SetRenderColor` устанавливает значение цвета по **RGBA**, где каждый параметр имеет тип `unsigned char` с возможным диапазоном значений `[0 .. 255]`\. При попытке передать аргументы с типом `float` дробная часть будет отброшена\. Это приведёт к тому, что параметры функции `r`, `g`, `b`, `a` будут иметь значения, равные `0`\.

К сожалению, в репозитории отсутствует информация по blame, поэтому у меня есть два предположения, как ошибка появилась в кодовой базе: 

1. Когда\-то функция обрабатывала цвета в вещественном представлении\. Затем обработку сменили на целые числа, но забыли поправить все точки вызова\.
1. Разработчик ошибочно посчитал, что функция `SetRenderColor` обрабатывает вещественные числа, и задал их таким способом\.

Дополнительно несколько аналогичных предупреждений:

* V674 The literal '0\.5f' of 'float' type is being implicitly cast to 'unsigned char' type while calling the 'SetRenderColor' function\. Inspect the second argument\. weapon\_bugbait\.cpp 171
* V674 The literal '25\.6' of 'double' type is being implicitly cast to 'int' type while calling the 'SetScrollRate' function\. Inspect the first argument\. grenade\_tripmine\.cpp 179

### Некорректное сравнение

```cpp
template <class T> void AddSortedByName(T * & head, T * node)
{
  if ( (! head) || (stricmp(node->m_Name, head->m_Name) == -1))
  {
    ....
  }
  ....
}
```

Предупреждение PVS\-Studio: [V698](https://pvs-studio.ru/ru/docs/warnings/v698/) Expression 'stricmp\(\.\.\.\.\) \=\= \-1' is incorrect\. This function can return not only the value '\-1', but any negative value\. Consider using 'stricmp\(\.\.\.\.\) < 0' instead\. [utlintrusivelist\.h](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/public/tier1/utlintrusivelist.h#L509) 509

Функция [`stricmp`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/stricmp-wcsicmp-mbsicmp-stricmp-l-wcsicmp-l-mbsicmp-l?view=msvc-170) может вернуть любое отрицательное значение, если `node->m_Name` лексикографически меньше, чем `head->m_Name` \(без учёта регистра\)\. Поэтому сравнивать результат функции `stricmp` со значением `-1` некорректно\.

Исправленный код:

```cpp
if ( (! head) || (stricmp(node->m_Name, head->m_Name) < 0))
{
  ....
}
```

## Аномалии

### Истинная и ложная ветки оператора 'if' полностью совпадают

```cpp
void CWeaponCrossbow::PrimaryAttack( void )
{
  if ( m_bInZoom && g_pGameRules->IsMultiplayer() )
  {
    //FireSniperBolt();
    FireBolt();
  }
  else
  {
    FireBolt();
  }
  ....
}
```

Предупреждение PVS\-Studio: [V523](https://pvs-studio.ru/ru/docs/warnings/v523/) The 'then' statement is equivalent to the 'else' statement\. [weapon\_crossbow\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/shared/hl2mp/weapon_crossbow.cpp#L542) 542

Анализатор обнаружил ситуацию, когда истинная и ложная ветки конструкции `if` полностью совпадают\. По комментарию в коде можно предположить, что когда\-то была возможность сделать прицельный огонь в мультиплеере, но потом от этого функционала решили избавиться\. Но сама проверка возможного вида стрельбы осталась в реализации\. В отчёте также были отображены ещё пять похожих мест:

<details>
   <summary>Аналогичные срабатывания\\\.</summary>

* V523 The 'then' statement is equivalent to the 'else' statement\. c\_basehlplayer\.cpp 634
* V523 The 'then' statement is equivalent to the 'else' statement\. hl2\_triggers\.cpp 752
* V523 The 'then' statement is equivalent to the 'else' statement\. npc\_combine\.cpp 2528
* V523 The 'then' statement is equivalent to the 'else' statement\. npc\_ichthyosaur\.cpp 724
* V523 The 'then' statement is equivalent to the 'else' statement\. Menu\.cpp 1185


</details>
### Независимо от условия, будет выполнено одно и то же действие

```cpp
float CInput::KeyState ( kbutton_t *key )
{
  ....
  if ( impulseup && !impulsedown )
  {
    // released this frame?
    val = down ? 0.0 : 0.0; // <=
  }
  ....
  return val;
}
```

Предупреждение PVS\-Studio: [V583](https://pvs-studio.ru/ru/docs/warnings/v583/) The '?:' operator, regardless of its conditional expression, always returns one and the same value: 0\.0\. [in\_main\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/client/in_main.cpp#L605) 605

Здесь, вероятнее всего, опечатка, так как возврат одного значения в условии выглядит подозрительно\.

### Присваивания разных значений одной переменной

```cpp
void CBaseVSShader::SetPixelShaderConstant_W(int pixelReg,
                                             int constantVar,
                                             float fWValue )
{
  ....
  float val[4];
  if (pPixelVar->GetType() == MATERIAL_VAR_TYPE_VECTOR)
    pPixelVar->GetVecValue( val, 4 );
  else
    val[0] = val[1] = val[2] = val[3] = pPixelVar->GetFloatValue();
  val[3]=fWValue; // <=
  ....
}
```

Предупреждение анализатора PVS\-Studio: [V519](https://pvs-studio.ru/ru/docs/warnings/v519/) The 'val\[3\]' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 124, 125\. [BaseVSShader\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/materialsystem/stdshaders/BaseVSShader.cpp#L125) 125

Анализатор обнаружил, что в переменную `val[3]` несколько раз подряд присваиваются разные значения\. Причём между этими присваиваниями сама переменная не используется\. Возможно, это опечатка, и планировалось написать что\-то вроде такого выражения: `val[3] += fWValue`\.

### Переменная присваивается сама себе

```cpp
void CNPC_Monk::Spawn()
{
  ....
  m_flFieldOfView = m_flFieldOfView = -0.707; // 270`
  ....
}
```

Предупреждение анализатора PVS\-Studio: [V570](https://pvs-studio.ru/ru/docs/warnings/v570/) The same value is assigned twice to the 'm\_flFieldOfView' variable\. [npc\_monk\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/game/server/hl2/npc_monk.cpp#L274) 274

Полю `m_flFieldOfView` дважды присваивается одно и то же значение `-0.707` \(косинус угла обзора в 135 градусов\)\. Либо это опечатка, и должно использоваться также другое поле, либо одно присваивание лишнее, и его можно убрать\.

### Параметр стал ненужным

```cpp
void SendProxy_UtlVectorElement(...., int iElement, ....)
{
  ....
  iElement = pProp->GetElementStride();
  ....
  if ( iElement >= pUtlVec->Count() )
  {
    ....
  }
  else
  {
    pExtra->m_ProxyFn(
      pProp, pData, 
      (char*)pUtlVec->Base() + iElement*pExtra->m_ElementStride,
      pOut, 0, objectID );
  }
}
```

Предупреждение анализатора PVS\-Studio: [V763](https://pvs-studio.ru/ru/docs/warnings/v763/) Parameter 'iElement' is always rewritten in function body before being used\. [dt\_utlvector\_send\.cpp](https://github.com/ValveSoftware/source-sdk-2013/blob/68c8b82fdcb41b8ad5abde9fe1f0654254217b8e/src/public/dt_utlvector_send.cpp#L49) 49

Параметр `iElement`, который функция принимает по копии, всегда перезаписывается и лишь затем используется\. Это странное поведение\. Возможно, надобность в параметре пропала, но на рефакторинг кода не было времени\. Тогда логично сделать параметр неименованным, и у других разработчиков будет меньше вопросов, почему он всегда перезаписывается:

```cpp
void SendProxy_UtlVectorElement(...., int /* iElement */, ....)
{
  ....
  auto iElement = pProp->GetElementStride();
  ....
}
```

<details>
   <summary>Предупреждения\\\.</summary>

* V763 Parameter 'flDist' is always rewritten in function body before being used\. npc\_cranedriver\.cpp 180
* V763 Parameter 'flDist' is always rewritten in function body before being used\. npc\_strider\.cpp 2561


</details>
## Заключение

Хочется поблагодарить вас, читателей, что отложили свои дела и провели это время со старейшим ронином Source SDK, сражаясь с чужеродными формами жизни\. Надеюсь, статья была интересной и познавательной\.

Если вы захотите [попробовать](https://pvs-studio.ru/ru/pvs-studio/try-free/) анализатор PVS\-Studio, для open source проектов у нас [предоставляется](https://pvs-studio.ru/ru/order/open-source-license/) бесплатная лицензия\.

Что ж, нам пора прощаться\. Всех благ вам, и до новых встреч\!

Подожди, слышите звуки? Кажется, мы нашли логово хедкрабов\. Предлагаю, подлатать свои раны и перезарядить обойму, чтобы окончательно победить этих тварей\. А пока сделаем привал перед нашим следующим путешествием\.

Продолжение следует\.