Интересный вопрос. Дело в том, что большинство людей просто напросто ленивы, чтобы производить какие либо исследования о том как получать динамические данные во время исполнения, и предпочитают хардкодинг динамическому извлечению данных (ДИД
), но иногда причина кроется в другом - им не хватает знаний и опыта.
Когда разговор заходит о читах, люди предпочтут хардкодинг только потому что гораздо легче сделать дамп смещения какой-то переменной и просто вставить его значение себе в проект, чем тратить время на выяснения того как всё устроено. Это вполне понятно в случае, когда ты работаешь с игрой не имеющей так называемые 'карты данных'
, обычно используемых при сохранении/загрузке в одиночной игре, однако... как насчёт Source Engine
? Он предоставляет великолепную возможность надёжно извлекать динамические смещения во время исполнения по имени и... люди всё равно продолжают хардкодить. Очень жаль, что большинство пользователей форума unknowncheats
даже не пытаются копать глубже, аргументируя это тем, что ДИД хуже и медленнее, чем захардкоденые смещения. Забавно, что когда доходит до логики и аргументов - они не могут сказать многого, кроме как нести бред о потере производительности и пустой трате времени. Суровая реальность такова, что им придётся обновлять смещения от обновления к обновлению
, и я лично считаю, что это, чёрт побери, тупо. Почему бы не потратить час и разработать гибкое API? Оно будет гораздо более удобное хотя бы потому что тебе не надо будет создавать глобальные/статические переменные, чтобы хранить в них те самые смещения, и код будет выглядеть приблизительно так:
C_BaseEntity *C_BasePlayer::GetActiveWeapon()
{
// Loop through the datamap structure looking for the string you pass, cache the offset and use it further.
DATAMAP_CACHE_CREATE( m_hActiveWeapon, GetPredDescMap(), "m_hActiveWeapon", NULL );
// Take the cached offset and extract the value.
return DATAMAP_CACHE_EXTRACT_VALUE( this, m_hActiveWeapon, CHandle ).Get();
}
Вместо этого:
inline C_BaseCombatWeapon *GetActiveWeapon( void )
{
CBaseHandle hActiveWeapon = *MakePtr( CBaseHandle *, BaseEnt(), gStaticVars.m_hActiveWeapon );
return dynamic_cast( g_pEntList->GetClientEntityFromHandle( hActiveWeapon ) );
}
Другая печальная вещь заключается в том, что большинство "разработчиков"
читов пытаются подражать своим "героям"
с различных форумов, таких как unknowncheats
. Однако эти "герои"
в некоторых случаях даже не понимают, о чём говорят, а иногда, их посты содержат термины и утверждения, которые, помимо того, что далеки от реальности, так ещё и отсутствуют в Source Engine
, или вообще по-другому называются. Но им вообще пофиг (очевидно потому что они даже не заморачиваются о том, кто читает их посты, а это, как правило, необразованные люди). Затем те, кто это читает, пытаются повторять за ними, распуская эти "знания"
в виде банального дизинформирования о том, как что устроено. Одним словом, это можно назвать "рак"
, так как никто не в силах контролировать распространение этой неправильной информации, однако люди, которые ведают эти "секреты"
, считают, что они правы.
Тоже самое применяется к виртуальным функциям. Дело в том, что существует публичное SDK
от Source Engine
, где можно просто взять готовые заголовки, для того, чтобы достичь функционала определённых интерфейсов/абстрактных классов, и не морочить себе голову, однако люди не могут осилить объём кода в SSDK
и, соответственно, продолжают хардкодить эти вызови в стиле:
INetChannelInfo *pNetChannel = ( ( INetChannelInfo * ( __thiscall * )( void * ) )( engine + 0xD4 ) )( engine );
Ужасно, не так ли? Почему бы просто не скопировать заголовки SSDK, чтобы код выглядел не так ужасно, а даже наоборот:
INetChannelInfo *pNetChannel = engine->GetNetChannelInfo();
Лучше, не так ли?
В общем, это что-то, чего я никогда не понимал, помимо факта того, что люди, которые хардкодят просто не могут оперировать кодом SSDK, наверное, потому что он "слишком сложный"
. Используйте свои собственные мозги и критическое мышление, когда думаете о том, как организовывать свой собственный проект, так как он всё-таки ваш.