Русское сообщество по скриптингу

Вперед в прошлое [Глава II]

Статьи или фрагменты кода для новичков и уже опытных скриптеров по Metamod.
Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по Metamod.

Вперед в прошлое [Глава II]

Сообщение 6a6kin » 19 фев 2011, 03:07

Автор: 6a6kin

ВНИМАНИЕ! Предполагается, что читатель знаком с C++ и уже имеет опыт написания программ.
I. Предисловие
  • В этой статье будет рассмотрено использование и обработка оригинальных функций, доступных из hlsdk. Плагин из первой главы, в действительности, взаимодействовал только с метамодом. Пришло время "вдохнуть жизнь" в наш первый плагин. А именно, начать работать с доступными оригинальными функциями движка.
  • На самом деле, meta-плагин ничем не отличается от обычной dynamic load library(.dll или .so). Точнее, это и есть динамическая библотека, только метод загрузки библиотеки у метамода свой. Поэтому вы можете в полной мере использовать все возможности C++. Тут хочу подчеркнуть, что структура исходных файлов программы может быть любой: от одного файла, до десятков\сотен.

II. Таблицы функций
Структуры, содержащие указатели на определенные функции называются таблицами функций. Существует множество разных таблиц, которые могут использоваться в ваших плагинах(даже ваши собственные), но мы остановимся на главном - таблицы оригинальных функций. Тут хочу подчеркнуть, что таблицы содержат указатели, или адреса функций, которые могут быть совершенно любыми - полученные от движка или определенные в вашем плагине(имеется в виду любые функции одного типа - возвращемое значение как и аргументы должны совпадать).

Таблицы оригинальных функций можно разделить на три группы:
  1. engine - Вы должны зарегистрироваться, чтобы видеть ссылки.
  2. dllapi - Вы должны зарегистрироваться, чтобы видеть ссылки.
  3. newdll - Вы должны зарегистрироваться, чтобы видеть ссылки.
Описания этих таблиц также есть в заголовочном файле eiface.h из hlsdk. Это структуры enginefuncs_t, DLL_FUNCTIONS, NEW_DLL_FUNCTIONS. Уже говорилось, что таблицы могут содержать любые функции одного типа, а значит эти же структуры могут содержать и "post" версии функций. Функции отличаются только тем, что "post" версии вызываются после выполнения оригинальной функции, поэтому нет необходимости создавать отдельные структуры для таких функций. Обычно таблицы различаются по методам, через которые они были переданы.
Также существует еще одна немаловажная таблица, о которой пойдет речь в пункте 4.

Таблица engine-функций уже использовалась в предыдущей главе. Функция GiveFnptrsToDll(т.е. сам движок) передает таблицу с адресами engine-функций. Это было необходимо, т.к. в одном из заголовочных файлов metasdk включен файл из hlsdk - enginecallback.h, который содержит объявление таблицы функций, которая определена уже в нашем плагине - g_engfuncs, а также содержит макросы для вызова оригинальных функций. Один из них был использован в нашем плагине - ALERT - функция, с помощью которой выводится сообщение в консоль сервера.

III. Вызов оригинальных функций
С вызовами функций все довольно просто:
  • Для вызова engine-функций предназначены макросы из файла enginecallback.h(также указаны на сайте метамода в списках функций, обычно ЗАГЛАВНЫМИ буквами)
  • Для вызова dllapi-функций предназначены команды типа MDLL_*(аргументы) - они были указаны в первой главе
  • Для вызова newdll-функций предназначены команды типа MNEW_*(аргументы) - они были указаны в первой главе

IV. Обработка вызовов оригинальных функций
Управление обработкой функций во главе осуществляет метамод, а значит ему мы должны передать таблицу, содержащую функции-трансферы, т.е. те функции, которые будут передавать таблицы функций-обработчиков(содержащие функции, которые будут вызываться перед или после вызова оригинальной функции в вашем плагине) непосредственно движку или метамоду. Эта структура носит название META_FUNCTIONS, которая состоит из 8 элементов. Если какая-либо из таблиц не используется, то на место функции-трансфера вы должны установить значение NULL.

