В смысле "разжевывания"? Там и так все очевидно. Единственое, что вызывает трудности, так это usecash - сложная процедура. Я помню когда то прописал логгинг всех вызовов процедур в таблицу. Интересная картина вышла. На счет юзкеша - учитывая ее линейную структуру можно ее проанализировать. *На правах оффа: сейчас я анализирую выполнение гораздо более сложных процедур в gamedbd и gdeliveryd*
Проанализируем mySql процедуры. 代码: PROCEDURE `acquireuserpasswd`(in name1 VARCHAR(64), out uid1 INTEGER, out passwd1 VARCHAR(64)) BEGIN SELECT id, passwd INTO uid1, passwd1 FROM users WHERE name = name1; END На вход идет логин игрока, на выходе дает id и хеш пароля. Для чего нужна и так понятно. Ищет строку у которой логин (name) совпадает со входным параметром (name1). Если такая находится, то возвращает ее айди (uid) и пароль (passwd) в выходные параметры (uid1 и passwd1 соответственно). 代码: PROCEDURE `addForbid`(in userid1 INTEGER, in type1 INTEGER, in forbid_time1 INTEGER, in reason1 BINARY(255), in gmroleid1 INTEGER) BEGIN DECLARE rowcount INTEGER; START TRANSACTION; UPDATE forbid SET ctime = now(), forbid_time = forbid_time1, reason = reason1, gmroleid = gmroleid1 WHERE userid = userid1 AND type = type1; SET rowcount = ROW_COUNT(); IF rowcount = 0 THEN INSERT INTO forbid VALUES(userid1, type1, now(), forbid_time1, reason1, gmroleid); END IF; COMMIT; END Добавляет бан на аккаунт. userid1 - айди пользователя, type1 - тип бана (вроде 100 на вход, 101 на чат, остальные вроде не играют роли), forbid_time1 - длинна бана в секундах, reason1 - причина бана (бинарный код текста в UTF16), gmroleid1 - айди персонажа ГМа, который дал бан. Если бан такого типа для такого же игрока существует, то лишь обновляется время в таблице, причина и гмайди. Иначе же создается новая строка для этого бана. 代码: PROCEDURE `addGM`(in userid INTEGER, in zoneid INTEGER) BEGIN DECLARE x INTEGER; START TRANSACTION; SET x = 0; WHILE x < 12 DO INSERT INTO auth VALUES (userid, zoneid, x); SET x = x + 1; END WHILE; SET x = 100; WHILE x < 106 DO INSERT INTO auth VALUES (userid, zoneid, x); SET x = x + 1; END WHILE; SET x = 200; WHILE x < 215 DO INSERT INTO auth VALUES (userid, zoneid, x); SET x = x + 1; END WHILE; SET x = 500; WHILE x < 519 DO INSERT INTO auth VALUES (userid, zoneid, x); SET x = x + 1; END WHILE; COMMIT; END Фактически, делает пользователя с айди userid ГМом в зоне zoneid. Если разжевать выполнение, то дает ему все привилегии которые нужны ГМу (да впрочем и те, что не нужны). К ним относятся: 0-11, 100-105, 200-214, 500-518. 代码: PROCEDURE `adduser`( in name1 VARCHAR(64), in passwd1 VARCHAR(64), in prompt1 VARCHAR(32), in answer1 VARCHAR(32), in truename1 VARCHAR(32), in idnumber1 VARCHAR(32), in email1 VARCHAR(32), in mobilenumber1 VARCHAR(32), in province1 VARCHAR(32), in city1 VARCHAR(32), in phonenumber1 VARCHAR(32), in address1 VARCHAR(64), in postalcode1 VARCHAR(8), in gender1 INTEGER, in birthday1 VARCHAR(32), in qq1 VARCHAR(32), in passwd21 VARCHAR(64) ) BEGIN DECLARE idtemp INTEGER; SELECT IFNULL(MAX(id), 16) + 16 INTO idtemp FROM users; INSERT INTO users (id,name,passwd,prompt,answer,truename,idnumber,email,mobilenumber,province,city,phonenumber,address,postalcode,gender,birthday,creatime,qq,passwd2) VALUES( idtemp, name1, passwd1, prompt1, answer1, truename1, idnumber1, email1, mobilenumber1, province1, city1, phonenumber1, address1, postalcode1, gender1, birthday1, now(), qq1, passwd21 ); END Создает пользователя со всема укзаными данными, с айди, который вычисляется следующим образом: Если не сущестует пользователей, то IFNULL(..., 16) вернет 16, а айди будет 32. Если пользователи существуют, то IFNULL(MAX(id), ...) вернет MAX(id), и соответственно айди пользователя будет (макс айди в базе) + 16. 代码: PROCEDURE `adduserpoint`(in uid1 INTEGER, in aid1 INTEGER, in time1 INTEGER) BEGIN DECLARE rowcount INTEGER; START TRANSACTION; UPDATE point SET time = IFNULL(time,0) + time1 WHERE uid1 = uid AND aid1 = aid; SET rowcount = ROW_COUNT(); IF rowcount = 0 THEN INSERT INTO point (uid,aid,time) VALUES (uid1,aid1,time1); END IF; COMMIT; END Обновляет проведенное время игроком в игре, используется вроде на серверах world1, где игровое время платное. К тому же относится и параметр freecreatetime в gdelivery. 代码: PROCEDURE `addUserPriv`(in userid INTEGER, in zoneid INTEGER, in rid INTEGER) BEGIN START TRANSACTION; INSERT INTO auth VALUES(userid, zoneid, rid); COMMIT; END Выдает пользователю конкретную ГМ привилегию (В отличие от addGM который выдает все возможные). 代码: PROCEDURE `changePasswd`(in name1 VARCHAR(64), in passwd1 VARCHAR(64)) BEGIN START TRANSACTION; UPDATE users SET passwd = passwd1 WHERE name = name1; COMMIT; END Меняет пароль пользователя. Интереса не представляет. Функция changePasswd2 делает аналогично для второго пароля который нашими mauthd и authd не используется. 代码: PROCEDURE `clearonlinerecords`(in zoneid1 INTEGER, in aid1 INTEGER) BEGIN START TRANSACTION; UPDATE point SET zoneid = NULL, zonelocalid = NULL WHERE aid = aid1 AND zoneid = zoneid1; COMMIT; END Вызывается при запуске mauthd. Как видим очищает записи онлайн в таблице point для зоны zoneid1 и (хзчего) aid1. 代码: PROCEDURE `deleteTimeoutForbid`(in userid1 INTEGER) BEGIN START TRANSACTION; DELETE FROM forbid WHERE userid = userid1 AND timestampdiff(second, ctime, now()) > forbid_time; COMMIT; END Насколько я помню вызывается каждый раз перед логином. Очищает баны которые уже истекли. 代码: PROCEDURE `delUserPriv`(in userid1 INTEGER, in zoneid1 INTEGER, in rid1 INTEGER, in deltype1 INTEGER) BEGIN START TRANSACTION; IF deltype1 = 0 THEN DELETE FROM auth WHERE userid = userid1 AND zoneid = zoneid1 AND rid = rid1; ELSE IF deltype1 = 1 THEN DELETE FROM auth WHERE userid = userid1 AND zoneid = zoneid1; ELSE IF deltype1 = 2 THEN DELETE FROM auth WHERE userid = userid1; END IF; END IF; END IF; COMMIT; END Достаточно интересная функция. Убирает некоторые ГМ привилегии у пользователя userid. Если deltype1 равен нулю, то убирает конретную привилегию rid1 в конкретной зоне zoneid1. Если deltype1 равен единице, то убирает ВСЕ привилегии в конкретной зоне zoneid1. Если deltype1 равен двум, то убирает ВСЕ привилегии во ВСЕХ зонах. 代码: PROCEDURE `enableiplimit`(in uid1 INTEGER, in enable1 CHAR(1)) BEGIN DECLARE rowcount INTEGER; START TRANSACTION; UPDATE iplimit SET enable=enable1 WHERE uid=uid1; SET rowcount = ROW_COUNT(); IF rowcount = 0 THEN INSERT INTO iplimit (uid,enable) VALUES (uid1,enable1); END IF; COMMIT; END Включает iplimit (для включения enable1 должен быть t, иначе работать не будет). Проанализировав код mauth сделал вывод что iplimit в чистом виде нигде не используется. Решил так, потому что функция checkIpLimit нигде не вызывается. Но айпилимит можно использовать и для других целей (см ниже). 代码: PROCEDURE `lockuser`(in uid1 INTEGER, in lockstatus1 CHAR(1)) BEGIN DECLARE rowcount INTEGER; START TRANSACTION; UPDATE iplimit SET lockstatus=lockstatus1 WHERE uid=uid1; SET rowcount = ROW_COUNT(); IF rowcount = 0 THEN INSERT INTO iplimit (uid,lockstatus,enable) VALUES (uid1,lockstatus1,'t'); END IF; COMMIT; END Если lockstatus1 равен t, то перманентно банит пользователя uid1. У него всегда будет выдаватся в причине бана какие то китайские буквы, а бан постоянно будет на где-то 576 тыс минут (и это число уменьшатся не будет). Проверено. Для разбана нужно ставить lockstatus1 что угодно, но только не t. 代码: PROCEDURE `recordoffline`(in uid1 INTEGER, in aid1 INTEGER, inout zoneid1 INTEGER, inout zonelocalid1 INTEGER, inout overwrite1 INTEGER) BEGIN DECLARE rowcount INTEGER; START TRANSACTION; UPDATE point SET zoneid = NULL, zonelocalid = NULL WHERE uid = uid1 AND aid = aid1 AND zoneid = zoneid1; SET rowcount = ROW_COUNT(); IF overwrite1 = rowcount THEN SELECT zoneid, zonelocalid INTO zoneid1, zonelocalid1 FROM point WHERE uid = uid1 AND aid = aid1; END IF; COMMIT; END Вызывается когда игрок уходит в офф. Аналогично clearOnlineRecords, но очищает данные лишь для конкретного пользователя uid1. 代码: PROCEDURE `recordonline`(in uid1 INTEGER, in aid1 INTEGER, inout zoneid1 INTEGER, inout zonelocalid1 INTEGER, inout overwrite INTEGER) BEGIN DECLARE tmp_zoneid INTEGER; DECLARE tmp_zonelocalid INTEGER; DECLARE rowcount INTEGER; START TRANSACTION; SELECT SQL_CALC_FOUND_ROWS zoneid, zonelocalid INTO tmp_zoneid, tmp_zonelocalid FROM point WHERE uid = uid1 and aid = aid1; SET rowcount = FOUND_ROWS(); IF rowcount = 0 THEN INSERT INTO point (uid, aid, time, zoneid, zonelocalid, lastlogin) VALUES (uid1, aid1, 0, zoneid1, zonelocalid1, now()); ELSE IF tmp_zoneid IS NULL OR overwrite = 1 THEN UPDATE point SET zoneid = zoneid1, zonelocalid = zonelocalid1, lastlogin = now() WHERE uid = uid1 AND aid = aid1; END IF; END IF; IF tmp_zoneid IS NULL THEN SET overwrite = 1; ELSE SET zoneid1 = tmp_zoneid; SET zonelocalid1 = tmp_zonelocalid; END IF; COMMIT; END Вызывается при входе пользователя в игру. Устанавливает в пойнте текущую зону, локалайди, и дату последнего входа. По неподтвержденным данным позволяет изменить зону, на которую входит игрок. !!! 代码: PROCEDURE `remaintime`(in uid1 INTEGER, in aid1 INTEGER, out remain INTEGER, out freetimeleft INTEGER) BEGIN DECLARE enddate1 DATETIME; DECLARE now1 DATETIME; DECLARE rowcount INTEGER; START TRANSACTION; SET now1 = now(); IF aid1 = 0 THEN SET remain = 86313600; SET enddate1 = date_add(now1, INTERVAL '30' DAY); ELSE SELECT time, IFNULL(enddate, now1) INTO remain, enddate1 FROM point WHERE uid = uid1 AND aid = aid1; SET rowcount = ROW_COUNT(); IF rowcount = 0 THEN SET remain = 0; INSERT INTO point (uid,aid,time) VALUES (uid1, aid1, remain); END IF; END IF; SET freetimeleft = 0; IF enddate1 > now1 THEN SET freetimeleft = timestampdiff(second, now1, enddate1); END IF; COMMIT; END Вычисляет количество оставшегося бесплатного игрового времени. Используется на world1 серверах. 代码: PROCEDURE `setiplimit`(in uid1 INTEGER, in ipaddr11 INTEGER, in ipmask11 VARCHAR(2), in ipaddr21 INTEGER, in ipmask21 VARCHAR(2), in ipaddr31 INTEGER, in ipmask31 VARCHAR(2), in enable1 CHAR(1)) BEGIN DECLARE rowcount INTEGER; START TRANSACTION; UPDATE iplimit SET ipaddr1 = ipaddr11, ipmask1 = ipmask11, ipaddr2 = ipaddr21, ipmask2 = ipmask21, ipaddr3 = ipaddr31, ipmask3 = ipmask31 WHERE uid = uid1; SET rowcount = ROW_COUNT(); IF rowcount = 0 THEN INSERT INTO iplimit (uid, ipaddr1, ipmask1, ipaddr2, ipmask2, ipaddr3, ipmask3, enable1) VALUES (uid1, ipaddr11, ipmask11, ipaddr21, ipmask21, ipaddr31, ipmask31,'t'); END IF; COMMIT; END Устанавливает айпилимит, который к сожалению с нашими mauthd не работает. Если найду время перекомпилирую mauthd. 代码: PROCEDURE `updateUserInfo`( in name1 VARCHAR(32), in prompt1 VARCHAR(32), in answer1 VARCHAR(32), in truename1 VARCHAR(32), in idnumber1 VARCHAR(32), in email1 VARCHAR(32), in mobilenumber1 VARCHAR(32), in province1 VARCHAR(32), in city1 VARCHAR(32), in phonenumber1 VARCHAR(32), in address1 VARCHAR(32), in postalcode1 VARCHAR(32), in gender1 INTEGER, in birthday1 VARCHAR(32), in qq1 VARCHAR(32) ) BEGIN START TRANSACTION; UPDATE users SET prompt = prompt1, answer = answer1, truename = truename1, idnumber = idnumber1, email = email1, mobilenumber = mobilenumber1, province = province1, city = city1, phonenumber = phonenumber1, address = address1, postalcode = postalcode1, gender = gender1, birthday = birthda1, qq = qq1 WHERE name = name1; COMMIT; END Изменяет информацию об пользователе. При этом логин и пароли не меняет. 代码: PROCEDURE `usecash`( in userid1 INTEGER, in zoneid1 INTEGER, in sn1 INTEGER, in aid1 INTEGER, in point1 INTEGER, in cash1 INTEGER, in status1 INTEGER, out error INTEGER ) BEGIN DECLARE sn_old INTEGER; DECLARE aid_old INTEGER; DECLARE point_old INTEGER; DECLARE cash_old INTEGER; DECLARE status_old INTEGER; DECLARE createtime_old DATETIME; DECLARE time_old INTEGER; DECLARE need_restore INTEGER; DECLARE exists1 INTEGER; DECLARE rowcount INTEGER; START TRANSACTION; SET error = 0; SET need_restore = 0; SELECT SQL_CALC_FOUND_ROWS sn, aid, point, cash, status, creatime INTO sn_old, aid_old, point_old, cash_old, status_old, createtime_old FROM usecashnow WHERE userid = userid1 AND zoneid = zoneid1 AND sn >= 0; SET rowcount = FOUND_ROWS(); IF rowcount = 1 THEN SET exists1 = 1; ELSE SET exists1 = 0; END IF; IF status1 = 0 THEN IF exists1 = 0 THEN SELECT aid, point INTO aid1, point1 FROM usecashnow WHERE userid = userid1 AND zoneid = zoneid1 AND sn = sn1; SET point1 = IFNULL(point1,0); UPDATE point SET time = time-point1 WHERE uid = userid1 AND aid = aid1 AND time >= point1; SET rowcount = ROW_COUNT(); IF rowcount = 1 THEN UPDATE usecashnow SET sn = 0, status = 1 WHERE userid = userid1 AND zoneid = zoneid1 AND sn = sn1; ELSE SET error = -8; END IF; END IF; ELSE IF status1 = 1 THEN IF exists1 = 0 THEN UPDATE point SET time = time-point1 WHERE uid = userid1 AND aid = aid1 AND time >= point1; SET rowcount = ROW_COUNT(); IF rowcount = 1 THEN INSERT INTO usecashnow (userid, zoneid, sn, aid, point, cash, status, creatime) VALUES (userid1, zoneid1, sn1, aid1, point1, cash1, status1, now()); ELSE INSERT INTO usecashnow SELECT userid1, zoneid1, IFNULL(min(sn),0)-1, aid1, point1, cash1, 0, now() FROM usecashnow WHERE userid = userid1 AND zoneid = zoneid1 AND 0 >= sn; SET error = -8; END IF; ELSE INSERT INTO usecashnow SELECT userid1, zoneid1, IFNULL(min(sn),0)-1, aid1, point1, cash1, 0, now() FROM usecashnow WHERE userid = userid1 AND zoneid = zoneid1 AND 0 >= sn; SET error = -7; END IF; ELSE IF status1 = 2 THEN IF exists1 = 1 AND status_old = 1 AND sn_old = 0 THEN UPDATE usecashnow SET sn = sn1, status = status1 WHERE userid = userid1 AND zoneid = zoneid1 AND sn = sn_old; ELSE SET error = -9; END IF; ELSE IF status1 = 3 THEN IF exists1 = 1 AND status_old = 2 THEN UPDATE usecashnow SET status = status1 WHERE userid = userid1 AND zoneid = zoneid1 AND sn = sn_old; ELSE SET error = -10; END IF; ELSE IF status1 = 4 THEN IF exists1 = 1 THEN DELETE FROM usecashnow WHERE userid = userid1 AND zoneid = zoneid1 AND sn = sn_old; INSERT INTO usecashlog (userid, zoneid, sn, aid, point, cash, status, creatime, fintime) VALUES (userid1, zoneid1, sn_old, aid_old, point_old, cash_old, status1, createtime_old, now()); END IF; IF NOT (exists1 = 1 AND status_old = 3) THEN SET error = -11; END IF; ELSE SET error = -12; END IF; END IF; END IF; END IF; END IF; IF need_restore = 1 THEN UPDATE point SET time = time+point_old WHERE uid = userid1 AND aid = aid_old; DELETE FROM usecashnow WHERE userid = userid1 AND zoneid = zoneid1 AND sn = sn_old; INSERT INTO usecashlog (userid, zoneid, sn, aid, point, cash, status, creatime, fintime) VALUES (userid1, zoneid1, sn_old, aid_old, point_old, cash_old, status1, createtime_old, now()); END IF; COMMIT; END Нами функция используется для выдачи голда игроку. На серверах с ограниченым игровым временем может использоватся для продления доступного игрового времени за голды. Если чесно, сам лично этого не проверял. Также чинит незавершенные транзакции с голдом.
Спасибо огромное. А можешь еще написать примеры запросов следующих процедур: - addforbid (немного не догнал с кодировкой причиной) - adduser (и немного подробней про пароль) - changePasswd (то есть: не представляет интереса? не меняет пароль?) - updateUserInfo (то есть этим запросом обновляется данные, даже если человек онлайн?)
updateUserInfo меняет данные пользователя в базе данных. эти данные нам интереса особого не представляют, так как использовались китаезами для их ЛК. я например в них храню ссылку на PWSAI пользователя в другой базе данных и айпи адрес регистрации. если перевести, то данные такие: 代码: PROCEDURE `updateUserInfo`( in name1 VARCHAR(32), - логин, данные которого будем менять in prompt1 VARCHAR(32), - секретный вопрос in answer1 VARCHAR(32), - секретный ответ in truename1 VARCHAR(32), - настоящее имя in idnumber1 VARCHAR(32), - идентификатор (у каждого чела в китае есть свой идент) in email1 VARCHAR(32), - электронная почта in mobilenumber1 VARCHAR(32), - мобильный телефон in province1 VARCHAR(32), - провинция in city1 VARCHAR(32), - город in phonenumber1 VARCHAR(32), - домашний телефон in address1 VARCHAR(32), - адрес in postalcode1 VARCHAR(32), - почтовый код in gender1 INTEGER, - пол in birthday1 VARCHAR(32), - день рождения in qq1 VARCHAR(32) - ??? ) На счет changePasswd - я имею ввиду что очень просто. Я лично меняю пароль через прямой запрос update в базу данных. На счет adduser - формат пароля зависит от реализации auth. Например, в оригинальном auth для mssql пароль хранится в бинарном md5 от конкетанации логина и пароля. В mauthd, он еще берется в base64 и хранится в тексте. На счет addforbid, не помню точно то ли там UTF16le то ли там UTF16be, когда вернусь и проверю напишу точно.
1) То есть идею об updateUserInfo для топа ПК можно выкидывать? 2) changePasswd - можно для даунов: пример запроса?) Или как ты делааешь, тоже пример запроса) 3) adduser - Binary(md5(passwd)) что ли? Если да, какой принцип кодирования в binary? В mauthd будет base64(binary(md5(passwd)))?
Если в php, то в mssql пароль будет md5($login.$password,true), а в mysql base64_encode(md5($login.$password,true)) 代码: $hash = base64_encode(md5($login.$password,true)); mysql_query("call changePasswd($login,$hash)"); И как ты данные об количестве ПК вытащишь?
Ну вообще они из айвеба берутся, который в свою очередь берет данные из gamedbd, которая не имеет ничего общемго с мускулом, **ять...
А разве у нас в gamedbd ведется учет количества ПК? *Черт*, это же возможно та неизвесная переменная, которую я недавно в протоколе GRoleDetail нашел... *Ушел ковырять GetRole обратно*
А, ты про количество времени ПК. А я про именно количество ПК. А не часов. ХМЛ это не юзерфрендли данные, самые юзерфрендли это данные потока. Ты вот это имеешь ввиду? 代码: Object GetRoleStatus: (a548ebc380d055fb1674e1e8a3f80217.class) Opcode: Arg=3015, Res=3015 Size: 1080 Xid: 87442 Arg: >Object RoleId: (3354c9e579b7d61f66e8ff297be4feae.class) > roleid: 34 Res: >Object RoleStatusRes: (8763e50cbe6b27946a7573db185a977d.class) > retcode: 0 > value: >Object GRoleStatus: (7f0b40a81bc26d1d596e284744b6ac83.class) > version: 1 > level: 1 > level2: 8 > exp: 0 > sp: 0 > pp: 0 > hp: 50 > mp: 70 > posx: 1263.2598877 > posy: 223.293426514 > posz: 1029.36865234 > worldtag: 1 >>>>>>> invader_state: 0 >>>>>>> invader_time: 0 >>>>>>> pariah_time: 0 > reputation: 200000 > custom_status: > [S] > [H] > filter_data: > [S] �� > [H] 0000000000000000 > charactermode: > [S] > [H] > instancekeylist: > [S] [Click to display] > [H] [Click to display] > dbltime_expire: 0 > dbltime_mode: 0 > dbltime_begin: 1279486800 > dbltime_used: 0 > dbltime_max: 50400 > time_used: 5409 > dbltime_data: > [S] ���䱉 > [H] 010000000000000000000000000000005000f40049004c00 > storesize: 0 > petcorral: > [S] � > [H] 0000000000000a000000 > property: > [S] ����2�F����䀀馚䂙�䁀�䂠������䀠����������������������������������� Ə� > [H] [Click to display] > var_data: > [S] �������ﻬꑺ����P���㬌ቖ줰ຑ㬌ቖˆ� > [H] [Click to display] > skills: > [S] �q����}����§���� > [H] [Click to display] > storehousepasswd: > [S] > [H] > waypointlist: > [S] ᑑ > [H] 51001400 > coolingtime: > [S] �� > [H] ff00ff00ff00ff000000000000000000 > reserved1: 0 > reserved2: 0 > reserved3: 0 > reserved4: 0
Это и есть XML, правда полученный через мое приложение. Я кстати нашел где же находится количество ПК убийств персонажа. А количество смертей и ПВП пока не нашел :lol:
Количество смертей и убийств персонажа можно посчитать пропарсив логи, но это конечно не оч вариант, если есть другой)
Спасибо, капитан. Но этот вариант я рассматриваю только если не найду другой. Когда закончу разбор GRoleDetail посмотрим найду или не найду эти количества. Кстати дам подсказку, что количество ПК находится в одной из октетных переменных в GRoleStatus.