Ultima modifica da un moderatore:
Intro:
Questo codice può esesere usato sia in lib scritte decentemente che nei source di game e creerà le seguenti funzioni nell'environment del lua:
mysql_direct_query, get_table_postfix, mysql_escape_string
mysql_direct_query ritorna due valori: il primo contiene il conteggio delle "affected rows" della query (valido per select, insert, update e così via) e il secondo, una table contenente tutti i valori ritornati. (vuota se nulla)
Il field type verrà auto-rilevato, il che significa:
Esempio: (Usa CTRL+Q e CTRL+K su notepad++ per de/commentare velocemente)
How To:
Copia ciò su questlua_global.cpp fuori dal namespace quest:
Copiale su questlua_global.cpp dentro il namespace quest:
Dentro global_functions, incollate:
Da sapere:
Altro:
1) Non sarai rallentato da questa feature (fin quando crei query decenti)
2) Non incorrerai in problemi di sicurezza se escaperai (con la funzione lua che ho creato) cose potenzialmente pericolose (come gli input() inviati dai player che interagiscono direttamente con la query)
3) Almeno, evita di dare un grant all al mysql account usato dal game/db (select, insert, update, delete sono più che sufficienti)
Note:
1) Il primo valore ritornato dalla direct_query function potrebbe contenere valori come -2147483648 (duplicate entry) e 4294967295 (syntax error) quando avviene un errore.
2) Il limite massimo di una query nel game secondo l'implementazione standard della ymir è di 4096 caratteri e % deve essere "escapizzato" con %% (va_arg issue)
Semplice esempio di classifica:
Questo codice può esesere usato sia in lib scritte decentemente che nei source di game e creerà le seguenti funzioni nell'environment del lua:
mysql_direct_query, get_table_postfix, mysql_escape_string
mysql_direct_query ritorna due valori: il primo contiene il conteggio delle "affected rows" della query (valido per select, insert, update e così via) e il secondo, una table contenente tutti i valori ritornati. (vuota se nulla)
Il field type verrà auto-rilevato, il che significa:
- Un campo numerico verrà pushato come lua number
- Un campo BLOB verrà pushato come table byte per byte
- Un campo NULL verrà pushato come nil (non mostrato quando si itera)
- I restanti verranno pushati a mo' di stringhe (tipo stringhe ed altri tipi di dati non catalogati da me)
Esempio: (Usa CTRL+Q e CTRL+K su notepad++ per de/commentare velocemente)
Codice:
-- local res1, res2 = mysql_direct_query("select player.name, player.level from player.player limit 2;")
local res1, res2 = mysql_direct_query("select * from player.player limit 1;")
-- local res1, res2 = mysql_direct_query("select name, skill_level from player.player limit 1;")
syschat(res2[1].name)
-- syschat(string.format("count(%d)", res1))
-- for i1=1, res1 do
-- syschat(string.format("\tindex(%d)", i1))
-- syschat(string.format("\t\tname(%s), level(%d)", res2[i1].name, res2[i1].level))
-- end
-- local res1, res2 = mysql_direct_query("select * from player.guild_comment;")
-- local res1, res2 = mysql_direct_query("INSERT INTO `guild_comment` (`id`) VALUES ('1');")
-- local res1, res2 = mysql_direct_query("INSERT INTO `guild_comment` (`guild_id`, `name`, `notice`, `content`, `time`) VALUES ('1', 'martytest', '0', 'blabla', NOW());")
syschat(string.format("count(%d)", res1))
for num1, str1 in ipairs(res2) do
syschat(string.format("\tindex(%d)", num1))
for key1, val1 in pairs(str1) do
-- syschat(string.format("\t\tkey(%s), value(%s)", key1, val1))
-- syschat(string.format("\t\tkey(%s), value(%s) type(%s)", key1, val1, type(val1)))
if (type(val1)=="table") then
syschat(string.format("\t\tkey(%s), size(%d), type(%s)", key1, table.getn(val1), type(val1)))
-- syschat(string.format("\t\tkey(%s), type(%s)", key1, type(val1)))
-- for num2, str2 in ipairs(val1) do
-- syschat(string.format("\t\t\tidx(%s), value(%s), type(%s)", num2, str2, type(num2)))
-- end
-- syschat(string.format("\t\tkey(%s), value(%s), type(%s)", key1, table.concat(val1, ", "), type(val1))) --client will crash
else
syschat(string.format("\t\tkey(%s), value(%s), type(%s)", key1, val1, type(val1)))
end
end
end
-- syschat(mysql_escape_string("abyy"))
-- syschat(mysql_escape_string("'schure'")) --\'schure\'
syschat(mysql_escape_string("'\"lewd'\"")) --\'\"lewd\'\"
-- syschat(mysql_escape_string("`\n``\t`"))
syschat(mysql_escape_string([["aww'omg"<?'3]])) --\"aww\'omg\"<?\'3
How To:
Copia ciò su questlua_global.cpp fuori dal namespace quest:
Codice:
#include "db.h"
Codice:
int _get_table_postfix(lua_State* L)
{
lua_pushstring(L, get_table_postfix());
return 1;
}
int _mysql_direct_query(lua_State* L)
{
// char szQuery[1024];
if (!lua_isstring(L, 1))
return 0;
// strncpy(szQuery, lua_tostring(L, 1), sizeof(szQuery));
int i=0, m=1;
MYSQL_ROW row;
MYSQL_FIELD * field;
MYSQL_RES * result;
// SQLMsg * pMsg = DBManager::instance().DirectQuery(szQuery);
std::auto_ptr<SQLMsg> pMsg(DBManager::instance().DirectQuery(lua_tostring(L, 1)));
if (pMsg.get())
{
// ret1 (number of affected rows)
lua_pushnumber(L, pMsg->Get()->uiAffectedRows); //-1 if error such as duplicate occurs (-2147483648)
// ret2 (table of affected rows)
lua_newtable(L);
if ((result = pMsg->Get()->pSQLResult) &&
!(pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1))
{
// LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
// ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %u fields\n", __FUNCTION__, mysql_num_fields(result));
// ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %u rows\n", __FUNCTION__, mysql_num_rows(result));
// ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Affected %u rows\n", __FUNCTION__, pMsg->Get()->uiAffectedRows);
// ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Num %u rows\n", __FUNCTION__, pMsg->Get()->uiNumRows);
while((row = mysql_fetch_row(result)))
{
lua_pushnumber(L, m);
lua_newtable(L);
while((field = mysql_fetch_field(result)))
{
lua_pushstring(L, field->name);
if (!(field->flags & NOT_NULL_FLAG) && (row[i]==NULL))
{
// lua_pushstring(L, "NULL");
lua_pushnil(L);
}
else if (IS_NUM(field->type))
{
double val = NAN;
lua_pushnumber(L, (sscanf(row[i],"%lf",&val)==1)?val:NAN);
}
else if (field->type == MYSQL_TYPE_BLOB)
{
lua_newtable(L);
for (DWORD iBlob=0; iBlob < field->max_length; iBlob++)
{
lua_pushnumber(L, row[i][iBlob]);
lua_rawseti(L, -2, iBlob+1);
}
}
else
lua_pushstring(L, row[i]);
// LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
// ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %d flag %s for %s\n", __FUNCTION__, field->type, field->name, row[i]?row[i]:"NULL");
lua_rawset(L, -3);
i++;
}
mysql_field_seek(result, 0);
i=0;
lua_rawset(L, -3);
m++;
}
}
}
else {lua_pushnumber(L, 0); lua_newtable(L);}
// delete pMsg;
return 2;
}
int _mysql_escape_string(lua_State* L)
{
char szQuery[1024] = {0};
if (!lua_isstring(L, 1))
return 0;
// mysql_escape_string(szQuery, lua_tostring(L, 1), sizeof(szQuery));
DBManager::instance().EscapeString(szQuery, sizeof(szQuery), lua_tostring(L, 1), strlen(lua_tostring(L, 1)));
lua_pushstring(L, szQuery);
return 1;
}
Dentro global_functions, incollate:
Codice:
{ "get_table_postfix", _get_table_postfix },
{ "mysql_direct_query", _mysql_direct_query },
{ "mysql_escape_string", _mysql_escape_string },
Da sapere:
Altro:
1) Non sarai rallentato da questa feature (fin quando crei query decenti)
2) Non incorrerai in problemi di sicurezza se escaperai (con la funzione lua che ho creato) cose potenzialmente pericolose (come gli input() inviati dai player che interagiscono direttamente con la query)
3) Almeno, evita di dare un grant all al mysql account usato dal game/db (select, insert, update, delete sono più che sufficienti)
Note:
1) Il primo valore ritornato dalla direct_query function potrebbe contenere valori come -2147483648 (duplicate entry) e 4294967295 (syntax error) quando avviene un errore.
2) Il limite massimo di una query nel game secondo l'implementazione standard della ymir è di 4096 caratteri e % deve essere "escapizzato" con %% (va_arg issue)
Semplice esempio di classifica:
Codice:
local query=[[
SELECT player.player.name as m_name, player.player.level as m_level, player.player.exp as m_exp
FROM player.player, account.account
WHERE
player.player.name not like '[%%]%%' AND
date_sub(now(), interval 1 day) < player.player.last_play AND
player.player.account_id=account.account.id AND
account.account.status='OK'
ORDER BY player.player.level DESC, player.player.exp DESC, player.player.name ASC
LIMIT 10;
]]
local res1, res2 = mysql_direct_query(query)
say_title("Top "..res1.." players.")
for num1, str1 in ipairs(res2) do
say_reward(num1.."\t"..str1.m_name.."\t"..str1.m_level.."\t"..str1.m_exp)
end