Для каждой группы функций существует по две функции-трансфера, отвечающие за передачу таблиц функций-обработчиков в метамод:[spoiler]
  • int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion );
    int GetEntityAPI_Post( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion );

    int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion );
    int GetEntityAPI2_Post( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion );

  • int GetNewDLLFunctions( NEW_DLL_FUNCTIONS *pNewFunctionTable, int *interfaceVersion );
    int GetNewDLLFunctions_Post( NEW_DLL_FUNCTIONS *pNewFunctionTable, int *interfaceVersion );

  • int GetEngineFunctions( enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion );
    int GetEngineFunctions_Post( enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion );
[/spoiler]
Вы заметили, что существует два варинта передачи dllapi-функций - GetAPI и GetAPI2? Вот что я нашел по этому поводу:
[spoiler]
Не совсем понятна разница между GetAPI и GetAPI2; они обе существуют для передачи одной и той же таблицы.

И все-таки только одна из них предназначена для использования. Если в DLL присутствует GetAPI2, движок вызовет её, но не будет вызывать GetAPI. Если движок не может обнаружить GetAPI2 в DLL, то он будет использовать GetAPI.

Получается, GetAPI2 создан для замены GetAPI. Он был добавлен в SDK версии 2.0. Я считаю, что с появлением нового SDK появилась необходимость проверять версию интерфейса, и движок не может определить, что DLL устарела через GetAPI, т.к. там не используется указатель для передачи версии интерфейса.

Остается непонятным одно - требуется ли вызов GetAPI в DLL, в которой используется SDK 2.0+ или нет...

В оригинале:
It's not clear what the difference is between GetAPI and GetAPI2; they
both appear to return the exact same function table.

Only one of them appears to be ever called, though. If the DLL provides
GetAPI2, the engine/hlds will call that, and will not call GetAPI. If
the engine couldn't find GetAPI2 in the DLL, it appears to fall back to
GetAPI.

So, GetAPI2 appears to replace GetAPI, and appears to have been added
with SDK 2.0. My best guess is that, with the new SDK, interface
version checking became important, and without the int ptr used in
GetAPI2, the engine can't find out the version of the DLL via GetAPI.

It's unclear whether a DLL coded under SDK2 needs to provide the older
GetAPI or not..
[/spoiler]
С помощью этих функций-трансферов передаются таблицы, содержащие функции-обработчики из вашего плагина. Если обработка этой для этой функции отсутствует - необходимо установить значени NULL на место этой функции. Таблица META_FUNCTIONS передается при вызове meta_attach(смотрите аргументы функции meta_attach).

Схема передачи таблиц функций в виде иерархии:
[spoiler]
  • Через meta_attach передается таблица META_FUNCTIONS

    • META_FUNCTIONS содержит функциии-трансферы(GetAPI2, GetEngine итд.)

      • Функции-трансферы передают таблицы enginefuncs_t, DLL_FUNCTIONS, NEW_DLL_FUNCTIONS

        • Таблицы enginefuncs_t, DLL_FUNCTIONS, NEW_DLL_FUNCTIONS содержат функции-обработчики

          • Функции-обработчики из вашего плагина вызываются перед или после нужными оригинальными функциями
[/spoiler]
Все станет ясно после небольшого примера.


V. Как это работает
Разберем на примере использование таблиц функций. Попутно будет затронут еще ряд вопросов. За основу будет взят плагин из первой главы.

Обратимся к основной функции - GiveFnptrsToDll. Как уже говорилось - эта функция принимает от движка таблицу с адресами оригинальных функций, которая используется для вызова этих функций из движка. Забирать указанную таблицу функций - обязательно, этого требует SDK, и вам она тоже обязательно пригодится.

Если вы взглянете на аргументы функции, то заметите одну интересную деталь - вместе с таблицой функций передается структура globalvars_t. Само название говорит о том, что она содержит глобальные переменные сервера. Её описание находится в файле progdefs.h. Я не зря заострил внимание на этом моменте - данная структура представляет собой полезный инструмент, например, прямой доступ к названию карты сервера. Пусть её изучение останется вашим домашним заданием.

В данном случае будет рассмотрена обработка только одной таблицы - необходимые сведения есть в пункте ниже - нету смысла писать одно и то же.

