PDA

Просмотр полной версии : [Урок] Простейшая регистрация на MySQL.



Ray_Grand
04.02.2016, 18:14
Доброго времени суток, пользователи портала Pro-Pawn. Совсем недавно принялся изучать язык программирования Lua и примерно за неделю, с трудом написал простенькую, но работоспособную регистрацию на плагине MySQL (http://wiki.multitheftauto.com/wiki/RU/Modules/MTA-MySQL) с примером загрузки информации игрока из базы данных. И так, приступим, постараюсь объяснить детально то, что самому известно.
http://s020.radikal.ru/i708/1602/3e/15c2d98d87b7.png
Вот что реализовано в системе:
1. Регистрация и авторизация, в обоих случаях придётся вводить только лишь логин и пароль.
2. Загрузка данных игрока из базы данных, я решил сделать последнюю позицию игрока, а так - же спавн по ней.
3. Обновление последней позиции игрока и спавн по ней.
4. Авторизация/регистрация проходит не по логину, который в клиенте у игрока, а по логину который введен в окне. (Например был ник "Player111", а в базе данных был аккаунт с ником "Player222", и если игрок "Player111" авторизовался под логином "Player222" то его логин сменится и он будет играть под введенным логином.

Подготовка к написанию самой системы:
Как установить плагин MySQL (клик) (http://wiki.multitheftauto.com/wiki/RU/Modules/MTA-MySQL) объяснять думаю не нужно, на wiki MTA (https://wiki.multitheftauto.com/wiki/Main_Page) всё доходчиво объяснено.

После установки плагина, приступим к созданию таблицы в базе данных. Структура таблицы "players" для аккаунтов на скриншоте ниже:
http://s017.radikal.ru/i438/1602/3a/fae26c874184.png
У кого не грузит нажмите на этот текст (http://s017.radikal.ru/i438/1602/3a/fae26c874184.png)

Сделали? Отлично, продолжим. Т.к регистрация и авторизация писалась полностью на чистом ресурсе, то идём в папку где находятся все ресурсы (MTA San Andreas 1.5\server\mods\deathmatch\resources) и создаем там новую папку, в которой будут находится наши файлы.

Приступаем к написанию самой системы.
В папке с нашим ресурсом создаем первый файл - meta.xml, и добавляем туда следующие строки:

<meta>
<script src="server.lua" type="server"/> //Серверная часть ресурса
<script src="client.lua" type="client"/> //Клиентская часть ресурса
</meta>

Дальше мы создаем файл client.lua, в котором будет написана клиентская часть ресурса, и в нём же сейчас пишем следующий код:

//Первым делом создаем переменные, необходимые для создания GUI окна регистрации и авторизации.
local Window = {}
local TabPanel = {}
local Tab = {}
local Button = {}
local Edit = {}
local Label = {}

Сразу после переменных, переходим к созданию первой функции - создания GUI окна. После переменных добавляем:

local function createGui()
//Тут скоро мы продолжим писать код.
end

addEventHandler("onClientResourceStart", getResourceRootElement(), function()
createGui() //Создаем окно с авторизацией и регистрацией.
end)

Теперь внутрь функции createGui, добавляем следующий код:

//Создаем переменные, определяющие положение окна с регистрацией/авторизацией.
local sWidth, sHeight = guiGetScreenSize()
local Width,Height = 370,219
local X = (sWidth/2) - (Width/2)
local Y = (sHeight/2) - (Height/2)
Window[1] = guiCreateWindow(X, Y, Width, Height, "Информационное окно",false) //Создаем само окно, где будут расположены все элементы (текста, панели, кнопки и прочее)
TabPanel[1] = guiCreateTabPanel(9,22,352,188,false, Window[1]) //Создаем TabPanel, на которой и будут расположены кнопки, с помощью которых можно будет переключаться между регистрацией и авторизацией.

//GUI элементы авторизации.
Tab[1] = guiCreateTab("Авторизация", TabPanel[1])
Edit[1] = guiCreateEdit(77,10,160,28,"",false,Tab[1]) //Создаем поле для ввода логина.
guiEditSetMaxLength (Edit[1], 22) //Устанавливаем максимальную длину введенного в поле текста - 22 символа.
Label[1] = guiCreateLabel(9,15,88,30,"Логин:",false,Tab[1]) //Создаем текст левее поля для ввода логина.
guiSetFont(Label[1],"default-bold-small") //Устанавливаем шрифт для текста выше.
Edit[2] = guiCreateEdit(77,45,160,28,"",false,Tab[1]) //Создаем поле для ввода пароля.
guiEditSetMaxLength (Edit[2], 30) //Устанавливаем максимальную длину введенного в поле текста - 30 символа.
Label[2] = guiCreateLabel(9,50,88,30,"Пароль:",false,Tab[1]) //Создаем текст левее поля для ввода пароля.
guiSetFont(Label[2],"default-bold-small") //Устанавливаем шрифт для текста выше.
Button[1] = guiCreateButton(97,80,112,29,"Авторизоваться",false,Tab[1]) //После всех элементов выше, создаем кнопку.

//GUI элементы регистрации.
Tab[2] = guiCreateTab("Регистрация",TabPanel[1])
Edit[3] = guiCreateEdit(77,10,160,28,"",false,Tab[2]) //Создаем поле для ввода логина.
guiEditSetMaxLength (Edit[3], 22) //Устанавливаем максимальную длину введенного в поле текста - 22 символа.
Label[3] = guiCreateLabel(9,15,88,30,"Логин:",false,Tab[2]) //Создаем текст левее поля для ввода логина.
guiSetFont(Label[3],"default-bold-small") //Устанавливаем шрифт для текста выше.
Edit[4] = guiCreateEdit(77,45,160,28,"",false,Tab[2]) //Создаем поле для ввода пароля.
guiEditSetMaxLength (Edit[4], 30) //Устанавливаем максимальную длину введенного в поле текста - 30 символа.
Label[4] = guiCreateLabel(9,50,88,30,"Пароль:",false,Tab[2]) //Создаем текст левее поля для ввода пароля.
guiSetFont(Label[4],"default-bold-small") //Устанавливаем шрифт для текста выше.
Button[2] = guiCreateButton(92,80,130,29,"Зарегистрироваться",false,Tab[2]) //Создаем кнопку.

showCursor(true) //Показываем курсор.

addEventHandler("onClientGUIClick", Button[1], function() //Событие, которое будет происходить при нажатии на кнопку авторизации.
local text1, text2 = guiGetText(Edit[1]), guiGetText(Edit[2]) //Записываем в 2 переменные введенный текст в поля логина и пароля.
if(text1 == "") or (text2 == "") then return end //Если поле для ввода логина или пароля пустое - то событие дальше не происходит.
triggerServerEvent("onPlayerLoginEx", getLocalPlayer(), text1, text2) //Вызываем серверное событие с двумя параметрами - логин, пароль.
end, false)

addEventHandler("onClientGUIClick", Button[2], function() //Событие, которое будет происходить при нажатии на кнопку регистрации.
local text1, text2 = guiGetText(Edit[3]), guiGetText(Edit[4])//Записываем в 2 переменные введенный текст в поля логина и пароля.
if(text1 == "") or (text2 == "") then return end //Если поле для ввода логина или пароля пустое - то событие дальше не происходит.
triggerServerEvent("onPlayerRegisterEx", getLocalPlayer(), text1, text2) //Вызываем серверное событие с двумя параметрами - логин, пароль.
end, false)

После всего выше написанного, добавляем последнее в клиентскую часть:

addEvent("destroyGui", true)
addEventHandler("destroyGui", getRootElement(), function() //Эта функция пригодится позже, после успешной авторизации или регистрации, для скрытия окна и курсора.
destroyElement(Window[1]) //Удаляем окно с регистрацией и авторизацией.
showCursor(false) //Скрываем курсор.
end)

С клиентской частью закончили. Теперь создаем server.lua и добавляем туда следующий код:

database = mysql_connect("хостинг", "пользователь", "пароль", "название базы данных") //Переменная с подключением к базе данных, настраивайте сами под себя.

После соединения с базой данных, создадим функцию onPlayerLoginEx, которая будет срабатывать когда вы будете нажимать на кнопку авторизации и внутри которой будут проходить все проверки на наличие аккаунта.

addEvent('onPlayerLoginEx', true)
addEventHandler("onPlayerLoginEx", getRootElement(), function(name, password) //Создаем функцию
local result = mysql_query(database, "SELECT * FROM players WHERE Name = '"..name.."' AND Password = '"..password.."';") //Запрос на проверку наличие аккаунта, по введенным в клиентской части данным (Логин, пароль).
if(result and mysql_num_rows(result) > 0) then //Если аккаунт такой существует, то идём дальше.
triggerClientEvent(source, "destroyGui", source) //Вызываем клиентскую функцию, которая удаляет окно и курсор с экрана.
setPlayerName(source, name) //Устанавливаем игроку имя, введенное в окно логина.
local row = mysql_fetch_assoc(result)
setElementData(source, "posX", (row['X'])) //Из поля 'X' в базе данных получаем значение и записываем его в ключ "posX' на сервере.
setElementData(source, "posY", (row['Y'])) //Из поля 'Y' в базе данных получаем значение и записываем его в ключ "posY' на сервере.
setElementData(source, "posZ", (row['Z'])) //Из поля 'Z' в базе данных получаем значение и записываем его в ключ "posZ' на сервере.
spawnPlayer(source, getElementData(source, "posX"), getElementData(source, "posY"), getElementData(source, "posZ")) //Спавним игрока, по загруженным выше координатам.
fadeCamera(source, true) //Чтобы не было черного экрана
setCameraTarget(source, source) //Делаем так, чтобы камера игрока, следила за ним самим же.
outputChatBox("Вы успешно авторизировались на сервере!", source, 0, 255, 0) //Приветственное сообщение.
else
outputChatBox("Введенный вами пароль неверный, или аккаунт не существует.", source, 255, 0, 0) //Если аккаунт с логином и паролем, введенным в окне авторизации отсутствует, то выводим сообщение, которое ниже.
end
end)

Осталась последняя функция - onPlayerRegisterEx, которая наверное как Вы и догадались, срабатывает когда игрок жмет на кнопку "Зарегистрироваться"

addEvent("onPlayerRegisterEx", true)
addEventHandler("onPlayerRegisterEx", getRootElement(), function(name, password) //Функция регистрации аккаунта в базе данных.
local result = mysql_query(database, "SELECT Name FROM players WHERE Name = '"..tostring(name).."';") //Проверяем, если ли аккаунт с именем введенным в окне регистрации.
if(result) then //Если запрос успешный, идём дальше.
if(mysql_num_rows(result) > 0) then //Аккаунт уже есть с таким именем.
outputChatBox("Аккаунт с указанным вами логином уже зарегистрирован, используйте другой!", source)
else //Аккаунта с таким именем нет, идём дальше.
result = mysql_query(database, "INSERT INTO players (Name, Password) VALUES ('"..tostring(name).."', '"..tostring(password).."');") //Создаем аккаунт в базе данных с логином и паролем из окна регистрации.
if(result) then //Если запрос успешный, идём дальше.
triggerClientEvent(source, "destroyGui", source) //Вызываем клиентское событие, которое удалит окно и курсор.
setPlayerName(source, name) //Устанавливаем игроку имя, которое он ввел в окне регистрации.
spawnPlayer(source, 1714.96875, -1913.5341796875, 13.566567420959) //Спавним игрока на вокзале Лос - Сантоса
setCameraTarget(source, source) //Делаем так, чтобы камера игрока, следила за ним самим же.
fadeCamera(source, true) //Чтобы не было черного экрана
outputChatBox("Вы успешно зарегистрировались на сервере!", source, 0, 255, 0) //Приветствуем игрока
else
outputChatBox("При регистрации аккаунта возникла ошибка.", source, 255, 0, 0) //Отправляем это сообщение, если аккаунт почему - то не удалось зарегистрировать.
end
end
end
end)

Осталось лишь сделать обновление позиции игрока при выходе, в серверную часть добавляем:

addEventHandler ("onPlayerQuit", getRootElement(), function(quitType) //Событие происходящее при выходе игрока.
local x, y, z = getElementPosition(source) //Получаем координаты игрока.
mysql_query(database, "UPDATE players SET X = '"..x.."', Y = '"..y.."', Z = '"..z.."' WHERE Name = '"..getPlayerName(source).."';") //Обновляем их в базе данных.
end)

pastebin (client.lua) (http://pastebin.com/e3asLg4r)
pastebin (server.lua) (http://pastebin.com/uTCj7UAh)

Автор: Ray_Grand (http://pro-pawn.ru/member.php?4662-Ray_Grand)

Урок был написан специально для Pro-Pawn, копирование на другие ресурсы без разрешения автора - запрещено!

P.S. Это моя самая первая система реализованная в MTA на Lua, поэтому с радостью выслушаю любую адекватную критику по коду, от более опытных пользователей.

Salvacore
04.02.2016, 18:27
Хороший урок.
Всё описано)

Disinterpreter
05.02.2016, 14:25
Как я уже говорил ранее в другой теме, не желательно юзать MySQL плагин.
Так же в целях безопасности необходимо проверять данные о логине и пароле регексом, а не их численность, да и это делать следует на сервере, хоть мта и безопасна, не стоит это делать на клиенте (мой пример, я работал со встроенной ORM на SQLite, но тут важна суть проверок https://github.com/Disinterpreter/MTADM/blob/master/loginpanel/login_s.lua#L8-L30).

Reim
10.02.2016, 19:09
Как я уже говорил ранее в другой теме, не желательно юзать MySQL плагин.


Между ними есть различия, или любым из них лучше не пользоваться?

https://wiki.multitheftauto.com/wiki/DbConnect
https://wiki.multitheftauto.com/wiki/Modules/MTA-MySQL
https://wiki.multitheftauto.com/wiki/Modules/MySQL

И кстати все же почему пользоваться mysql в mta лучше не надо?

Disinterpreter
12.02.2016, 01:06
Между ними есть различия, или любым из них лучше не пользоваться?

https://wiki.multitheftauto.com/wiki/DbConnect
https://wiki.multitheftauto.com/wiki/Modules/MTA-MySQL
https://wiki.multitheftauto.com/wiki/Modules/MySQL

И кстати все же почему пользоваться mysql в mta лучше не надо?

Я говорил именно про МОДУЛИ т.к им уже много лет и они никем не дорабатываться, а основные разработчики забили... В модуле могут быть дыры и различные баги, а это опасно.