impl.inc 55 KB

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