Для начала вы должны решить - какие функции вам понадобятся и создать для них функции-обработчики(эквивалентно client_connect, fw_PlayerKilled итд. из amxx-плагинов). После того, как вы определились и создали свои функции-обработчики, необходимо сформировать таблицу функций типа enginefuncs_t и заполнить её, установив на место необходимых функций ваши функции-обработчики, а на места остальных - значение NULL. Расположение функций в таблице можно посмотреть в описании структуры, либо в листинге ниже.

Как только таблица функций готова, необходимо сформировать функцию передачи этой таблицы метамоду/движку. Здесь есть пара нюансов. Перед тем, как отправить таблицу с вашими функциями необходимо сделать ряд проверок:
  • Первая и немаловажная проверка - правильность адреса конечной таблицы, передаваемой через аргументы функции. Эта проверка важна тем, что при попытке получить доступ к нулевой памяти вы получите segfault или access violation. Поэтому лучше подобную ситуацию предотвратить.
  • Вторая проверка - сравнивание версий интерфейсов. Она также обязательна, потому что при не совпадении структур и при попытке обратиться по несуществующему адресу также обернется для вас ошибкой доступа к памяти, либо логической ошибкой, что еще хуже.
После выполнения проверок можете смело копировать вашу таблицу в пункт назначения - первый параметр функции.

Есть еще одна важная структура - глобальные переменные метамода - структура meta_globals_t. Данная структура обязательна в использовании, т.к. содержит в себе текущее значение META_RESULT функции(взгляните макро RETURN_META или листинг структуры и все поймете). Пусть её изучение также останется на дом.

В итоге мы получаем это:
[spoiler]
Код: Выделить всё
#include <extdll.h>
#include <meta_api.h>

meta_globals_t *gpMetaGlobals;

plugin_info_t info = {
   META_INTERFACE_VERSION,            // ifvers
   "HELLO WORLD",                  // name
   "1.01",                        // version
   "2011/02/19",                  // date
   "6a6kin",                     // author
   "http://ultra.ucoz.ru",            // url
   "HELLOWORLD",                  // logtag, all caps please
   PT_ANYTIME,                     // (when) loadable
   PT_ANYPAUSE                     // (when) unloadable
};

static META_FUNCTIONS gMetaFunctionTable =
{
   NULL,            // pfnGetEntityAPI            HL SDK; called before game DLL
   NULL,            // pfnGetEntityAPI_Post         META; called after game DLL
   NULL,            // pfnGetEntityAPI2            HL SDK2; called before game DLL
   NULL,            // pfnGetEntityAPI2_Post      META; called after game DLL
   NULL,            // pfnGetNewDLLFunctions      HL SDK2; called before game DLL
   NULL,            // pfnGetNewDLLFunctions_Post   META; called after game DLL
   GetEngineFunctions,   // pfnGetEngineFunctions      META; called before HL engine
   NULL            // pfnGetEngineFunctions_Post   META; called after HL engine
};

enginefuncs_t g_engfuncs;
globalvars_t  *gpGlobals;

#if defined _MSC_VER
   #pragma comment(linker, "/EXPORT:[email protected]")
#endif

C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals)
{
   memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t));
   gpGlobals = pGlobals;
   ALERT(at_console, "[HELLOWORLD]: GiveFnptrsToDll\n");
}

C_DLLEXPORT int Meta_Query(char *interfaceVersion, plugin_info_t **pinfo, mutil_funcs_t *pMetaUtilFuncs)
{
   *pinfo = &info;
   ALERT(at_console, "[HELLOWORLD]: meta_query\n");
   return(TRUE);
}

C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs)
{

   if(!pFunctionTable)
   {
      return(FALSE);
   }


   memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS));
   gpMetaGlobals = pMGlobals;
   ALERT(at_console, "[HELLOWORLD]: meta_attach\n");
   return(TRUE);
}

C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason)
{
   ALERT(at_console, "[HELLOWORLD]: meta_detach\n");
   return(TRUE);
}

int fnPre(char *s)
{
   ALERT(at_console, "[HELLOWORLD]: model precaching\n");
   RETURN_META_VALUE(MRES_IGNORED, 0);
}

enginefuncs_t my_tracers =
{
   fnPre,                  // pfnPrecacheModel()
   NULL,                  // pfnPrecacheSound()
   NULL,                  // pfnSetModel()
   NULL,                  // pfnModelIndex()
   NULL,                  // pfnModelFrames()

   NULL,                  // pfnSetSize()
   NULL,                  // pfnChangeLevel()
   NULL,                  // pfnGetSpawnParms()
   NULL,                  // pfnSaveSpawnParms()

   NULL,                  // pfnVecToYaw()
   NULL,                  // pfnVecToAngles()
   NULL,                  // pfnMoveToOrigin()
   NULL,                  // pfnChangeYaw()
   NULL,                  // pfnChangePitch()

   NULL,                  // pfnFindEntityByString()
   NULL,                  // pfnGetEntityIllum()
   NULL,                  // pfnFindEntityInSphere()
   NULL,                  // pfnFindClientInPVS()
   NULL,                  // pfnEntitiesInPVS()

   NULL,                  // pfnMakeVectors()
   NULL,                  // pfnAngleVectors()

   NULL,                  // pfnCreateEntity()
   NULL,                  // pfnRemoveEntity()
   NULL,                  // pfnCreateNamedEntity()

   NULL,                  // pfnMakeStatic()
   NULL,                  // pfnEntIsOnFloor()
   NULL,                  // pfnDropToFloor()

   NULL,                  // pfnWalkMove()
   NULL,                  // pfnSetOrigin()

   NULL,                  // pfnEmitSound()
   NULL,                  // pfnEmitAmbientSound()

   NULL,                  // pfnTraceLine()
   NULL,                  // pfnTraceToss()
   NULL,                  // pfnTraceMonsterHull()
   NULL,                  // pfnTraceHull()
   NULL,                  // pfnTraceModel()
   NULL,                  // pfnTraceTexture()
   NULL,                  // pfnTraceSphere()
   NULL,                  // pfnGetAimVector()

   NULL,                  // pfnServerCommand()
   NULL,                  // pfnServerExecute()
   NULL,                  // pfnClientCommand()

   NULL,                  // pfnParticleEffect()
   NULL,                  // pfnLightStyle()
   NULL,                  // pfnDecalIndex()
   NULL,                  // pfnPointContents()

   NULL,                  // pfnMessageBegin()
   NULL,                  // pfnMessageEnd()

   NULL,                  // pfnWriteByte()
   NULL,                  // pfnWriteChar()
   NULL,                  // pfnWriteShort()
   NULL,                  // pfnWriteLong()
   NULL,                  // pfnWriteAngle()
   NULL,                  // pfnWriteCoord()
   NULL,                  // pfnWriteString()
   NULL,                  // pfnWriteEntity()

   NULL,                  // pfnCVarRegister()
   NULL,                  // pfnCVarGetFloat()
   NULL,                  // pfnCVarGetString()
   NULL,                  // pfnCVarSetFloat()
   NULL,                  // pfnCVarSetString()

   NULL,                  // pfnAlertMessage()
   NULL,                  // pfnEngineFprintf()

   NULL,                  // pfnPvAllocEntPrivateData()
   NULL,                  // pfnPvEntPrivateData()
   NULL,                  // pfnFreeEntPrivateData()

   NULL,                  // pfnSzFromIndex()
   NULL,                  // pfnAllocString()

   NULL,                   // pfnGetVarsOfEnt()
   NULL,                  // pfnPEntityOfEntOffset()
   NULL,                  // pfnEntOffsetOfPEntity()
   NULL,                  // pfnIndexOfEdict()
   NULL,                  // pfnPEntityOfEntIndex()
   NULL,                  // pfnFindEntityByVars()
   NULL,                  // pfnGetModelPtr()

   NULL,                  // pfnRegUserMsg()

   NULL,                  // pfnAnimationAutomove()
   NULL,                  // pfnGetBonePosition()

   NULL,                  // pfnFunctionFromName()
   NULL,                  // pfnNameForFunction()

   NULL,                  // pfnClientPrintf()
   NULL,                  // pfnServerPrint()

   NULL,                  // pfnCmd_Args()
   NULL,                  // pfnCmd_Argv()
   NULL,                  // pfnCmd_Argc()

   NULL,                  // pfnGetAttachment()

   NULL,                  // pfnCRC32_Init()
   NULL,                  // pfnCRC32_ProcessBuffer()
   NULL,                  // pfnCRC32_ProcessByte()
   NULL,                  // pfnCRC32_Final()

   NULL,                  // pfnRandomLong()
   NULL,                  // pfnRandomFloat()

   NULL,                  // pfnSetView()
   NULL,                  // pfnTime()
   NULL,                  // pfnCrosshairAngle()

   NULL,                  // pfnLoadFileForMe()
   NULL,                  // pfnFreeFile()

   NULL,                  // pfnEndSection()
   NULL,                  // pfnCompareFileTime()
   NULL,                  // pfnGetGameDir()
   NULL,                  // pfnCvar_RegisterVariable()
   NULL,                  // pfnFadeClientVolume()
   NULL,                  // pfnSetClientMaxspeed()
   NULL,                  // pfnCreateFakeClient()
   NULL,                  // pfnRunPlayerMove()
   NULL,                  // pfnNumberOfEntities()

   NULL,                  // pfnGetInfoKeyBuffer()
   NULL,                  // pfnInfoKeyValue()
   NULL,                  // pfnSetKeyValue()
   NULL,                  // pfnSetClientKeyValue()

   NULL,                  // pfnIsMapValid()
   NULL,                  // pfnStaticDecal()
   NULL,                  // pfnPrecacheGeneric()
   NULL,                   // pfnGetPlayerUserId()
   NULL,                  // pfnBuildSoundMsg()
   NULL,                  // pfnIsDedicatedServer()
   NULL,                  // pfnCVarGetPointer()
   NULL,                  // pfnGetPlayerWONId()

   NULL,                  // pfnInfo_RemoveKey()
   NULL,                  // pfnGetPhysicsKeyValue()
   NULL,                  // pfnSetPhysicsKeyValue()
   NULL,                  // pfnGetPhysicsInfoString()
   NULL,                  // pfnPrecacheEvent()
   NULL,                  // pfnPlaybackEvent()

   NULL,                  // pfnSetFatPVS()
   NULL,                  // pfnSetFatPAS()

   NULL,                  // pfnCheckVisibility()

   NULL,                  // pfnDeltaSetField()
   NULL,                  // pfnDeltaUnsetField()
   NULL,                  // pfnDeltaAddEncoder()
   NULL,                  // pfnGetCurrentPlayer()
   NULL,                  // pfnCanSkipPlayer()
   NULL,                  // pfnDeltaFindField()
   NULL,                  // pfnDeltaSetFieldByIndex()
   NULL,                  // pfnDeltaUnsetFieldByIndex()

   NULL,                  // pfnSetGroupMask()

   NULL,                  // pfnCreateInstancedBaseline()
   NULL,                  // pfnCvar_DirectSet()

   NULL,                  // pfnForceUnmodified()

   NULL,                  // pfnGetPlayerStats()

   NULL,                  // pfnAddServerCommand()

   NULL,                  // pfnVoice_GetClientListening()
   NULL,                  // pfnVoice_SetClientListening()

   NULL,                  // pfnGetPlayerAuthId()

   NULL,                  // pfnSequenceGet()
   NULL,                  // pfnSequencePickSentence()
   NULL,                  // pfnGetFileSize()
   NULL,                  // pfnGetApproxWavePlayLen()
   NULL,                  // pfnIsCareerMatch()
   NULL,                  // pfnGetLocalizedStringLength()
   NULL,                  // pfnRegisterTutorMessageShown()
   NULL,                  // pfnGetTimesTutorMessageShown()
   NULL,                  // pfnProcessTutorMessageDecayBuffer()
   NULL,                  // pfnConstructTutorMessageDecayBuffer()
   NULL,                  // pfnResetTutorMessageDecayData()
   NULL,                  // pfnQueryClientCvarValue()
   NULL,                  // pfnQueryClientCvarValue2()
};

