impl.inc 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742
  1. /**--------------------------------------------------------------------------**\
  2. ================================
  3. Y Sever Includes - Commands Core
  4. ================================
  5. Description:
  6. Runs commands registered with the system and calls the required functions.
  7. Also handles alternate names and prefixes. Based very loosely on dcmd.
  8. Legal:
  9. Version: MPL 1.1
  10. The contents of this file are subject to the Mozilla Public License Version
  11. 1.1 (the "License"); you may not use this file except in compliance with
  12. the License. You may obtain a copy of the License at
  13. http://www.mozilla.org/MPL/
  14. Software distributed under the License is distributed on an "AS IS" basis,
  15. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  16. for the specific language governing rights and limitations under the
  17. License.
  18. The Original Code is the YSI commands include.
  19. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  20. Portions created by the Initial Developer are Copyright (C) 2011
  21. the Initial Developer. All Rights Reserved.
  22. Contributors:
  23. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  24. Thanks:
  25. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  26. ZeeX - Very productive conversations.
  27. koolk - IsPlayerinAreaEx code.
  28. TheAlpha - Danish translation.
  29. breadfish - German translation.
  30. Fireburn - Dutch translation.
  31. yom - French translation.
  32. 50p - Polish translation.
  33. Zamaroht - Spanish translation.
  34. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  35. for me to strive to better.
  36. Pixels^ - Running XScripters where the idea was born.
  37. Matite - Pestering me to release it and using it.
  38. Very special thanks to:
  39. Thiadmer - PAWN, whose limits continue to amaze me!
  40. Kye/Kalcor - SA:MP.
  41. SA:MP Team past, present and future - SA:MP.
  42. Version:
  43. 2.0
  44. Changelog:
  45. 26/12/13:
  46. Added sections.
  47. 23/06/13:
  48. Rewritten from scratch for MORE speed.
  49. Punycode support.
  50. More failure messages.
  51. Cleaner code.
  52. 20/10/10:
  53. Fixed a bug with insensitive commands - my fault for not testing.
  54. 06/01/08:
  55. Improved master and /help support.
  56. 04/01/08:
  57. Fixed bad element in Command_SetDeniedReturn.
  58. 12/08/07:
  59. Added master support.
  60. 24/06/07:
  61. Modifed a few functions to use Bit_GetBit for speed.
  62. 04/05/07:
  63. Completed command use support.
  64. Added invalid character protection.
  65. 02/05/07:
  66. Added YSI_ prefix to all globals.
  67. 14/04/07:
  68. Updated header documentation with more than changelog/functions.
  69. Added function name requesting.
  70. 13/04/07:
  71. Added function documentation.
  72. Added wrapped functions for e_COMM_FLAG values missing them.
  73. Added header function list.
  74. 12/04/07:
  75. Added command removal.
  76. 11/04/07:
  77. Changed system slightly to handle names and alt names separately. Still
  78. need a better way of ignoring names when alt names are used.
  79. 10/04/07:
  80. First version.
  81. Functions:
  82. Public:
  83. -
  84. Core:
  85. -
  86. Stock:
  87. -
  88. Static:
  89. -
  90. Inline:
  91. -
  92. API:
  93. -
  94. Callbacks:
  95. -
  96. Definitions:
  97. -
  98. Enums:
  99. -
  100. Macros:
  101. -
  102. Tags:
  103. -
  104. Variables:
  105. Global:
  106. -
  107. Static:
  108. -
  109. Commands:
  110. -
  111. Compile options:
  112. -
  113. \**--------------------------------------------------------------------------**/
  114. /*
  115. ad88888ba
  116. d8" "8b ,d
  117. Y8, 88
  118. `Y8aaaaa, ,adPPYba, MM88MMM 88 88 8b,dPPYba,
  119. `"""""8b, a8P_____88 88 88 88 88P' "8a
  120. `8b 8PP""""""" 88 88 88 88 d8
  121. Y8a a8P "8b, ,aa 88, "8a, ,a88 88b, ,a8"
  122. "Y88888P" `"Ybbd8"' "Y888 `"YbbdP'Y8 88`YbbdP"'
  123. 88
  124. 88
  125. */
  126. enum E_COMMAND
  127. {
  128. // HASH_MAP_DATA<MAX_COMMAND_LENGTH char>,
  129. // Share a memory location with the hashmap stored name.
  130. E_COMMAND_NAME[MAX_COMMAND_LENGTH char] = 0,
  131. // IGNORE THESE, THEY COVER HASH MAP DATA.
  132. E_COMMAND_HASH_MAP[HASH_MAP_DATA],
  133. // _E_COMMAND_PAD_0, _E_COMMAND_PAD_1,
  134. // Who can use this command?
  135. PlayerArray:E_COMMAND_USERS<MAX_PLAYERS>,
  136. #if defined Y_COMMANDS_USE_CHARS
  137. E_COMMAND_PREFIX,
  138. #endif
  139. // Function pointer.
  140. E_COMMAND_POINTER
  141. }
  142. enum e_COMMAND_ERRORS
  143. {
  144. // The majority of these are even - odd numbers return "1" not "0".
  145. COMMAND_ZERO_RET = 0 , // The command returned 0.
  146. COMMAND_OK = 1 , // Called corectly.
  147. COMMAND_UNDEFINED = 2 , // Command doesn't exist.
  148. COMMAND_DENIED = 3 , // Can't use the command.
  149. COMMAND_HIDDEN = 4 , // Can't use the command don't let them know it exists.
  150. COMMAND_NO_PLAYER = 6 , // Used by a player who shouldn't exist.
  151. COMMAND_DISABLED = 7 , // All commands are disabled for this player.
  152. COMMAND_BAD_PREFIX = 8 , // Used "/" instead of "#", or something similar.
  153. COMMAND_INVALID_INPUT = 10, // Didn't type "/something".
  154. }
  155. enum e_COMMAND_FLAGS (<<= 1)
  156. {
  157. e_COMMAND_FLAGS_ZERO_RET = 1, // The command returned 0.
  158. e_COMMAND_FLAGS_OK, // Called corectly.
  159. e_COMMAND_FLAGS_NOT_FOUND, // Command doesn't exist.
  160. e_COMMAND_FLAGS_DENIED, // Can't use the command.
  161. e_COMMAND_FLAGS_HIDDEN,
  162. e_COMMAND_FLAGS_NO_PLAYER, // Used by a player who shouldn't exist.
  163. e_COMMAND_FLAGS_DISABLED, // All commands are disabled for this player.
  164. e_COMMAND_FLAGS_BAD_PREFIX,
  165. e_COMMAND_FLAGS_INVALID_INPUT, // Didn't type "/something".
  166. // Save counts for callbacks.
  167. e_COMM_FLAG_OPCP = 0x00FF0000,
  168. e_COMM_FLAG_OPCP_ADD = 0x00010000,
  169. e_COMM_FLAG_OPCR = 0xFF000000,
  170. e_COMM_FLAG_OPCR_ADD = 0x01000000
  171. }
  172. // Store which script(s) own which commands.
  173. MASTER_DATA<MAX_COMMANDS>
  174. // Information for returning error messages.
  175. static stock __declspec(dist_tagged) e_COMMAND_FLAGS:YSI_g_sCommandFlags;
  176. static stock __declspec(distributed) YSI_g_sErrorMessages[e_COMMAND_ERRORS][144];
  177. // Who has had ALL their commands disabled?
  178. static stock __declspec(dist_special) PlayerArray:YSI_g_sDisabledPlayers<MAX_PLAYERS>;
  179. static stock
  180. YSI_g_sCurrentID = COMMAND_NOT_FOUND,
  181. BitArray:YSI_g_sPrefixes<128>,
  182. YSI_g_sHighestID,
  183. YSI_g_sReturnBuffer[YSI_MAX_STRING],
  184. // Quickly reference and store commands by name.
  185. HashMap:YSI_g_sCommandMap<MAX_COMMANDS>;
  186. static stock __declspec(dist_master) YSI_g_sCommands[MAX_COMMANDS][E_COMMAND];
  187. static stock const
  188. YSI_gscOPCR[] = "OnPlayerCommandReceived",
  189. YSI_gscOPCP[] = "OnPlayerCommandPerformed",
  190. YSI_gscISI[] = "isi",
  191. YSI_gscISII[] = "isii";
  192. // "YCMD:" macros. The true core is "RC:", which is in "y_master".
  193. #define _YCMD_0:_YCMD_1:_YCMD_2:%0(%1[]%2) RC:%0(%1[]%2)
  194. #define _YCMD_1:_YCMD_2:%0, Command_GetID(#%0),
  195. #define _YCMD_2:%0) Command_GetID(#%0))
  196. #define @YCMD:%0; Command_TouchNamed(#%0);
  197. #define YCMD: _YCMD_0:_YCMD_1:_YCMD_2:
  198. // ZCMD compatibility.
  199. #define CMD:%0(%1) RC:%0(%1,__help)if(__help)return 0;else
  200. #define COMMAND CMD
  201. // Forwards for optional command callbacks.
  202. forward e_COMMAND_ERRORS:OnPlayerCommandReceived(playerid, cmdtext[], e_COMMAND_ERRORS:success);
  203. forward e_COMMAND_ERRORS:OnPlayerCommandPerformed(playerid, cmdtext[], e_COMMAND_ERRORS:success);
  204. /*
  205. 88b d88
  206. 888b d888
  207. 88`8b d8'88
  208. 88 `8b d8' 88 ,adPPYYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba,
  209. 88 `8b d8' 88 "" `Y8 a8" "" 88P' "Y8 a8" "8a I8[ ""
  210. 88 `8b d8' 88 ,adPPPPP88 8b 88 8b d8 `"Y8ba,
  211. 88 `888' 88 88, ,88 "8a, ,aa 88 "8a, ,a8" aa ]8I
  212. 88 `8' 88 `"8bbdP"Y8 `"Ybbd8"' 88 `"YbbdP"' `"YbbdP"'
  213. */
  214. #define _Command_GetPlayer(%0,%1) (PA_Get(YSI_g_sCommands[(%0)][E_COMMAND_USERS], (%1)))
  215. /**--------------------------------------------------------------------------**\
  216. <summary>_Command_IsEmptySlot</summary>
  217. <param name="idx">Command to test.</param>
  218. <returns>
  219. -
  220. </returns>
  221. <remarks>
  222. Tests if the given slot is empty.
  223. </remarks>
  224. \**--------------------------------------------------------------------------**/
  225. #define _Command_IsEmptySlot(%0) (!YSI_g_sCommands[(%0)][E_COMMAND_NAME])
  226. /**--------------------------------------------------------------------------**\
  227. <summary>_Command_IsAlt</summary>
  228. <param name="idx">Command to test.</param>
  229. <returns>
  230. -
  231. </returns>
  232. <remarks>
  233. Tests if the given slot is an alternate command.
  234. </remarks>
  235. \**--------------------------------------------------------------------------**/
  236. #define _Command_IsAlt(%0) (YSI_g_sCommands[(%0)][E_COMMAND_POINTER] & cellmin)
  237. /**--------------------------------------------------------------------------**\
  238. <summary>_Command_GetReal</summary>
  239. <param name="ptr">AMX function pointer.</param>
  240. <param name="idx">Index of the parent command data.</param>
  241. <param name="name">Destination for the parent function name.</param>
  242. <returns>
  243. -
  244. </returns>
  245. <remarks>
  246. Finds the original version of an alt command. Updated to not contain long
  247. chains (along with "Command_AddAlt").
  248. </remarks>
  249. \**--------------------------------------------------------------------------**/
  250. #define _Command_GetReal(%0,%1,%2); \
  251. if((%0=YSI_g_sCommands[(%1)][E_COMMAND_POINTER])&cellmin) \
  252. %1=(%0)&~cellmin, \
  253. %0=YSI_g_sCommands[(%1)][E_COMMAND_POINTER], \
  254. strunpack(%2,YSI_g_sCommands[(%1)][E_COMMAND_NAME]);
  255. /**--------------------------------------------------------------------------**\
  256. <summary>Command_OnReceived</summary>
  257. <param name="error">Which error to show.</param>
  258. <param name="playerid">Player who typed the command.</param>
  259. <param name="cmdtext">What they typed.</param>
  260. <returns>
  261. -
  262. </returns>
  263. <remarks>
  264. Call OnPlayerCommandReceived once the system knows how the player can use
  265. this command (if they can). The order of the parameters is such that the
  266. error comes first. This is because it is compile-time concatenated to make
  267. the error enum value, and putting that parameter first means that we don't
  268. need to ommit the space after any comma.
  269. </remarks>
  270. \**--------------------------------------------------------------------------**/
  271. #if defined COMMAND_USE_ERRORS
  272. #define Command_ErrorRet(%2) (YSI_g_sCommandFlags&e_COMMAND_FLAGS:(1<<_:(%2)))
  273. #define Command_ErrorMsg(%2) YSI_g_sErrorMessages[%2]
  274. #if defined _Text_Send
  275. #define Command_Error(%0,%2) (Command_ErrorMsg(%2)[0]?(Text_Send((%0),Command_ErrorMsg(%2)),Command_ErrorRet(%2)):Command_ErrorRet(%2))
  276. #else
  277. #define Command_Error(%0,%2) (Command_ErrorMsg(%2)[0]?(SendClientMessage((%0),0xFF0000AA,Command_ErrorMsg(%2)),Command_ErrorRet(%2)):Command_ErrorRet(%2))
  278. #endif
  279. #else
  280. #define Command_Error(%0,%2) ((%2) & e_COMMAND_ERRORS:1)
  281. #endif
  282. #define Command_OnReceived(%2,%0,%1) ((sErr=(YSI_g_sCommandFlags&e_COMM_FLAG_OPCR)?(e_COMMAND_ERRORS:W@(YSI_gscOPCR,YSI_gscISI,(%0),(%1),(_:COMMAND_%2))):(COMMAND_%2)),Command_Error(%0,sErr))
  283. /**--------------------------------------------------------------------------**\
  284. <summary>_Command_IsActive</summary>
  285. <param name="command">Command to get for.</param>
  286. <returns>
  287. Is this command ID active?
  288. </returns>
  289. <remarks>
  290. Doesn't do any bounds checks - use "_Command_IsValid" for that.
  291. </remarks>
  292. \**--------------------------------------------------------------------------**/
  293. #define _Command_IsActive(%0) (YSI_g_sCommands[(%0)][E_COMMAND_NAME])
  294. /**--------------------------------------------------------------------------**\
  295. <summary>_Command_IsValid</summary>
  296. <param name="command">Command to get for.</param>
  297. <returns>
  298. Is this command ID valid?
  299. </returns>
  300. <remarks>
  301. Internal direct-access check.
  302. </remarks>
  303. \**--------------------------------------------------------------------------**/
  304. //#define _Command_IsValid(%0) ((0 <= (%0) < YSI_g_sHighestID) && _Command_IsActive(%0))
  305. #define _Command_IsValid(%0) (IS_IN_RANGE((%0), 0, MAX_COMMANDS) && _Command_IsActive(%0))
  306. /**--------------------------------------------------------------------------**\
  307. <summary>_Command_IsPrefix</summary>
  308. <param name="idx">Command to test.</param>
  309. <returns>
  310. -
  311. </returns>
  312. <remarks>
  313. Checks to see if a character is a possible prefix character. May use an
  314. unsigned comparison.
  315. </remarks>
  316. \**--------------------------------------------------------------------------**/
  317. #if defined Y_COMMANDS_USE_CHARS
  318. #define _Command_IsPrefix(%0) (IS_IN_RANGE((%0), 0, 128 + 1) && Bit_Get(YSI_g_sPrefixes, (%0)))
  319. #else
  320. #define _Command_IsPrefix(%0) ((%0) == '/')
  321. #endif
  322. /**--------------------------------------------------------------------------**\
  323. <summary>_Command_GetPrefix</summary>
  324. <param name="c">Command to get.</param>
  325. <returns>
  326. The prefix for this command.
  327. </returns>
  328. <remarks>
  329. -
  330. </remarks>
  331. \**--------------------------------------------------------------------------**/
  332. #define _Command_GetPrefix(%0) (YSI_g_sCommands[(%0)][E_COMMAND_PREFIX])
  333. /**--------------------------------------------------------------------------**\
  334. <summary>Command_Name</summary>
  335. <param name="f">Command to get the name of.</param>
  336. <returns>
  337. -
  338. </returns>
  339. <remarks>
  340. -
  341. </remarks>
  342. \**--------------------------------------------------------------------------**/
  343. #define Command_Name(%0) (YSI_g_sCommands[(%0)][E_COMMAND_NAME])
  344. /*
  345. 88b d88 88 db 88888888ba 88
  346. 888b d888 "" d88b 88 "8b 88
  347. 88`8b d8'88 d8'`8b 88 ,8P 88
  348. 88 `8b d8' 88 ,adPPYYba, 88 8b,dPPYba, d8' `8b 88aaaaaa8P' 88
  349. 88 `8b d8' 88 "" `Y8 88 88P' `"8a d8YaaaaY8b 88""""""' 88
  350. 88 `8b d8' 88 ,adPPPPP88 88 88 88 d8""""""""8b 88 88
  351. 88 `888' 88 88, ,88 88 88 88 d8' `8b 88 88
  352. 88 `8' 88 `"8bbdP"Y8 88 88 88 d8' `8b 88 88
  353. */
  354. /**--------------------------------------------------------------------------**\
  355. <summary>Command_GetID</summary>
  356. <param name="function[]">Function name to find.</param>
  357. <returns>
  358. The ID of the passed function.
  359. </returns>
  360. <remarks>
  361. -
  362. native Command_GetID(function[])
  363. </remarks>
  364. \**--------------------------------------------------------------------------**/
  365. foreign Command_GetID(string:function[]);
  366. global Command_GetID(string:function[])
  367. {
  368. P:2("Command_GetID called: \"%s\"", function);
  369. return Command_Find(function);
  370. }
  371. /**--------------------------------------------------------------------------**\
  372. <summary>Command_AddAlt</summary>
  373. <param name="oidx">The function this is an alternate to.</param>
  374. <param name="cmd[]">The new name.</param>
  375. <returns>
  376. The command's ID.
  377. </returns>
  378. <remarks>
  379. -
  380. </remarks>
  381. \**--------------------------------------------------------------------------**/
  382. foreign Command_AddAlt(oidx, string:cmd[]);
  383. global Command_AddAlt(oidx, string:cmd[])
  384. {
  385. static
  386. sCmd[64],
  387. sHash;
  388. if (!_Command_IsValid(oidx)) return COMMAND_NOT_FOUND;
  389. // Check the pointer is valid.
  390. new
  391. id = YSI_g_sCommands[oidx][E_COMMAND_POINTER];
  392. // The command we are pointing to is already an alternate for a third
  393. // command - point this new command at the parent.
  394. if (id & cellmin) oidx = id & ~cellmin;
  395. Puny_EncodeHash(sCmd, cmd, sHash, .delimiter = '@');
  396. // Now point this new command at the real (software) command.
  397. strpack(sCmd, sCmd, cellmax);
  398. if ((id = Command_Find(sCmd)) == COMMAND_NOT_FOUND)
  399. {
  400. // Command doesn't already exist, add it.
  401. if ((id = Command_GetEmptySlot()) == COMMAND_NOT_FOUND)
  402. {
  403. P:E("Could not add alt command to array.");
  404. return COMMAND_NOT_FOUND;
  405. }
  406. #if defined Y_COMMANDS_USE_CHARS
  407. YSI_g_sCommands[id][E_COMMAND_PREFIX] = '/',
  408. #endif
  409. // Save the new highest ID for loops later.
  410. YSI_g_sHighestID = max(YSI_g_sHighestID, id + 1),
  411. // Save the command's pointer.
  412. YSI_g_sCommands[id][E_COMMAND_POINTER] = cellmin | oidx,
  413. // Add this command to the hash map (does the name too).
  414. HashMap_Add(YSI_g_sCommandMap, sCmd, id),
  415. // Add all players, or defer to y_groups.
  416. PA_FastInit(YSI_g_sCommands[id][E_COMMAND_USERS]);
  417. NO_GROUPS(id)
  418. {
  419. PA_Init(YSI_g_sCommands[id][E_COMMAND_USERS], true);
  420. }
  421. // Copy the master script information. Note that this won't be kept up
  422. // to date with new scripts unfortunately.
  423. MASTER_COPY<id, oidx>
  424. }
  425. return id;
  426. }
  427. /**--------------------------------------------------------------------------**\
  428. <summary>Command_ReProcess</summary>
  429. <param name="playerid">Player who entered the command.</param>
  430. <param name="cmdtext[]">Text entered.</param>
  431. <param name="help">Called from the help commmand or OnPlayerCommandText.</param>
  432. <returns>
  433. true - success or hidden fail.
  434. false - fail.
  435. </returns>
  436. <remarks>
  437. Does all the command and error handling.
  438. </remarks>
  439. \**--------------------------------------------------------------------------**/
  440. #define Command_ReProcess(%0,%1,%2,%3) Command_ReProcess(%0,%1, _:(%2)|(_:(%3)<<1))
  441. foreign Command_ReProcess(p,string:c[],h);
  442. global Command_ReProcess(p,string:c[],h)
  443. {
  444. static
  445. sCmd[64] = "@yC_",
  446. sPos,
  447. sRet,
  448. sHash,
  449. e_COMMAND_ERRORS:sErr;
  450. // Check that the input is a valid command. Note that changing the command
  451. // prefix here would be VERY trivial!
  452. if ((sRet = _:_Command_IsPrefix(c[0]))) // Relies on "true = 1" later on!
  453. {
  454. if (!c[1]) return Command_OnReceived(INVALID_INPUT, p, c);
  455. }
  456. else
  457. {
  458. if (isnull(c)) return Command_OnReceived(INVALID_INPUT, p, NULL);
  459. }
  460. // Check for a valid player.
  461. #if !defined Y_COMMANDS_NO_IPC
  462. if (!IsPlayerConnected(p)) return Command_OnReceived(NO_PLAYER, p, c);
  463. #endif
  464. if (PA_Get(YSI_g_sDisabledPlayers, p)) return Command_OnReceived(DISABLED, p, c);
  465. P:1("Commands_OnPlayerCommandText called: %d %s", p, c);
  466. new
  467. prevID = YSI_g_sCurrentID;
  468. // Get the hashed version of the decoded string, skipping the possible "/".
  469. sPos = Puny_EncodeHash(sCmd[4], c[sRet], sHash, .delimiter = '@') + sRet;
  470. while (c[sPos] == ' ') ++sPos; // Better/slower: ('\0' < c[sPos] <= ' ').
  471. // Find the command in the array.
  472. YSI_g_sCurrentID = HashMap_GetWithHash(YSI_g_sCommandMap, sCmd[4], sHash);
  473. P:5("Commands_OnPlayerCommandText: %s, %d, %d, %d", sCmd[4], sPos, sHash, YSI_g_sCurrentID);
  474. if (YSI_g_sCurrentID == COMMAND_NOT_FOUND)
  475. {
  476. return
  477. YSI_g_sCurrentID = prevID,
  478. Command_OnReceived(UNDEFINED, p, c);
  479. }
  480. #if defined Y_COMMANDS_USE_CHARS
  481. if (sRet && _Command_GetPrefix(YSI_g_sCurrentID) != c[0])
  482. {
  483. // Have a prefix, but not the right one. Calling this function
  484. // directly always works for all possible command prefixes.
  485. return Command_OnReceived(BAD_PREFIX, p, c);
  486. }
  487. #endif
  488. P:5("Commands_OnPlayerCommandText: Use %d", _Command_GetPlayer(YSI_g_sCurrentID, p));
  489. // Can the player use this command?
  490. if ((h&2) || _Command_GetPlayer(YSI_g_sCurrentID, p))
  491. {
  492. // This macro sets "sErr".
  493. sRet = Command_OnReceived(OK, p, c);
  494. if (sErr != COMMAND_OK)
  495. {
  496. return
  497. YSI_g_sCurrentID = prevID,
  498. sRet;
  499. }
  500. // Find the true version of the command (alts etc).
  501. _Command_GetReal(sHash, YSI_g_sCurrentID, sCmd[4]);
  502. P:5("Commands_OnPlayerCommandText: Read %d", YSI_g_sCurrentID);
  503. P:5("Commands_OnPlayerCommandText: Master %d %d", Master_ID(), _:MASTER_GET<YSI_g_sCurrentID>);
  504. #if YSIM_HAS_MASTER
  505. if (MASTER_EXCLUSIVE<YSI_g_sCurrentID>)
  506. #endif
  507. {
  508. P:5("Commands_OnPlayerCommandText: Local");
  509. // In this script. More to the point, in ONLY this script, so
  510. // we can't have another script as the master. I tried updating
  511. // this code but then realised that the update would ignore the
  512. // case where a command was in both the current script and
  513. // another script, but the other script was the master script.
  514. h=h&1;
  515. #emit PUSH.S h
  516. #emit LOAD.pri sPos
  517. #emit LOAD.S.alt c
  518. #emit IDXADDR
  519. #emit PUSH.pri
  520. #emit PUSH.S p
  521. #emit PUSH.C 12
  522. #emit LCTRL 6
  523. #emit ADD.C 28
  524. #emit PUSH.pri
  525. #emit LOAD.pri sHash
  526. #emit SCTRL 6
  527. #emit STOR.pri sRet
  528. P:5("Command_ReProces: Result = %d %d %d", sRet, Command_Error(p, e_COMMAND_ERRORS:sRet), _:COMMAND_OK);
  529. }
  530. #if YSIM_HAS_MASTER
  531. else
  532. {
  533. // This is in another script, or multiple scripts.
  534. // Call the command in another script. If no particular script
  535. // is set up as the "master", call it in the first one found...
  536. if (c[sPos]) CallRemoteFunction(sCmd, YSI_gscISII, p, c[sPos], h&1, Cell_GetLowestBit(_:MASTER_GET<YSI_g_sCurrentID>));
  537. else CallRemoteFunction(sCmd, YSI_gscISII, p, NULL, h&1, Cell_GetLowestBit(_:MASTER_GET<YSI_g_sCurrentID>));
  538. sRet = getproperty(8, YSIM_RETURN);
  539. }
  540. #endif
  541. if (YSI_g_sCommandFlags & e_COMM_FLAG_OPCP) sRet = CallRemoteFunction(YSI_gscOPCP, YSI_gscISI, p, c, sRet);
  542. return
  543. YSI_g_sCurrentID = prevID,
  544. Command_Error(p, e_COMMAND_ERRORS:sRet);
  545. }
  546. return
  547. sRet = Command_OnReceived(DENIED, p, c),
  548. YSI_g_sCurrentID = prevID,
  549. sRet;
  550. }
  551. /*
  552. 88888888ba 88 88
  553. 88 "8b "" ""
  554. 88 ,8P
  555. 88aaaaaa8P' ,adPPYba, 8b,dPPYba, 88,dPYba,,adPYba, 88 ,adPPYba, ,adPPYba, 88 ,adPPYba, 8b,dPPYba, ,adPPYba,
  556. 88""""""' a8P_____88 88P' "Y8 88P' "88" "8a 88 I8[ "" I8[ "" 88 a8" "8a 88P' `"8a I8[ ""
  557. 88 8PP""""""" 88 88 88 88 88 `"Y8ba, `"Y8ba, 88 8b d8 88 88 `"Y8ba,
  558. 88 "8b, ,aa 88 88 88 88 88 aa ]8I aa ]8I 88 "8a, ,a8" 88 88 aa ]8I
  559. 88 `"Ybbd8"' 88 88 88 88 88 `"YbbdP"' `"YbbdP"' 88 `"YbbdP"' 88 88 `"YbbdP"'
  560. */
  561. /**--------------------------------------------------------------------------**\
  562. <summary>Command_GetPlayer</summary>
  563. <param name="command">Command to get for.</param>
  564. <param name="playerid">Player to get.</param>
  565. <returns>
  566. Can this player use this command?
  567. </returns>
  568. <remarks>
  569. native bool:Command_GetPlayer(command, playerid);
  570. </remarks>
  571. \**--------------------------------------------------------------------------**/
  572. foreign bool:Command_GetPlayer(cmd, pid);
  573. global bool:Command_GetPlayer(cmd, pid)
  574. {
  575. if (_Command_IsValid(cmd) && VALID_PLAYERID(pid)) return _Command_GetPlayer(cmd, pid);
  576. return false;
  577. }
  578. /**--------------------------------------------------------------------------**\
  579. <summary>Command_GetPlayerNamed</summary>
  580. <param name="funcname[]">Command to get for.</param>
  581. <param name="playerid">Player to get.</param>
  582. <returns>
  583. -
  584. </returns>
  585. <remarks>
  586. Like Command_GetPlayer but for a function name.
  587. native bool:Command_GetPlayerNamed(funcname[], playerid);
  588. </remarks>
  589. \**--------------------------------------------------------------------------**/
  590. foreign bool:Command_GetPlayerNamed(string:func[], playerid);
  591. global bool:Command_GetPlayerNamed(string:func[], playerid)
  592. {
  593. return Command_GetPlayer(Command_Find(func), playerid);
  594. }
  595. /**--------------------------------------------------------------------------**\
  596. <summary>Command_SetPlayer</summary>
  597. <param name="command">Command to set for.</param>
  598. <param name="playerid">Player to set.</param>
  599. <param name="bool:set">Wether or not this player can use this command.</param>
  600. <returns>
  601. -
  602. </returns>
  603. <remarks>
  604. native bool:Command_SetPlayer(command, playerid, bool:set);
  605. </remarks>
  606. \**--------------------------------------------------------------------------**/
  607. foreign void:Command_SetPlayer(c, p, bool:s);
  608. global void:Command_SetPlayer(c, p, bool:s)
  609. {
  610. P:2("Command_SetPlayer called: %i, %i, %i", c, p, s);
  611. if (_Command_IsValid(c) && VALID_PLAYERID(p)) PA_Set(YSI_g_sCommands[c][E_COMMAND_USERS], p, s);
  612. }
  613. /**--------------------------------------------------------------------------**\
  614. <summary>Command_SetPlayerNamed</summary>
  615. <param name="funcname[]">Command to set for.</param>
  616. <param name="playerid">Player to set.</param>
  617. <param name="set">Wether or not this player can use this command.</param>
  618. <returns>
  619. -
  620. </returns>
  621. <remarks>
  622. Like Command_SetPlayer but for a function name.
  623. native bool:Command_SetPlayerNamed(funcname[], playerid, bool:set);
  624. </remarks>
  625. \**--------------------------------------------------------------------------**/
  626. foreign void:Command_SetPlayerNamed(string:f[],p,bool:s);
  627. global void:Command_SetPlayerNamed(string:f[],p,bool:s)
  628. {
  629. Command_SetPlayer(Command_Find(f), p, s);
  630. }
  631. /**--------------------------------------------------------------------------**\
  632. <summary>Command_Find</summary>
  633. <param name="cmd[]">The command name to find.</param>
  634. <returns>
  635. The array slot of this command, or -1.
  636. </returns>
  637. <remarks>
  638. -
  639. </remarks>
  640. \**--------------------------------------------------------------------------**/
  641. foreign Command_Find(string:cmd[]);
  642. global Command_Find(string:cmd[])
  643. {
  644. static
  645. sCmd[64] = "",
  646. sHash;
  647. Puny_EncodeHash(sCmd, cmd, sHash, .delimiter = '@');
  648. return HashMap_Get(YSI_g_sCommandMap, sCmd);
  649. }
  650. /**--------------------------------------------------------------------------**\
  651. <summary>Command_TouchNamed</summary>
  652. <param name="string:command[]">Command to "touch".</param>
  653. <returns>
  654. -
  655. </returns>
  656. <remarks>
  657. Used within "GROUP_ADD" to quickly assign a load of commands to just one
  658. group.
  659. </remarks>
  660. \**--------------------------------------------------------------------------**/
  661. foreign void:Command_TouchNamed(string:command[]);
  662. global void:Command_TouchNamed(string:command[])
  663. {
  664. new
  665. id = Command_Find(command);
  666. if (id != COMMAND_NOT_FOUND)
  667. {
  668. NO_GROUPS(id)
  669. {
  670. return;
  671. }
  672. }
  673. }
  674. /**--------------------------------------------------------------------------**\
  675. <summary>Command_Touch</summary>
  676. <param name="string:command[]">Command to "touch".</param>
  677. <returns>
  678. -
  679. </returns>
  680. <remarks>
  681. Used within "GROUP_ADD" to quickly assign a load of commands to just one
  682. group.
  683. </remarks>
  684. \**--------------------------------------------------------------------------**/
  685. foreign void:Command_Touch(command);
  686. global void:Command_Touch(command)
  687. {
  688. if (_Command_IsValid(command))
  689. {
  690. NO_GROUPS(command)
  691. {
  692. return;
  693. }
  694. }
  695. }
  696. /*
  697. 88888888888 db 88888888ba 88
  698. 88 ,d d88b 88 "8b 88
  699. 88 88 d8'`8b 88 ,8P 88
  700. 88aaaaa 8b, ,d8 MM88MMM 8b,dPPYba, ,adPPYYba, d8' `8b 88aaaaaa8P' 88
  701. 88""""" `Y8, ,8P' 88 88P' "Y8 "" `Y8 d8YaaaaY8b 88""""""' 88
  702. 88 )888( 88 88 ,adPPPPP88 d8""""""""8b 88 88
  703. 88 ,d8" "8b, 88, 88 88, ,88 d8' `8b 88 88
  704. 88888888888 8P' `Y8 "Y888 88 `"8bbdP"Y8 d8' `8b 88 88
  705. */
  706. /**--------------------------------------------------------------------------**\
  707. <summary>Command_AddAltNamed</summary>
  708. <param name="function[]">The function this is an alternate to.</param>
  709. <param name="altname[]">The new name.</param>
  710. <returns>
  711. -
  712. </returns>
  713. <remarks>
  714. Add an alternate command for an existing command.
  715. native Command_AddAltNamed(function[], altname[]);
  716. </remarks>
  717. \**--------------------------------------------------------------------------**/
  718. foreign Command_AddAltNamed(string:function[], string:altname[]);
  719. global Command_AddAltNamed(string:function[], string:altname[])
  720. {
  721. return Command_AddAlt(Command_Find(function), altname);
  722. }
  723. /**--------------------------------------------------------------------------**\
  724. <summary>Command_Remove</summary>
  725. <param name="func">The slot of the command to remove.</param>
  726. <returns>
  727. -
  728. </returns>
  729. <remarks>
  730. native Command_Remove(func);
  731. </remarks>
  732. \**--------------------------------------------------------------------------**/
  733. foreign void:Command_Remove(func);
  734. global void:Command_Remove(func)
  735. {
  736. // Annoyingly, this is actually better with "HashMap_RemoveKey", but then we
  737. // don't have the ID for later use.
  738. if (HashMap_RemoveValue(YSI_g_sCommandMap, func))
  739. {
  740. #if defined Y_COMMANDS_USE_CHARS
  741. Command_FlushPrefixes(_Command_GetPrefix(func)),
  742. #endif
  743. YSI_g_sCommands[func][E_COMMAND_POINTER] = -1;
  744. }
  745. }
  746. /**--------------------------------------------------------------------------**\
  747. <summary>Command_RemoveNamed</summary>
  748. <param name="func">The name of the command to remove.</param>
  749. <returns>
  750. -
  751. </returns>
  752. <remarks>
  753. native Command_RemoveNamed(string:func[]);
  754. </remarks>
  755. \**--------------------------------------------------------------------------**/
  756. foreign void:Command_RemoveNamed(string:func[]);
  757. global void:Command_RemoveNamed(string:func[])
  758. {
  759. Command_Remove(Command_Find(func));
  760. }
  761. /**--------------------------------------------------------------------------**\
  762. <summary>Command_IsValid</summary>
  763. <param name="command">Command to get for.</param>
  764. <returns>
  765. Is this command ID valid?
  766. </returns>
  767. <remarks>
  768. -
  769. </remarks>
  770. \**--------------------------------------------------------------------------**/
  771. foreign bool:Command_IsValid(cmd);
  772. global bool:Command_IsValid(cmd)
  773. {
  774. return _Command_IsValid(cmd);
  775. }
  776. /**--------------------------------------------------------------------------**\
  777. <summary>Command_GetCurrent</summary>
  778. <returns>
  779. The command currently being processed, or "COMMAND_NOT_FOUND".
  780. </returns>
  781. <remarks>
  782. -
  783. </remarks>
  784. \**--------------------------------------------------------------------------**/
  785. foreign Command_GetCurrent();
  786. global Command_GetCurrent()
  787. {
  788. return YSI_g_sCurrentID;
  789. }
  790. /**--------------------------------------------------------------------------**\
  791. <summary>Command_GetPlayerCommandCount</summary>
  792. <param name="playerid">Player to count for.</param>
  793. <returns>
  794. -
  795. </returns>
  796. <remarks>
  797. Gets the number of comamnds this player can use.
  798. native Command_GetPlayerCommandCount(playerid);
  799. </remarks>
  800. \**--------------------------------------------------------------------------**/
  801. foreign Command_GetPlayerCommandCount(playerid);
  802. global Command_GetPlayerCommandCount(playerid)
  803. {
  804. P:2("Command_GetPlayerCommandCount called: %i", playerid);
  805. new
  806. slot = PA_Slot(playerid),
  807. Bit:mask = PA_Mask(playerid),
  808. count = 0;
  809. for (new i = 0; i != YSI_g_sHighestID; ++i)
  810. {
  811. if (_Command_IsActive(i) && YSI_g_sCommands[i][E_COMMAND_USERS][slot] & mask)
  812. {
  813. ++count;
  814. }
  815. }
  816. return count;
  817. }
  818. /*
  819. 888b 88
  820. 8888b 88
  821. 88 `8b 88
  822. 88 `8b 88 ,adPPYYba, 88,dPYba,,adPYba, ,adPPYba, ,adPPYba,
  823. 88 `8b 88 "" `Y8 88P' "88" "8a a8P_____88 I8[ ""
  824. 88 `8b 88 ,adPPPPP88 88 88 88 8PP""""""" `"Y8ba,
  825. 88 `8888 88, ,88 88 88 88 "8b, ,aa aa ]8I
  826. 88 `888 `"8bbdP"Y8 88 88 88 `"Ybbd8"' `"YbbdP"'
  827. */
  828. /**--------------------------------------------------------------------------**\
  829. <summary>Command_GetName</summary>
  830. <param name="f">Command to get the name of.</param>
  831. <returns>
  832. -
  833. </returns>
  834. <remarks>
  835. native Command_GetName(funcid);
  836. </remarks>
  837. \**--------------------------------------------------------------------------**/
  838. foreign string:Command_GetName(f);
  839. global string:Command_GetName(f)
  840. {
  841. YSI_g_sReturnBuffer[0] = '\0';
  842. if (_Command_IsValid(f)) strunpack(YSI_g_sReturnBuffer, Command_Name(f));
  843. return YSI_g_sReturnBuffer;
  844. }
  845. /**--------------------------------------------------------------------------**\
  846. <summary>Command_GetDisplay</summary>
  847. <param name="f">Command to get the real name of.</param>
  848. <param name="p">Player to get the name for.</param>
  849. <returns>
  850. The name of a command for a single player.
  851. </returns>
  852. <remarks>
  853. Abstracted because there's a crash when chain returning a string from a
  854. foreign function (see "Command_GetDisplayNamed").
  855. native Command_GetDisplay(funcid, playerid);
  856. </remarks>
  857. \**--------------------------------------------------------------------------**/
  858. foreign string:Command_GetDisplay(funcid, playerid);
  859. global string:Command_GetDisplay(funcid, playerid)
  860. {
  861. return
  862. _Command_GetDisplay(funcid, playerid),
  863. YSI_g_sReturnBuffer;
  864. }
  865. /**--------------------------------------------------------------------------**\
  866. <summary>Command_GetDisplayNamed</summary>
  867. <param name="f[]">Command to get the real name of.</param>
  868. <param name="p">Player to get the name for.</param>
  869. <returns>
  870. The name of a named function for one player.
  871. </returns>
  872. <remarks>
  873. Remote function call for Command_GetDisplayNameNamed - avoids needing to
  874. expose users to the master system's odd way of returning strings. This is
  875. the only part I've not yet fixed up to be nice and hidden.
  876. native string:Command_GetDisplayNamed(string:funcid[], playerid);
  877. </remarks>
  878. \**--------------------------------------------------------------------------**/
  879. foreign string:Command_GetDisplayNamed(string:func[], playerid);
  880. global string:Command_GetDisplayNamed(string:func[], playerid)
  881. {
  882. return
  883. _Command_GetDisplay(Command_Find(func), playerid),
  884. YSI_g_sReturnBuffer;
  885. }
  886. /**--------------------------------------------------------------------------**\
  887. <summary>Command_GetNext</summary>
  888. <param name="index">Index of the next command for this player.</param>
  889. <param name="playerid">Player to get the name for.</param>
  890. <returns>
  891. The name of a command for a single player.
  892. </returns>
  893. <remarks>
  894. -
  895. native Command_GetNext(index, playerid);
  896. </remarks>
  897. \**--------------------------------------------------------------------------**/
  898. foreign string:Command_GetNext(index, playerid);
  899. global string:Command_GetNext(index, playerid)
  900. {
  901. P:2("Command_GetNext called: %i, %i", index, playerid);
  902. YSI_g_sReturnBuffer[0] = '\0';
  903. if (0 <= index < YSI_g_sHighestID)
  904. {
  905. // Don't recalculate this every loop.
  906. new
  907. slot = PA_Slot(playerid),
  908. Bit:mask = PA_Mask(playerid);
  909. for (new i = 0; i != YSI_g_sHighestID; ++i)
  910. {
  911. if (_Command_IsActive(i) && YSI_g_sCommands[i][E_COMMAND_USERS][slot] & mask)
  912. {
  913. // Skip already displayed ones.
  914. if (index)
  915. {
  916. --index;
  917. }
  918. else
  919. {
  920. strunpack(YSI_g_sReturnBuffer, Command_Name(i));
  921. return YSI_g_sReturnBuffer;
  922. }
  923. }
  924. }
  925. }
  926. return YSI_g_sReturnBuffer;
  927. }
  928. /*
  929. 88
  930. 88 ,d ,d
  931. 88 88 88
  932. 88 MM88MMM ,adPPYba, 8b,dPPYba, ,adPPYYba, MM88MMM ,adPPYba, 8b,dPPYba, ,adPPYba,
  933. 88 88 a8P_____88 88P' "Y8 "" `Y8 88 a8" "8a 88P' "Y8 I8[ ""
  934. 88 88 8PP""""""" 88 ,adPPPPP88 88 8b d8 88 `"Y8ba,
  935. 88 88, "8b, ,aa 88 88, ,88 88, "8a, ,a8" 88 aa ]8I
  936. 88 "Y888 `"Ybbd8"' 88 `"8bbdP"Y8 "Y888 `"YbbdP"' 88 `"YbbdP"'
  937. */
  938. /**--------------------------------------------------------------------------**\
  939. <summary>Command</summary>
  940. <param name="start">Last value.</param>
  941. <returns>
  942. The next command.
  943. </returns>
  944. <remarks>
  945. Internal implementation of the "Command()" iterator for "foreach". Returns
  946. all the commands that exist. Normally iterator functions take two
  947. parameters, but this needs only one. Really quite simple, but probably
  948. faster this way as it has access to internal information.
  949. </remarks>
  950. \**--------------------------------------------------------------------------**/
  951. #define Iterator@Command @iterfunc
  952. foreign Iter_Func@Command(start);
  953. global Iter_Func@Command(start)
  954. {
  955. for (new i = start + 1; i < YSI_g_sHighestID; ++i)
  956. {
  957. if (_Command_IsActive(i)) return i;
  958. }
  959. return COMMAND_NOT_FOUND;
  960. }
  961. #define iterstart@Command -1
  962. /**--------------------------------------------------------------------------**\
  963. <summary>PlayerCommand</summary>
  964. <param name="pid">Player to check for.</param>
  965. <param name="start">Last value.</param>
  966. <returns>
  967. The next command.
  968. </returns>
  969. <remarks>
  970. Internal implementation of the "PlayerCommand()" iterator for "foreach".
  971. Returns all the commands this player can use.
  972. This is similar to "Command_GetNext", but returns an ID not a string - I
  973. actually think this way is slightly better.
  974. </remarks>
  975. \**--------------------------------------------------------------------------**/
  976. #define Iterator@PlayerCommand @iterfunc
  977. foreign Iter_Func@PlayerCommand(start, pid);
  978. global Iter_Func@PlayerCommand(start, pid)
  979. {
  980. new
  981. slot = PA_Slot(pid),
  982. Bit:mask = PA_Mask(pid);
  983. for (new i = start + 1; i < YSI_g_sHighestID; ++i)
  984. {
  985. if (_Command_IsActive(i) && YSI_g_sCommands[i][E_COMMAND_USERS][slot] & mask) return i;
  986. }
  987. return COMMAND_NOT_FOUND;
  988. }
  989. #define iterstart@PlayerCommand -1
  990. /*
  991. 88888888ba ad88 88
  992. 88 "8b d8" ""
  993. 88 ,8P 88
  994. 88aaaaaa8P' 8b,dPPYba, ,adPPYba, MM88MMM 88 8b, ,d8 ,adPPYba, ,adPPYba,
  995. 88""""""' 88P' "Y8 a8P_____88 88 88 `Y8, ,8P' a8P_____88 I8[ ""
  996. 88 88 8PP""""""" 88 88 )888( 8PP""""""" `"Y8ba,
  997. 88 88 "8b, ,aa 88 88 ,d8" "8b, "8b, ,aa aa ]8I
  998. 88 88 `"Ybbd8"' 88 88 8P' `Y8 `"Ybbd8"' `"YbbdP"'
  999. */
  1000. /**--------------------------------------------------------------------------**\
  1001. <summary>Command_GetPrefix</summary>
  1002. <param name="c">Command to get.</param>
  1003. <returns>
  1004. The prefix for this command ('/' by default).
  1005. </returns>
  1006. <remarks>
  1007. -
  1008. </remarks>
  1009. \**--------------------------------------------------------------------------**/
  1010. #if defined Y_COMMANDS_USE_CHARS
  1011. foreign Command_GetPrefix(c);
  1012. global Command_GetPrefix(c)
  1013. {
  1014. if (_Command_IsValid(c)) return _Command_GetPrefix(c);
  1015. return '/';
  1016. }
  1017. foreign Command_GetPrefixNamed(string:c[]);
  1018. global Command_GetPrefixNamed(string:c[])
  1019. {
  1020. return Command_GetPrefix(Command_Find(c));
  1021. }
  1022. // Don't compile at all if it is disabled.
  1023. #endif
  1024. /**--------------------------------------------------------------------------**\
  1025. <summary>Command_IsValidPrefix</summary>
  1026. <param name="prefix">Possible prefix character.</param>
  1027. <returns>
  1028. Is this a valid character for a prefix?
  1029. </returns>
  1030. <remarks>
  1031. This is the ONLY place the list of valid prefixes is defined! They are
  1032. symbols, not an alphanumerics, and under 128.
  1033. </remarks>
  1034. \**--------------------------------------------------------------------------**/
  1035. #if defined Y_COMMANDS_USE_CHARS
  1036. stock bool:Command_IsValidPrefix(prefix)
  1037. {
  1038. return (
  1039. '!' <= prefix <= '/' ||
  1040. ':' <= prefix <= '@' ||
  1041. '[' <= prefix <= '`' ||
  1042. '{' <= prefix <= '~');
  1043. }
  1044. #endif
  1045. /**--------------------------------------------------------------------------**\
  1046. <summary>Command_IsPrefixUsed</summary>
  1047. <param name="prefix">Possible prefix character.</param>
  1048. <returns>
  1049. Is this a prefix used for any command?
  1050. </returns>
  1051. <remarks>
  1052. -
  1053. </remarks>
  1054. \**--------------------------------------------------------------------------**/
  1055. #if defined Y_COMMANDS_USE_CHARS
  1056. foreign bool:Command_IsPrefixUsed(prefix);
  1057. global bool:Command_IsPrefixUsed(prefix)
  1058. {
  1059. return Command_IsValidPrefix(prefix) && Bit_Get(YSI_g_sPrefixes, prefix);
  1060. }
  1061. #endif
  1062. /**--------------------------------------------------------------------------**\
  1063. <summary>Command_FlushPrefixes</summary>
  1064. <param name="prefix">Prefix to maybe remove.</param>
  1065. <returns>
  1066. -
  1067. </returns>
  1068. <remarks>
  1069. If one command uses a prefix, then STOPS using said prefix, the global list
  1070. of valid prefixes will need to be updated.
  1071. </remarks>
  1072. \**--------------------------------------------------------------------------**/
  1073. #if defined Y_COMMANDS_USE_CHARS
  1074. static stock Command_FlushPrefixes(prefix)
  1075. {
  1076. if (prefix == '/') return 1;
  1077. for (new i = 0; i != YSI_g_sHighestID; ++i)
  1078. {
  1079. if (_Command_IsActive(i) && _Command_GetPrefix(i) == prefix) return 1;
  1080. }
  1081. Bit_Vet(YSI_g_sPrefixes, prefix);
  1082. return 0;
  1083. }
  1084. #endif
  1085. /**--------------------------------------------------------------------------**\
  1086. <summary>Command_SetPrefix</summary>
  1087. <param name="c">Command to set.</param>
  1088. <param name="prefix">First character of the command.</param>
  1089. <returns>
  1090. -
  1091. </returns>
  1092. <remarks>
  1093. Change what command to type "/x" vs "#x" for example.
  1094. </remarks>
  1095. \**--------------------------------------------------------------------------**/
  1096. #if defined Y_COMMANDS_USE_CHARS
  1097. foreign bool:Command_SetPrefix(c, prefix);
  1098. global bool:Command_SetPrefix(c, prefix)
  1099. {
  1100. if (Command_IsValidPrefix(prefix))
  1101. {
  1102. if (_Command_IsValid(c))
  1103. {
  1104. // Set this command's prefix, and add the prefix to the list.
  1105. new
  1106. tmp = YSI_g_sCommands[c][E_COMMAND_PREFIX];
  1107. return
  1108. Bit_Let(YSI_g_sPrefixes, prefix),
  1109. YSI_g_sCommands[c][E_COMMAND_PREFIX] = prefix,
  1110. Command_FlushPrefixes(tmp),
  1111. true;
  1112. }
  1113. }
  1114. return false;
  1115. }
  1116. #endif
  1117. /**--------------------------------------------------------------------------**\
  1118. <summary>Command_SetPrefixNamed</summary>
  1119. <param name="c">Named command to set.</param>
  1120. <param name="prefix">First character of the command.</param>
  1121. <returns>
  1122. -
  1123. </returns>
  1124. <remarks>
  1125. Change what command to type "/x" vs "#x" for example.
  1126. </remarks>
  1127. \**--------------------------------------------------------------------------**/
  1128. #if defined Y_COMMANDS_USE_CHARS
  1129. foreign bool:Command_SetPrefixNamed(string:c[], prefix);
  1130. global bool:Command_SetPrefixNamed(string:c[], prefix)
  1131. {
  1132. return Command_SetPrefix(Command_Find(c), prefix);
  1133. }
  1134. #endif
  1135. /*
  1136. 88 88 88
  1137. 88 88 88
  1138. 88 88 88
  1139. 88aaaaaaaa88 ,adPPYba, ,adPPYba, 88 ,d8 ,adPPYba,
  1140. 88""""""""88 a8" "8a a8" "8a 88 ,a8" I8[ ""
  1141. 88 88 8b d8 8b d8 8888[ `"Y8ba,
  1142. 88 88 "8a, ,a8" "8a, ,a8" 88`"Yba, aa ]8I
  1143. 88 88 `"YbbdP"' `"YbbdP"' 88 `Y8a `"YbbdP"'
  1144. */
  1145. /**--------------------------------------------------------------------------**\
  1146. <summary>Command_OnPlayerText</summary>
  1147. <param name="playerid">Player who typed something.</param>
  1148. <param name="text[]">What they typed.</param>
  1149. <returns>
  1150. 0 - Could not process the command.
  1151. 1 - Called the command.
  1152. </returns>
  1153. <remarks>
  1154. Used to implement alternate command prefixes.
  1155. </remarks>
  1156. \**--------------------------------------------------------------------------**/
  1157. mhook OnPlayerText(playerid, text[])
  1158. {
  1159. // Is this prefix used anywhere?
  1160. if (_Command_IsPrefix(text[0]))
  1161. {
  1162. // The default return for OnPlayerText is opposite OnPlayerCommandText.
  1163. return !Command_ReProcess(playerid, text, 0);
  1164. }
  1165. // Default return, do nothing.
  1166. return 1;
  1167. }
  1168. /**--------------------------------------------------------------------------**\
  1169. <summary>Command_OnPlayerCommandText</summary>
  1170. <param name="playerid">Player who typed a command.</param>
  1171. <param name="cmdtext[]">What they typed.</param>
  1172. <returns>
  1173. 0 - Could not process the command.
  1174. 1 - Called the command.
  1175. </returns>
  1176. <remarks>
  1177. The core of the command processor. Now vsatly simplified.
  1178. This function first finds the command in our hash map. If it exists, it
  1179. checks if the player can use it. If they can, it checks if it is only in
  1180. the current script. If it is it calls it directly, if it isn't it calls it
  1181. using "CallRemoteFunction", which takes in to account master states in
  1182. multiple scripts and the special master 23, which calls it in only one
  1183. other script.
  1184. </remarks>
  1185. \**--------------------------------------------------------------------------**/
  1186. mhook OnPlayerCommandText(playerid, cmdtext[])
  1187. {
  1188. // An interesting side-effect of this code is that, in theory, hacks that
  1189. // submit commands without the "/" will still work. Or you could hook
  1190. // "OnPlayerText" to call "Command_ReProcess" and still work as well.
  1191. return Command_ReProcess(playerid, cmdtext, 0);
  1192. }
  1193. /**--------------------------------------------------------------------------**\
  1194. <summary>OnScriptInit</summary>
  1195. <returns>
  1196. -
  1197. </returns>
  1198. <remarks>
  1199. Add all local commands in to the system.
  1200. </remarks>
  1201. \**--------------------------------------------------------------------------**/
  1202. hook OnScriptInit()
  1203. {
  1204. P:1("Command_OnScriptInit called");
  1205. #if YSIM_NOT_CLIENT
  1206. #if defined Y_COMMANDS_USE_CHARS
  1207. Bit_SetAll(YSI_g_sPrefixes, false),
  1208. Bit_Let(YSI_g_sPrefixes, '/'),
  1209. #endif
  1210. HashMap_Init(YSI_g_sCommandMap, YSI_g_sCommands, E_COMMAND_HASH_MAP);
  1211. for (new func = 0; func != MAX_COMMANDS; ++func)
  1212. {
  1213. YSI_g_sCommands[func][E_COMMAND_POINTER] = -1;
  1214. }
  1215. #endif
  1216. P:2("_Command_DoInit <> called");
  1217. new
  1218. entry,
  1219. buffer[32 char],
  1220. idx,
  1221. id2;
  1222. P:5("Command_OnScriptInit: Pre-loop");
  1223. while ((idx = AMX_GetPublicEntryPrefix(idx, entry, _A<@yC_>)))
  1224. {
  1225. // Add the command name and pointer, but skip the leading "@yC_".
  1226. P:6("Command_OnScriptInit: Adding %d", entry);
  1227. AMX_ReadString(AMX_BASE_ADDRESS + AMX_Read(entry + 4), buffer),
  1228. buffer[0] = ('@' << 24) | ('_' << 16) | ('y' << 08) | ('C' << 00),
  1229. id2 = funcidx(buffer);
  1230. if (id2 != -1) entry = AMX_HEADER_PUBLICS + id2 * 8;
  1231. // Check that the function name is all lower-case.
  1232. for (id2 = 4; buffer{id2}; ++id2)
  1233. {
  1234. if (buffer{id2} != tolower(buffer{id2})) P:E("Commands must be in lower-case in your source code.");
  1235. }
  1236. // Add the command regardless.
  1237. Command_Add(buffer[1], AMX_Read(entry));
  1238. P:6("Command_OnScriptInit: Name %s", unpack(buffer[1]));
  1239. }
  1240. if (funcidx(YSI_gscOPCR) != -1) Command_IncOPCR();
  1241. if (funcidx(YSI_gscOPCP) != -1) Command_IncOPCP();
  1242. }
  1243. /**--------------------------------------------------------------------------**\
  1244. <summary>HANDOFF</summary>
  1245. <returns>
  1246. -
  1247. </returns>
  1248. <remarks>
  1249. Passes additional commands data to the new master.
  1250. </remarks>
  1251. \**--------------------------------------------------------------------------**/
  1252. HANDOFF()
  1253. {
  1254. P:1("Commands_OnScriptExit <_YCM:p> called");
  1255. // Copy settings.
  1256. DISTRIBUTE(YSI_g_sCommandFlags);
  1257. DISTRIBUTE(YSI_g_sErrorMessages);
  1258. DISTRIBUTE(YSI_g_sDisabledPlayers);
  1259. // This accounts for master IDs.
  1260. DISTRIBUTE(YSI_g_sCommands);
  1261. // Now we've sent over the remaining valid commands, build the hash map.
  1262. _Command_Rebuild();
  1263. }
  1264. /**--------------------------------------------------------------------------**\
  1265. <summary>OnScriptExit</summary>
  1266. <returns>
  1267. -
  1268. </returns>
  1269. <remarks>
  1270. When a script ends, update the status of any new callback hooks.
  1271. </remarks>
  1272. \**--------------------------------------------------------------------------**/
  1273. hook OnScriptExit()
  1274. {
  1275. P:1("Commands_OnScriptExit <> called");
  1276. if (funcidx(YSI_gscOPCR) != -1) Command_DecOPCR();
  1277. if (funcidx(YSI_gscOPCP) != -1) Command_DecOPCP();
  1278. }
  1279. #if YSIM_HAS_MASTER
  1280. mhook OnMasterSystemClose(id)
  1281. {
  1282. new
  1283. cmdname[32 char] = {('@' << 24) | ('_' << 16) | ('y' << 08) | ('C' << 00), 0, 0, ...},
  1284. Bit:cur = Bit:(1 << Master_ID()),
  1285. Bit:bit = Bit:(1 << id),
  1286. Bit:rem = ~bit;
  1287. for (new i = 0; i != MAX_COMMANDS; ++i)
  1288. {
  1289. if (_Command_IsActive(i))
  1290. {
  1291. if (YSI_g_sMasterData[i] == bit)
  1292. {
  1293. // Only existed in one (other) script. Remove it.
  1294. Command_Remove(i);
  1295. }
  1296. if ((YSI_g_sMasterData[i] &= rem) == cur)
  1297. {
  1298. if(YSI_g_sCommands[i][E_COMMAND_POINTER]&cellmin) continue;
  1299. // Existed in multiple, now only one.
  1300. strpack(cmdname[1], Command_Name(i), 31);
  1301. new
  1302. id2 = funcidx(cmdname);
  1303. if (id2 == -1)
  1304. {
  1305. P:W("Command marked EXCLUSIVE, but doesn't exist");
  1306. Command_Remove(i);
  1307. continue;
  1308. }
  1309. // Store the new pointer.
  1310. YSI_g_sCommands[i][E_COMMAND_POINTER] = AMX_Read(AMX_HEADER_PUBLICS + id2 * 8);
  1311. }
  1312. }
  1313. }
  1314. return 1;
  1315. }
  1316. #endif
  1317. /*
  1318. 88 88
  1319. 88 ,d 88
  1320. 88 88 88
  1321. 88 8b,dPPYba, MM88MMM ,adPPYba, 8b,dPPYba, 8b,dPPYba, ,adPPYYba, 88
  1322. 88 88P' `"8a 88 a8P_____88 88P' "Y8 88P' `"8a "" `Y8 88
  1323. 88 88 88 88 8PP""""""" 88 88 88 ,adPPPPP88 88
  1324. 88 88 88 88, "8b, ,aa 88 88 88 88, ,88 88
  1325. 88 88 88 "Y888 `"Ybbd8"' 88 88 88 `"8bbdP"Y8 88
  1326. */
  1327. /**--------------------------------------------------------------------------**\
  1328. <summary>_Command_Rebuild</summary>
  1329. <returns>
  1330. -
  1331. </returns>
  1332. <remarks>
  1333. Rebuilds the hashmap of command pointers after a master script hands off.
  1334. </remarks>
  1335. \**--------------------------------------------------------------------------**/
  1336. foreign void:_Command_Rebuild();
  1337. global void:_Command_Rebuild()
  1338. {
  1339. new
  1340. cmdname[32 char] = {('@' << 24) | ('_' << 16) | ('y' << 08) | ('C' << 00), 0, 0, ...};
  1341. HashMap_Init(YSI_g_sCommandMap, YSI_g_sCommands, E_COMMAND_HASH_MAP);
  1342. for (new i = 0; i != MAX_COMMANDS; ++i)
  1343. {
  1344. #if defined Y_COMMANDS_USE_CHARS
  1345. Bit_SetAll(YSI_g_sPrefixes, false),
  1346. Bit_Let(YSI_g_sPrefixes, '/');
  1347. #endif
  1348. if (_Command_IsActive(i))
  1349. {
  1350. #if defined Y_COMMANDS_USE_CHARS
  1351. Bit_Let(YSI_g_sPrefixes, _Command_GetPrefix(i)),
  1352. #endif
  1353. strpack(cmdname[1], Command_Name(i), 31),
  1354. HashMap_Add(YSI_g_sCommandMap, cmdname[1], i),
  1355. YSI_g_sHighestID = i + 1;
  1356. }
  1357. }
  1358. }
  1359. /**--------------------------------------------------------------------------**\
  1360. <summary>Command_IncOPCR</summary>
  1361. <returns>
  1362. -
  1363. </returns>
  1364. <remarks>
  1365. This function, and the three other related ones, increment and decrement the
  1366. number of callbacks known to exist on the server. If they are 0, there's no
  1367. point trying to call them on errors etc.
  1368. </remarks>
  1369. \**--------------------------------------------------------------------------**/
  1370. foreign void:Command_IncOPCR();
  1371. global void:Command_IncOPCR()
  1372. {
  1373. P:2("Command_IncOPCR called");
  1374. YSI_g_sCommandFlags += e_COMM_FLAG_OPCR_ADD;
  1375. }
  1376. foreign void:Command_DecOPCR();
  1377. global void:Command_DecOPCR()
  1378. {
  1379. P:2("Command_DecOPCR called");
  1380. YSI_g_sCommandFlags -= e_COMM_FLAG_OPCR_ADD;
  1381. }
  1382. foreign void:Command_IncOPCP();
  1383. global void:Command_IncOPCP()
  1384. {
  1385. P:2("Command_IncOPCP called");
  1386. YSI_g_sCommandFlags += e_COMM_FLAG_OPCP_ADD;
  1387. }
  1388. foreign void:Command_DecOPCP();
  1389. global void:Command_DecOPCP()
  1390. {
  1391. P:2("Command_DecOPCP called");
  1392. YSI_g_sCommandFlags -= e_COMM_FLAG_OPCP_ADD;
  1393. }
  1394. /**--------------------------------------------------------------------------**\
  1395. <summary>Command_GetEmptySlot</summary>
  1396. <returns>
  1397. The first available slot in "YSI_g_sCommands".
  1398. </returns>
  1399. <remarks>
  1400. -
  1401. </remarks>
  1402. \**--------------------------------------------------------------------------**/
  1403. static stock Command_GetEmptySlot()
  1404. {
  1405. for (new i = 0; i != MAX_COMMANDS; ++i)
  1406. {
  1407. // No name for the command, can't exist.
  1408. if (_Command_IsEmptySlot(i)) return i;
  1409. }
  1410. return COMMAND_NOT_FOUND;
  1411. }
  1412. /**--------------------------------------------------------------------------**\
  1413. <summary>Command_Add</summary>
  1414. <param name="cmd[]">The command name to add.</param>
  1415. <param name="ptr">The command's pointer.</param>
  1416. <param name="id">Where to store the command (default -1 = find).</param>
  1417. <returns>
  1418. The command's ID.
  1419. </returns>
  1420. <remarks>
  1421. This was an external API function, but there is no reason for it to be as it
  1422. is called for all found commands at mode start.
  1423. </remarks>
  1424. \**--------------------------------------------------------------------------**/
  1425. @foreign Command_Add(string:cmd[], ptr);
  1426. @global Command_Add(string:cmd[], ptr)
  1427. {
  1428. // The commands all need to be stored packed.
  1429. strpack(cmd, cmd, cellmax);
  1430. P:2("Command_Add: %s, %d", unpack(cmd), ptr);
  1431. new
  1432. id = Command_Find(cmd);
  1433. P:5("Command_Add: found %d", id);
  1434. if (id == COMMAND_NOT_FOUND)
  1435. {
  1436. // Command doesn't already exist, add it.
  1437. if ((id = Command_GetEmptySlot()) == COMMAND_NOT_FOUND)
  1438. {
  1439. P:E("Could not add command to array.");
  1440. return COMMAND_NOT_FOUND;
  1441. }
  1442. #if defined Y_COMMANDS_USE_CHARS
  1443. YSI_g_sCommands[id][E_COMMAND_PREFIX] = '/',
  1444. #endif
  1445. YSI_g_sHighestID = max(YSI_g_sHighestID, id + 1),
  1446. // Save the command's pointer.
  1447. YSI_g_sCommands[id][E_COMMAND_POINTER] = ptr,
  1448. // Add this command to the hash map (does the name too).
  1449. HashMap_Add(YSI_g_sCommandMap, cmd, id),
  1450. // Add all players, or defer to y_groups.
  1451. PA_FastInit(YSI_g_sCommands[id][E_COMMAND_USERS]);
  1452. NO_GROUPS(id)
  1453. {
  1454. PA_Init(YSI_g_sCommands[id][E_COMMAND_USERS], true);
  1455. }
  1456. // Add the calling script as having this command.
  1457. MASTER_SET<id>
  1458. }
  1459. else
  1460. {
  1461. // Add this script to the list of providers.
  1462. MASTER_ADD<id>
  1463. }
  1464. return id;
  1465. }
  1466. /**--------------------------------------------------------------------------**\
  1467. <summary>_Command_GetDisplay</summary>
  1468. <param name="f">Command to get the real name of.</param>
  1469. <param name="p">Player to get the name for.</param>
  1470. <returns>
  1471. The name of a command for a single player.
  1472. </returns>
  1473. <remarks>
  1474. -
  1475. </remarks>
  1476. \**--------------------------------------------------------------------------**/
  1477. static stock _Command_GetDisplay(funcid, playerid)
  1478. {
  1479. YSI_g_sReturnBuffer[0] = '\0';
  1480. if (_Command_IsValid(funcid) && VALID_PLAYERID(playerid))
  1481. {
  1482. new
  1483. slot = PA_Slot(playerid),
  1484. Bit:mask = PA_Mask(playerid);
  1485. // Check if they can use the original version.
  1486. if (YSI_g_sCommands[funcid][E_COMMAND_USERS][slot] & mask)
  1487. {
  1488. return strunpack(YSI_g_sReturnBuffer, Command_Name(funcid));
  1489. }
  1490. // BAD REUSE OF THE "playerid" VARIABLE.
  1491. if ((playerid = YSI_g_sCommands[funcid][E_COMMAND_POINTER]) & cellmin)
  1492. {
  1493. // The given function is an alternate version of a real function -
  1494. // test the parent function first.
  1495. // BAD REUSE OF THE "playerid" VARIABLE.
  1496. if (YSI_g_sCommands[(funcid = playerid & ~cellmin)][E_COMMAND_USERS][slot] & mask)
  1497. {
  1498. return strunpack(YSI_g_sReturnBuffer, Command_Name(funcid));
  1499. }
  1500. }
  1501. // Now we have a root command, check all alternates to this one.
  1502. funcid |= cellmin;
  1503. for (new i = 0; i != YSI_g_sHighestID; ++i)
  1504. {
  1505. if (_Command_IsActive(i) && YSI_g_sCommands[i][E_COMMAND_POINTER] == funcid && (YSI_g_sCommands[i][E_COMMAND_USERS][slot] & mask))
  1506. {
  1507. return strunpack(YSI_g_sReturnBuffer, Command_Name(i));
  1508. }
  1509. }
  1510. }
  1511. return 0;
  1512. }