· 6 years ago · Oct 15, 2019, 04:36 PM
1#pragma semicolon 1
2
3#include <sourcemod>
4#include <sdktools>
5#include <cstrike>
6#include <smlib>
7#include <multicolors>
8#include <achievements>
9
10public Plugin myinfo =
11{
12 name = "Achievements Core",
13 author = "Hirsw0w",
14 description = "The Backend of the achievements system.",
15 version = "1.0",
16 url = "www.play-il.co.il"
17};
18
19int AchievementsCount = 0;
20
21new String:Categories[MAX_CATEGORIES][32];
22
23enum AchievementsData {
24 Id,
25 String:Name[32],
26 String:Description[128],
27 String:StatName[32],
28 Category,
29 Tiers[MAX_TIERS],
30 TiersCount,
31 CashReward[MAX_TIERS],
32 ExpReward[MAX_TIERS]
33}
34new Achievements[MAX_ACHIEVEMENTS][AchievementsData];
35
36new String:CustomReward[MAX_ACHIEVEMENTS][MAX_TIERS][128];
37
38enum PlayersData {
39 String:AuthId[32],
40 openAchievementId,
41 openAchievementTier,
42 pAchievement[MAX_ACHIEVEMENTS],
43 pAchStats[MAX_ACHIEVEMENTS],
44 pCurrentTier[MAX_ACHIEVEMENTS]
45}
46new pData[MAXPLAYERS][PlayersData];
47
48char buff[128];
49new Handle:db = INVALID_HANDLE;
50
51new Handle:FwdOnPlayerCompleteAchievement;
52
53public void OnPluginStart()
54{
55 RegConsoleCmd("sm_ach", AchievementsMenu);
56 RegConsoleCmd("sm_achievements", AchievementsMenu);
57 RegConsoleCmd("sm_achievement", AchievementsMenu);
58
59 FwdOnPlayerCompleteAchievement = CreateGlobalForward("OnPlayerCompleteAchievement", ET_Hook, Param_Cell, Param_Cell, Param_Cell); // client, achId, tier
60
61 InitializeDB();
62 OpenLogDirectory("achievements");
63}
64
65
66public void OnPluginEnd() {
67 for (int i = 1; i <= MaxClients;i++) {
68 if(!IsClientInGame(i))
69 continue;
70
71 SaveClient(i);
72 }
73}
74
75public void OnMapEnd() {
76 for (int i = 1; i <= MaxClients;i++) {
77 if(!IsClientInGame(i))
78 continue;
79
80 SaveClient(i);
81 }
82}
83
84public OnMapStart() {
85 OpenLogFile("achievements");
86
87 for (int i = 0; i < MAX_CATEGORIES;i++)
88 Categories[i][0] = EOS;
89
90 for (int i = 0; AchievementsData:i < AchievementsData;i++) {
91 Achievements[i][AchievementsData:i] = 0;
92 }
93
94 for (int i = 0; PlayersData:i < PlayersData;i++) {
95 pData[i][PlayersData:i] = 0;
96 }
97}
98
99public Action AchievementsMenu(client, args) {
100 OpenAchievementsMenu(client, "");
101}
102
103
104public OpenAchievementsMenu(client, char[] category) {
105 Handle menu = CreateMenu(achievementMenu);
106 SetMenuTitle(menu, "[Play-IL] %N's Achievements:%s%s\nCompleted Achievements: %d/%d",client, category[0] != EOS ? "\nCurrent Category: ":"", category, Achievements_GetPlayerCompleted(client), AchievementsCount);
107
108 int categoryId = -1;
109 if(category[0] != EOS) {
110 for (int i = 0; i < MAX_CATEGORIES;i++) {
111 if(Categories[i][0] == EOS)
112 continue;
113
114 if(StrEqual(Categories[i], category, false)) {
115 categoryId = i;
116 break;
117 }
118 }
119 }
120
121 char temp[4];
122 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
123 if(Achievements[i][Id] == 0)
124 continue;
125
126 if(Achievements[i][Category] == categoryId) {
127 Format(buff, 128, "%s (Tier %d)", Achievements[i][Name], pData[client][pCurrentTier][i]);
128 IntToString(i+1, temp, 4);
129 AddMenuItem(menu, temp, buff);
130 }
131 }
132
133 if(categoryId == -1) {
134 for (int i = 0; i < MAX_CATEGORIES;i++) {
135 if(Categories[i][0] == EOS)
136 continue;
137
138 Format(buff, 128, "Category: %s", Categories[i]);
139 AddMenuItem(menu, Categories[i], buff);
140
141 }
142 }
143
144 DisplayMenu(menu, client, 45);
145 return;
146}
147
148public int achievementMenu(Menu menu, MenuAction action, int client, int itemNum)
149{
150 if(action != MenuAction_Select || client < 1 || client > MaxClients || !IsClientInGame(client))
151 {
152 return 1;
153 }
154
155 char info[32];
156 GetMenuItem(menu, itemNum, info, 32);
157 int item = StringToInt(info) - 1;
158
159 if(item >= 0) {
160 AchievementInfo(client, item, Achievements[item][TiersCount] <= (pData[client][pCurrentTier][item]+1) ? Achievements[item][TiersCount]:(pData[client][pCurrentTier][item]+1));
161 }
162 else OpenAchievementsMenu(client, info);
163
164
165 return 0;
166}
167
168public AchievementInfo(client, achId, tier) {
169 pData[client][openAchievementId] = achId;
170 pData[client][openAchievementTier] = tier;
171
172 new Handle:panel = CreatePanel();
173 Format(buff, 128, "[Play-IL] %N's Achievements:", client);
174 SetPanelTitle(panel, buff);
175
176 DrawPanelItem(panel, "", ITEMDRAW_SPACER);
177
178 Format(buff, 128, "Achievement: %s (Tier %d)", Achievements[achId][Name], tier);
179 DrawPanelText(panel, buff);
180
181 Format(buff, 128, "Description: %s", Achievements[achId][Description]);
182 new String:temp[16];
183 Format(temp, 16, "%d", Achievements[achId][Tiers][tier-1]);
184 ReplaceString(buff, 128, "{tier}", temp);
185 DrawPanelText(panel, buff);
186
187 if(CustomReward[achId][tier-1][0] != EOS) {
188 Format(buff, 128, "Reward: %s", CustomReward[achId][tier-1]);
189 DrawPanelText(panel, buff);
190 }
191 else {
192 Format(buff, 128, "Reward: %d Cash, %d Exp", Achievements[achId][CashReward][tier-1],Achievements[achId][ExpReward][tier-1]);
193 DrawPanelText(panel, buff);
194 }
195
196 DrawPanelItem(panel, "", ITEMDRAW_SPACER);
197
198 if(pData[client][pCurrentTier][achId] >= Achievements[achId][TiersCount]) {
199 DrawPanelText(panel, "Stats: COMPLETED");
200 DrawPanelText(panel, "Tier: MAX");
201 }
202 else {
203 Format(buff, 128, "Stats: %d/%d %s", pData[client][pAchStats][achId],Achievements[achId][Tiers][pData[client][pCurrentTier][achId]], Achievements[achId][StatName]);
204 DrawPanelText(panel, buff);
205
206 Format(buff, 128, "Tier: %d/%d", pData[client][pCurrentTier][achId], Achievements[achId][TiersCount]);
207 DrawPanelText(panel, buff);
208 }
209 DrawPanelItem(panel, "", ITEMDRAW_SPACER);
210
211 SetPanelCurrentKey(panel, 6);
212 DrawPanelItem(panel, "▲ Higher Tier", tier == (Achievements[achId][TiersCount]) ? ITEMDRAW_DISABLED:ITEMDRAW_CONTROL);
213
214 SetPanelCurrentKey(panel, 7);
215 DrawPanelItem(panel, "▼ Lower Tier", tier == 1 ? ITEMDRAW_DISABLED:ITEMDRAW_CONTROL);
216
217 DrawPanelItem(panel, "", ITEMDRAW_SPACER);
218
219 SetPanelCurrentKey(panel, 9);
220 DrawPanelItem(panel, "Back", ITEMDRAW_CONTROL);
221
222 SendPanelToClient(panel, client, achievementInfoPanel, 60);
223 return;
224}
225
226public int achievementInfoPanel(Handle:menu, MenuAction:action, int client, int key)
227{
228 if(client < 1 || client > MaxClients || !IsClientInGame(client))
229 return 1;
230
231 if(action != MenuAction_Select || key == 9)
232 {
233 OpenAchievementsMenu(client, "");
234 return 1;
235 }
236
237 int achId = pData[client][openAchievementId];
238 int tier = pData[client][openAchievementTier];
239 if(key == 6)
240 tier += 1;
241 else if(key == 7)
242 tier -= 1;
243
244 AchievementInfo(client, achId, tier);
245 return 1;
246}
247
248
249public OnClientPostAdminCheck(client)
250{
251 if(IsFakeClient(client))
252 return;
253
254 for(int i;PlayersData:i < PlayersData;i++) {
255 pData[client][PlayersData:i] = 0;
256 }
257
258 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
259 if(Achievements[i][Id] == 0) {
260 pData[client][pAchievement][i] = 0;
261 pData[client][pAchStats][i] = 0;
262 pData[client][pCurrentTier][i] = 0;
263 continue;
264 }
265
266 pData[client][pAchievement][i] = Achievements[i][Id];
267 pData[client][pAchStats][i] = -1;
268 pData[client][pCurrentTier][i] = -1;
269 }
270
271 GetClientAuthId(client, AuthId_SteamID64, pData[client][AuthId], 32,false);
272 LoadClient(client);
273 return;
274}
275
276public OnClientDisconnect(client) {
277 SaveClient(client);
278}
279
280public GetAchievementIndex(int id) {
281 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
282 if(Achievements[i][Id] == id)
283 return i;
284 }
285 return -1;
286}
287
288public SaveClient(client)
289{
290 if(client < 1 || client > MaxClients || IsFakeClient(client) || !IsClientInGame(client))
291 return;
292
293 GetClientName(client, buff, 128);
294 SQL_EscapeString(db, buff, buff, 128);
295 char buffer[256];
296 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
297 if(pData[client][pAchievement][i] == 0)
298 continue;
299
300 Format(buffer, 256, "UPDATE players SET name='%s', stats = '%d', currenttier = '%d' WHERE steamid = '%s' AND achid = '%d'",buff, pData[client][pAchStats][i], pData[client][pCurrentTier][i], pData[client][AuthId], pData[client][pAchievement][i]);
301 SQL_TQuery(db, SQLErrorCheckCallback, buffer);
302 }
303 return;
304}
305
306public LoadClient(client) {
307 if(client < 1 || client > MaxClients || IsFakeClient(client) || !IsClientInGame(client))
308 return;
309
310 Format(buff, sizeof(buff), "SELECT * FROM players WHERE steamid = '%s' ORDER BY `achid` ASC", pData[client][AuthId]);
311 SQL_TQuery(db, SqlLoadClient, buff, client);
312 return;
313}
314
315
316public SqlLoadClient(Handle:owner, Handle:hndl, const String:error[], any:data)
317{
318 if(!IsClientInGame(data))
319 {
320 return;
321 }
322
323 if(hndl == INVALID_HANDLE)
324 {
325 ThrowError("steam search SQL error: %s", error);
326 return;
327 }
328
329 int client = data;
330 if(SQL_GetRowCount(hndl) > 0) {
331 while(SQL_FetchRow(hndl)) {
332 int achId = SQL_FetchIntByName(hndl, "achid");
333 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
334 if(pData[client][pAchievement][i] != achId)
335 continue;
336
337 pData[client][pCurrentTier][i] = SQL_FetchIntByName(hndl, "currenttier");
338 pData[client][pAchStats][i] = SQL_FetchIntByName(hndl, "stats");
339 }
340 }
341 }
342
343 char buffer[256];
344 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
345 if(pData[client][pAchievement][i] == 0 || pData[client][pCurrentTier][i] != -1)
346 continue;
347
348 GetClientName(client, buff, 128);
349 SQL_EscapeString(db, buff, buff, 128);
350 Format(buffer, 256, "INSERT INTO `players`(`name`, `steamid`, `achid`, `stats`, `currenttier`) VALUES ('%s', '%s', %d, 0, 0)",buff, pData[client][AuthId], pData[client][pAchievement][i]);
351 SQL_LockDatabase(db);
352 SQL_Query(db, buffer);
353 SQL_UnlockDatabase(db);
354 pData[client][pCurrentTier][i] = 0;
355 pData[client][pAchStats][i] = 0;
356 }
357}
358
359public CheckPlayerAchievement(client, achId) {
360 if(client < 1 || client > 32 || !IsClientInGame(client) || achId < 0 || achId > MAX_ACHIEVEMENTS || Achievements[achId][Id] == 0)
361 return;
362
363 if (pData[client][pCurrentTier][achId] >= Achievements[achId][TiersCount])
364 return;
365
366 if(pData[client][pAchStats][achId] >= Achievements[achId][Tiers][pData[client][pCurrentTier][achId]]) {
367 CPrintToChatAll("[{green}Play-IL{default}] {green}%N {default}has completed the achievement {purple}%s{lightred}(TIER %d) {default}.", client, Achievements[achId][Name], pData[client][pCurrentTier][achId] + 1);
368
369 if(CustomReward[achId][pData[client][pCurrentTier][achId]][0] != EOS) {
370 CPrintToChat(client, "[{green}Play-IL{default}] You have earned {purple}%s{default}.", CustomReward[achId][pData[client][pCurrentTier][achId]]);
371 } else {
372 // TODO: GIVE CASH AND EXP
373 CPrintToChat(client, "[{green}Play-IL{default}] You have earned {purple}%d Cash {default}and {purple}%d Exp{default}.", Achievements[achId][CashReward][pData[client][pCurrentTier][achId]], Achievements[achId][ExpReward][pData[client][pCurrentTier][achId]]);
374 }
375 pData[client][pCurrentTier][achId]++;
376 Call_StartForward(FwdOnPlayerCompleteAchievement);
377 Call_PushCell(client);
378 Call_PushCell(achId);
379 Call_PushCell(pData[client][pCurrentTier][achId]);
380 Call_Finish();
381 SaveClient(client);
382 }
383}
384
385public AddAchievement(char[] name, char[] desc,char[] statname, char[] category) {
386 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
387 if(Achievements[i][Id] == 0)
388 continue;
389
390 if(StrEqual(Achievements[i][Name], name, false) && (Achievements[i][Category] != -1 && StrEqual(Categories[Achievements[i][Category]], category, false))) {
391 Format(Achievements[i][Description], 128, desc);
392 Format(Achievements[i][StatName], 32, statname);
393 return i;
394 }
395 }
396
397 new String:buffer[256], String:temp[32];
398 SQL_EscapeString(db, name, buff, 128);
399 SQL_EscapeString(db, category, temp, 32);
400
401 Format(buffer, sizeof(buffer), "SELECT * FROM `achievements` WHERE `name` = '%s' AND `category` = '%s' LIMIT 1", buff, temp);
402 SQL_LockDatabase(db);
403 DBResultSet query = SQL_Query(db, buffer);
404 SQL_UnlockDatabase(db);
405
406 if(SQL_FetchRow(query))
407 {
408 Achievements[AchievementsCount][Id] = SQL_FetchIntByName(query, "id");
409 delete query;
410 }
411 else {
412 Achievements[AchievementsCount][Id] = -1;
413 Format(buff, 128, "INSERT INTO achievements (`name`, `category`) VALUES('%s', '%s')", buff, temp);
414 SQL_TQuery(db, SQLInsertAchievement, buff, AchievementsCount);
415 }
416
417 Format(Achievements[AchievementsCount][Name], 32, name);
418 Format(Achievements[AchievementsCount][Description], 128, desc);
419 Format(Achievements[AchievementsCount][StatName], 32, statname);
420 int categoryId = -1;
421 if(category[0] != EOS) {
422 for (int i = 0; i < MAX_CATEGORIES;i++) {
423 if(Categories[i][0] == EOS) {
424 categoryId = i;
425 continue;
426 }
427
428 if (StrEqual(Categories[i], category, false)) {
429 categoryId = i;
430 break;
431 }
432 }
433 if(Categories[categoryId][0] == EOS) {
434 Format(Categories[categoryId], 32, category);
435 }
436 }
437 Achievements[AchievementsCount][Category] = categoryId;
438 return AchievementsCount++;
439}
440
441public SQLInsertAchievement(Handle:owner, Handle:hndl, const String:error[], any data) {
442 if(hndl == INVALID_HANDLE)
443 {
444 ThrowError("SQL error: %s", error);
445 return;
446 }
447
448 int achId = data;
449 Achievements[achId][Id] = SQL_GetInsertId(db);
450 LogError("Last Inserted Id: %d", Achievements[achId][Id]);
451}
452
453public InitializeDB()
454{
455 new String:error[255];
456 new Handle:kv = CreateKeyValues("sql");
457 KvSetString(kv, "driver", "default");
458 KvSetString(kv, "host", "185.185.134.238");
459 KvSetString(kv, "port", "3306");
460 KvSetString(kv, "database", "csgo_achievements");
461 KvSetString(kv, "user", "csgo_achievement");
462 KvSetString(kv, "pass", "NL7M51KjIK4SFrzY");
463
464
465 db = SQL_ConnectCustom(kv, error, 255, true);
466 if(db == INVALID_HANDLE)
467 {
468 SetFailState(error);
469 }
470 SQL_LockDatabase(db);
471 SQL_FastQuery(db, "SET NAMES \"UTF8\"");
472 SQL_FastQuery(db, "CREATE TABLE IF NOT EXISTS achievements (id int(11) PRIMARY KEY AUTO_INCREMENT, name TEXT, category TEXT);");
473 SQL_FastQuery(db, "CREATE TABLE IF NOT EXISTS players (id int(11) PRIMARY KEY AUTO_INCREMENT, name TEXT, steamid TEXT, achid int(11), stats int(11), currenttier int(11));");
474 SQL_UnlockDatabase(db);
475}
476
477public SQLErrorCheckCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
478{
479 if(!StrEqual("", error))
480 {
481 LogError("Last Connect SQL Error: %s", error);
482 PrintToServer("Last Connect SQL Error: %s", error);
483 }
484}
485
486// ------- NATIVES FUNCTIONS
487
488public APLRes:AskPluginLoad2(Handle:h_Myself, bool:bLateLoaded, String:sError[], error_max)
489{
490 CreateNative("Achievements_Count", Native_GetAchievementsCount);
491 CreateNative("Achievements_CategoriesCount", Native_GetCategoriesCount);
492
493 CreateNative("Achievements_Add", Native_Add);
494 CreateNative("Achievements_AddTier", Native_AddTier);
495 CreateNative("Achievements_AddTierEx", Native_AddTierEx);
496
497 CreateNative("Achievements_GetPayerTier", Native_GetPlayerTier);
498 CreateNative("Achievements_GetPlayerStat", Native_GetPlayerStat);
499 CreateNative("Achievements_AddPlayerStat", Native_AddPlayerStat);
500 CreateNative("Achievements_GetPlayerCompleted", Native_GetPlayerCompleted);
501 return APLRes_Success;
502}
503
504public int Native_GetAchievementsCount(Handle:hPlugin,nParam) {
505 return AchievementsCount;
506}
507
508public int Native_GetCategoriesCount(Handle:hPlugin,nParam) {
509 int count = 0;
510 for (int i = 0; i < MAX_CATEGORIES;i++) {
511 if(Categories[i][0] != EOS)
512 count++;
513 }
514 return count;
515}
516
517public int Native_Add(Handle:hPlugin,nParam) {
518 int argLen;
519 GetNativeStringLength(1, argLen);
520 if (argLen <= 0)
521 return 0;
522
523 new String:desc[128], String:category[64],String:statname[32];
524 GetNativeString(1, buff, 128);
525 GetNativeString(2, desc, 128);
526 GetNativeString(3, statname, 32);
527 GetNativeString(4, category, 32);
528 int id = AddAchievement(buff, desc, statname, category);
529 return id;
530}
531
532public int Native_AddTier(Handle:hPlugin,nParam) {
533 int achId = GetNativeCell(1);
534 if(achId < 0 || Achievements[achId][Id] == 0 || Achievements[achId][TiersCount] >= MAX_TIERS)
535 return 0;
536
537 int tierId = Achievements[achId][TiersCount]++;
538 Achievements[achId][Tiers][tierId] = GetNativeCell(2);
539 Achievements[achId][CashReward][tierId] = GetNativeCell(3);
540 Achievements[achId][ExpReward][tierId] = GetNativeCell(4);
541 return tierId+1;
542}
543
544public int Native_AddTierEx(Handle:hPlugin,nParam) {
545 int achId = GetNativeCell(1);
546 if(achId < 0 || Achievements[achId][Id] == 0 || Achievements[achId][TiersCount] >= MAX_TIERS)
547 return 0;
548
549 int tierId = Achievements[achId][TiersCount]++;
550 Achievements[achId][Tiers][tierId] = GetNativeCell(2);
551 GetNativeString(3, CustomReward[achId][tierId], 128);
552 return tierId+1;
553}
554
555public int Native_GetPlayerTier(Handle:hPlugin,nParam) {
556 int client = GetNativeCell(1);
557 int achId = GetNativeCell(2);
558 if(achId < 0 || Achievements[achId][Id] == 0 || client < 1 || client > MaxClients || !IsClientInGame(client))
559 return -1;
560
561 return pData[client][pCurrentTier][achId];
562}
563
564public int Native_GetPlayerStat(Handle:hPlugin,nParam) {
565 int client = GetNativeCell(1);
566 int achId = GetNativeCell(2);
567 if(achId < 0 || Achievements[achId][Id] == 0 || client < 1 || client > MaxClients || !IsClientInGame(client))
568 return 0;
569
570 return pData[client][pAchStats][achId];
571}
572
573public int Native_AddPlayerStat(Handle:hPlugin,nParam) {
574 int client = GetNativeCell(1);
575 int achId = GetNativeCell(2);
576 int value = GetNativeCell(3);
577 if(achId < 0 || Achievements[achId][Id] == 0 || client < 1 || client > MaxClients || !IsClientInGame(client))
578 return 0;
579
580 pData[client][pAchStats][achId] += value;
581 CheckPlayerAchievement(client, achId);
582 return 1;
583}
584
585public int Native_GetPlayerCompleted(Handle:hPlugin, nParam) {
586 int client = GetNativeCell(1);
587
588 if(client < 1 || client > MaxClients || !IsClientInGame(client))
589 return 0;
590
591 int count = 0;
592 for (int i = 0; i < MAX_ACHIEVEMENTS;i++) {
593 if(Achievements[i][Id] == 0)
594 continue;
595
596 if(pData[client][pCurrentTier][i] == Achievements[i][TiersCount])
597 count++;
598 }
599 return count;
600}
601
602// ------- LOG FUNCTIONS
603
604new String:g_sFilePath[PLATFORM_MAX_PATH];
605new String:FormatedTime[64];
606
607stock OpenLogDirectory(char[] folder) {
608 BuildPath(Path_SM, g_sFilePath, sizeof(g_sFilePath), "logs/%s/", folder);
609
610 if (!DirExists(g_sFilePath))
611 {
612 CreateDirectory(g_sFilePath, 511);
613
614 if (!DirExists(g_sFilePath))
615 SetFailState("Failed to create directory at /sourcemod/logs/%s - Please manually create that path and reload this plugin.", folder);
616 }
617}
618
619stock OpenLogFile(char[] folder) {
620 FormatTime(FormatedTime, 100, "%d_%b_%Y", GetTime()); //name the file 'day month year'
621
622 BuildPath(Path_SM, g_sFilePath, sizeof(g_sFilePath), "/logs/%s/%s.txt", folder, FormatedTime);
623
624 new Handle:FileHandle = OpenFile(g_sFilePath, "a+");
625
626 FormatTime(FormatedTime, 100, "%X", GetTime());
627 new String:MapName[32];
628 GetCurrentMap(MapName, 32);
629
630 WriteFileLine(FileHandle, "");
631 WriteFileLine(FileHandle, "%s - ===== Map change to %s =====", FormatedTime, MapName);
632 WriteFileLine(FileHandle, "");
633
634 CloseHandle(FileHandle);
635}
636
637stock Log_Add(char[] logmessage) {
638 new Handle:FileHandle = OpenFile(g_sFilePath, "a+");
639
640 FormatTime(FormatedTime, 64, "%X", GetTime());
641 WriteFileLine(FileHandle, "[%s] %s", FormatedTime, logmessage);
642
643 CloseHandle(FileHandle);
644}