C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion)
{
   if(!pengfuncsFromEngine)
   {
      return(FALSE);
   } else if(*interfaceVersion != ENGINE_INTERFACE_VERSION)
   {
      *interfaceVersion = ENGINE_INTERFACE_VERSION;
      return(FALSE);
   }

   memcpy(pengfuncsFromEngine, &my_tracers, sizeof(enginefuncs_t));
   ALERT(at_console, "[HELLOWORLD]: transfer successfully ended\n");
   
   return(TRUE);
}

[/spoiler]
После загрузки карты в консоли сервера вы увидете множество таких сообщений:
[HELLOWORLD]: model precaching


VI. TraceAPI
Исходники метамода включают в себя очень интересный плагин, который называется TraceAPI. Этот плагин - практическая реализации сказанного в данной статье. Данный плагин осуществляет обработку ВСЕХ доступных оригинальных функций(также и "post" функций). В оригинале:
[spoiler]
This was originally intended as a (more or less) complete example of a Metamod plugin. It catches every call available to it (dll routines both before and after the game, as well as engine functions both before and after the engine).
[/spoiler]
Плагин получил свое название в связи с тем, что он отслеживает(ориг. "trace, tracing") вызовы оригинальный функций. Плагин удобно использовать для отслеживания активности Half-Life движка и его модов. Настоятельно рекомендую ознакомиться с листингом плагина, если вы серьезно решили заняться написанием плагина(ов) для metamod.

