- Регистрация
- 3 Июн 2013
- Сообщения
- 99
- Лучшие ответы
- 0
- Репутация
- 13
Представляю вашему вниманию оригинальную систему регистрации.
Подобного вида система используется на RP-Gameworld. Уверен, данная система внесет каплю разнообразия на ваш сервер.
Видео
- демонстрация системы
Описание
Каждый пункт динамичен и выполнен в виде переключателя. С помощью этой системы вы сможете собрать в одном месте все поля для ввода данных, которые вам необходимо получить от игрока (к примеру, email, код безопасности, раса и т.д., и т.п.)
Скрипт тестировался на чистом проекте, который я приложу в конце темы и создавался с ориентиром на использование его в модах типа "с нуля". Но приложив совсем чуть-чуть усилий, его можно с легкостью адаптировать под любой мод.
К некоторым строкам в коде добавлено описание для пущего понимания.
Урок
Ссылка на чистый мод с одной только регистрацией:
Ссылка на virustotal, если вы боитесь хацкерафф:
На этом всё
И по традиции, если у вас возникли вопросы или предложения, пишите в эту тему, либо мне в ЛС.
Автор - я.
На сторонних pawn-порталах эта тема может быть продублирована мной под ником Atu.
В этой системе нет md5 шифрования, нет проверок на кириллицу и т.п., но это всё можно легко добавить, используя мануалы из интернета, либо я могу в персональном порядке на платной основе добавить в систему то, что вам понадобиться. Держу связь через ЛС, либо через свою страничку ВК: vk.com/pwnblog
Всем приятной работы!
Подобного вида система используется на RP-Gameworld. Уверен, данная система внесет каплю разнообразия на ваш сервер.
Видео
Описание
Каждый пункт динамичен и выполнен в виде переключателя. С помощью этой системы вы сможете собрать в одном месте все поля для ввода данных, которые вам необходимо получить от игрока (к примеру, email, код безопасности, раса и т.д., и т.п.)
Скрипт тестировался на чистом проекте, который я приложу в конце темы и создавался с ориентиром на использование его в модах типа "с нуля". Но приложив совсем чуть-чуть усилий, его можно с легкостью адаптировать под любой мод.
К некоторым строкам в коде добавлено описание для пущего понимания.
Урок
Для начала нам необходимо создать базу данных. Для этого откройте панель управления базой и с помощью запросов импортируйте таблицу:
Сама база:
И непосредственно таблица
Скриншот пустой таблицы
Сама база:
Код:
CREATE DATABASE `newmod` DEFAULT CHARACTER SET cp1251 COLLATE cp1251_general_ci;
USE `newmod`;
Код:
CREATE TABLE IF NOT EXISTS `accounts` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Nickname` varchar(32) NOT NULL,
`Password` varchar(64) NOT NULL,
`Level` int(11) NOT NULL DEFAULT '1',
`Sex` int(11) NOT NULL,
`Referal` varchar(64) NOT NULL DEFAULT 'No',
`Admin` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1;
Подключаем mysql плагин. Как указано в шапке темы, для этой системы я использовал mysql от blueg версии R39-7
Добавляем необходимые идентификаторы
Добавляем необходимые переменные и конструкции
Добавляем форварды
Далее в любое свободное место объявляем коллбеки к этим форвардам
Добавляем несколько необходимых стоков
Поясню за всё, что мы добавили:
LoadPlayerAccount(playerid) - загружает данные игрока после успешной авторизации
IsValidAccount(playerid) - проверяет, существует ли в базе данных аккаунт
OnPlayerRegister(playerid) - вызывается после самой регистрации и добавления аккаунта в базу данных
ResetRegData(playerid) - обнуляет переменные, которые мы использовали для заполнения полей при регистрации
RemovePlayerData(playerid) - обнуляет переменные, используемые для хранения данных игрока (необходимо использовать как минимум при коннекте или дисконнекте)
UpdatePlayerData(playerid) - сохраняет данные авторизованного игрока
PHP:
#include <a_mysql>
PHP:
#define REG_DIALOG 555
#define LOG_DIALOG 777
#define TABLE_ACCOUNTS "accounts" //название таблицы с аккаунтами
#define SQL_HOST "localhost" //хост базы
#define SQL_USER "root" //имя пользователя
#define SQL_DB "newmod" //имя базы, к которой мы подключаемся
#define SQL_PASS "" //пароль
#define SQL_PORT 3306 //порт соединения
#define SQL_RECONNECT true //переподключение в случае ошибок
#define SQL_POOLSIZE 10 //максимальное к-лво одновременных подключений (необходимо для mysql_pquery)
#define MIN_PASS_LENGTH 6 //минимальная длинна пароля
#define MAX_PASS_LENGTH 64 //максимальная длинна пароля
#define ENCRYPT_PASS 1 //Значение 1 включает шифрование пароля в диалоге. Т.е. при пароле 123123 будет, к примеру ***123
PHP:
/* Данные */
enum regdata
{
pAcceptRules,
pSex,
pReferal[32+1],
pPassword[64]
}
new RegData[MAX_PLAYERS][regdata]; //хранение информации при регистрации
enum player_data
{
pDatabaseID,
pName[MAX_PLAYER_NAME+1],
pPassword[64],
pReferal[32+1],
pLevel,
pSex,
pAdmin,
bool:pLogged
}
new pData[MAX_PLAYERS][player_data]; //хранение информации авторизованого игрока
new WrongPass[MAX_PLAYERS];
new TextStr[512]; //массив для форматирования самого диалога регистрации
/* MYSQL */
new MySQL; //для записи connection handle (номер соединения)
PHP:
/* Форварды */
forward IsValidAccount(playerid);
forward OnPlayerRegister(playerid);
forward LoadPlayerAccount(playerid);
PHP:
public LoadPlayerAccount(playerid)
{
if(cache_get_row_count(MySQL) == 1)
{
/* Грузим данные из БД на сервер */
pData[playerid][pDatabaseID] = cache_get_field_content_int(0, "ID", MySQL);
cache_get_field_content(0, "Referal", pData[playerid][pReferal], MySQL, 64);
pData[playerid][pLevel] = cache_get_field_content_int(0, "Level", MySQL);
pData[playerid][pSex] = cache_get_field_content_int(0, "Sex", MySQL);
pData[playerid][pAdmin] = cache_get_field_content_int(0, "Admin", MySQL);
pData[playerid][pLogged] = true; //авторизуем
SendClientMessage(playerid, -12, "Данные успешно загружены. Приятной игры!");
SpawnPlayer(playerid);
/* Тестовые функции для првоерки загрузки */
print("\n\n============================================== ===");
printf("Nickname: %s", pData[playerid][pName]);
printf("DB: %i", pData[playerid][pDatabaseID]);
printf("Referal: %s", pData[playerid][pReferal]);
printf("Level: %d", pData[playerid][pLevel]);
printf("Sex: %d", pData[playerid][pSex]);
printf("Admin: %d", pData[playerid][pAdmin]);
print("=================================================\ n\n");
}
return 1;
}
public IsValidAccount(playerid)
{
if(cache_get_row_count(MySQL) == 1) //если нашло 1 нужный нам аккаунт
{
cache_get_row(0, 2, pData[playerid][pPassword], MySQL, 64); //заправшиваем пароль для проверки
SendClientMessage(playerid, -1, "Ваш аккаунт успешно найден в базе данных. Пройдите пожалуйста авторизацию");
ShowPlayerDialog(playerid, LOG_DIALOG, DIALOG_STYLE_INPUT, "Авторизация", "Ваш аккаунт найден в базе данных\nПожалуйста, авторизуйтесь..", ">>", "Выход");
}
else //если 1 нужный нам аккаунт не найден
{
SendClientMessage(playerid, -1, "Ваш аккаунт не найден в базе данных. Пройдите пожалуйста регистрацию");
ResetRegData(playerid); //обнуляем данные
ShowRegisterDialog(playerid);
}
return 1;
}
public OnPlayerRegister(playerid)
{
pData[playerid][pDatabaseID] = cache_insert_id(); //записываем ид аккаунта в базе. По нему мы будем сохранять аккаунт. В БД он добавляется автоматически
pData[playerid][pLevel] = 1;
pData[playerid][pAdmin] = 0;
pData[playerid][pLogged] = true; //авторизуем
SpawnPlayer(playerid);
SendClientMessage(playerid, -1, "Поздравляем с успешной регистрацией!!!");
return 1;
}
PHP:
stock ResetRegData(playerid)
{
RegData[playerid][pAcceptRules] = 0;
RegData[playerid][pSex] = 0;
RegData[playerid][pReferal] = EOS;
RegData[playerid][pPassword] = EOS;
return 1;
}
stock RemovePlayerData(playerid)
{
pData[playerid][pLevel] = 1;
pData[playerid][pSex] = 0;
pData[playerid][pAdmin] = 0;
pData[playerid][pLogged] = false;
WrongPass[playerid] = 0;
return 1;
}
stock UpdatePlayerData(playerid)
{
if(!pData[playerid][pLogged]) return 1; //не сохраняем, если игрок не авторизован
new save_query[512]; //замените этот массив на свой массив, который вы используете для запросов, если он есть
new str_q[128];
format(str_q, sizeof str_q, "UPDATE `"TABLE_ACCOUNTS"` SET "), strcat(save_query, str_q);
format(str_q, sizeof str_q, "`Nickname` = '%s',", pData[playerid][pName]), strcat(save_query, str_q);
format(str_q, sizeof str_q, "`Password` = '%s',", pData[playerid][pPassword]), strcat(save_query, str_q);
format(str_q, sizeof str_q, "`Level` = '%d',", pData[playerid][pLevel]), strcat(save_query, str_q);
format(str_q, sizeof str_q, "`Sex` = '%d',", pData[playerid][pSex]), strcat(save_query, str_q);
format(str_q, sizeof str_q, "`Referal` = '%s',", pData[playerid][pReferal]), strcat(save_query, str_q);
format(str_q, sizeof str_q, "`Admin` = '%d'", pData[playerid][pAdmin]), strcat(save_query, str_q);
format(str_q, sizeof str_q," WHERE `ID` = '%i'", pData[playerid][pDatabaseID]), strcat(save_query, str_q);
mysql_tquery(MySQL, save_query, "", "");
return 1;
}
stock ShowRegisterDialog(playerid)
{
new str_local[64], switch_str[32];
TextStr = "\0"; //предварительно очищаем
switch(RegData[playerid][pAcceptRules]) //тут мы просто проверяем, сколько страниц правил просмотрел игрок
{
case 0,1: switch_str = "Не ознакомлены";
default: switch_str = "Ознакомлены";
}
format(str_local, sizeof(str_local), "Правила сервера\t{86a366}%s\n", switch_str), strcat(TextStr, str_local);
switch(RegData[playerid][pSex]) //тут мы просто проверяем, сколько страниц правил просмотрел игрок
{
case 1: switch_str = "Мужской";
case 2: switch_str = "Женский";
default: switch_str = "Не указан";
}
format(str_local, sizeof(str_local), "Пол персонажа\t{86a366}%s\n", switch_str), strcat(TextStr, str_local);
/*
Что до пункта реферал, то тут уж можете доделывать, либо заменить на какой угодно параметр
Я же для наглядности просто буду проверять длинну никнейма
*/
if(strlen(RegData[playerid][pReferal]) < 4)
{
format(str_local, sizeof(str_local), "Реферал\t{86a366}Не указан\n"), strcat(TextStr, str_local);
}
else format(str_local, sizeof(str_local), "Реферал\t{86a366}%s\n", RegData[playerid][pReferal]), strcat(TextStr, str_local);
if(strlen(RegData[playerid][pPassword]) < MIN_PASS_LENGTH || strlen(RegData[playerid][pPassword]) > MAX_PASS_LENGTH)
{
format(str_local, sizeof(str_local), "Пароль\t{86a366}Не указан\n"), strcat(TextStr, str_local);
}
else //если пароля подходит по длинне
{
#if ENCRYPT_PASS
new encrypt_pass[64];
strcat(encrypt_pass, RegData[playerid][pPassword], 64); //лублируем текст, чтобы над ним можно было совершать манипуляции
strdel(encrypt_pass, 0, strlen(encrypt_pass)-3); //удаляем все символы, кроме 3 последних
for(new s; s < strlen(RegData[playerid][pPassword])-3; s++) strins(encrypt_pass, "*", 0); //вставляем звездочки в начало текста
format(str_local, sizeof(str_local), "Пароль\t{86a366}%s\n", encrypt_pass), strcat(TextStr, str_local); //тут необходимо показать измененную строчку
#else
/*
Тут мы не используем шифрование, поэтому вставляем RegData[playerid][pPassword]
*/
format(str_local, sizeof(str_local), "Пароль\t{86a366}%s\n", RegData[playerid][pPassword]), strcat(TextStr, str_local);
#endif
}
format(str_local, sizeof(str_local), "Завершить регистрацию >>"), strcat(TextStr, str_local);
ShowPlayerDialog(playerid, REG_DIALOG, DIALOG_STYLE_TABLIST, "Регистрация", TextStr, ">>", "");
return 1;
}
LoadPlayerAccount(playerid) - загружает данные игрока после успешной авторизации
IsValidAccount(playerid) - проверяет, существует ли в базе данных аккаунт
OnPlayerRegister(playerid) - вызывается после самой регистрации и добавления аккаунта в базу данных
ResetRegData(playerid) - обнуляет переменные, которые мы использовали для заполнения полей при регистрации
RemovePlayerData(playerid) - обнуляет переменные, используемые для хранения данных игрока (необходимо использовать как минимум при коннекте или дисконнекте)
UpdatePlayerData(playerid) - сохраняет данные авторизованного игрока
В паблик OnGameModeInit добавляем код, который соединит сервер с нашей базой данных
В OnGameModeExit добавляем отключение от БД
В начало пабликов OnPlayerRequestClass и OnPlayerRequestSpawn добавляем код, который не позволит неавторизованному игроку перейти к спавну
В OnPlayerConnect добавляем следующий код
В паблике OnDialogResponse, к оператору выбора switch, добавляем следующий код
Если вы используете оператор условия if, то это легко исправить заменив case REG_DIALOG... на if(dialogid == REG_DIALOG...)
С кодом справились, переходим к аспектам.
PHP:
MySQL = mysql_connect(SQL_HOST, SQL_USER, SQL_DB, SQL_PASS, SQL_PORT, SQL_RECONNECT, SQL_POOLSIZE);
mysql_log(LOG_ALL, LOG_TYPE_HTML);
if(mysql_errno() != 0) print("[-] MYSQL Connection does not exist");
else
{
print("[+] MYSQL Connection accepted ");
mysql_query(MySQL, "SET NAMES 'cp1251'",false);
mysql_set_charset("cp1251", MySQL);
}
PHP:
mysql_close(); //закрываем соединение
PHP:
if(!pData[playerid][pLogged]) return 0;
PHP:
new db_str[128]; //создаем массив для формирования запроса
RemovePlayerData(playerid); //очищаем данные энуменатора player_data
GetPlayerName(playerid, pData[playerid][pName], MAX_PLAYER_NAME); //запрашиваем имя
/* форматируем и отправляем запрос */
mysql_format(MySQL, db_str, sizeof(db_str), "SELECT * FROM `"TABLE_ACCOUNTS"` WHERE `Nickname` = '%e'", pData[playerid][pName]);
mysql_pquery(MySQL, db_str, "IsValidAccount", "d", playerid);
SetSpawnInfo(playerid, 0, 222, 1128.9762,-1488.1531,22.7690,360.0000, 0, 0, 0, 0, 0, 0); //устанавливаем данные для спавна (необходимо для теста)
PHP:
case LOG_DIALOG:
{
new wrong_pass[8];
new load_query[512];
if(!response)
{
/*
Тут необходимо указать действие при отказе ввода
я для наглядности поставлю кик
*/
Kick(playerid);
return 1;
}
if(strlen(pData[playerid][pPassword]) < MIN_PASS_LENGTH || strlen(pData[playerid][pPassword]) > MAX_PASS_LENGTH)
{
ShowPlayerDialog(playerid, LOG_DIALOG, DIALOG_STYLE_INPUT, "Авторизация", "Неправильная длинна пароля! Попробуйте ещё раз", ">>", "Выход");
return 1;
}
if(!strcmp(pData[playerid][pPassword], inputtext, true))
{
mysql_format(MySQL, load_query, 512, "SELECT * FROM `"TABLE_ACCOUNTS"` WHERE `Nickname` = '%s' AND `Password` = '%s'", pData[playerid][pName], pData[playerid][pPassword]);
mysql_tquery(MySQL, load_query, "LoadPlayerAccount", "d", playerid);
SendClientMessage(playerid, -12, "Идет загрузка данных игрока...");
}
else
{
WrongPass[playerid] ++;
if(WrongPass[playerid] > 2) //если больше двух ошибок, то всё!!!
{
/*
Тут необходимо указать действие при трёхкратном введении неправильного пароля
я опять же для наглядности поставлю кик
*/
Kick(playerid);
return 1;
}
ShowPlayerDialog(playerid, LOG_DIALOG, DIALOG_STYLE_INPUT, "Авторизация", "Вы ввели неправильный пароль!!! Попробуйте ещё раз", ">>", "Выход");
format(wrong_pass, 8, "~r~%d/3", WrongPass[playerid]);
GameTextForPlayer(playerid, wrong_pass, 3000, 6);
}
return 1;
}
case REG_DIALOG:
{
switch(listitem)
{
case 0: //правила
{
if(RegData[playerid][pAcceptRules] > 1) //если прочитал все страницы правил
{
SendClientMessage(playerid, -1, "Вы ознакомилены с правилами");
ShowRegisterDialog(playerid);
return 1;
}
ShowPlayerDialog(playerid, REG_DIALOG+1, DIALOG_STYLE_MSGBOX, "Правила сервера", "Правило #1 Не наруйшай правил\nПравило #2 Будь паинькой", ">>", "");
RegData[playerid][pAcceptRules] = 1; //игрок посмотрел первую страницу
}
case 1: //пол персонажа
{
ShowPlayerDialog(playerid, REG_DIALOG+2, DIALOG_STYLE_MSGBOX, "Выбор пола персонажа", "Выберите пол вашего персонажа", "Мужской", "Женский");
}
case 2:
{
ShowPlayerDialog(playerid, REG_DIALOG+3, DIALOG_STYLE_INPUT, "Реферал", "Введите имя игрока, который пригласил вас на сервер", ">>", "Назад");
}
case 3:
{
ShowPlayerDialog(playerid, REG_DIALOG+4, DIALOG_STYLE_INPUT, "Пароль", "Придумайте пароль для вашего аккаунта", ">>", "Назад");
}
case 4: //завершение регистрации
{
if(RegData[playerid][pAcceptRules] < 2) return SendClientMessage(playerid, -1, "Ошибка: Вы не ознакомились с правилами сервера!"), ShowRegisterDialog(playerid);
if(!RegData[playerid][pSex]) return SendClientMessage(playerid, -1, "Ошибка: Вы не указали пол персонажа!"), ShowRegisterDialog(playerid);
if(strlen(RegData[playerid][pPassword]) < MIN_PASS_LENGTH || strlen(RegData[playerid][pPassword]) > MAX_PASS_LENGTH) return SendClientMessage(playerid, -1, "Ошибка: Вы не указали пароль!"), ShowRegisterDialog(playerid);
new add_db[512]; //замените этот массив на свой массив, который вы используете для запросов, если он есть
/* Переносим введенные данные в конструкцию для хранения информации об аккаунте */
format(pData[playerid][pReferal], 64, RegData[playerid][pReferal]);
format(pData[playerid][pPassword], 64, RegData[playerid][pPassword]);
RegData[playerid][pSex] = pData[playerid][pSex];
/* Отправляем запрос на добавление аккаунта в базу */
mysql_format(MySQL, add_db, 512, "INSERT INTO `"TABLE_ACCOUNTS"` (`Nickname`, `Password`, `Level`, `Sex`, `Referal`, `Admin`) VALUES ('%s', '%s', 1, '%d', '%s', 0)", pData[playerid][pName], pData[playerid][pPassword], pData[playerid][pSex], pData[playerid][pReferal]);
mysql_pquery(MySQL, add_db, "OnPlayerRegister", "d", playerid);
ResetRegData(playerid); //обнуляем даныне регистрации
/*
Запрос на внесение аккаунта в базу добавлен.
В паблике OnPlayerRegister внесутся стандартные данные и произойдет спавн игрока
*/
}
}
return 1;
}
case REG_DIALOG+1:
{
if(RegData[playerid][pAcceptRules] > 1)
{
SendClientMessage(playerid, -1, "Вы успешно ознакомились с правилами сервера");
ShowRegisterDialog(playerid);
return 1;
}
ShowPlayerDialog(playerid, REG_DIALOG+1, DIALOG_STYLE_MSGBOX, "Правила сервера", "Правило #3 Не флуди\nПравило #4 Не вреди", ">>", "");
RegData[playerid][pAcceptRules] = 2;
return 1;
}
case REG_DIALOG+2:
{
if(response) RegData[playerid][pSex] = 1;
else RegData[playerid][pSex] = 2;
SendClientMessage(playerid, -1, "Пол успешно установлен");
ShowRegisterDialog(playerid);
return 1;
}
case REG_DIALOG+3:
{
if(!response) return ShowRegisterDialog(playerid);
if(strlen(inputtext) < 4 || strlen(inputtext) > 32)
{
SendClientMessage(playerid, -1, "Имя реферала должно содержать минимум 3 и максимум 32 символа");
ShowPlayerDialog(playerid, REG_DIALOG+3, DIALOG_STYLE_INPUT, "Реферал", "Введите имя игрока, который пригласил вас на сервер", ">>", "Назад");
return 1;
}
format(RegData[playerid][pReferal], 32, inputtext);
SendClientMessage(playerid, -1, "Реферал успешно указан");
ShowRegisterDialog(playerid);
return 1;
}
case REG_DIALOG+4:
{
/*
Тут вы можете поизвращаться над паролем: добавляйте мд5, проверяйте пароль на символы/кириллицу и т.д. и т.п. на ваш взгляд
Это базовая версия системы регистрации, поэтому я не буду добавлять никаких проверок, а оставлю это удовольствие вам! :)
*/
if(strlen(inputtext) < MIN_PASS_LENGTH || strlen(inputtext) > MAX_PASS_LENGTH)
{
SendClientMessage(playerid, -1, "Недопустимая длинна пароля");
ShowPlayerDialog(playerid, REG_DIALOG+4, DIALOG_STYLE_INPUT, "Пароль", "Придумайте пароль для вашего аккаунта", ">>", "Назад");
return 1;
}
format(RegData[playerid][pPassword], 64, inputtext);
SendClientMessage(playerid, -1, "Пароль успешно указан");
ShowRegisterDialog(playerid);
return 1;
}
С кодом справились, переходим к аспектам.
- Вы можете включать/отключать шифрование пароля в переключателе меняя значение идендификатора ENCRYPT_PASS на 1 и 0 соответственно
- В список можно легко добавить новый пункт. Для этого в энуменатор regdata нужно добавить новую переменную, а в диалог регистрации дописать название пункта по изложенному примеру
- Вы можете добавлять сколько угодно страниц правил просто меняя значение последней в условиях в диалоге REG_DIALOG+1
- В список можно легко добавить новый пункт. Для этого в энуменатор regdata нужно добавить новую переменную, а в диалог регистрации дописать название пункта по изложенному примеру
- Вы можете добавлять сколько угодно страниц правил просто меняя значение последней в условиях в диалоге REG_DIALOG+1
Ссылка на чистый мод с одной только регистрацией:
Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!
Ссылка на virustotal, если вы боитесь хацкерафф:
Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!
На этом всё
И по традиции, если у вас возникли вопросы или предложения, пишите в эту тему, либо мне в ЛС.
Автор - я.
На сторонних pawn-порталах эта тема может быть продублирована мной под ником Atu.
В этой системе нет md5 шифрования, нет проверок на кириллицу и т.п., но это всё можно легко добавить, используя мануалы из интернета, либо я могу в персональном порядке на платной основе добавить в систему то, что вам понадобиться. Держу связь через ЛС, либо через свою страничку ВК: vk.com/pwnblog
Всем приятной работы!
Последнее редактирование: