2
0

RPFW.0.0a Build 4.pwn 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387
  1. // Developers notes
  2. /* # Coding guideline
  3. * Add +1 at the end of array declarations to visually account for null bit.
  4. * Always log first.
  5. * Always notify discord last.
  6. * Always wait for other queries to finish before initiating one: "sql_wait(sqlHandle); // Wait for other queries to finish"
  7. * Delete GVar strings as soon as possible, but at least before the player quits or the gamemode exits, as per: https://forum.sa-mp.com/showthread.php?t=151076
  8. */
  9. /* # Style guide
  10. * GLOBAL_CONSTANT
  11. * local_variable
  12. * someFunction
  13. * SomeClass
  14. */
  15. /* # Adding a discord bot to your guild.
  16. https://discordapp.com/oauth2/authorize?client_id=%CLIENT_ID%&scope=bot&permissions=3072
  17. Where %CLEINT_ID% is the client id.
  18. */
  19. /* # To do:
  20. * Add geoiplib, to make connect messages fancier, and add a locate command.
  21. * Show underscores as spaces and convert spaces in changename input to undescores before regex chack.
  22. * Add table for bans and kicks.
  23. * Vip limit on characters.
  24. */
  25. /// Global definitions
  26. // Colours
  27. /*
  28. #define COLOR_GREEN 0x33AA33AA
  29. #define COLOR_YELLOW 0xFFFF00AA
  30. #define COLOR_WHITE 0xFFFFFFAA
  31. #define COLOR_BLUE 0x0000BBAA
  32. #define COLOR_LIGHTBLUE 0x33CCFFAA
  33. #define COLOR_ORANGE 0xFF9900AA
  34. #define COLOR_RED 0xAA3333AA
  35. #define COLOR_LIME 0x10F441AA
  36. #define COLOR_NAVY 0x000080AA
  37. #define COLOR_AQUA 0xF0F8FFAA
  38. #define COLOR_CRIMSON 0xDC143CAA
  39. #define COLOR_BISQUE 0xFFE4C4AA
  40. #define COLOR_BLACK 0x000000AA
  41. #define COLOR_CHARTREUSE 0x7FFF00AA
  42. #define COLOR_BROWN 0XA52A2AAA
  43. #define COLOR_CORAL 0xFF7F50AA
  44. #define COLOR_GOLD 0xB8860BAA
  45. #define COLOR_GREENYELLOW 0xADFF2FAA
  46. #define COLOR_INDIGO 0x4B00B0AA
  47. #define COLOR_IVORY 0xFFFF82AA
  48. #define COLOR_LAWNGREEN 0x7CFC00AA
  49. #define COLOR_SEAGREEN 0x20B2AAAA
  50. #define COLOR_SEAGREEN 0x2E8B57AA
  51. #define COLOR_LIMEGREEN 0x32CD32AA //<--- Dark lime
  52. #define COLOR_MIDNIGHTBLUE 0X191970AA
  53. #define COLOR_MAROON 0x800000AA
  54. #define COLOR_OLIVE 0x808000AA
  55. #define COLOR_ORANGERED 0xFF4500AA
  56. #define COLOR_PINK 0xFFC0CBAA // - Light light pink
  57. #define COLOR_SPRINGGREEN 0x00FF7FAA
  58. #define COLOR_TOMATO 0xFF6347AA // - Tomato >:/ sounds wrong lol... well... :P
  59. #define COLOR_YELLOWGREEN 0x9ACD32AA //- like military green
  60. #define COLOR_MEDIUMAQUA 0x83BFBFAA
  61. #define COLOR_MEDIUMMAGENTA 0x8B008BAA // dark magenta ^^
  62. */
  63. #define COLOR_ACTIVEBORDER 0xB4B4B4FF
  64. #define COLOR_ACTIVECAPTION 0x99B4D1FF
  65. #define COLOR_ACTIVECAPTIONTEXT 0x000000FF
  66. #define COLOR_ALICEBLUE 0xF0F8FFFF
  67. #define COLOR_ANTIQUEWHITE 0xFAEBD7FF
  68. #define COLOR_APPWORKSPACE 0xABABABFF
  69. #define COLOR_AQUA 0x00FFFFFF
  70. #define COLOR_AQUAMARINE 0x7FFFD4FF
  71. #define COLOR_AZURE 0xF0FFFFFF
  72. #define COLOR_BEIGE 0xF5F5DCFF
  73. #define COLOR_BISQUE 0xFFE4C4FF
  74. #define COLOR_BLACK 0x000000FF
  75. #define COLOR_BLANCHEDALMOND 0xFFEBCDFF
  76. #define COLOR_BLUE 0x0000FFFF
  77. #define COLOR_BLUEVIOLET 0x8A2BE2FF
  78. #define COLOR_BROWN 0xA52A2AFF
  79. #define COLOR_BURLYWOOD 0xDEB887FF
  80. #define COLOR_BUTTONFACE 0xF0F0F0FF
  81. #define COLOR_BUTTONHIGHLIGHT 0xFFFFFFFF
  82. #define COLOR_BUTTONSHADOW 0xA0A0A0FF
  83. #define COLOR_CADETBLUE 0x5F9EA0FF
  84. #define COLOR_CHARTREUSE 0x7FFF00FF
  85. #define COLOR_CHOCOLATE 0xD2691EFF
  86. #define COLOR_CONTROL 0xF0F0F0FF
  87. #define COLOR_CONTROLDARK 0xA0A0A0FF
  88. #define COLOR_CONTROLDARKDARK 0x696969FF
  89. #define COLOR_CONTROLLIGHT 0xE3E3E3FF
  90. #define COLOR_CONTROLLIGHTLIGHT 0xFFFFFFFF
  91. #define COLOR_CONTROLTEXT 0x000000FF
  92. #define COLOR_CORAL 0xFF7F50FF
  93. #define COLOR_CORNFLOWERBLUE 0x6495EDFF
  94. #define COLOR_CORNSILK 0xFFF8DCFF
  95. #define COLOR_CRIMSON 0xDC143CFF
  96. #define COLOR_CYAN 0x00FFFFFF
  97. #define COLOR_DARKBLUE 0x00008BFF
  98. #define COLOR_DARKCYAN 0x008B8BFF
  99. #define COLOR_DARKGOLDENROD 0xB8860BFF
  100. #define COLOR_DARKGRAY 0xA9A9A9FF
  101. #define COLOR_DARKGREEN 0x006400FF
  102. #define COLOR_DARKKHAKI 0xBDB76BFF
  103. #define COLOR_DARKMAGENTA 0x8B008BFF
  104. #define COLOR_DARKOLIVEGREEN 0x556B2FFF
  105. #define COLOR_DARKORANGE 0xFF8C00FF
  106. #define COLOR_DARKORCHID 0x9932CCFF
  107. #define COLOR_DARKRED 0x8B0000FF
  108. #define COLOR_DARKSALMON 0xE9967AFF
  109. #define COLOR_DARKSEAGREEN 0x8FBC8BFF
  110. #define COLOR_DARKSLATEBLUE 0x483D8BFF
  111. #define COLOR_DARKSLATEGRAY 0x2F4F4FFF
  112. #define COLOR_DARKTURQUOISE 0x00CED1FF
  113. #define COLOR_DARKVIOLET 0x9400D3FF
  114. #define COLOR_DEEPPINK 0xFF1493FF
  115. #define COLOR_DEEPSKYBLUE 0x00BFFFFF
  116. #define COLOR_DEFAULT_CHAT 0xFFFFFFFF
  117. #define COLOR_DESKTOP 0x000000FF
  118. #define COLOR_DIMGRAY 0x696969FF
  119. #define COLOR_DODGERBLUE 0x1E90FFFF
  120. #define COLOR_FIREBRICK 0xB22222FF
  121. #define COLOR_FLBLUE 0x6495EDAA
  122. #define COLOR_FLORALWHITE 0xFFFAF0FF
  123. #define COLOR_FORESTGREEN 0x228B22FF
  124. #define COLOR_GAINSBORO 0xDCDCDCFF
  125. #define COLOR_GHOSTWHITE 0xF8F8FFFF
  126. #define COLOR_GOLD 0xFFD700FF
  127. #define COLOR_GOLDENROD 0xDAA520FF
  128. #define COLOR_GRADIENTACTIVECAPTION 0xB9D1EAFF
  129. #define COLOR_GRADIENTINACTIVECAPTION 0xD7E4F2FF
  130. #define COLOR_GRAY 0x808080FF
  131. #define COLOR_GRAYTEXT 0x808080FF
  132. #define COLOR_GREEN 0x008000FF
  133. #define COLOR_GREENYELLOW 0xADFF2FFF
  134. #define COLOR_GREY 0xAFAFAFAA
  135. #define COLOR_HIGHLIGHT 0x3399FFFF
  136. #define COLOR_HIGHLIGHTTEXT 0xFFFFFFFF
  137. #define COLOR_HONEYDEW 0xF0FFF0FF
  138. #define COLOR_HOTPINK 0xFF69B4FF
  139. #define COLOR_HOTTRACK 0x0066CCFF
  140. #define COLOR_INACTIVEBORDER 0xF4F7FCFF
  141. #define COLOR_INACTIVECAPTION 0xBFCDDBFF
  142. #define COLOR_INACTIVECAPTIONTEXT 0x434E54FF
  143. #define COLOR_INDIANRED 0xCD5C5CFF
  144. #define COLOR_INDIGO 0x4B0082FF
  145. #define COLOR_INFO 0xFFFFE1FF
  146. #define COLOR_INFOTEXT 0x000000FF
  147. #define COLOR_IVORY 0xFFFFF0FF
  148. #define COLOR_KHAKI 0xF0E68CFF
  149. #define COLOR_LAVENDER 0xE6E6FAFF
  150. #define COLOR_LAVENDERBLUSH 0xFFF0F5FF
  151. #define COLOR_LAWNGREEN 0x7CFC00FF
  152. #define COLOR_LEMONCHIFFON 0xFFFACDFF
  153. #define COLOR_LIGHTBLUE 0xADD8E6FF
  154. #define COLOR_LIGHTCORAL 0xF08080FF
  155. #define COLOR_LIGHTCYAN 0xE0FFFFFF
  156. #define COLOR_LIGHTGOLDENRODYELLOW 0xFAFAD2FF
  157. #define COLOR_LIGHTGRAY 0xD3D3D3FF
  158. #define COLOR_LIGHTGREEN 0x90EE90FF
  159. #define COLOR_LIGHTPINK 0xFFB6C1FF
  160. #define COLOR_LIGHTSALMON 0xFFA07AFF
  161. #define COLOR_LIGHTSEAGREEN 0x20B2AAFF
  162. #define COLOR_LIGHTSKYBLUE 0x87CEFAFF
  163. #define COLOR_LIGHTSLATEGRAY 0x778899FF
  164. #define COLOR_LIGHTSTEELBLUE 0xB0C4DEFF
  165. #define COLOR_LIGHTYELLOW 0xFFFFE0FF
  166. #define COLOR_LIME 0x00FF00FF
  167. #define COLOR_LIMEGREEN 0x32CD32FF
  168. #define COLOR_LINEN 0xFAF0E6FF
  169. #define COLOR_MAGENTA 0xFF00FFFF
  170. #define COLOR_MAROON 0x800000FF
  171. #define COLOR_MEDIUMAQUAMARINE 0x66CDAAFF
  172. #define COLOR_MEDIUMBLUE 0x0000CDFF
  173. #define COLOR_MEDIUMORCHID 0xBA55D3FF
  174. #define COLOR_MEDIUMPURPLE 0x9370DBFF
  175. #define COLOR_MEDIUMSEAGREEN 0x3CB371FF
  176. #define COLOR_MEDIUMSLATEBLUE 0x7B68EEFF
  177. #define COLOR_MEDIUMSPRINGGREEN 0x00FA9AFF
  178. #define COLOR_MEDIUMTURQUOISE 0x48D1CCFF
  179. #define COLOR_MEDIUMVIOLETRED 0xC71585FF
  180. #define COLOR_MENU 0xF0F0F0FF
  181. #define COLOR_MENUBAR 0xF0F0F0FF
  182. #define COLOR_MENUHIGHLIGHT 0x3399FFFF
  183. #define COLOR_MENUTEXT 0x000000FF
  184. #define COLOR_MIDNIGHTBLUE 0x191970FF
  185. #define COLOR_MINTCREAM 0xF5FFFAFF
  186. #define COLOR_MISTYROSE 0xFFE4E1FF
  187. #define COLOR_MOCCASIN 0xFFE4B5FF
  188. #define COLOR_NAVAJOWHITE 0xFFDEADFF
  189. #define COLOR_NAVY 0x000080FF
  190. #define COLOR_OLDLACE 0xFDF5E6FF
  191. #define COLOR_OLIVE 0x808000FF
  192. #define COLOR_OLIVEDRAB 0x6B8E23FF
  193. #define COLOR_ORANGE 0xFFA500FF
  194. #define COLOR_ORANGERED 0xFF4500FF
  195. #define COLOR_ORCHID 0xDA70D6FF
  196. #define COLOR_PALEGOLDENROD 0xEEE8AAFF
  197. #define COLOR_PALEGREEN 0x98FB98FF
  198. #define COLOR_PALETURQUOISE 0xAFEEEEFF
  199. #define COLOR_PALEVIOLETRED 0xDB7093FF
  200. #define COLOR_PAPAYAWHIP 0xFFEFD5FF
  201. #define COLOR_PEACHPUFF 0xFFDAB9FF
  202. #define COLOR_PERU 0xCD853FFF
  203. #define COLOR_PINK 0xFFC0CBFF
  204. #define COLOR_PLUM 0xDDA0DDFF
  205. #define COLOR_POWDERBLUE 0xB0E0E6FF
  206. #define COLOR_PURPLE 0x800080FF
  207. #define COLOR_RED 0xFF0000FF
  208. #define COLOR_ROSYBROWN 0xBC8F8FFF
  209. #define COLOR_ROYALBLUE 0x4169E1FF
  210. #define COLOR_SADDLEBROWN 0x8B4513FF
  211. #define COLOR_SALMON 0xFA8072FF
  212. #define COLOR_SANDYBROWN 0xF4A460FF
  213. #define COLOR_SCROLLBAR 0xC8C8C8FF
  214. #define COLOR_SEAGREEN 0x2E8B57FF
  215. #define COLOR_SEASHELL 0xFFF5EEFF
  216. #define COLOR_SIENNA 0xA0522DFF
  217. #define COLOR_SILVER 0xC0C0C0FF
  218. #define COLOR_SKYBLUE 0x87CEEBFF
  219. #define COLOR_SLATEBLUE 0x6A5ACDFF
  220. #define COLOR_SLATEGRAY 0x708090FF
  221. #define COLOR_SNOW 0xFFFAFAFF
  222. #define COLOR_SPRINGGREEN 0x00FF7FFF
  223. #define COLOR_STEELBLUE 0x4682B4FF
  224. #define COLOR_TAN 0xD2B48CFF
  225. #define COLOR_TEAL 0x008080FF
  226. #define COLOR_THISTLE 0xD8BFD8FF
  227. #define COLOR_TOMATO 0xFF6347FF
  228. #define COLOR_TRANSPARENT 0xFFFFFF00
  229. #define COLOR_TURQUOISE 0x40E0D0FF
  230. #define COLOR_VIOLET 0xEE82EEFF
  231. #define COLOR_WHEAT 0xF5DEB3FF
  232. #define COLOR_WHITE 0xFFFFFFFF
  233. #define COLOR_WHITESMOKE 0xF5F5F5FF
  234. #define COLOR_WINDOW 0xFFFFFFFF
  235. #define COLOR_WINDOWFRAME 0x646464FF
  236. #define COLOR_WINDOWTEXT 0x000000FF
  237. #define COLOR_YELLOW 0xFFFF00FF
  238. #define COLOR_YELLOWGREEN 0x9ACD32FF
  239. #define STEALTH_ORANGE 0xFF880000
  240. #define STEALTH_OLIVE 0x66660000
  241. #define STEALTH_GREEN 0x33DD1100
  242. #define STEALTH_PINK 0xFF22EE00
  243. #define STEALTH_BLUE 0x0077BB00
  244. // Color groups
  245. #define COLOR_DEFAULT_COMMAND_OUTPUT 0xFFFFFFFF // White
  246. #define COLOR_NOTICE 0xAFAFAFAA // Grey
  247. #define COLOR_WARNING_MESSAGE 0xFFA500FF // Orange? (Looks more yellow to me)
  248. // SQL datatypes
  249. #define SQL_INTERGER_LENGTH 10
  250. // Log levels
  251. #define LOGLEVEL_CHAT -2
  252. #define LOGLEVEL_COMMAND -1
  253. #define LOGLEVEL_DEBUG 0
  254. #define LOGLEVEL_INFO 1
  255. #define LOGLEVEL_NOTICE 2
  256. #define LOGLEVEL_WARNING 3
  257. #define LOGLEVEL_ERROR 4
  258. #define LOGLEVEL_CRITICAL 5
  259. #define LOGLEVEL_PANIC 6
  260. // Userlevels
  261. enum{ // Noted values as comments, for database reference.
  262. UNREGISTERED_PLAYER, // 0
  263. REGISTERED_PLAYER, // 1
  264. REGULAR_PLAYER, // 2
  265. MODERATOR_CREW, // 3
  266. ADMIN_CREW, // 4
  267. MANAGEMENT_CREW, // 5
  268. FOUNDER_PLAYER // 6
  269. }
  270. // Discord channels
  271. enum{
  272. ECHO_CHANNEL,
  273. MAIN_CHANNEL,
  274. ADMIN_ECHO_CHANNEL,
  275. ADMIN_CHANNEL,
  276. MANAGEMENT_CHANNEL
  277. }
  278. // Dialogs
  279. enum{
  280. DIALOG_CHANGENAME,
  281. DIALOG_REGISTER,
  282. DIALOG_ACCOUNT_CREATED,
  283. DIALOG_LOGIN,
  284. DIALOG_CHANGE_USERNAME,
  285. DIALOG_CHARACTERS,
  286. DIALOG_LOGIN_FAILED
  287. }
  288. // Environment settings THESE SHOULD ALL BE READ FROM A CONFIG FILE!
  289. static bool:scriptDebug = true; // Debug setting
  290. #define MODE_NAME "0.0a Build 4"
  291. #define SERVER_NAME "Bone County RPG"
  292. #define PG_HOST "127.0.0.1"
  293. #define PG_ROLE "rpfw-dev"
  294. #define PG_PASS "1234"
  295. #define PG_DB "rpfw-dev"
  296. #define PG_PORT 5432
  297. #define DISCORD_HOME_GUILD_ID "666077037470941184" // Emerald City Roleplay
  298. #define DISCORD_ECHO_CHANNEL_ID "667396220402270228" // #development
  299. //#define DISCORD_ECHO_CHANNEL_ID "666078187079598080" // #ecrp-echo
  300. #define DISCORD_MAIN_CHANNEL_ID "667396220402270228" // #development
  301. //#define DISCORD_MAIN_CHANNEL_ID "667396220402270228" // #development
  302. #define DISCORD_ADMIN_ECHO_CHANNEL_ID "672841892169383936" // #admin-echo
  303. #define DISCORD_ADMIN_CHANNEL_ID "672841892169383936" // #admins
  304. #define DISCORD_MANAGEMENT_CHANNEL_ID "666091376240361492" // #management
  305. // Game-mode limits
  306. #define MAX_CHARACTERS_PER_USER 20 // Maximum of MAX_CHARACTERS_PER_USER_DIGITS digits (Due to SQL query string length)
  307. #define MAX_CHARACTERS_PER_USER_DIGITS 3
  308. // SQL plugin
  309. new SQL:sqlHandle;
  310. // discord-connector
  311. //static DCC_Guild:homeGuild; // Discord guild controlled by game community.
  312. static DCC_Channel:echoChannel; // Public echo channel.
  313. static DCC_Channel:mainChannel; // Channel for communirty notifications.
  314. static DCC_Channel:adminEchoChannel; // Admin echo channel.
  315. static DCC_Channel:adminChannel; // Channel for admin notifications.
  316. static DCC_Channel:managementChannel; // Channel for management notifications.
  317. DiscordEcho(const message[], messageLevel){ // Write to echo channels.
  318. switch(messageLevel) // Output facilities.
  319. {
  320. case ECHO_CHANNEL: {DiscordSendChannelMessage(echoChannel, message);} // Public echo message.
  321. case MAIN_CHANNEL: {DiscordSendChannelMessage(mainChannel, message);} // Public notification.
  322. case ADMIN_ECHO_CHANNEL: {DiscordSendChannelMessage(adminEchoChannel, message);} // Admin echo message.
  323. case ADMIN_CHANNEL: {DiscordSendChannelMessage(adminChannel, message);} // Admin notification.
  324. case MANAGEMENT_CHANNEL: {DiscordSendChannelMessage(managementChannel, message);} // Management notification.
  325. }
  326. return 0;
  327. }
  328. // Natives
  329. native WP_Hash(buffer[], len, const str[]); // https://forum.sa-mp.com/showthread.php?t=570945
  330. //native Float:loadavg(); // https://forum.sa-mp.com/showthread.php?t=260206 LINUX ONLY
  331. // Includes
  332. #include <a_samp> // https://sa-mp.com
  333. #include <sql> // https://github.com/udan11/samp-plugin-sql (Fastest player in town and only one supporting postgreSQL) | Examples: https://pastebin.com/67y2nq2n https://github.com/udan11/samp-plugin-sql/issues/10
  334. #include <sscanf2> // Newest version: https://github.com/maddinat0r/sscanf/releases | Better readme: https://github.com/Y-Less/sscanf
  335. #include <strlib> // https://github.com/oscar-broman/strlib
  336. #include <Pawn.Regex> // https://github.com/urShadow/Pawn.Regex
  337. #include <Pawn.CMD> // https://github.com/urShadow/Pawn.CMD (Fastest player in town)
  338. #include <gvar> // https://github.com/samp-incognito/samp-gvar-plugin
  339. #include <discord-connector> // https://github.com/maddinat0r/samp-discord-connector (Only player in town)
  340. //#include <streamer> // https://github.com/samp-incognisto/samp-streamer-plugin NOT USED YET
  341. /// Middle-ware
  342. // Logging
  343. Logger(const log_level, const message[]){ // Write to logging facility
  344. // Do not log commands or debug when script debugging is turned off.
  345. if(scriptDebug == false){ // Debug is off
  346. if(log_level == LOGLEVEL_COMMAND || log_level == LOGLEVEL_DEBUG){ // Command or debug message
  347. return 0; // Stop and do not log
  348. }
  349. }
  350. // Messagelevel tag
  351. new human_readable_log_level[8 + 1];
  352. switch(log_level){ // Assign log level.
  353. case LOGLEVEL_CHAT: human_readable_log_level = "chat";
  354. case LOGLEVEL_COMMAND: human_readable_log_level = "command";
  355. case LOGLEVEL_DEBUG: human_readable_log_level = "debug";
  356. case LOGLEVEL_INFO: human_readable_log_level = "info";
  357. case LOGLEVEL_NOTICE: human_readable_log_level = "notice";
  358. case LOGLEVEL_WARNING: human_readable_log_level = "warning";
  359. case LOGLEVEL_ERROR: human_readable_log_level = "error";
  360. case LOGLEVEL_CRITICAL: human_readable_log_level = "critical";
  361. case LOGLEVEL_PANIC: human_readable_log_level = "panic";
  362. }
  363. printf("[%s] %s", human_readable_log_level, message); // Print to STDOUT.
  364. return 0;
  365. }
  366. /*// SQL plugin (BROKEN, look at it again after I have more then a week of experience)
  367. //forward sqlQuery(SQL:handle, query[]);
  368. //public sqlQuery(SQL:handle, query[]){
  369. sqlQuery(SQL:handle, query[]){
  370. //sql_wait(handle); // Wait for all queries to finish.
  371. new Result:result = sql_query(handle, query);
  372. if(sql_error(result)){printf("SQL error");} // Did not work during a test with a faulty statement.
  373. return result;
  374. }*/
  375. // discord-connector
  376. DiscordSendChannelMessage(DCC_Channel:channel, const message[]){
  377. if(scriptDebug){ // Log middle-ware event in debugging mode only
  378. new channel_name[100 + 1]; // Default value from tutorial
  379. DCC_GetChannelName(channel, channel_name);
  380. new logMessage[26 + 100 + 2000 + 1]; // Discord max message length = 2000
  381. format(logMessage, sizeof(logMessage), "Send to discord channel %s: %s", channel_name, message);
  382. Logger(LOGLEVEL_DEBUG, logMessage); // Actually log the message
  383. }
  384. DCC_SendChannelMessage(channel, message); // Call discord-connector DISABLE THIS TO STOP ALL OUTGOING DISCORD MESSAGES
  385. }
  386. /// Game-mode
  387. // Functions
  388. forward changeName(playerid, const name[]);
  389. public changeName(playerid, const name[]){ // Check if name is valid and force change if needed.
  390. // Prevent regex error when comparing against null
  391. if(isempty(name)){ // No name entered
  392. ShowPlayerDialog(playerid, DIALOG_CHANGENAME, DIALOG_STYLE_INPUT, "Character name", "You must enter a name.\n\nExamples:\n Jo_Bo\n Dingle_P._J._Berry\n Jackson_DeForest_Kelley\n MaryJo_Ann_LaFluer", "Change", ""); // Force RP name.
  393. return 0;
  394. }
  395. // Check name
  396. new Regex:r = Regex_New("^[A-Z][a-z]{1,}([A-Z][a-z]{1,})?(_([A-Z][a-z]{1,}([A-Z][a-z]{1,})?|[A-Z]\\.(_[A-Z]\\.)?))?_[A-Z][a-z]{1,}([A-Z][a-z]{1,})?$"); // Regex name filter
  397. new isValidName = Regex_Check(name, r); // Validate name to filter
  398. Regex_Delete(r);
  399. if(!isValidName){ // Invalid role-play name
  400. ShowPlayerDialog(playerid, DIALOG_CHANGENAME, DIALOG_STYLE_INPUT, "Character name", "Please pick a realistic name, separate first and last name with an underscore.\n\nExamples:\n Jo_Bo\n Dingle_P._J._Berry\n Jackson_DeForest_Kelley\n MaryJo_Ann_LaFluer", "Change", ""); // Force RP name.
  401. print("past DIALOG_CHANGENAME");
  402. }
  403. else { // Valid role-play name
  404. // Check for name in use
  405. /*new escaped_name[MAX_PLAYER_NAME + 1]; // TODO should be more, to account for escape characters.
  406. sql_escape_string(sqlHandle, name, escaped_name, sizeof(escaped_name)); // Escape player name BROKEN argument type mismatch on argument 2, but name is a sting...*/
  407. new character_query[999 + MAX_PLAYER_NAME + 1];
  408. format(character_query, sizeof(character_query), "SELECT id FROM character WHERE name = '%s'", name);
  409. new Result:character_result = sql_query(sqlHandle, character_query);
  410. if(sql_num_rows(character_result) > 0){ // Name taken
  411. ShowPlayerDialog(playerid, DIALOG_CHANGENAME, DIALOG_STYLE_INPUT, "Character name", "Name already taken. Please pick an original name.", "Change", ""); // Force RP name.
  412. }
  413. else{ // Name free
  414. // Rename or character creation
  415. if(GetPlayerState(playerid) == PLAYER_STATE_NONE){ // Not spawned: character creation
  416. // Do nothing let continue to spawn, after spawn character will be saved to database.
  417. }
  418. else{ // Spawned: renaming existing character
  419. }
  420. // Inform player & change name
  421. /*if(GetGVarInt("userlevel", playerid) > 1){ // Registered player.
  422. // Get username
  423. new username[MAX_PLAYER_NAME], message[75 + MAX_PLAYER_NAME + 1];
  424. GetGVarString("username", username, sizeof(username), playerid);
  425. // Update or create character name in database
  426. new callback[1];
  427. new Result:result;
  428. new query[51 + MAX_PLAYER_NAME + MAX_PLAYER_NAME + 1];
  429. format(query, sizeof(query), "UPDATE \"user\"(name) VALUES('%s') WHERE username == '%s'", name, username);
  430. sql_wait(sqlHandle); // Wait for other queries to finish
  431. result = sql_query(sqlHandle, query, callback = "", "r");
  432. if(sql_error(result)){ // Untested.
  433. printf("SQL error");
  434. return 0;
  435. }
  436. // Inform user
  437. format(message, sizeof(message), "SERVER: Your username remains unchanged, next time connect as: %s", username);
  438. SendClientMessage(playerid, COLOR_WHITE, message);
  439. }*/
  440. SetPlayerName(playerid, name); // Change name in-game
  441. }
  442. }
  443. return 0;
  444. }
  445. forward register(playerid);
  446. public register(playerid){ // Register player in database
  447. if(GetPlayerState(playerid) == PLAYER_STATE_NONE){ // Not spawned
  448. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "ERROR: You need to have spawned to register.");
  449. }
  450. else{ // Spawned player
  451. // Get username
  452. //new username[MAX_PLAYER_NAME + 1];
  453. //GetGVarString("username", username, sizeof(username), playerid);
  454. if(GetGVarInt("userlevel", playerid) != UNREGISTERED_PLAYER){ // Registered player.
  455. printf("3");
  456. new message[35 + MAX_PLAYER_NAME + 1];
  457. format(message, sizeof(message), "ERROR: You are already registered"); //, %s.",
  458. SendClientMessage(playerid, COLOR_WHITE, message);
  459. }
  460. else{ // Unregistered player.
  461. ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Password", "Please pick a strong and safe password, that you are able to remember.", "Continue", "Cancel"); // Password prompt
  462. }
  463. }
  464. }
  465. forward getIPID(playerid);
  466. public getIPID(playerid){
  467. new player_ip[45 + 1], query[37 + 45 + 1]; // Create varaibles
  468. GetPlayerIp(playerid, player_ip, sizeof(player_ip)); // Polulate player_ip variable
  469. format(query, sizeof(query), "SELECT id FROM ip WHERE address = '%s'", player_ip); // Format query string
  470. new Result:result = sql_query(sqlHandle, query); // Execute query
  471. return sql_get_field_assoc_int(result, "id"); // Get id from result
  472. }
  473. forward kickPlayer(playerid, kickerid, const reason[]);
  474. public kickPlayer(playerid, kickerid, const reason[]){ // Kick a player TODO table for all kicks
  475. new playername[MAX_PLAYER_NAME +1 ], kickername[MAX_PLAYER_NAME +1];
  476. GetPlayerName(playerid, playername, sizeof(playername));
  477. GetPlayerName(kickerid, kickername, sizeof(kickername));
  478. // Notify all players
  479. new message[25 + 4 + MAX_PLAYER_NAME + 120 + 1]; // Max samp chat message length 128 - 8 for "/kick ? ".
  480. format(message, sizeof(message), "* [%i] %s kicked %s, reason: %s", kickerid, kickername, playername, reason);
  481. SendClientMessageToAll(COLOR_NOTICE, message); // Notify all players.
  482. // Inform player (Redundant, but some people are blind or stupid)
  483. new playerMessage[25 + 4 + MAX_PLAYER_NAME + 120 + 1]; // Max samp chat message length 128 - 8 for "/kick ? ".
  484. format(message, sizeof(message), "You have been kicked by %s, reason: %s", kickerName, reason);
  485. SendClientMessage(playerid, COLOR_RED, playerMessage); // Notify player
  486. SetTimerEx("Kick", 50, false, "i", playerid); // Give the message 50 milliseconds to reach the player before kicking.
  487. DiscordEcho(message, ECHO_CHANNEL); // Notify discord public echo
  488. }
  489. forward createCharacterRecord(playerid, id);
  490. public createCharacterRecord(playerid, id){
  491. new playername[MAX_PLAYER_NAME + 1], character_query[94 + 10 + MAX_PLAYER_NAME + 3 + 1];
  492. GetPlayerName(playerid, playername, sizeof(playername));
  493. format(character_query, sizeof(character_query), "INSERT INTO character(character_id, name, skin_id) VALUES(%i, '%s', %i)", id, playername, GetPlayerSkin(playerid));
  494. sql_query(sqlHandle, character_query);
  495. }
  496. forward getUserID(playerid);
  497. public getUserID(playerid){
  498. // SQL escape username
  499. new client_connect_username[MAX_PLAYER_NAME + 1], escaped_username[MAX_PLAYER_NAME + 1]; // TODO escaped_username should be longer to account for escape characters (Also increate database column size!)
  500. GetGVarString("client_connect_username", client_connect_username, sizeof(client_connect_username), playerid); // Get client connect name
  501. sql_escape_string(sqlHandle, client_connect_username, escaped_username, sizeof(escaped_username)); // Escape player name
  502. // Get user ID
  503. new userid_query[47 + MAX_PLAYER_NAME + 1];
  504. format(userid_query, sizeof(userid_query), "SELECT id FROM \"user\" WHERE name = '%s'", escaped_username);
  505. new Result:result = sql_query(sqlHandle, userid_query);
  506. return sql_get_field_assoc_int(result, "id");
  507. }
  508. forward authenticate(playerid);
  509. public authenticate(playerid){
  510. ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Sign in", "Enter your password to log on", "Log in", "Change name"); // Show password confirmation dialog
  511. }
  512. forward characterSelection(playerid);
  513. public characterSelection(playerid){
  514. // If spawned, save current character
  515. if(GetPlayerState(playerid) != PLAYER_STATE_NONE){
  516. savePlayerState(playerid);
  517. }
  518. new id = getUserID(playerid);
  519. new character_query[87 + SQL_INTERGER_LENGTH + MAX_CHARACTERS_PER_USER_DIGITS + 1];
  520. format(character_query, sizeof(character_query), "SELECT id, name FROM character WHERE character_id = '%i' LIMIT %i", id, MAX_CHARACTERS_PER_USER);
  521. new Result:character_result = sql_query(sqlHandle, character_query);
  522. new character_string[1024 + 1], character_array[MAX_CHARACTERS_PER_USER];
  523. for(new i = 0; i < sql_num_rows(character_result); i++){
  524. new name[MAX_PLAYER_NAME + 1];
  525. sql_get_field_assoc_ex(character_result, i, "name", name, sizeof(name));
  526. format(character_string, sizeof(character_string), "%s%s\n", character_string, name);
  527. character_array[i] = sql_get_field_assoc_int_ex(character_result, i, "id");
  528. }
  529. new character_array_string[MAX_CHARACTERS_PER_USER * SQL_INTERGER_LENGTH + 1];
  530. strfrombin(character_array_string, character_array); // Convert array to string for use with GVar
  531. SetGVarString("character_array_string", character_array_string, playerid);
  532. ShowPlayerDialog(playerid, DIALOG_CHARACTERS, DIALOG_STYLE_LIST, "Characters", character_string, "Spawn", "New");
  533. }
  534. forward savePlayerState(playerid);
  535. public savePlayerState(playerid){
  536. new playername[MAX_PLAYER_NAME], escaped_playername[MAX_PLAYER_NAME + 1], character_query[187 + 10 + 3 + 3 + 1 + 16 + 16 + 16 + 16 + MAX_PLAYER_NAME + 1]; // Should be longer to allow for escaped characters
  537. GetPlayerName(playerid, playername, sizeof(playername));
  538. sql_escape_string(sqlHandle, playername, escaped_playername, sizeof(escaped_playername)); // Escape player name
  539. new Float:health, Float:armour, Float:x, Float:y, Float:z, Float:Angle;
  540. // Unrealiable TODO change to gvars
  541. GetPlayerHealth(playerid, health);
  542. GetPlayerArmour(playerid, armour);
  543. GetPlayerPos(playerid, x, y, z);
  544. GetPlayerFacingAngle(playerid, Angle);
  545. format(character_query, sizeof(character_query), "UPDATE character SET (cash, health, armor, jailed, pos_x, pos_y, pos_z, rotation) = ('%i', '%f', '%f', '%i', '%f', '%f', '%f', '%f') WHERE name = '%s'", GetPlayerMoney(playerid), health, armour, 0, x, y, z, Angle, escaped_playername);
  546. sql_query(sqlHandle, character_query);
  547. }
  548. forward deleteAllGVars(playerid);
  549. public deleteAllGVars(playerid){
  550. // Delete GVars as per https://forum.sa-mp.com/showthread.php?t=151076
  551. //DeleteGVar("IPID", playerid); // From OnPlayerConnect
  552. DeleteGVar("client_connect_username", playerid); // From OnPlayerConnect
  553. DeleteGVar("userlevel", playerid); // From OnPlayerConnect
  554. DeleteGVar("hash", playerid); // From DIALOG_REGISTER
  555. DeleteGVar("character_array_string", playerid); // From OnDialogResponse
  556. DeleteGVar("authentication_count", playerid); // From DIALOG_LOGIN
  557. }
  558. // Commands
  559. public OnPlayerCommandPerformed(playerid, cmd[], params[], result, flags)
  560. {
  561. if(result == -1)
  562. {
  563. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "SERVER: Unknown command.");
  564. return 0;
  565. }
  566. return 1;
  567. }
  568. cmd:help(playerid, params[]){
  569. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "SERVER: Ask questions in the chat, on Discord or the forum.\nTo list all commands: /cmds");
  570. return 1;
  571. }
  572. alias:help("h");
  573. cmd:cmds(playerid, params[]){
  574. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "/cmds /my /v /p");
  575. return 0;
  576. }
  577. alias:cmds("commands", "cmd");
  578. cmd:register(playerid, params[]){
  579. register(playerid);
  580. }
  581. cmd:my(playerid, params[]){
  582. // No option specified
  583. if(strlen(params) < 1){
  584. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "Usage: /my <option> <value>");
  585. return SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "Options: account");
  586. }
  587. // Options
  588. if(!strcmp(params, "account", true, 7)){ // /my account
  589. strdel(params, 0, 8); // Remove first 8 characters, "account ", from the string.
  590. // Values
  591. if (isequal(params, "register", .ignorecase = true)){ // /my account register
  592. register(playerid); // Call public register function
  593. }
  594. else if (isequal(params, "characters", .ignorecase = true)){ // /my account characters
  595. characterSelection(playerid);
  596. }
  597. else{ // Invalid value
  598. printf("else isequals register");
  599. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "Usage: /my account <value>");
  600. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "Values: register");
  601. }
  602. }
  603. else{ // Invalid option
  604. printf("else !strcmp account");
  605. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "/my options: account");
  606. }
  607. return 0;
  608. }
  609. cmd:v(playerid, params[]){
  610. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "health/armor [AMOUNT]");
  611. return 1;
  612. }
  613. alias:v("vehicle", "veh");
  614. cmd:p(playerid, params[]){
  615. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "health/armor [AMOUNT]");
  616. return 1;
  617. }
  618. alias:p("player");
  619. public PC_OnInit(){ // TODO
  620. return 1; // Remove this once stuff is in place.
  621. }
  622. // a_samp events
  623. public OnGameModeInit(){
  624. Logger(LOGLEVEL_NOTICE, "* Global game-mode initialization."); // Log event.
  625. // Hardcoded settings
  626. ShowPlayerMarkers(PLAYER_MARKERS_MODE_STREAMED); // Player radar blip markers only visible to nearby players.
  627. // Set mode name
  628. SetGameModeText(SERVER_NAME);
  629. // SQL log level
  630. if(scriptDebug){
  631. sql_debug(LOG_ALL, LOG_ALL); // Log everything everywhere.
  632. }
  633. else{
  634. sql_debug(LOG_INFO, LOG_WARNING); // Loglevel info for file and worning for console.
  635. }
  636. // Connect to database
  637. sqlHandle = SQL:sql_connect(SQL_HANDLER_POSTGRESQL, PG_HOST, PG_ROLE, PG_PASS, PG_DB, PG_PORT);
  638. printf("sqlconnection = %d", _:sqlHandle);
  639. if(!sql_ping(sqlHandle)){
  640. print( " + Database connection" );
  641. }
  642. else{
  643. print( "Database connection failed" );
  644. }
  645. // Initialize Discord channels
  646. //homeGuild = DCC_GetGuildId(DISCORD_HOME_GUILD_ID); // Set home guild ID. NOT NEEDED FOR NOW AND BROKEN
  647. echoChannel = DCC_FindChannelById(DISCORD_ECHO_CHANNEL_ID); // Set main echo channel ID.
  648. mainChannel = DCC_FindChannelById(DISCORD_MAIN_CHANNEL_ID); // Set main notification channel ID.
  649. adminEchoChannel = DCC_FindChannelById(DISCORD_ADMIN_ECHO_CHANNEL_ID); // Set admin echo channel ID.
  650. adminChannel = DCC_FindChannelById(DISCORD_ADMIN_CHANNEL_ID); // Set admin notification channel ID.
  651. managementChannel = DCC_FindChannelById(DISCORD_MANAGEMENT_CHANNEL_ID); // Set management notification channel ID.
  652. // Notify Discord
  653. new message[36 + 22 + 1];
  654. format(message, sizeof(message), "* Global game-mode initialization: v%s", MODE_NAME);
  655. DiscordEcho(message, ECHO_CHANNEL);
  656. //DiscordEcho(message, MAIN_CHANNEL); // Enable after we are stable
  657. // Hobo's with a cane (0 ammo value makes them lose the cane as soon as they switch weapon)
  658. // Only homeless skins, as players should slowly class up in society.
  659. AddPlayerClass(134, -184.7607, 950.5010, 16.7740, 358.3032, 15, 0, 0, 0, 0, false); // Fort Carson West boulevard right curb.
  660. AddPlayerClass(10, -184.7607, 950.5010, 16.7740, 358.3032, 15, 0, 0, 0, 0, false); // Fort Carson West boulevard right curb.
  661. AddPlayerClass(78, 111.0115, 1189.2029, 18.1627, 89.0095, 15, 0, 0, 0, 0, false); // Fort Carson South boulevard left curb.
  662. AddPlayerClass(129, 111.0115, 1189.2029, 18.1627, 89.0095, 15, 0, 0, 0, 0, false); // Fort Carson South boulevard left curb.
  663. AddPlayerClass(162, -109.4227, 1242.4860, 16.8223, 183.5798, 15, 0, 0, 0, 0, false); // Fort Carson East boulevard left curb.
  664. AddPlayerClass(77, -109.4227, 1242.4860, 16.8223, 183.5798, 15, 0, 0, 0, 0, false); // Fort Carson East boulevard left curb.
  665. AddPlayerClass(79, -201.5379, 948.1683, 15.9131, 359.9720, 15, 0, 0, 0, 0, false); // Fort Carson West boulevard left curb.
  666. AddPlayerClass(196, -201.5379, 948.1683, 15.9131, 359.9720, 15, 0, 0, 0, 0, false); // Fort Carson West boulevard left curb.
  667. AddPlayerClass(239, 62.4694, 1205.0531, 18.8153, 89.9380, 15, 0, 0, 0, 0, false); // Fort Carson South boulevard right curb.
  668. AddPlayerClass(89, 62.4694, 1205.0531, 18.8153, 89.9380, 15, 0, 0, 0, 0, false); // Fort Carson South boulevard right curb.
  669. AddPlayerClass(135, -126.0831, 1242.5745, 18.6138, 183.2986, 15, 0, 0, 0, 0, false); // Fort Carson East boulevard right curb.
  670. AddPlayerClass(197, -126.0831, 1242.5745, 18.6138, 183.2986, 15, 0, 0, 0, 0, false); // Fort Carson East boulevard right curb.
  671. return 1;
  672. }
  673. public OnGameModeExit(){
  674. Logger(LOGLEVEL_NOTICE, "* Global game-mode termination."); // Log event
  675. sql_wait(sqlHandle); // Wait for queries to finish.
  676. sql_disconnect(sqlHandle); // Disconnect from database.
  677. DiscordEcho("* Global game-mode termination.", ECHO_CHANNEL); // Notify discord
  678. //DiscordEcho("* Global game-mode termination.", MAIN_CHANNEL); // Enable when stable
  679. // Cycle every player
  680. for(new playerid, a = GetMaxPlayers(); playerid < a; playerid++){
  681. if(IsPlayerConnected(playerid))
  682. {
  683. deleteAllGVars(playerid); // Delete GVars as per https://forum.sa-mp.com/showthread.php?t=151076
  684. // Set name back to username
  685. new client_connect_username[MAX_PLAYER_NAME + 1];
  686. GetGVarString("client_connect_username", client_connect_username, sizeof(client_connect_username), playerid);
  687. SetPlayerName(playerid, client_connect_username); // Change name in-game
  688. }
  689. }
  690. return 1;
  691. }
  692. public OnPlayerRequestClass(playerid, classid){ // Skin selection before spawn.
  693. if(scriptDebug){
  694. new playerName[MAX_PLAYER_NAME], message[21 + MAX_PLAYER_NAME + 1];
  695. GetPlayerName(playerid, playerName, sizeof(playerName));
  696. format(message, sizeof(message), "* [%d] %s Class selection.", playerid, playerName);
  697. Logger(LOGLEVEL_DEBUG, message); // Log event.
  698. }
  699. SetPlayerPos(playerid, -185.5514, 944.2042, 15.9337); // In front of Fort Carson city limits sign.
  700. SetPlayerFacingAngle(playerid, 182.7345); // Charater looks toward the camera.
  701. SetPlayerCameraPos(playerid, -185.5514, 939.0957, 15.6594); // Further in front of the Fort Carson city limits sign.
  702. SetPlayerCameraLookAt(playerid, -185.5514, 944.2042, 15.9337); // In front of Fort Carson city limits sign.
  703. return 1; // Must return one, or skin selection breaks
  704. }
  705. public OnPlayerConnect(playerid){
  706. // Create variables
  707. new playername[MAX_PLAYER_NAME], playerip[45 + 1];
  708. // Populate variables
  709. GetPlayerName(playerid, playername, sizeof(playername));
  710. GetPlayerIp(playerid, playerip, sizeof(playerip));
  711. // Global connection notification
  712. new admin_message[22 + MAX_PLAYER_NAME + 45 + 1];
  713. format(admin_message, sizeof(admin_message), "* [%d] %s (IP:%s) connected.", playerid, playername);
  714. Logger(LOGLEVEL_INFO, admin_message); // Log event
  715. //SendClientMessageToAll(COLOR_NOTICE, adminMessage); // Notify all players. CHANGE TO ADMINS.
  716. DiscordEcho(admin_message, ADMIN_ECHO_CHANNEL); // Notify Discord admin echo.
  717. // Create IP record or update connection attempts
  718. new ip_query[109 + 45 + 1]; // 45 for IPv6
  719. format(ip_query, sizeof(ip_query), "INSERT INTO ip(address) VALUES('%s') ON CONFLICT (address) DO UPDATE SET connections = ip.connections+1", playerip);
  720. sql_query(sqlHandle, ip_query);
  721. // Check if IP is banned
  722. new ban_query[40 + 45 + 1]; // 45 for IPv6
  723. format(ban_query, sizeof(ban_query), "SELECT is_banned FROM ip WHERE address = '%s'", playerip);
  724. new Result:ban_result = sql_query(sqlHandle, ban_query);
  725. new banned = sql_get_field_assoc_int(ban_result, "is_banned");
  726. printf("isbanned: %i", banned);
  727. // TODO kick if IP is banned.
  728. // Get user record
  729. new escaped_username[MAX_PLAYER_NAME + 1], user_query[47 + MAX_PLAYER_NAME + 1]; // Should be longer to allow for escaped characters
  730. sql_escape_string(sqlHandle, playername, escaped_username, sizeof(escaped_username)); // Escape player name
  731. format(user_query, sizeof(user_query), "SELECT id FROM \"user\" WHERE name = '%s'", escaped_username);
  732. //new Result:result = sqlQuery(sqlHandle, user_query); // Middleware broken.
  733. new Result:result = sql_query(sqlHandle, user_query);
  734. SetGVarString("client_connect_username", playername, playerid); // Used by register and DIALOG_LOGIN
  735. if(sql_num_rows(result) == 0){ // Unkown user
  736. SetGVarInt("userlevel", UNREGISTERED_PLAYER, playerid); // Set userlevel unregistered
  737. changeName(playerid, playername); // Check, force and set role-play name
  738. }
  739. else{ // Known user
  740. ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Sign in", "Enter your password to log on", "Log in", "Cancel"); // Show password confirmation dialog
  741. }
  742. return 1;
  743. }
  744. public OnPlayerDisconnect(playerid, reason){
  745. // Save character
  746. savePlayerState(playerid);
  747. deleteAllGVars(playerid); // Delete GVars as per https://forum.sa-mp.com/showthread.php?t=151076
  748. new playername[MAX_PLAYER_NAME], message[39 + 4 + MAX_PLAYER_NAME + 1];
  749. GetPlayerName(playerid, playername, sizeof(playername));
  750. switch(reason)
  751. {
  752. case 0: format(message, sizeof(message), "* [%d] %s disconnected. (Lost Connection)", playerid, playername);
  753. case 1: format(message, sizeof(message), "* [%d] %s disconnected. (Leaving)", playerid, playername);
  754. case 2: format(message, sizeof(message), "* [%d] %s disconnected. (Kicked)", playerid, playername); // Leave this in place for RCON kicks.
  755. }
  756. Logger(LOGLEVEL_INFO, message); // Log event
  757. SendClientMessageToAll(COLOR_NOTICE, message); // Notify all players.
  758. DiscordEcho(message, 0); // Notify discord.
  759. return 1;
  760. }
  761. public OnPlayerSpawn(playerid){
  762. if(scriptDebug){ // Log event in case of debugging.
  763. new playerName[MAX_PLAYER_NAME], message[MAX_PLAYER_NAME + 14 + 1];
  764. GetPlayerName(playerid, playerName, sizeof(playerName));
  765. format(message, sizeof(message), "* [%d] %s spawned.", playerid, playerName);
  766. Logger(LOGLEVEL_DEBUG, message); // Log event.
  767. }
  768. if(GetGVarInt("userlevel", playerid) == UNREGISTERED_PLAYER){ // Unregistered player.
  769. SendClientMessage(playerid, COLOR_DEFAULT_COMMAND_OUTPUT, "SERVER: To reserve your name, save your character, statistics and money, type: /register");
  770. }
  771. return 1;
  772. }
  773. public OnPlayerDeath(playerid, killerid, reason){ // WORK TO FINISH: Paste correct DiscordEcho messages in limetless script.
  774. new playerName[MAX_PLAYER_NAME], message[15 + MAX_PLAYER_NAME + 1];
  775. GetPlayerName(playerid, playerName, sizeof(playerName));
  776. format(message, sizeof(message), "* [%d] %s died.", playerid, playerName); // Add killerid & reason.
  777. Logger(LOGLEVEL_DEBUG, message); // Log event
  778. DiscordEcho(message, 2); // Notify Discord admin echo.
  779. return 1;
  780. }
  781. public OnVehicleSpawn(vehicleid) // TODO for 0.0a
  782. {
  783. return 1;
  784. }
  785. public OnVehicleDeath(vehicleid, killerid) // TODO for 0.0a
  786. {
  787. return 1;
  788. }
  789. public OnPlayerText(playerid, text[]){
  790. new playerName[MAX_PLAYER_NAME], message[5 + MAX_PLAYER_NAME + 128 + 1]; // 128 = samp text input limit.
  791. GetPlayerName(playerid, playerName, sizeof(playerName));
  792. format(message, sizeof(message), "[%d] %s: %s", playerid, playerName, text);
  793. Logger(LOGLEVEL_CHAT, message); // Log event. NOT NEEDED FOR LIMITLESS: limitless already outputs local chat, but not /b /g /pm etc.
  794. DiscordEcho(message, 2); // Notify Discord admins echo.
  795. return 1;
  796. }
  797. public OnPlayerCommandText(playerid, cmdtext[]){
  798. // Make conditional on scriptDebug, but not as long we base for limetless.
  799. new playerName[MAX_PLAYER_NAME], message[5 + 4 + MAX_PLAYER_NAME + 128 + 1]; // 128 = samp text input limit.
  800. GetPlayerName(playerid, playerName, sizeof(playerName));
  801. format(message, sizeof(message), "[%d] %s: %s", playerid, playerName, cmdtext);
  802. Logger(LOGLEVEL_COMMAND, message); // Log event
  803. return 0;
  804. }
  805. public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger) // TODO for 0.0a
  806. {
  807. return 1;
  808. }
  809. public OnPlayerExitVehicle(playerid, vehicleid) // TODO for 0.0a
  810. {
  811. return 1;
  812. }
  813. public OnPlayerStateChange(playerid, newstate, oldstate) // TODO for 0.0a
  814. {
  815. return 1;
  816. }
  817. public OnPlayerEnterCheckpoint(playerid) // TODO for 0.0a
  818. {
  819. return 1;
  820. }
  821. public OnPlayerLeaveCheckpoint(playerid) // TODO for 0.0a
  822. {
  823. return 1;
  824. }
  825. public OnPlayerEnterRaceCheckpoint(playerid) // TODO for 0.0a
  826. {
  827. return 1;
  828. }
  829. public OnPlayerLeaveRaceCheckpoint(playerid) // TODO for 0.0a
  830. {
  831. return 1;
  832. }
  833. public OnRconCommand(cmd[]){
  834. new message[8 + 128 + 1]; // Max samp message legnth = 128.
  835. format(message, sizeof(message), "* RCON: %s", cmd);
  836. Logger(LOGLEVEL_NOTICE, message); // Log event
  837. return 1;
  838. }
  839. public OnPlayerRequestSpawn(playerid){ // After picking a skin in class selection.
  840. if(scriptDebug){ // Only lig spawns when debuggins script.
  841. new playerName[MAX_PLAYER_NAME], message[14 + MAX_PLAYER_NAME + 1];
  842. GetPlayerName(playerid, playerName, sizeof(playerName));
  843. format(message, sizeof(message), "* [%d] %s Chose a character.", playerid, playerName);
  844. Logger(LOGLEVEL_DEBUG, message); // Log event
  845. }
  846. if(GetGVarInt("userlevel", playerid) > UNREGISTERED_PLAYER){ // Registered player
  847. createCharacterRecord(playerid, getUserID(playerid)); // Save character
  848. }
  849. return 1;
  850. }
  851. public OnObjectMoved(objectid) // TODO for 0.0a
  852. {
  853. return 1;
  854. }
  855. public OnPlayerObjectMoved(playerid, objectid) // TODO for 0.0a
  856. {
  857. return 1;
  858. }
  859. public OnPlayerPickUpPickup(playerid, pickupid) // TODO for 0.0a
  860. {
  861. return 1;
  862. }
  863. public OnVehicleMod(playerid, vehicleid, componentid) // TODO for 0.0a
  864. {
  865. return 1;
  866. }
  867. public OnVehiclePaintjob(playerid, vehicleid, paintjobid) // TODO for 0.0a
  868. {
  869. return 1;
  870. }
  871. public OnVehicleRespray(playerid, vehicleid, color1, color2) // TODO for 0.0a
  872. {
  873. return 1;
  874. }
  875. public OnPlayerSelectedMenuRow(playerid, row) // TODO for 0.0a
  876. {
  877. return 1;
  878. }
  879. public OnPlayerExitedMenu(playerid) // TODO for 0.0a
  880. {
  881. return 1;
  882. }
  883. public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid) // TODO for 0.0a
  884. {
  885. return 1;
  886. }
  887. public OnPlayerKeyStateChange(playerid, newkeys, oldkeys) // TODO for 0.0a
  888. {
  889. return 1;
  890. }
  891. public OnRconLoginAttempt(ip[], password[], success){
  892. new message[30 + 45 + 1]; // IPv6 max length = 45
  893. if(success){
  894. format(message, sizeof(message), "* [%s] authenticated via RCON.", ip);
  895. }
  896. else{
  897. format(message, sizeof(message), "* [%s] invalid RCON authentication.", ip);
  898. }
  899. Logger(1, message); // Log event
  900. DiscordEcho(message, 4); // Notify Discord management.
  901. return 1;
  902. }
  903. public OnPlayerUpdate(playerid) // Don't use for now.
  904. {
  905. return 1;
  906. }
  907. public OnPlayerStreamIn(playerid, forplayerid){
  908. new playerName[MAX_PLAYER_NAME], forPlayerName[MAX_PLAYER_NAME], message[30 + 4 + MAX_PLAYER_NAME + 4 + MAX_PLAYER_NAME + 1];
  909. GetPlayerName(playerid, playerName, sizeof(playerName));
  910. GetPlayerName(forplayerid, forPlayerName, sizeof(forPlayerName));
  911. format(message, sizeof(message), "* [%d] %s is now streamed in for [%d] %s.", playerid, playerName, forplayerid, forPlayerName);
  912. Logger(1, message); // Log event
  913. /*format(message, sizeof(message), "[%d] is now streamed in for you.", playerid);
  914. SendClientMessage(forplayerid, 0xFFFFFFFF, string);*/
  915. // Is this when they reach eachoters drawdistance, or when they sync?
  916. return 1;
  917. }
  918. public OnPlayerStreamOut(playerid, forplayerid) // TODO for 0.0a
  919. {
  920. return 1;
  921. }
  922. public OnVehicleStreamIn(vehicleid, forplayerid) // TODO for 0.0a
  923. {
  924. return 1;
  925. }
  926. public OnVehicleStreamOut(vehicleid, forplayerid) // TODO for 0.0a
  927. {
  928. return 1;
  929. }
  930. public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]){
  931. switch(dialogid){
  932. case DIALOG_CHANGENAME:{
  933. changeName(playerid, inputtext);
  934. }
  935. case DIALOG_REGISTER:{
  936. if(response){ // If they clicked 'Yes' or pressed enter
  937. // Check password complexity
  938. new Regex:r = Regex_New("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\\$%\\^&\\*\\(\\)\\-\\_=+[{\\]}\\\\|;:'\",<.>\\/?]).{8,}$"); // Regex password filter
  939. new isSecurePassword = Regex_Check(inputtext, r); // Validate name to filter
  940. Regex_Delete(r);
  941. if(!isSecurePassword){ // Insecure password
  942. ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Insecure password", "Enter a secure password, containg at least:\n\n * A lowercase letter (a-z)\n * A capiral letter (A-Z)\n * A number (0-9).\n * A special character (!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?).\n * 8 characters.", "Register", "Cancel"); // Show password requirements.
  943. }
  944. else { // Secure password
  945. // Hash password
  946. new buffer[129], hash[129];
  947. WP_Hash(buffer, sizeof(buffer), inputtext);
  948. GetGVarString("hash", hash, sizeof(hash), playerid);
  949. if(isempty(hash)){ // First password dialog
  950. SetGVarString("hash", buffer, playerid); // Save password has a GVar
  951. ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Password confirmation", "Repeat your password to confirm.", "Register", "Cancel"); // Show password confirmation dialog
  952. }
  953. else{ // Password confirmation
  954. if(!isequal(hash, buffer)){ // Password does not match confirmation
  955. DeleteGVar("hash", playerid);
  956. SendClientMessage(playerid, COLOR_RED, "ERROR: Password does not match password confirmation.");
  957. ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Password", "Enter the same password twice.", "Continue", "Cancel"); // Password prompt
  958. }
  959. else{ // Password matches confirmation
  960. DeleteGVar("hash", playerid);
  961. new client_connect_username[MAX_PLAYER_NAME + 1], playerIP[45 + 1], escaped_username[MAX_PLAYER_NAME + 1]; // 45 for IPv6
  962. GetGVarString("client_connect_username", client_connect_username, sizeof(client_connect_username), playerid);
  963. GetPlayerIp(playerid, playerIP, sizeof(playerIP));
  964. sql_escape_string(sqlHandle, client_connect_username, escaped_username, sizeof(escaped_username));
  965. // Create user record
  966. new user_query[80 + MAX_PLAYER_NAME + 129 + 45 + 1]; // 45 for IPv6.
  967. format(user_query, sizeof(user_query), "INSERT INTO \"user\"(name, password, last_address) VALUES('%s', '%s', '%s')", escaped_username, hash, playerIP);
  968. //new Result:user_result = sql_query(sqlHandle, user_query);
  969. //new id = sql_insert_id(user_result); // Broken, always returns 0.
  970. // sql_insert_id workaround
  971. sql_query(sqlHandle, user_query);
  972. new id = getUserID(playerid); // Get ID user user record
  973. SetGVarInt("userlevel", REGISTERED_PLAYER, playerid);
  974. // Create user_ip relation table record
  975. new user_ip_query[50 + 10 + 10 + 1 ];
  976. format(user_ip_query, sizeof(user_ip_query), "INSERT INTO user_ip(id, id) VALUES(%i, %i)", getIPID(playerid), id);
  977. sql_query(sqlHandle, user_ip_query);
  978. createCharacterRecord(playerid, id); // Save character
  979. // Inform user
  980. new dialogMessage[135 + MAX_PLAYER_NAME + 1], message[31 + MAX_PLAYER_NAME + 1];
  981. format(dialogMessage, sizeof(dialogMessage), "To login as any of your characters, connect as: \nRemember your username carefully, as you have to use it to connect with SA-MP client!", client_connect_username);
  982. ShowPlayerDialog(playerid, DIALOG_ACCOUNT_CREATED, DIALOG_STYLE_MSGBOX, "Account created", dialogMessage, "Play", "");
  983. format(message, sizeof(message), "SERVER: Remember your username, %s!", client_connect_username);
  984. SendClientMessage(playerid, COLOR_WARNING_MESSAGE, message);
  985. }
  986. }
  987. }
  988. }
  989. else{ // Pressed ESC or clicked cancel
  990. DeleteGVar("hash", playerid);
  991. }
  992. }
  993. case DIALOG_LOGIN:{
  994. if(response){ // If they clicked 'Yes' or pressed enter
  995. // Escape username
  996. new client_connect_username[MAX_PLAYER_NAME + 1], escaped_username[MAX_PLAYER_NAME + 1];
  997. GetGVarString("client_connect_username", client_connect_username, sizeof(client_connect_username), playerid);
  998. sql_escape_string(sqlHandle, client_connect_username, escaped_username, sizeof(escaped_username));
  999. // Get account
  1000. new user_query[71 + MAX_PLAYER_NAME + 1], hash[129];
  1001. format(user_query, sizeof(user_query), "SELECT id, password, level FROM \"user\" WHERE name = '%s'", escaped_username);
  1002. new Result:user_result = sql_query(sqlHandle, user_query);
  1003. sql_get_field_assoc_ex(user_result, 0, "password", hash, sizeof(hash));
  1004. // Compare hashes
  1005. new buffer[128 + 1];
  1006. WP_Hash(buffer, sizeof(buffer), inputtext);
  1007. if (!isequal(buffer, hash)){ // Hashes don't match
  1008. // Brute-force protection
  1009. new authentication_count = GetGVarInt("authentication_count", playerid);
  1010. authentication_count++;
  1011. SetGVarInt("authentication_count", authentication_count, playerid);
  1012. printf("auth count %i", authentication_count);
  1013. ShowPlayerDialog(playerid, DIALOG_LOGIN_FAILED, DIALOG_STYLE_MSGBOX, "Authenticaion failed ", "Invalid password.", "", "");
  1014. switch(authentication_count){
  1015. case 0: SetTimerEx("authenticate", 3000, false, "i", playerid); // Return to authentication workflow in 3 second.
  1016. case 1: SetTimerEx("authenticate", 5000, false, "i", playerid); // Return to authentication workflow in 5 seconds.
  1017. case 2: SetTimerEx("authenticate", 7000, false, "i", playerid); // Return to authentication workflow in 7 seconds.
  1018. default: ShowPlayerDialog(playerid, DIALOG_CHANGE_USERNAME, DIALOG_STYLE_INPUT, "Username", "Pick an unregistered username.", "Change", ""); // After 4 failed authentication attempts
  1019. }
  1020. }
  1021. else{ // Hashes match
  1022. // bankick if banned
  1023. // Set userlevel
  1024. new userlevel = sql_get_field_assoc_int(user_result, "level");
  1025. SetGVarInt("userlevel", userlevel, playerid);
  1026. // Get character names
  1027. characterSelection(playerid);
  1028. /*new id = sql_get_field_assoc_int(user_result, "id");
  1029. new character_query[87 + SQL_INTERGER_LENGTH + MAX_CHARACTERS_PER_USER_DIGITS + 1];
  1030. format(character_query, sizeof(character_query), "SELECT id, name FROM character WHERE character_id = '%i' LIMIT %i", id, MAX_CHARACTERS_PER_USER);
  1031. new Result:character_result = sql_query(sqlHandle, character_query);
  1032. new character_string[1024 + 1], character_array[MAX_CHARACTERS_PER_USER];
  1033. for(new i = 0; i < sql_num_rows(character_result); i++){
  1034. new name[MAX_PLAYER_NAME + 1];
  1035. sql_get_field_assoc_ex(character_result, i, "name", name, sizeof(name));
  1036. format(character_string, sizeof(character_string), "%s%s\n", character_string, name);
  1037. character_array[i] = sql_get_field_assoc_int_ex(character_result, i, "id");
  1038. }
  1039. new character_array_string[MAX_CHARACTERS_PER_USER * SQL_INTERGER_LENGTH + 1];
  1040. strfrombin(character_array_string, character_array); // Convert array to string for use with GVar
  1041. SetGVarString("character_array_string", character_array_string, playerid);
  1042. ShowPlayerDialog(playerid, DIALOG_CHARACTERS, DIALOG_STYLE_LIST, "Characters", character_string, "Spawn", "New");*/
  1043. }
  1044. }
  1045. else{ // Pressed ESC or clicked cancel
  1046. ShowPlayerDialog(playerid, DIALOG_CHANGE_USERNAME, DIALOG_STYLE_INPUT, "Username", "Pick an unregistered username.", "Change", "");
  1047. }
  1048. }
  1049. case DIALOG_CHANGE_USERNAME:{
  1050. if(response){ // If they clicked 'Yes' or pressed enter
  1051. new Regex:r = Regex_New("^[0-9a-zA-Z\\[\\]\\(\\)\\$@._=]{1,}$"); // Regex name filter
  1052. new isValidName = Regex_Check(inputtext, r);
  1053. Regex_Delete(r);
  1054. if(isValidName){ // Valid name
  1055. // check if taken
  1056. // Get user record
  1057. new escaped_username[MAX_PLAYER_NAME + 1], user_query[999 + MAX_PLAYER_NAME + 1];
  1058. sql_escape_string(sqlHandle, inputtext, escaped_username, sizeof(escaped_username)); // Escape player name
  1059. format(user_query, sizeof(user_query), "SELECT id FROM \"user\" WHERE name = '%s'", escaped_username);
  1060. //new Result:result = sqlQuery(sqlHandle, user_query); // Middleware broken.
  1061. new Result:result = sql_query(sqlHandle, user_query);
  1062. if(sql_num_rows(result) > 0){ // Username taken
  1063. ShowPlayerDialog(playerid, DIALOG_CHANGE_USERNAME, DIALOG_STYLE_INPUT, "Username", "Pick an unregistered username.", "Change", "");
  1064. }
  1065. else{ // Username free
  1066. SetGVarString("client_connect_username", inputtext, playerid); // Used by register and DIALOG_LOGIN
  1067. SetPlayerName(playerid, inputtext); // Change name in-game
  1068. return 1; // Do nothing to let player continue class selection
  1069. }
  1070. }
  1071. else{ // Invalid name
  1072. ShowPlayerDialog(playerid, DIALOG_CHANGE_USERNAME, DIALOG_STYLE_INPUT, "Username", "Invalid name. Names may only contain numbers (0-9), letters (a-z) & (A-Z) and special characters ([]()$@._=)", "Change", "");
  1073. }
  1074. }
  1075. else{ // Pick another username
  1076. ShowPlayerDialog(playerid, DIALOG_CHANGE_USERNAME, DIALOG_STYLE_INPUT, "Username", "Pick an unregistered username.", "Change", "");
  1077. }
  1078. }
  1079. case DIALOG_CHARACTERS:{
  1080. if(response){ // Spawn as character
  1081. new character_array_string[MAX_CHARACTERS_PER_USER * SQL_INTERGER_LENGTH + 1], character_array[MAX_CHARACTERS_PER_USER];
  1082. GetGVarString("character_array_string", character_array_string, sizeof(character_array_string), playerid);
  1083. DeleteGVar("character_array_string");
  1084. strtobin(character_array, character_array_string);
  1085. new character_query[999 + MAX_PLAYER_NAME + 1], name[24 + 1];
  1086. format(character_query, sizeof(character_query), "SELECT name, skin_id, cash, armor, health, pos_x, pos_y, pos_z, rotation FROM character WHERE id = '%i'", character_array[listitem]);
  1087. new Result:character_result = sql_query(sqlHandle, character_query);
  1088. sql_get_field_assoc(character_result, "name", name, sizeof(name));
  1089. new skin_id = sql_get_field_assoc_int(character_result, "skin_id");
  1090. new cash = sql_get_field_assoc_int(character_result, "cash");
  1091. new armor = sql_get_field_assoc_int(character_result, "armor");
  1092. new health = sql_get_field_assoc_int(character_result, "health");
  1093. new pos_x = sql_get_field_assoc_int(character_result, "pos_x");
  1094. new pos_y = sql_get_field_assoc_int(character_result, "pos_y");
  1095. new pos_z = sql_get_field_assoc_int(character_result, "pos_z");
  1096. new rotation = sql_get_field_assoc_int(character_result, "rotation");
  1097. SetSpawnInfo(playerid, 0, skin_id, pos_x, pos_y, pos_z, rotation, 0, 0, 0, 0, 0, 0);
  1098. //SetSpawnInfo(playerid, 0, skin_id, -144.0328, 1225.0564, 19.8992, 175.5507, 0, 0, 0, 0, 0, 0 );
  1099. SetPlayerName(playerid, name); // Change name in-game
  1100. SpawnPlayer(playerid);
  1101. }
  1102. else{ // New character
  1103. // character cap
  1104. new id = getUserID(playerid);
  1105. new character_query[62 + SQL_INTERGER_LENGTH + 1];
  1106. format(character_query, sizeof(character_query), "SELECT id FROM character WHERE character_id = '%i'", id);
  1107. new Result:character_result = sql_query(sqlHandle, character_query);
  1108. printf("%i", sql_num_rows(character_result));
  1109. if(sql_num_rows(character_result) >= MAX_CHARACTERS_PER_USER){ // At or over character limit
  1110. SendClientMessage(playerid, COLOR_WARNING_MESSAGE, "SERVER: Maximum characters reached, can not create another.");
  1111. characterSelection(playerid);
  1112. }
  1113. else{
  1114. changeName(playerid, ""); // Pick name and save character
  1115. }
  1116. }
  1117. }
  1118. case DIALOG_LOGIN_FAILED:{
  1119. ShowPlayerDialog(playerid, DIALOG_LOGIN_FAILED, DIALOG_STYLE_MSGBOX, "Authenticaion failed ", "Please wait...", "", "");
  1120. }
  1121. }
  1122. return 0; // MUST return 0 here, just like OnPlayerCommandText.
  1123. }
  1124. public OnPlayerClickPlayer(playerid, clickedplayerid, source) // TODO for 0.0a
  1125. {
  1126. return 1;
  1127. }
  1128. public OnEnterExitModShop(playerid, enterexit, interiorid) // TODO for 0.0a
  1129. {
  1130. return 1;
  1131. }
  1132. public OnPlayerGiveDamage(playerid, damagedid, Float: amount, weaponid) // TODO for 0.0a
  1133. {
  1134. return 1;
  1135. }
  1136. public OnPlayerTakeDamage(playerid, issuerid, Float: amount, weaponid) // TODO for 0.0a
  1137. {
  1138. return 1;
  1139. }
  1140. // discord-connector events
  1141. public DCC_OnMessageCreate(DCC_Message:message){
  1142. // Originating Discord channel
  1143. new DCC_Channel:channel;
  1144. DCC_GetMessageChannel(message, channel);
  1145. // Originating Discord user
  1146. new DCC_User:author;
  1147. DCC_GetMessageAuthor(message, author);
  1148. // Message content
  1149. new str[256];
  1150. new command[32], params[128];
  1151. DCC_GetMessageContent(message, str);
  1152. sscanf(str, "s[32]s[128]", command, params);
  1153. // Ignore bots
  1154. new bool:isBot;
  1155. DCC_IsUserBot(author, isBot);
  1156. if(isBot){
  1157. return 1;
  1158. }
  1159. /*// Beyond this point, don't respond to commands on foreign guilds.
  1160. if(guild != homeGuild){
  1161. return 1;
  1162. }*/
  1163. // Beyond this point, only respond to privilaged channels.
  1164. if(channel != adminEchoChannel && channel != adminChannel && channel != managementChannel){
  1165. return 1;
  1166. }
  1167. // Test command.
  1168. if(!strcmp(command, "!test", true)){
  1169. DiscordSendChannelMessage(channel, "Works.");
  1170. print("Some said !test in the echo channel.");
  1171. }
  1172. return 1;
  1173. }
  1174. // Entrypoint
  1175. main(){
  1176. // Credits
  1177. print("\n*----------------------------------*");
  1178. print(" RPFW");
  1179. new message[21 + 20 + 1];
  1180. format(message, sizeof(message), " Role-play framework v%s", MODE_NAME);
  1181. print(message);
  1182. //printf("System load average: %f",loadavg()); // Linux only TODO: test
  1183. print("*----------------------------------*\n");
  1184. }