Список доступных кваров и команд:
[spoiler]
  • Серверные квары
    //-------------------------------------------------------------------
    // Уровень логирования; большее значение соответствует большему количеству обрабатываемых процедур.
    // Может принимать значения от 0 до 50.
    // В "api_info.cpp" указан уровень логирования для различных функций.
    //-------------------------------------------------------------------
    // Уровень логирования для dllapi функций.
    trace_dllapi

    // Уровень логирования для "new" dllapi функций.
    trace_newapi

    // Уровень логирования для engine функций.
    trace_engine
    //-------------------------------------------------------------------
    // Включает неограниченное логированние, однако за одну секунду обрабатывается
    // только одна функция, дабы предотвратить перегрузку сервера.
    // "1" - вкл,, "0" - выкл.(стандартное значение - "0")
    trace_unlimit
  • Серверные команды
    // Включает обработку заданной функции, вне зависимости от уровня логирования("trace_*").
    // В "api_info.cpp" указаны имена доступных функций. Регистр не имеет значения.
    trace set <APIroutine>
    //-------------------------------------------------------------------
    // Отключает обработку заданной функции, если она была предварительно включена.
    // Не влияет на функции, попадающие под действие уровня логирования("trace_*").
    trace unset <APIroutine>
    //-------------------------------------------------------------------
    // Показывает обработанные оригинальные функции.
    trace show
    //-------------------------------------------------------------------
    // Список доступных для отслеживания оригинальных функций.
    trace list dllapi
    trace list newapi
    trace list engine
    trace list all
    //-------------------------------------------------------------------
    // Версия плагина.
    trace version
[/spoiler]
Небольшой комментарий автора:
[spoiler]
Замечу, что в данном плагине при логировании функции информация, передаваемая пользователю, минимальна. Передается та информация, которую я посчитал нужным добавить(аргументы ClientCommand итд.). В некоторых случаях я недостаточно знал об этих функциях, чтобы добавить вывод необходимой информации(CreateBaseline итд.). Вы можете менять плагин как хотите и выводить любую интересующую вас информацию через логи; примеры должны давать вам практические знания. Я был бы не против добавить стоящие поправки в распространяемый вариант.

В оригинале:
Note the information it logs on each routine invocation is, at the moment, relatively minimal. I included information that seemed obvious (args for a ClientCommand, etc), and I've added info for other routines as I've come across a need. Most routines I still know too little about to log any particular information (CreateBaseline, etc). Feel free to add information that you're interested in to the log messages in the routines; the examples should be pretty self-explanatory. I'd be interested in knowing as well, for adding it to the distribution code.
[/spoiler]
В следующей главе будет рассмотрена тема использования кваров, команд, меню и пр. Просьба тут оставлять комментарии и вопросы ТОЛЬКО к статье, а все вопросы по кодингу в соответствующем разделе.

tags/тэги: metamod, кодинг, плагины, таблицы функций, создание, написание
Последний раз редактировалось 6a6kin 23 сен 2012, 14:14, всего редактировалось 1 раз.
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 333
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Вперед в прошлое [Глава II]

Сообщение 6a6kin » 16 мар 2011, 21:15

Подправил статью - добавил спойлеры для удобочитаемости.
Собственно, хотелось бы сделать еще три статьи:
1. 3 глава, рассказывающая про квары, команды, меню и пр.
2. Игровая механика - мессаги, инвенты и пр.; упор будет сделан на сравнение с amxx-плагинами
3. Работа с mysql(сам когда-то столкнулся с кое-каким проблемами, поэтому и хотелось бы осветить)

Но, к сожалению, времени недостаточно, поэтому интересно знать - нужны ли в ближайшее время эти статьи?
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 333
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Вперед в прошлое [Глава II]

Сообщение DJ_WEST » 18 мар 2011, 10:24

Нужны =)
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3747
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2164 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Вперед в прошлое [Глава II]

Сообщение Fedcomp » 19 мар 2011, 17:25

_ОЧЕНЬ_ нужны. Ибо это очень интересно
Не помогаю в ЛС - есть форум.
Плагины тоже не пишу, на форуме достаточно хороших скриптеров.
Аватара пользователя
Fedcomp
Администратор
 
Сообщения: 4348
Зарегистрирован: 28 авг 2009, 20:47
Благодарил (а): 699 раз.
Поблагодарили: 1186 раз.
Языки программирования: Counter-Strike 1.6

Re: Вперед в прошлое [Глава II]

Сообщение Arseny » 21 мар 2011, 16:23

Нужны, не обсуждается =)
Аватара пользователя
Arseny
 
Сообщения: 34
Зарегистрирован: 18 сен 2010, 13:13
Откуда: Украина
Благодарил (а): 22 раз.
Поблагодарили: 3 раз.
Опыт программирования: Около 3 месяцев
Языки программирования: Counter-Strike 1.6

Re: Вперед в прошлое [Глава II]

Сообщение in1ernal_error » 27 мар 2011, 19:52

Если вы взглянете на аргументы функции, то заметите одну интересную деталь - вместе с таблицой функций передается структура globalvars_t. Само название говорит о том, что она содержит глобальные переменные сервера. Её описание находится в файле progdefs.h. Я не зря заострил внимание на этом моменте - данная структура представляет собой полезный инструмент, например, прямой доступ к названию карты сервера. Пусть её изучение останется вашим домашним заданием.


То есть через эту globalvars_t я смогу поменять любой параметр сервера, а в том числе и название карты, и все в режиме реального времени? :yahoo:
Аватара пользователя
in1ernal_error
 
Сообщения: 5
Зарегистрирован: 24 мар 2011, 13:27
Благодарил (а): 2 раз.
Поблагодарили: 0 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6

Re: Вперед в прошлое [Глава II]

Сообщение 6a6kin » 28 мар 2011, 00:23

Если честно, я не работал с ней. Чисто теоритически - да, но предполагаю, что при изменении вызовет некоторые проблемы. И для Вашей задачи не подойдет.
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 333
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Вперед в прошлое [Глава II]

Сообщение KORD_12.7 » 28 июн 2011, 07:45

Когда глава III ? :-)

_http://aghl.ru/ - Half-Life и Adrenaline Gamer: за пределами возможного
Аватара пользователя
KORD_12.7
Скриптер
 
Сообщения: 298
Зарегистрирован: 28 сен 2009, 10:14
Откуда: Владивосток
Благодарил (а): 141 раз.
Поблагодарили: 251 раз.
Опыт программирования: Больше трех лет
Языки программирования: Half-Life
Opposing Force
Adrenaline Gamer
Counter-Strike

Re: Вперед в прошлое [Глава II]

Сообщение 6a6kin » 05 июл 2011, 01:57

Я не знаю, имеет ли практическое применение эти статьи, но планировал раньше. Может быть и напишу, если получится :dntknw:
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 333
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 269 раз.

Re: Вперед в прошлое [Глава II]

Сообщение noo00oob » 05 июл 2011, 14:53

6a6ka писал(а):Я не знаю, имеет ли практическое применение эти статьи, но планировал раньше. Может быть и напишу, если получится :dntknw:

Даже и не сомнивайся - имеют :-)
Один фрукт, страдающий недостачей времени, нашел его ради меня любимого и писал(а):
noo00oob, зачем родился на свет вообще? срать на форумах это понятно.. больше изъеба не найти как бэ? а то, что ты недоношенная скотина, сдерживайся, детка.
noo00oob
 
Сообщения: 1063
Зарегистрирован: 09 янв 2010, 21:52
Благодарил (а): 258 раз.
Поблагодарили: 393 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Half-Life

След.

Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron