Sign in to follow this  
Followers 0
odosenok

Перехват функций

1 post in this topic

1. Введение. Суть перехватов.

В данном уроке я постараюсь рассказать про то, что такое перехваты. Мы разберем некоторые примеры перехватов функций, покажем пример, где эти перехваты могут пригодиться.

 

Что такое перехваты? Зачастую возникает ситуация, когда нам необходимо как-то изменить действия, происходящие в функции. Но редактирование уже имеющейся функции (как правило, это стандартные функции) не есть лучший вариант. Почему? Если мы хотим внести какие-то дополнительные действия в нужную нам функцию, придется открывать файл, в котором содержится данная функция, ее редактировать. А если выйдет новая версия файла a_samp? Все наши действия придется повторить сначала, теперь уже в новом файле.

 

Для того собственно и придуман перехват. Так, мы перехватываем заранее объявленную функцию, вследствие чего меняется порядок вызова функций:

перехваченная_функция_1 -> перехваченная_функция_2 -> перехваченная функция_3 -> основная_функция_которую_перехватывали

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

 

Для перехвата функций нам потребуются некоторые директивы (#define, #if defined, #endif), а также сами функции, которые мы внедряем в перехваты.

 

2. Построение перехватов.

Давайте попробуем немного проанализировать перехват функций, не имея пока что представления о том, как их правильно перехватывать.

 

У нас есть функция A. Мы хотим, чтобы в ней происходил вывод текста "Hello world" в логи сервера. Как говорили ранее, внедрять этот текст в код функции A мы не будем (потому, что если выйдет обновление версии файла, в котором содержится эта функция, мы утратим свои корректировки в данной функции). Значит, нам нужно сделать так, чтобы это изменение могли внести без изменения кода файла.

 

Рассмотрим следующий вариант:

#define InterceptFunctionA()\
	printf("Hello world");A();

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

 

Так я постарался дать понять Вам, что на самом деле перехваты не создают визуальных неудобств: функция будет называться так, как называлась, свой код в макросы вы не вписываете.

 

В общем и целом метод перехватов, которому мы сейчас обучаемся, выглядит следующим образом:

<перехватчик 1>
	<выполняем действия перехватчика 1>
  	<вызываем следующий перехватчик>
<конец перехватчика>

<перехватчик 2>
	<выполняем действия перехватчика 2>
	<вызываем следующий перехватчик>
<конец перехватчика>
  
<перехватчик 3>
	<выполняем действия перехватчика 3>
	<вызываем перехватываемую функцию>
<конец перехватчика>

Каждый перехватчик есть ни что иное как отдельная функция. И, соответственно, в ходе перехватов мы поочередно вызываем функции, в которых прописаны Ваши действия перехватчика.

 

Перехваты нативных функций и функций, вызывающихся автоматически, несколько отличаются. Поэтому урок мы немного разделим.

Спойлер

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

 


stock prefix_НазваниеПерехватываемойФункции(аргументы функции) // создаем функцию-перехватчик
{
	// наши действия
	return НазваниеПерехватываемойФункции(аргументы функции); // вызываем перехватываемую функцию
}

Обратите внимание на prefix. Это некоторый префикс, который мы задаем перехватываемой функции. Заметьте: для одной и той же функции не должны встречаться одинаковые префиксы. Сами префиксы должны зависеть от цели перехвата. Если это, например, античит, то будет вполне логично сделать префикс "ac" или "anticheat":


stock ac_НазваниеПерехватываемойФункции(аргументы функции)

Итак, мы создали перехватчик - функцию, которая будет вызываться до перехватываемой функции и выполнять наши действия. Но есть один нюанс: для ее вызова придется прописывать всегда "prefix_НазваниеПерехватываемойФункции(аргументы функции)". А как же вызвать этот перехватчик с таким названием,которое имеет сама перехватываемая функция? Объявляем ниже функции макрос для этой цели:


#define НазваниеПерехватываемойФункции prefix_НазваниеПерехватываемойФункции

Казалось бы все! Но нет... Ранее мы говорили о том, что функция может содержать множество перехватчиков. Но таким метод эта синхронность теряется. Поэтому нам необходимо добавить проверку: если существует еще один перехватчик, следует вызвать его, а если не существует - вызвать саму перехватываемую функцию.


#if defined _ALS_НазваниеПерехватываемойФункции
  	#undef НазваниеПерехватываемойФункции
#else
	#define _ALS_НазваниеПерехватываемойФункции
#endif

* не спрашивайте что такое ALS, можно ли его изменить и все в этом духе. Просто возьмите за правило, что только такой префикс и никакой иной.

 

И теперь нам остается лишь сдвинуть подмену названий в самый конец перехватчика. Итого наш вариант будет иметь следующий вид:


stock prefix_НазваниеПерехватываемойФункции(аргументы функции)
{
	// Ваш код
	return НазваниеПерехватываемойФункции(аргументы);
}
#if defined _ALS_НазваниеПерехватываемойФункции
	#undef НазваниеПерехватываемойФункции
#else
	#define _ALS_НазваниеПерехватываемойФункции
#endif
#define НазваниеПерехватываемойФункции prefix_НазваниеПерехватываемой функции

И теперь нам лишь остается такой код вставлять всегда туда, где Вы хотите произвести перехват функции. Ниже я предоставлю пример перехвата функции GivePlayerMoney.


stock ac_GivePlayerMoney(playerid, money)
{
	printf("[LOGS MONEY] playerid: %d, money: %d", playerid, money);
	return GivePlayerMoney(playerid, money);
}
#if defined _ALS_GivePlayerMoney
	#undef GivePlayerMoney
#else
	#define _ALS_GivePlayerMoney
#endif
#define GivePlayerMoney ac_GivePlayerMoney

 

 

Спойлер

Как было сказано ранее, перехват автовызываемых функций все-таки немного отличается. Прежде всего, теперь префикс появляется не в названии перехватчика, а в перехватываемой функции. Также, перед вызовом следующей перехватываемой функции, мы теперь должны проверить: а существует ли она?


public НазваниеПерехватываемойФункции(аргументы функции)
{
	// код перехватчика 
	#if defined prefix_НазваниеПерехватываемойФункции
		return prefix_НазваниеПерехватываемойФункции(аргументы функции);
	#endif
}

Изменение названия и выстраивание цепочки вызовов осуществляется также, как и с нативными функциями:


#if defined _ALS_НазваниеПерехватываемойФункции
	#undef НазваниеПерехватываемойФункции
#else
	#define _ALS_НазваниеПерехватываемойФункции
#endif
#define НазваниеПерехватываемойФункции prefix_НазваниеПерехватываемойФункции

Последнее нововведение заключается в том, что необходимо добавить создание декларации к функции (forward). Причем создавать нужно только тогда, когда существует следующий перехватчик. В конец этого перехватчика добавляем:


#if defined prefix_НазваниеПерехватываемойФункции
	forward prefix_НазваниеПерехватываемойФункции(аргументы функции);
#endif

Итого, наш код перехватчика представляется так:


public НазваниеПерехватываемойФункции(аргументы функции)
{
	// код перехватчика
	#if defined
		return prefix_НазваниеПерехватываемойФункции(аргументы функции);
	#endif
	return 1;
}
#if defined _ALS_НазваниеПерехватываемойФункции
	#undef НазваниеПерехватываемойФункции
#else
	#define _ALS_НазваниеПерехватываемойФункции
#endif
#define НазваниеПерехватываемойФункции prefix_НазваниеПерехватываемойФункции
#if defined prefix_НазваниеПерехватываемойФункции
	forward prefix_НазваниеПерехватываемойФункции(аргументы функции);
#endif

Для конкретики осуществим перехват OnPlayerConnect:


public OnPlayerConnect(playerid)
{
	printf("[CONNECT LOG] playerid: %d", playerid);
	#if defined log_OnPlayerConnect
		return log_OnPlayerConnect(playerid);
	#else
		return 1;
	#endif
}
#if defined _ALS_OnPlayerConnect
	#undef OnPlayerConnect
#else
	#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect log_OnPlayerConnect
#if defined log_OnPlayerConnect
	forward log_OnPlayerConnect(playerid);
#endif

 

 

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

 

3. Перехваты функций: за и против.

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

 

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

2. Перехват функций есть ни что иное как последовательные вызовы функций. Несомненно, на вызов функций уходит какая-то часть времени, но это время ничтожно для того, чтобы Ваш сервер действительно стал подтормаживать.

3. Если перехватываемую функцию использовать до того, как она перехвачена, функционировать она не будет (как Вы хотите). При этом про проблему в перехвате подумаете далеко не сразу. И главное - компилятор Вам не даст понять, что у Вас это не сработает, ведь со стороны его логики все сделано правильно. Поэтому перехватам вводят альтернативу: создают новую функцию с постфиксом "Ex", в которую уже помещают код перехвата и вызов самой функции:

stock ПерехватываемаяФункцияEx(аргументы функции)
{
	// ваш код
	return ПерехватываемаяФункция(аргументы функции);
}

Нюанс данной реализации в том, что теперь Вам придется использовать только ПерехватываемаяФункцияEx, о чем Вы можете забыть и вызвать не данную функцию, а ее оригинал.

 

4. Авторское право.

Автор данной статьи: @odosenok

Отдельное спасибо за разъяснения: Daniel_Cortez

Edited by odosenok
Причина: исправление опечатки.

Share this post


Link to post
Share on other sites

Your content will need to be approved by a moderator

Guest
You are commenting as a guest. If you have an account, please sign in.
Reply to this topic...

×   You have pasted content with formatting.   Remove formatting

  Only 75 emoticons maximum are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

Loading...
Sign in to follow this  
Followers 0

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • odosenok
      By odosenok
      1. Описание макроса.
      Данный макрос возвращает модуль указанного числа.
      2. Исходный код макроса.
      #if defined abs #undef abs #endif #define abs(%0)\ (%0 < 0) ? (-(%0)) : (%0) 3. Пример использования макроса.
      new a = random(100)-90; // значение переменной может быть отрицательным и положительным printf("Исходное число: %d. Модуль числа: %d.", a, abs(a));  
    • Santa_Claus
      By Santa_Claus
       
      Visual Studio Code - визуальный редактор кода, с красивым дизайном.
      Сегодня мы будем адаптировать эту программу под язык Pawn.
       
      АВТОР -  shinra.tensei 
      Видеоролик по установки Visual Studio Code:
      Click!
      Кому-то легко сделать по видеоролику, кому-то читая статью...
       
      Там наглядно все показано!
       
       
       
      После обновления VSC некоторые кнопки изменились, в видео показан новый способ кодировки и компилировании..
       
       
      Начало
      Для начала, нужно скачать саму программу. 
      Далее, логично будет ее установить.
      После установки, запускаем программу.

      После того, как мы насладились этим прекрасным интерфейсом, следует нажать на этот значок:
       
      Открывается такое меню, в котором нам нужно ввести pawn и установить первое расширение в списке.
      Теперь перезапускаем программу, жмем Файл > Открыть папку, и открываем папку с вашим сервером.
      Теперь мы можем открыть наш мод, если виден синтаксис павна, то мы все сделали правильно
       
      Компилирование
      Зайдем в папку нашего сервера и создадим папку .vscode
       
       
      Теперь нужно настроить компилирование, для этого заходим в Задачи > Настроить задачи.
      Далее тыкаем все как на скринах ниже:
       
      У нас открылся файл tasks.json
      Содержимое этого файла нужно заменить на следующее и сохранить:
      { "version": "2.0.0", "tasks": [ { "label": "build-normal", "type": "shell", "command": "${workspaceRoot}/pawno/pawncc.exe", "args": ["'${fileBasename}'", "'-D${fileDirname}'", "'-;+'", "'-(+'", "'-d3'"], "group": { "kind": "build", "isDefault": true }, "isBackground": false, "presentation": { "reveal": "silent", "panel": "dedicated" }, "problemMatcher": "$pawncc" } ] }  
      Основная часть закончена, теперь нужно забиндить клавишу F5 под компилирование.
      Для этого переходим в Файл > Параметры > Сочетание клавиш
       
      Теперь нужно кликнуть по keybindings.json
       
      Откроется полный список биндов, и пустой список справа. Вставляем следующий код в список справа, не забываем сохранить все это.
      [ { "key": "f5", "command": "workbench.action.tasks.build" } ]  
      Кодировка
       
      Переходим в Файл > Параметры > Параметры
      Вводим в поиск слово 'Кодировка'
      (если у вас стоит русская локализация на Visual Studio Code)
      и меняете кодировку на:
      "windows1251"  
      Конец
       
      Поздравляю, мы все сделали. Теперь мы можем компилировать наш мод нажатием F5. 

    • daltwb
      By daltwb
      Привет всем "обитателям" форума pawno-rus.ru
      В этом уроке я покажу как создать желаемую фракцию в pawno
      В этом уроке будет:
      [1] Создание самой фракции;
      [2] Создание фракционных машин(с проверкой, состоит ли игрок в данной организации или нет);
      [3] Создание пикапов входа и выхода в интерьер;
      [4] Создание команды /makeleader и /warehouse;
      [5] Сохранение всего в базе данных MySQL (R41);
      Все нужные инклуды и плагины:
      (текст кликабелен)
      Pawn.CMD
      sscanf
      #include <a_samp> #include <a_mysql> #include <Pawn.CMD> #include <sscanf2> Начнем на примере будет организация LSPD! Если вы найдете какие-то баги, недоработки и т.д. и т.п. то пишите сюда или мне в личку ВК: vk.com/martun_mkrt
       
      Для начала вставьте это в начало вашего мода(где хранятся все дефайны)
      #define GN(%0) player_name[%0] #define SCM SendClientMessage #define SPD ShowPlayerDialog #define DSI DIALOG_STYLE_INPUT #define DSL DIALOG_STYLE_LIST #define DSM DIALOG_STYLE_MSGBOX #define DSTH DIALOG_STYLE_TABLIST_HEADERS #define DSP DIALOG_STYLE_PASSWORD #define SCMTA SendClientMessageToAll // #if !defined isnull #define isnull(%0) ((!(%0[0])) || (((%0[0]) == '\1') && (!(%0[1])))) #endif // new connect_mysql, // подключение к базе данных lspd_pick[7], //Кол-во пикапов lspd_car[27], //Кол-во машин Float: pos_pick[3][MAX_PLAYERS], bool: anti_flood_pick[MAX_PLAYERS char], leaders_info[1][4+1] = { //При создании нововй фракции то учитывайте название (не более 4 символов) "LSPD" }; Затем рядом со всеми енумами создаем новый:
      enum mInfo { mLSPD, } Затем в enum dInfo и pInfo -  вставляем следущий код(если нету enum dInfo то создаем):
      enum pInfo // если у вас уже есть этот енум и все эти значения то заменяйте их на свои { pID, pName[MAX_PLAYER_NAME], pPass[65], pSkin, pLeader, pModel, pMember, pRank, } new PlayerInfo[MAX_PLAYERS][pInfo]; enum dInfo { DLG_LSPD, } Создаем паблик с телом:
      public OnPlayerStateChange(playerid, newstate, oldstate) { new carid = GetPlayerVehicleID(playerid); if(newstate == PLAYER_STATE_DRIVER) { if(carid >= lspd_car[0] && carid <= lspd_car[26]) { if(PlayerInfo[playerid][pMember] == 1) { }// PlayerInfo - меняем на свое else { RemovePlayerFromVehicle(playerid); SCM(playerid, COLOR_GREY, !"Транспорт принадлежит Депортаменту г. Лос-Сантос!"); } } } return true; } Потом находим паблик OnDialogOnDialogResponse, и в тело кидаем этот код:
      public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]) { switch(dialogid) { case 19: { if(response) { switch(listitem) { case 0: { static const fmt_str[] = "%s взял Deagle со склада"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; format(string, sizeof(string), fmt_str, GN(playerid)); ProxDetector(5.0, playerid, string, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE); GivePlayerWeapon(playerid, 24, 35); } case 1: { static const fmt_str[] = "%s взял ShotGun со склада"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; format(string, sizeof(string), fmt_str, GN(playerid)); ProxDetector(5.0, playerid, string, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE); GivePlayerWeapon(playerid, 25, 20); } case 2: { static const fmt_str[] = "%s взял MP5 со склада"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; format(string, sizeof(string), fmt_str, GN(playerid)); ProxDetector(5.0, playerid, string, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE); GivePlayerWeapon(playerid, 29, 60); } case 3: { static const fmt_str[] = "%s взял дубинку со склада"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; format(string, sizeof(string), fmt_str, GN(playerid)); ProxDetector(5.0, playerid, string, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE); GivePlayerWeapon(playerid, 3, 1); } case 4: { static const fmt_str[] = "%s взял сухпаёк со склада"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; format(string, sizeof(string), fmt_str, GN(playerid)); ProxDetector(5.0, playerid, string, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE); SetPlayerHealth(playerid, 100.0); } case 5: { static const fmt_str[] = "%s взял бронежилет со склада"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; format(string, sizeof(string), fmt_str, GN(playerid)); ProxDetector(5.0, playerid, string, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE, COLOR_PURPLE); SetPlayerArmour(playerid, 100.0); } } DialogGunLSPD(playerid); } } } return true; } Потом в конце мода создаем сток:
      stock DialogGunLSPD(playerid) { SPD(playerid, 19, DSTH, "Оружейный склад", !"\ {FFFFFF}Имя:\t\tПатроны:\ \n1) Deagle\t\t35\ \n2) ShotGun\t\t20\ \n3) MP5\t\t60\ \n4) Дубинка\ \n5) Сухпаёк\ \n6) Бронежилет", "Выбрать", "Отмена\ "); } stock Потом в паблик OnPlayerPickUp кидаем это:
      public OnPlayerPickUpDynamicPickup(playerid, pickupid) { if(anti_flood_pick{playerid} == true) return true; else { anti_flood_pick{playerid} = true; GetPlayerPos(playerid, pos_pick[0][playerid], pos_pick[1][playerid], pos_pick[2][playerid]); } if(pickupid == lspd_pick[0]) { SetPlayerPos(playerid, 246.5715,65.2846,1003.6406); // Значения меняем на свои! SetPlayerFacingAngle(playerid, 2.3383); SetPlayerVirtualWorld(playerid, 1); SetPlayerInterior(playerid, 6); SetCameraBehindPlayer(playerid); } if(pickupid == lspd_pick[1]) { SetPlayerPos(playerid, 246.7730,84.8201,1003.6406); // Значения меняем на свои! SetPlayerFacingAngle(playerid, 179.3500); SetPlayerVirtualWorld(playerid, 1); SetPlayerInterior(playerid, 6); SetCameraBehindPlayer(playerid); } if(pickupid == lspd_pick[2]) { SetPlayerPos(playerid, 1552.6343,-1675.7346,16.1953); // Значения меняем на свои! SetPlayerFacingAngle(playerid, 86.4747); SetPlayerVirtualWorld(playerid, 0); SetPlayerInterior(playerid, 0); SetCameraBehindPlayer(playerid); } if(pickupid == lspd_pick[3]) { SetPlayerPos(playerid, 1568.7280,-1693.8474,5.8906); // Значения меняем на свои! SetPlayerFacingAngle(playerid, 180.4991); SetPlayerVirtualWorld(playerid, 0); SetPlayerInterior(playerid, 0); SetCameraBehindPlayer(playerid); } if(pickupid == lspd_pick[4]) { SetPlayerPos(playerid, 316.5714,-167.7457,999.5938); // Значения меняем на свои! SetPlayerFacingAngle(playerid, 359.0539); SetPlayerVirtualWorld(playerid, 1); SetPlayerInterior(playerid, 6); SetCameraBehindPlayer(playerid); } if(pickupid == lspd_pick[5]) { SetPlayerPos(playerid, 1527.6619,-1677.7692,5.8906); // Значения меняем на свои! SetPlayerFacingAngle(playerid, 269.1499); SetPlayerVirtualWorld(playerid, 0); SetPlayerInterior(playerid, 0); SetCameraBehindPlayer(playerid); } if(pickupid == lspd_pick[6]) { if(PlayerInfo[playerid][pMember] != 1) return SCM(playerid, COLOR_GREY, !"Вы не состоите в LSPD"); DialogGunLSPD(playerid); } return true; } Потом перед ранее созданым стоком "stock DialogGunLspd" создаем еще два стока:
      stock Pickups() { // LSPD lspd_pick[0] = CreateDynamicPickup(1318, 23, 1555.5059,-1675.7415,16.1953); // дверь улица lspd_pick[1] = CreateDynamicPickup(1318, 23, 1568.6741,-1689.9702,6.2188); // дверь гаража улицы lspd_pick[2] = CreateDynamicPickup(1318, 23, 246.8043,62.3237,1003.6406, 1, 6); // дверь в участке lspd_pick[3] = CreateDynamicPickup(1318, 23, 246.4056,88.0078,1003.6406, 1, 6); // дверь в гараж lspd_pick[4] = CreateDynamicPickup(1318, 23, 1524.4835,-1677.8490,6.2188); // дверь в оружейную lspd_pick[5] = CreateDynamicPickup(1318, 23, 316.3202,-170.2966,999.5938); // дверь из оружейной lspd_pick[6] = CreateDynamicPickup(2061, 23, 312.1859,-168.7103,999.5938); // Боеприпасы } stock Cars() { // LSPD lspd_car[0] = AddStaticVehicleEx(596, 1602.4960, -1683.9705, 5.6106, 89.9966, 0, 1, 600); // координаты изменяем по своему усмотрению lspd_car[1] = AddStaticVehicleEx(596, 1602.4961, -1688.1863, 5.6106, 89.9944, 0, 1, 600); lspd_car[2] = AddStaticVehicleEx(596, 1602.4960, -1692.1858, 5.6106, 89.9963, 0, 1, 600); lspd_car[3] = AddStaticVehicleEx(596, 1602.4961, -1696.4399, 5.6106, 89.9965, 0, 1, 600); lspd_car[4] = AddStaticVehicleEx(596, 1602.4961, -1700.4475, 5.6105, 89.9928, 0, 1, 600); lspd_car[5] = AddStaticVehicleEx(596, 1602.4973, -1704.7720, 5.6111, 89.9599, 0, 1, 600); lspd_car[6] = AddStaticVehicleEx(596, 1591.1188, -1710.8458, 5.6106, 359.9953, 0, 1, 600); lspd_car[7] = AddStaticVehicleEx(596, 1587.0615, -1710.8457, 5.6106, 359.9914, 0, 1, 600); lspd_car[8] = AddStaticVehicleEx(596, 1582.9340, -1710.8418, 5.6106, 359.9973, 0, 1, 600); lspd_car[9] = AddStaticVehicleEx(596, 1578.0387, -1710.8459, 5.6106, 359.9960, 0, 1, 600); lspd_car[10] = AddStaticVehicleEx(596, 1574.0431, -1710.8464, 5.6115, 359.9565, 0, 1, 600); lspd_car[11] = AddStaticVehicleEx(596, 1569.6342, -1710.8481, 5.5788, 359.9579, 0, 1, 600); lspd_car[12] = AddStaticVehicleEx(596, 1558.3914, -1710.8352, 5.6231, 359.9962, 0, 1, 600); lspd_car[13] = AddStaticVehicleEx(596, 1584.5720, -1672.3798, 5.6135, 269.9952, 0, 1, 600); lspd_car[14] = AddStaticVehicleEx(596, 1584.5693, -1668.1559, 5.6123, 269.9404, 0, 1, 600); lspd_car[15] = AddStaticVehicleEx(523, 1545.6881, -1684.3760, 5.4628, 89.8548, 0, 1, 600); lspd_car[16] = AddStaticVehicleEx(523, 1545.6832, -1680.3370, 5.4628, 90.0000, 0, 1, 600); lspd_car[17] = AddStaticVehicleEx(523, 1545.6796, -1676.2489, 5.4715, 90.0000, 0, 1, 600); lspd_car[18] = AddStaticVehicleEx(523, 1545.6934, -1672.1342, 5.4719, 90.0000, 0, 1, 600); lspd_car[19] = AddStaticVehicleEx(523, 1545.7043, -1667.6414, 5.4657, 89.5909, 0, 1, 600); lspd_car[20] = AddStaticVehicleEx(523, 1545.6930, -1663.1404, 5.4719, 90.0000, 0, 1, 600); lspd_car[21] = AddStaticVehicleEx(601, 1545.4475, -1659.0334, 5.6477, 89.9272, 0, 1, 600); lspd_car[22] = AddStaticVehicleEx(601, 1545.4456, -1655.0054, 5.6652, 89.9993, 0, 1, 600); lspd_car[23] = AddStaticVehicleEx(427, 1538.1487, -1644.9996, 6.0336, 180.0003, 0, 1, 600); lspd_car[24] = AddStaticVehicleEx(427, 1534.1730, -1644.9996, 6.0335, 180.0002, 0, 1, 600); lspd_car[25] = AddStaticVehicleEx(427, 1529.9532, -1644.9991, 6.0335, 180.0003, 0, 1, 600); lspd_car[26] = AddStaticVehicleEx(497, 1559.2762,-1644.1458,28.5774,90.9602, 0, 1, 600); } Потом после стока  "DialogGunLSPD" создаем еще два стока:
      stock LoadMaterials() { mysql_query(connect_mysql, "SELECT * FROM `materials`"); materials[mlspd] = cache_get_field_content_int(0, "LSPD"); } stock SaveMaterials() { static const fmt_str[] = "UPDATE `materials` SET `LSPD` = '%d'"; new string[sizeof(fmt_str)+5]; format(string, sizeof(string), fmt_str, materials[mLSPD]); mysql_tquery(connect_mysql, string, false, "", ""); } ВНИМАНИЕ: НЕ ЗАБУДТЕ СОЗДАТЬ В БАЗЕ ДАННЫХ НОВУЮ ТАБЛИЦУ "materials" и внести туда столбец LSPD со значением INT 
       
      В конце мода создаем команду для проверки материалов:
      CMD:warehouse(playerid) { if(PlayerInfo[playerid][pMember] == 0) return SCM(playerid, COLOR_GREY, !"Вы не уполномочены использовать данную команду!"); switch(PlayerInfo[playerid][pMember]) { case 1: { static const fmt_str[] = "Склад LSPD: %d/100000"; new string[sizeof(fmt_str)+5]; format(string, sizeof(string), fmt_str, materials[mLSPD]); SCM(playerid, COLOR_BLUE, string); } } return true; } И в начало мода создаем дефайны цветов чтобы не было ошибок:
      #define COLOR_WHITE 0xFFFFFFFF #define COLOR_LIGHTRED 0xFF463CFF #define COLOR_GREY 0xA6A69FFF #define COLOR_GREY_2 0xC8C8C8C8 #define COLOR_GREY_3 0xAAAAAAAA #define COLOR_GREY_4 0x8C8C8C8C #define COLOR_RED 0xFF0000FF #define COLOR_ORANGE 0xDF8600FF #define COLOR_GREEN 0x2CB816FF #define COLOR_YELLOW 0xFFFF00FF #define COLOR_BLUE 0x00BFFFFF #define COLOR_PURPLE 0xC2A2DAAA И последнее! 
      В  паблик OnPlayerConnect добавляем:
      GetPlayerName(playerid, player_name[playerid], MAX_PLAYER_NAME); static fmt_str[] = "SELECT `ID` FROM `accounts` WHERE `Name` = '%s' LIMIT 1"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; А в паблик OnPlayerDisconnect добавляем:
      static fmt_str[] = "UPDATE `ID` FROM `accounts` WHERE `Name` = '%s' LIMIT 1"; new string[sizeof(fmt_str)+MAX_PLAYER_NAME-1]; НЕ ЗАБУДЬТЕ СОЗДАТЬ В БАЗЕ ДАННЫХ ТАБЛИЦУ "accounts" и добавить туда столбцы из enum pInfo, только без первой буквы "p".
       
      Если будут ошибки/проблемы обращайтесь!
       
      VK: wk.com/martun.mkrt
    • #Copyright
      By #Copyright
      Здравствуйте дорогие друзья, это уже мой третий урок - в данном уроке вы сможете делать команду на кнопку, приступим.

      Что нам понадобится:
      - Сама команда которая будет активирована по нажатие кнопки
      - Ровные руки и свободные 10 минут.

      Для того чтобы кнопка активировала нашу команду, мы ищем наш public "public OnPlayerKeyStateChange"
      С корень паблика мы должны будем вставить код:

       
      if (newkeys == ID) { OnPlayerCommandText(playerid,"");  }

      Пояснение:

       
      //newkeys == ID - заместо слова "ID" мы пишем на ID кнопки которая будет активировать команду ( Список всех ID кнопок будет ниже ) //OnPlayerCommandText - Функция которая вызывает активирование команды после нажатия на кнопку //(playerid, ""); - В скобка мы пишем команду которая будет активирована после нажатия на кнопку.

      Вот и все, наш урок на этом заканчивается, спасибо за внимание.
      Автор: #CopyRight
      Специально для PAWNO-RUS

      ID's

       
       
    • SCRIPTMAN
      By SCRIPTMAN
      Привет, уважаемые пользователи.
       
      Наверняка у каждого из вас была такая проблема, что размер бд был больше 2 048 кб (2 мб) - такой лимит в Denwer'e, да и вообще, в phpMyAdmin, на большинстве хостингов кстати тоже. Отсюда у нас проблема, как загрузить базу данных, если ёё размер, например, 100 мб (и такое было), можно конечно ее сжать, но в среднем, со 100 мб вы ее сожмётё до 10-20 мб. Именно поэтому я и решил написать этот урок, мы увеличим лимит базы данных. Итак, начнем.
       
      1. Запускаем Denwer, кто соглашался с добавлением нового диска, те переходят в этот диск, а те, кто нет - ищите папку WebServers (куда Denwer устанавливали) и тоже переходите в эту папку.
      2. Переходите в папку "usr", дальше "local" и в "php5".
      3. В папке "php5" будет файл под названием "php.ini" (Если у вас не показывается тип файла, то включите в параметрах папок), открываете его через текстовый редактор, в моем случае "Notepad++"
      4. Нажимаете сочетание клавиш "CTRL + F" или просто "Поиск" и пишите туда: "post_max_size" - это максимальный размер отправляемого файла, если вы до этого ничего не меняли, то у вас будет так: "post_max_size = 2M", допустим, мы хотим увеличить лимит до 20 МБ, то выглядеть это будет так: "post_max_size = 20M" (M - не трогайте). Теперь нужно изменить лимит на загрузку базы данных, ищем строку "upload_max_filesize" - максимальный размер загружаемой базы данных, в стандартном виде она будет выглядеть так: "upload_max_filesize = 2M", чтобы увеличить  лимит, просто пишите нужное число, например до 20 МБ давайте, она примет такой вид "upload_max_filesize = 20M" (M - не трогать). 
      Сохраняете, делаете рестарт денвера и всё, лимит увеличен, и ничего не нужно сжимать.
       
      ВНИМАНИЕ: "post_max_size" и "upload_max_filesize" должны иметь одинаковое значение (в нашем случае 20).