/*
GameCMS_Achievs_Core
1.3
[+] forward OnAchivesComplete(iClient, idKey)
* Выполняется при открытии игроком очередного достижения
* @iClient - индекс игрока
* @idKey - уникальный номер достижения
* @noreturn
[*] поправлен натив CmsCheckPlayerAchive
[+] добавлено состояние ачивки eAchievedStatus:AchStatusJustCompleted - только что завершено
*/
#include <amxmodx>
#include <amxmisc>
#include <sqlx>
#include <gamecms5>
#include <gamecms_achievs>
new const PLUGIN[] = "GameCMS_Achievs_Core";
new const VERSION[] = "1.3";
new const AUTHOR[] = "zhorzh78";
new cpLogs, cpRegPlayerOnly, cpStorageDays, item_access;
new Trie:trhAchives, Trie:g_Players, Array:arhAchIndex;
new Handle:g_SqlX, AuthIDs[MAX_PLAYERS + 1][MAX_AUTHID_LENGTH/2], regUserId[MAX_PLAYERS + 1];
new const sqlCoreTable[] = "achievs";
new const sqlStatsTable[] = "achievs_stats";
new const sqlPlayersTable[] = "achievs_players";
new const gSoundNewLevel[] = "sound\events\task_complete.wav";
new QueryString[3500], utf_supp[MAX_STRING_LEN], lenS;
new g_fwd_StopAchCore, g_fwd_OnAchievesComplete, retFwd;
enum _:eAchivesData
{
achUnicId,
achName[MAX_STRING_LEN*2],
achRusName[MAX_STRING_LEN*4],
achValue,
achAvatar[MAX_STRING_LEN]
}
enum _:eAchPlData
{
userAch_id,
userId,
userValue,
uFullValue,
uCurrValue,
uCollect,
uCollDate[MAX_INT_LEN*2],
bool:uModify
}
enum
{
QueryLoadAchives = 1,
QueryLoadPlayerAchives,
QueryPlayerDisconnect
}
public OnAPIPluginStop()
{
log_amx("Plugin paused. GameCMS_API is not loaded");
ExecuteForward(g_fwd_StopAchCore, retFwd);
pause("ad");
}
public OnAPIPluginLoaded(Handle:sqlTuple)
{
if(!(g_SqlX = sqlTuple))
return;
#if AMXX_VERSION_NUM < 183
utf_supp = "SET NAMES `utf8`; ";
#else
SQL_SetCharset(g_SqlX, "utf8");
#endif
LoadAchives(LOAD_ALL);
if(get_playersnum()) //Если игроки зашли ДО соединения с БД
{
for(new i = 1; i <= MaxClients; i++)
{
if(!is_user_connected(i))
continue;
LoadAchives(i);
}
}
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
cpLogs = register_cvar("cms_achives_logs", "1"); //вкл/выкл логов
cpRegPlayerOnly = register_cvar("cms_achives_reg_only", "1"); //записывать в БД: 1- только зарегистрированных на сайте, 0- всех
cpStorageDays = register_cvar("cms_achives_storage_days", "170"); //сколько дней хранить неактивных игроков, дней. 0- не удалять
register_clcmd("say /achievs", "CmdShowAchievesMenu"); //меню просмотра достижений игроков
g_fwd_StopAchCore = CreateMultiForward("OnAchivesCoreStopped", ET_IGNORE);
if(FindPluginFunction("OnAchievesCompleted"))
g_fwd_OnAchievesComplete = CreateMultiForward("OnAchievesCompleted", ET_IGNORE, FP_CELL, FP_CELL);
}
public plugin_cfg()
{
if(is_plugin_loaded("GameCMS_API") == INVALID_PLUGIN_ID)
{
ExecuteForward(g_fwd_StopAchCore, retFwd);
log_amx("GameCMS_API is not loaded...");
pause("ad");
}
#if AMXX_VERSION_NUM < 183
MaxClients = get_maxplayers();
#endif
arhAchIndex = ArrayCreate();
trhAchives = TrieCreate();
g_Players = TrieCreate();
get_cvar_string("cms_url", SiteUrl, charsmax(SiteUrl));
}
public client_authorized(id)
{
FnTrieClear(id);
regUserId[id] = 0;
if(is_user_bot(id) || is_user_hltv(id))
return;
get_user_authid(id, AuthIDs[id], charsmax(AuthIDs[]));
if(!get_pcvar_num(cpRegPlayerOnly))
LoadAchives(id);
}
public client_disconnected(id)
{
if(is_user_bot(id) || is_user_hltv(id))
return;
if(get_pcvar_num(cpRegPlayerOnly))
if(!regUserId[id])
return;
WriteAchieves(id);
}
public OnAPIMemberConnected(id)
{
regUserId[id] = cmsapi_is_user_member(id);
if(get_pcvar_num(cpRegPlayerOnly))
LoadAchives(id);
}
public LoadAchives(id)
{
new Data[2];
new sqlData[300];
switch(id)
{
case LOAD_ALL:
{
new iLen, iDays = get_pcvar_num(cpStorageDays);
iLen += formatex(sqlData[iLen], charsmax(sqlData) - iLen, "SELECT *, cast(convert(`rus_name` using utf8) as binary) as `russ_name` FROM `%s`;", sqlCoreTable);
if(iDays > 0)
iLen += formatex(sqlData[iLen], charsmax(sqlData) - iLen, "DELETE FROM `%s` WHERE UNIX_TIMESTAMP(NOW()) > \
UNIX_TIMESTAMP(DATE_ADD( FROM_UNIXTIME(`last_join`), INTERVAL '%d' DAY));", sqlPlayersTable, iDays);
Data[0] = QueryLoadAchives;
}
default:
{
formatex(sqlData, charsmax(sqlData), "SELECT * FROM `%s` WHERE `user_auth` = '%s'", sqlStatsTable, AuthIDs[id]);
Data[0] = QueryLoadPlayerAchives;
Data[1] = id;
}
}
SQL_ThreadQuery(g_SqlX, "QueryAchives_post", sqlData, Data, sizeof(Data));
}
public WriteAchieves(id)
{
new Trie:tmpTrie;
if(!TrieGetCell(g_Players, AuthIDs[id], tmpTrie))
return;
new prNum = ArraySize(arhAchIndex);
new tmpAchives[eAchivesData], tmpKey;
new tmpClAchives[eAchPlData];
QueryString[0] = EOS, lenS = 0;
static Data[2];
Data[0] = QueryPlayerDisconnect, Data[1] = id;
new timestamp = get_systime();
lenS += formatex(QueryString[lenS], charsmax(QueryString)- lenS,
"%s INSERT INTO `%s` SET `user_id`='%d',`user_auth`='%s',`last_join`='%d' ON DUPLICATE KEY UPDATE `user_id`='%d', `last_join`='%d';",
utf_supp, sqlPlayersTable, regUserId[id], AuthIDs[id], timestamp, regUserId[id], timestamp);
for(new i; i < prNum; i++)
{
tmpKey = ArrayGetCell(arhAchIndex, i);
if(!TrieGetArray(tmpTrie, get_id_key(tmpKey), tmpClAchives, sizeof(tmpClAchives)))
continue;
if(!tmpClAchives[uModify])
continue;
TrieGetArray(trhAchives, get_id_key(tmpKey), tmpAchives, sizeof(tmpAchives))
mysql_insert_string(tmpAchives[achName], charsmax(tmpAchives[achName]));
new tmpUnic[34];
formatex(tmpUnic, charsmax(tmpUnic), "%s+%d", AuthIDs[id], tmpKey)
new QueryBody[350], len;
if(!tmpClAchives[uFullValue])
len = formatex(QueryBody, charsmax(QueryBody),
"%s INSERT INTO `%s` SET `hash_id`=MD5('%s'),`user_id`='%d',`user_auth`='%s',`ach_id`='%d',`ach_name`='%s',`value` = '%d',\
`curr_value`=`curr_value`+'%d',`ach_collect`='%d',`collect_date`='%s'",
utf_supp, sqlStatsTable, tmpUnic, regUserId[id], AuthIDs[id], tmpKey, tmpAchives[achName], tmpAchives[achValue],
tmpClAchives[uCurrValue], tmpClAchives[uCollect], tmpClAchives[uCollDate]);
else
len = formatex(QueryBody, charsmax(QueryBody),
"%s UPDATE `%s` SET `ach_name`='%s',`value` = '%d', `curr_value`=`curr_value`+'%d',`ach_collect`='%d',`collect_date`='%s' WHERE `hash_id`=MD5('%s')",
utf_supp, sqlStatsTable, tmpAchives[achName], tmpAchives[achValue], tmpClAchives[uCurrValue], tmpClAchives[uCollect], tmpClAchives[uCollDate], tmpUnic);
if((lenS + len) > charsmax(QueryString) - 10)
{
SQL_ThreadQuery(g_SqlX, "QueryAchives_post", QueryString, Data, sizeof(Data));
QueryString[0]= EOS; lenS = 0;
}
lenS +=formatex(QueryString[lenS], charsmax(QueryString)- lenS, "%s; ", QueryBody);
}
if(get_pcvar_num(cpLogs) > 1)
log_to_file("gcms_achives.log", "%s", QueryString);
SQL_ThreadQuery(g_SqlX, "QueryAchives_post", QueryString, Data, sizeof(Data));
}
public QueryAchives_post(failstate, Handle:query, const error[], errornum, const postData[], DataSize)
{
if(SQL_Error(error, errornum, failstate))
return;
new id = postData[1];
switch(postData[0])
{
case QueryLoadAchives: //load achives
{
new tmpAchives[eAchivesData], tmpId[7], iNum;
while(SQL_MoreResults(query))
{
tmpAchives[achUnicId] = SQL_ReadResult(query, SQL_FieldNameToNum(query, "unic_id"));
num_to_str(tmpAchives[achUnicId], tmpId, charsmax(tmpId));
if(TrieGetArray(trhAchives, tmpId, tmpAchives, sizeof(tmpAchives)))
{
SQL_ReadResult(query, SQL_FieldNameToNum(query, "name"), tmpAchives[achName], charsmax(tmpAchives[achName]));
SQL_ReadResult(query, SQL_FieldNameToNum(query, "russ_name"), tmpAchives[achRusName], charsmax(tmpAchives[achRusName]));
SQL_ReadResult(query, SQL_FieldNameToNum(query, "ach_img"), tmpAchives[achAvatar], charsmax(tmpAchives[achAvatar]));
tmpAchives[achValue] = SQL_ReadResult(query, SQL_FieldNameToNum(query, "value"));
mysql_escape_string(tmpAchives[achName], charsmax(tmpAchives[achName]));
mysql_escape_string(tmpAchives[achRusName], charsmax(tmpAchives[achRusName]));
#if AMXX_VERSION_NUM < 183
TrieDeleteKey(trhAchives, tmpId)
#endif
TrieSetArray(trhAchives, tmpId, tmpAchives, sizeof(tmpAchives));
iNum++;
}
SQL_NextRow(query);
}
if(get_pcvar_num(cpLogs))
log_to_file("gcms_achives.log", "[Core] Loaded achives: %d from %d", iNum, SQL_NumResults(query));
}
case QueryLoadPlayerAchives: //client putinserver
{
new Trie:tmpTrie = TrieCreate();
TrieSetCell(g_Players, AuthIDs[id], tmpTrie);
new tmpPlAchives[eAchPlData], tmpId[7];
new nums = SQL_NumResults(query);
if(!nums)
return;
while(SQL_MoreResults(query))
{
tmpPlAchives[userAch_id] = SQL_ReadResult(query, SQL_FieldNameToNum(query, "ach_id"));
num_to_str(tmpPlAchives[userAch_id], tmpId, charsmax(tmpId));
if(TrieKeyExists(trhAchives, tmpId))
{
tmpPlAchives[userId] = SQL_ReadResult(query, SQL_FieldNameToNum(query, "user_id"));
tmpPlAchives[uFullValue] = SQL_ReadResult(query, SQL_FieldNameToNum(query, "curr_value"));
tmpPlAchives[uCollect] = SQL_ReadResult(query, SQL_FieldNameToNum(query, "ach_collect"));
#if AMXX_VERSION_NUM < 183
TrieDeleteKey(tmpTrie, tmpId)
#endif
TrieSetArray(tmpTrie, tmpId, tmpPlAchives, sizeof(tmpPlAchives));
}
SQL_NextRow(query);
}
if(get_pcvar_num(cpLogs))
log_to_file("gcms_achives.log", "[Core] Loaded achives for player [%s]: %d", AuthIDs[id], nums);
}
case QueryPlayerDisconnect: FnTrieClear(id); //client disconnected
}
}
FnTrieClear(id)
{
new Trie:tmpTrie;
if(TrieGetCell(g_Players, AuthIDs[id], tmpTrie))
TrieDestroy(tmpTrie);
TrieDeleteKey(g_Players, AuthIDs[id]);
}
public plugin_end()
if(arhAchIndex)
ArrayDestroy(arhAchIndex);
/*======== Меню игроков =========*/
public CmdShowAchievesMenu(id)
{
new MenuCB = menu_makecallback("Menu_callback");
new PlayersMenu = menu_create("\yДостижения игрока", "MuteMenu_Handler", 1);
new pID, szpID[3], players[MAX_PLAYERS], pnum;
get_players(players, pnum, "ch");
for (new i; i < pnum; i++)
{
pID = players[i];
num_to_str(pID, szpID, charsmax(szpID));
menu_additem(PlayersMenu, "", szpID, 0, MenuCB);
}
menu_setprop(PlayersMenu, MPROP_BACKNAME, "\yНазад");
menu_setprop(PlayersMenu, MPROP_NEXTNAME, "\yДалее");
menu_setprop(PlayersMenu, MPROP_EXITNAME, "\yВыход");
menu_display(id, PlayersMenu, 0);
return PLUGIN_CONTINUE;
}
public Menu_callback(id, menu, item)
{
new szData[3], callback, pID, szName[MAX_NAME_LENGTH], szFmtName[MAX_NAME_LENGTH*2];
menu_item_getinfo(menu, item, item_access, szData, charsmax(szData),_,_, callback);
pID = str_to_num(szData);
get_user_name(pID, szName, charsmax(szName));
szData[0] = cmsapi_is_user_member(pID);
formatex(szFmtName, charsmax(szFmtName), "%s%s", szData[0] ? "\y" : "\r", szName);
menu_item_setname(menu, item, szFmtName);
}
public MuteMenu_Handler(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu);
return PLUGIN_HANDLED;
}
new item_callback, szData[7];
menu_item_getinfo(menu, item, item_access, szData, charsmax(szData), _, _, item_callback);
new pID = str_to_num(szData);
new motd[MAX_STRING_LEN*4];
formatex(motd, charsmax(motd), "http://%s/modules_extra/cms_achievs/index.php?auth=%s", SiteUrl, AuthIDs[pID]);
show_motd(id, motd,"Достижения");
new imenu, newmenu, menupage;
player_menu_info(id, imenu, newmenu, menupage);
menu_display(id, menu, menupage);
return PLUGIN_HANDLED;
}
/*==============================================================*/
public plugin_natives()
{
set_native_filter("native_filter");
register_native("CmsActivateAchive", "native_CmsActivateAchive");
register_native("CmsGetAchiveInfoByKey", "native_CmsGetAchiveInfoByKey");
register_native("CmsGetAchivesCount", "native_CmsGetAchivesCount");
register_native("CmsGetAchivesIndexes", "native_CmsGetAchivesIndexes");
register_native("CmsCheckPlayerAchive", "native_CmsCheckPlayerAchive");
register_native("CmsGetPlayerAchive", "native_CmsGetPlayerAchive");
}
public native_filter(const name[], index, trap)
return !trap ? PLUGIN_HANDLED : PLUGIN_CONTINUE;
public native_CmsActivateAchive()
{
new tmpAchives[eAchivesData];
tmpAchives[achUnicId] = get_param(1);
if(TrieSetArray(trhAchives, get_id_key(tmpAchives[achUnicId]), tmpAchives, sizeof(tmpAchives)))
if(ArrayPushCell(arhAchIndex, tmpAchives[achUnicId]))
return 1;
return 0;
}
/**
* Получение данных о достижении по его уникальному номеру
*
* @idKey - уникальный номер достижения
* @RusName[] - название достижения на русском
* @nameLen - размер буфера для записи названия
* @Avatar[] - название (имя файла) картинки (аватара)
* @avaLen - размер буфера для записи названия аватара
* @return Возвратит количество действий, необходимое для открытия достижения
*
native CmsGetAchiveInfoByKey(idKey, RusName[], nameLen, Avatar[], avaLen)
*/
public native_CmsGetAchiveInfoByKey()
{
new itemNum = get_param(1);
if(!itemNum)
return 0;
new tmpAchives[eAchivesData]
if(TrieGetArray(trhAchives, get_id_key(itemNum), tmpAchives, sizeof(tmpAchives)))
{
set_string(2, tmpAchives[achRusName], get_param(3));
set_string(4, tmpAchives[achAvatar], get_param(5));
return tmpAchives[achValue];
}
return 0;
}
public native_CmsGetAchivesCount()
return ArraySize(arhAchIndex);
public native_CmsGetAchivesIndexes()
{
new nHandle[MAX_INT_LEN];
formatex(nHandle, charsmax(nHandle), "%d", arhAchIndex);
return str_to_num(nHandle);
}
public native_CmsCheckPlayerAchive()
{
new id = get_param(1);
new achKey[MAX_INT_LEN], status = AchStatusNone;
new iAchKey = get_param(2);
num_to_str(iAchKey, achKey, charsmax(achKey));
if(!achKey[0] || !id)
return status;
new Trie:tmpTrie;
if(!TrieGetCell(g_Players, AuthIDs[id], tmpTrie))
return status;
new tmpAchives[eAchivesData]
if(!TrieGetArray(trhAchives, achKey, tmpAchives, sizeof(tmpAchives)))
return status;
new tmpClAchives[eAchPlData];
if(TrieGetArray(tmpTrie, achKey, tmpClAchives, sizeof(tmpClAchives)))
{
if(tmpClAchives[uCollect])
{
return AchStatusCompleted;
}
}
new val = get_param(3);
tmpClAchives[uCurrValue] += val ? val : 1;
if(tmpClAchives[uFullValue] + tmpClAchives[uCurrValue] >= tmpAchives[achValue])
{
tmpClAchives[uCollect] = 1;
format_time(tmpClAchives[uCollDate], charsmax(tmpClAchives[uCollDate]), "%Y-%m-%d %H:%M:%S");
status = AchStatusJustCompleted;
if(!bool:get_param(4))
FnOnAchivesComplete(id, tmpAchives[achUnicId], tmpAchives[achRusName], tmpAchives[achAvatar]);
if(get_pcvar_num(cpLogs))
log_to_file("gcms_achives.log", "Игрок %s открыл достижение %s", AuthIDs[id], tmpAchives[achRusName]);
if(g_fwd_OnAchievesComplete)
ExecuteForward(g_fwd_OnAchievesComplete, retFwd, id, iAchKey);
}
else
status = AchStatusInProgress;
tmpClAchives[uModify] = true;
#if AMXX_VERSION_NUM < 183
TrieDeleteKey(tmpTrie, achKey);
#endif
TrieSetArray(tmpTrie, achKey, tmpClAchives, sizeof(tmpClAchives));
return status;
}
//native CmsGetkPlayerAchive(plId, achID, &currValue = 0, &maxValue = 0); //проверка ачивки
public native_CmsGetPlayerAchive()
{
new id = get_param(1);
new achKey[MAX_INT_LEN], status = AchStatusNone;
num_to_str(get_param(2), achKey, charsmax(achKey));
if(!achKey[0] || !id)
return status;
new Trie:tmpTrie;
if(!TrieGetCell(g_Players, AuthIDs[id], tmpTrie))
return status;
new tmpAchives[eAchivesData];
if(!TrieGetArray(trhAchives, achKey, tmpAchives, sizeof(tmpAchives)))
return status;
new tmpClAchives[eAchPlData];
if(TrieGetArray(tmpTrie, achKey, tmpClAchives, sizeof(tmpClAchives)))
{
set_param_byref(3, (tmpClAchives[uFullValue] + tmpClAchives[uCurrValue]));
set_param_byref(4, tmpAchives[achValue]);
if(tmpClAchives[uCollect])
return AchStatusCompleted;
return AchStatusInProgress;
}
return status;
}
public FnOnAchivesComplete(id, Key, RusName[], Avatar[])
{
new Name[MAX_NAME_LENGTH];
get_user_name(id, Name, charsmax(Name));
client_cmd(0, "play %s", gSoundNewLevel);
client_print_color(0, 0, "^1Игрок ^4%s ^1открыл достижение ^4%s", Name, RusName);
if(get_pcvar_num(cpRegPlayerOnly))
if(!regUserId[id])
client_print_color(id, 0, "^4Достижения не сохраняются! ^1Для сохранения, зарегистрируйся на ^4%s", SiteUrl);
client_print_color(id, 0, "^1Для просмотра достижений пиши ^4/achievs");
}