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

Вперед в прошлое [Глава 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 раз.
Поблагодарили: 270 раз.

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

Сообщение 6a6kin » 06 фев 2013, 00:18

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

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

Сообщение Bos93 » 06 фев 2013, 00:21

Нет.
Всем добра, любви и осознанности.

Nosce animum tuum.

А осознание и есть, что понял и осмыслил..
А коль не думал ты о том, то кто о том замыслил..?
Аватара пользователя
Bos93
 
Сообщения: 1429
Зарегистрирован: 03 апр 2010, 13:44
Благодарил (а): 149 раз.
Поблагодарили: 511 раз.

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

Сообщение 6a6kin » 06 фев 2013, 00:35

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

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

Сообщение Chuvi » 06 фев 2013, 00:42

Bos93, сервер твой? Запускаешь под screen?
Попробуй добавить -L к параметрам запуска скрина. Весь вывод будет логироваться.

"Незнание английского языка - это ваша проблема."

Плагинами на заказ не занимаюсь. Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2253
Зарегистрирован: 24 ноя 2011, 08:03
Благодарил (а): 127 раз.
Поблагодарили: 560 раз.

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

Сообщение Bos93 » 06 фев 2013, 00:44

На вдс тестирую.

Эм,это куда ?

./hlds_run -console +sv_lan 0 -game cstrike +maxplayers 6 +port 27015 +ip 159.253.22.96 +map de_survivor
Всем добра, любви и осознанности.

Nosce animum tuum.

А осознание и есть, что понял и осмыслил..
А коль не думал ты о том, то кто о том замыслил..?
Аватара пользователя
Bos93
 
Сообщения: 1429
Зарегистрирован: 03 апр 2010, 13:44
Благодарил (а): 149 раз.
Поблагодарили: 511 раз.

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

Сообщение Chuvi » 06 фев 2013, 00:50

screen -A -L -m -d -S hlds_screen ./hlds_run -console... и так далее

Для того, чтобы перейти к заскриненному приложению пишешь
screen -r hlds_screen (ну или то, что ты после -S напишешь. Это имя скрина)
Чтобы "отцепиться" от скрина жмёшь Alt+D

"Незнание английского языка - это ваша проблема."

Плагинами на заказ не занимаюсь. Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2253
Зарегистрирован: 24 ноя 2011, 08:03
Благодарил (а): 127 раз.
Поблагодарили: 560 раз.

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

Сообщение Bos93 » 06 фев 2013, 01:26

loading models/hgibs.mdl
loading models/agibs.mdl
[pawn]
  1. Cvar_Set: variable v_dark not found

  2. loading models/metalplategibs_green.mdl

  3. 0 entities inhibited

  4. Looking up breakpad interfaces from steamclient

  5. Calling BreakpadMiniDumpSystemInit

  6. "sv_region" changed to "255"

  7. PF_MessageEnd_I:  Unknown User Msg 127

  8. "sv_accelerate" changed to "5"

  9. "sv_stopspeed" changed to "75"

  10. 6 player server started

  11.  

  12. Sys_InitializeGameDLL called twice, skipping second call
[/pawn]

Больше ничего не обычного или странного.

Добавлено спустя 29 минут 40 секунд:
6a6kin,компильнул ваш плагин:

[pawn]
  1. core.cpp:45:13: ошибка: «DLLVISIBLE» не является именем типа

  2.  

  3. core.cpp:52:13: ошибка: «DLLVISIBLE» не является именем типа

  4.  

  5. core.cpp:59:13: ошибка: «DLLVISIBLE» не является именем типа

  6.  

  7. core.cpp:126:13: ошибка: «DLLVISIBLE» не является именем типа

  8.  

  9. core.cpp:29:23: предупреждение: «gMetaFunctionTable» определена, но нигде не используется [-Wunused-variable]
[/pawn]
Всем добра, любви и осознанности.

Nosce animum tuum.

А осознание и есть, что понял и осмыслил..
А коль не думал ты о том, то кто о том замыслил..?
Аватара пользователя
Bos93
 
Сообщения: 1429
Зарегистрирован: 03 апр 2010, 13:44
Благодарил (а): 149 раз.
Поблагодарили: 511 раз.

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

Сообщение 6a6kin » 06 фев 2013, 10:00

Bos93, использовались metasdk & hlsdk из репозитария? И через cmake?
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 333
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 270 раз.

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

Сообщение Bos93 » 07 фев 2013, 00:20

Да,только я не понял что за cmake,я просто добавил cpp файлы в мэйк файл и компилилировал через команду make

Мб вы имели в ввиду "с make"

Добавлено спустя 5 минут 20 секунд:
Я скомпилировал amxx модуль,он заработал,а мету не хочет загружать.


6a6kin,пожалуйста протестируйте Вы должны зарегистрироваться, чтобы видеть ссылки. ( Компилированный этот шаблон. )
Всем добра, любви и осознанности.

Nosce animum tuum.

А осознание и есть, что понял и осмыслил..
А коль не думал ты о том, то кто о том замыслил..?
Аватара пользователя
Bos93
 
Сообщения: 1429
Зарегистрирован: 03 апр 2010, 13:44
Благодарил (а): 149 раз.
Поблагодарили: 511 раз.

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

Сообщение 6a6kin » 07 фев 2013, 00:49

Bos93, нет, именно cmake. Утилита для кросскомпиляции.
просто добавил cpp файлы в мэйк файл

Так не надо было делать :)

Для данного плагина мой сервер вот что сказал:
WARNING: dll: Failed query plugin '<stub_i386.so>'; Couldn't find Meta_Query(): /home/mr6a6kin/hlds/cstrike/addons/stub_i386.so: undefined symbol: Meta_Query

Это значит, что не видны нужные символы. Надо убрать вот это из makefile(можно ещё по-другому проблему решить):
Код: Выделить всё
ifeq "$(GCC_VERSION)" "4"
        OPT_FLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
endif
На заказ не пишу.
Аватара пользователя
6a6kin
Скриптер
 
Сообщения: 333
Зарегистрирован: 09 мар 2010, 16:40
Благодарил (а): 37 раз.
Поблагодарили: 270 раз.

Пред.След.

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

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

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