-- login_client.lua

local soundElementData = "mySoundElement"

screenWidth,screenHeight = guiGetScreenSize()
mainWidth,mainHeight = 1300,600
regWidth,regHeight = 800,688

-- Создаем статическое изображение размером 30% от размера экрана
local screenWidth, screenHeight = guiGetScreenSize() -- получаем размер экрана
local imageWidth, imageHeight = screenWidth*0.3, screenHeight*0.3 -- устанавливаем размер изображения на 30% от размера экрана
backgroundImage = guiCreateStaticImage(screenWidth/2 - imageWidth/2, screenHeight/2 - imageHeight/2, imageWidth, imageHeight, "images/reglog_pic.png", false)

-- создаем жирный шрифт
local boldFont = guiCreateFont("ljk_Adventure.ttf", 20)

-- создаем метки "Логин" и "Пароль" поверх изображения
local loginLabel = guiCreateLabel(0.1, 0.15, 0.8, 0.1, "Логин:", true, backgroundImage)
local passwordLabel = guiCreateLabel(0.1, 0.45, 0.8, 0.1, "Пароль:", true, backgroundImage)

-- применяем жирный шрифт к меткам
guiSetFont(loginLabel, boldFont)
guiSetFont(passwordLabel, boldFont)

-- центрируем текст меток
guiLabelSetHorizontalAlign(loginLabel, "center")
guiLabelSetHorizontalAlign(passwordLabel, "center")

-- создаем поля для ввода логина и пароля поверх изображения
usernameField = guiCreateEdit(0.1, 0.25, 0.8, 0.15, "", true, backgroundImage)
passwordField = guiCreateEdit(0.1, 0.55, 0.8, 0.15, "", true, backgroundImage)

-- создаем кнопки "Логин" и "Регистрация" поверх изображения
loginButton = guiCreateButton(0.1, 0.75, 0.35, 0.15, "Войти", true, backgroundImage)
registerButton = guiCreateButton(0.55, 0.75, 0.35, 0.15, "Регистрация", true, backgroundImage)

-- Изначально все элементы GUI выключены
guiSetVisible(backgroundImage, false)

function loginPanel()

Ck_racing1 = guiCreateLabel (1300,730,1280,40,"",false,mainWindow)
guiLabelSetVerticalAlign(Ck_racing1,"center")
guiLabelSetHorizontalAlign(Ck_racing1,"center",false)
guiSetFont (Ck_racing1,"clear-normal")
guiLabelSetColor(Ck_racing1,0,0,0)
local table = {{255, 0, 0},{255, 255, 255},{255,100, 100},{100, 100, 100}}
setTimer(
function ()
guiLabelSetColor( Ck_racing1, unpack(table[math.random(#table)]) )
end
, 1000, 0 )
	setPlayerHudComponentVisible("radar",false)  
	setPlayerHudComponentVisible("area_name",false)  
    toggleControl("radar",false)
	showChat(false)	
	local PositionX,PositionY,PositionZ = 2067.5349,41.928261,34.788815;
	local LookAtX,LookAtY,LookAtZ = 2067.5349,41.928261,34.788815;
	setCameraMatrix(PositionX,PositionY,PositionZ,LookAtX,LookAtY,LookAtZ) 
	fadeCamera(true) 	
	function CameraPosition ()
		PositionX = PositionX+0.03; 
		LookAtX = LookAtX+0.3;
		setCameraMatrix(PositionX,PositionY,PositionZ,LookAtX,LookAtY,LookAtZ)
	end
	addEventHandler("onClientPreRender",getRootElement(),CameraPosition)	
	mainWindow = guiCreateStaticImage(screenWidth/2-mainWidth/2,screenHeight/2-mainHeight/2,mainWidth,mainHeight,"images/logo.png",false)
	btnLogin = guiCreateStaticImage(400,400,484,148,"images/play.png",false,mainWindow) 
	guiSetProperty(btnLogin, "NormalTextColour", "FFFF0000")
	guiSetFont(btnLogin,"clear-normal")
	guiSetVisible(mainWindow,true)
	guiSetInputEnabled(true)
	showCursor(true)
	addEventHandler("onClientGUIClick",btnLogin,onClickLogin)	
end
addEventHandler("onClientResourceStart",getResourceRootElement(getThisResource()),loginPanel)

-- обработчик события нажатия на кнопку "btnLogin"
function onClickLogin(button,state)
	if(button == "left" and state == "up") then
		if (source == btnLogin) then
		    playSound("sounds/sfx/button.mp3")
            guiSetVisible(mainWindow, false) -- закрываем основное окно
            guiSetVisible(backgroundImage, true) -- открываем окно для ввода логина и пароля
		end
	end
end

-- обработчики событий нажатия на кнопки "Логин" и "Регистрация"
addEventHandler("onClientGUIClick", loginButton,
    function()
        local username = guiGetText(usernameField)
        local password = guiGetText(passwordField)
		playSound("sounds/sfx/enterserver.mp3")
        if username ~= "" and password ~= "" then
            triggerServerEvent("onRequestEnter", getLocalPlayer(), username, password)
        else
			outputChatBox("Вы должны ввести имя пользователя и пароль.")
			-- exports.notyf_system:badnotyShow("Вы должны ввести имя пользователя и пароль!", source)
        end
    end
)

-- Создание GUI с иконками ВК, Ютуб и Телеграм
-- local iconGUI = guiCreateStaticImage(screenWidth / 2 - 80, screenHeight - 64, 160, 64, "images/blank.png", false)

-- Создание кнопок с иконками
local buttonvk = guiCreateStaticImage(screenWidth / 2 - 70, screenHeight - 50, 32, 32, "images/vk_button.png", false, iconGUI)
local buttonyt = guiCreateStaticImage(screenWidth / 2 - 30, screenHeight - 50, 32, 32, "images/yt_button.png", false, iconGUI)
local buttontg = guiCreateStaticImage(screenWidth / 2 + 10, screenHeight - 50, 32, 32, "images/tg_button.png", false, iconGUI)

function openBrowserURL(url)
    local browser = guiCreateBrowser(0, 0, 800, 600, true, true, false)
    guiSetBrowserURL(browser, url)
end

-- Oбработчики событий для кликов по иконкам
addEventHandler("onClientGUIClick", buttonvk, function(button, state)
    if button == "left" and state == "up" then
        outputChatBox("Нажал на ВК.")
        playSound("sounds/sfx/button.mp3")
        openBrowserURL("https://vk.com/yourpage")
		loadBrowserURL( browser, "https://www.youtube.com/" )
    end
end, false)

addEventHandler("onClientGUIClick", buttonyt, function(button, state)
    if button == "left" and state == "up" then
        outputChatBox("Нажал на ЮТУБ.")
        playSound("sounds/sfx/button.mp3")
        openBrowserURL("https://www.youtube.com/channel/yourchannel")
		loadBrowserURL( browser, "https://www.youtube.com/" )
    end
end, false)

addEventHandler("onClientGUIClick", buttontg, function(button, state)
    if button == "left" and state == "up" then
        outputChatBox("Нажал на ТЕЛЕГУ.")
        playSound("sounds/sfx/button.mp3")
        openBrowserURL("https://t.me/yourtelegram")
		loadBrowserURL( browser, "https://www.youtube.com/" )
    end
end, false)

-- GUI для регистрации
registerGUI = guiCreateStaticImage(screenWidth/2 - imageWidth/2, screenHeight/2 - imageHeight/2, imageWidth, imageHeight, "images/reglog_pic.png", false)
local registerUsernameLabel = guiCreateLabel(0.1, 0.05, 0.8, 0.1, "Логин:", true, registerGUI)
local emailLabel = guiCreateLabel(0.1, 0.25, 0.8, 0.1, "Почта:", true, registerGUI)
local registerPasswordLabel = guiCreateLabel(0.1, 0.45, 0.8, 0.1, "Пароль:", true, registerGUI)
local confirmPasswordLabel = guiCreateLabel(0.1, 0.65, 0.8, 0.1, "Повторите пароль:", true, registerGUI)

guiSetFont(registerUsernameLabel, boldFont)
guiSetFont(emailLabel, boldFont)
guiSetFont(registerPasswordLabel, boldFont)
guiSetFont(confirmPasswordLabel, boldFont)

guiLabelSetHorizontalAlign(registerUsernameLabel, "center")
guiLabelSetHorizontalAlign(emailLabel, "center")
guiLabelSetHorizontalAlign(registerPasswordLabel, "center")
guiLabelSetHorizontalAlign(confirmPasswordLabel, "center")

registerUsernameField = guiCreateEdit(0.1, 0.15, 0.8, 0.08, "", true, registerGUI)
emailField = guiCreateEdit(0.1, 0.35, 0.8, 0.08, "", true, registerGUI)
registerPasswordField = guiCreateEdit(0.1, 0.55, 0.8, 0.08, "", true, registerGUI)
confirmPasswordField = guiCreateEdit(0.1, 0.75, 0.8, 0.08, "", true, registerGUI)

local backButton = guiCreateButton(0.1, 0.85, 0.35, 0.1, "Назад", true, registerGUI)
local confirmRegisterButton = guiCreateButton(0.55, 0.85, 0.35, 0.1, "Зарегистрироваться", true, registerGUI)

guiSetEnabled(backButton, false)
guiSetEnabled(confirmRegisterButton, false)

guiSetVisible(registerGUI, false)

addEventHandler("onClientGUIClick", registerButton, function()
    playSound("sounds/sfx/button.mp3")
    guiSetVisible(backgroundImage, false)
	guiSetVisible(buttonvk, false)
	guiSetVisible(buttonyt, false)
	guiSetVisible(buttontg, false)
    guiSetVisible(registerGUI, true)
    guiSetEnabled(backButton, true)
    guiSetEnabled(confirmRegisterButton, true)
end)

addEventHandler("onClientGUIClick", backButton, function()
    playSound("sounds/sfx/button.mp3")
    guiSetVisible(registerGUI, false)
	guiSetVisible(buttonvk, true)
	guiSetVisible(buttonyt, true)
	guiSetVisible(buttontg, true)
    guiSetVisible(backgroundImage, true)
    guiSetEnabled(backButton, false)
    guiSetEnabled(confirmRegisterButton, false)
end)

addEventHandler("onClientGUIClick", confirmRegisterButton, function()
    playSound("sounds/sfx/button.mp3")
    local username = guiGetText(registerUsernameField)
    local email = guiGetText(emailField)
    local password = guiGetText(registerPasswordField)
    local confirmPassword = guiGetText(confirmPasswordField)
    if password == confirmPassword then
        triggerServerEvent("onRequestRegister", getLocalPlayer(), username, email, password)
    else
        exports.notyf_system:badnotyShow("Пароли не совпадают!", source)
    end
end)

function hideLoginWindow()
    removeEventHandler("onClientPreRender", root, CameraPosition)
    setCameraTarget(getLocalPlayer())
    showChat(true)
    guiSetInputEnabled(false)
    guiSetVisible(backgroundImage, false)
    guiSetVisible(registerGUI, false)
	guiSetVisible(buttonvk, false)
	guiSetVisible(buttonyt, false)
	guiSetVisible(buttontg, false)
    showCursor(false)
end
addEvent("hideLoginWindow", true)
addEventHandler("hideLoginWindow", root, hideLoginWindow)

-- МУЗЫКА НА ФОНЕ
function startSound()
    local sounds = {"sounds/loginsong_1.mp3", "sounds/loginsong_2.mp3", "sounds/loginsong_3.mp3"}
    local randomSound = sounds[math.random(#sounds)]
    local sound = playSound(randomSound)

    if not sound then
		setSoundVolume(sound, 0.2) -- устанавливаем громкость звука на 50%
        outputChatBox("Ошибка воспроизведения звука: " .. randomSound) -- выводим сообщение об ошибке в чат, если звук не воспроизводится
    else
        setElementData(getLocalPlayer(), soundElementData, sound) -- сохраняем элемент звука в элементарных данных
    end
end
addEventHandler("onClientResourceStart", getResourceRootElement(getThisResource()), startSound)

function stopMySound()
    local sound = getElementData(getLocalPlayer(), soundElementData) -- получаем элемент звука из элементарных данных

    if isElement(sound) then
        destroyElement(sound)
        removeElementData(getLocalPlayer(), soundElementData) -- удаляем данные элемента после уничтожения звука
    end
end
addEventHandler("onClientPlayerSpawn", getLocalPlayer(), stopMySound)

-- login_server.lua

local function validateCredentials(username, password)
    if string.len(username) < 4 or string.len(username) > 15 then
        -- outputChatBox("Имя пользователя должно содержать от 4 до 15 символов", source)
		exports.notyf_system:badnotyShow("Имя пользователя должно содержать от 4 до 15 символов!", source)
        return false
    end
    if string.len(password) < 4 or string.len(password) > 20 then
        -- outputChatBox("Пароль должен содержать от 4 до 20 символов", source)
		exports.notyf_system:badnotyShow("Пароль должен содержать от 4 до 20 символов!", source)
        return false
    end
    if string.match(username, "%W") then
        -- outputChatBox("Имя пользователя может содержать только буквы и цифры", source)
		exports.notyf_system:badnotyShow("Имя пользователя может содержать только буквы и цифры!", source)
        return false
    end
    return true
end

local function isValidEmail(email)
    if not email then
        return false
    end
    if string.find(email, "^[%w._%-]+@[%w.-]+%.%a+$") then
        return true
    end
    return false
end

function enterPlayer(username, password)
	outputConsole("enterPlayer function is called with username: " .. username .. " and password: " .. password)
    local account = getAccount(username)
    if (account == false) then
        -- outputChatBox("Такого аккаунта не существует", source)
		exports.notyf_system:badnotyShow("Такого аккаунта не существует!", source)
        return
    end
    if not logIn(source, account, password) then
        -- outputChatBox("Неверный пароль!", source)
		exports.notyf_system:badnotyShow("Неверный пароль!", source)
    else
        triggerClientEvent(source, "hideLoginWindow", getRootElement())
        triggerEvent("onPlayerDayZLogin", getRootElement(), username, password, source)
    end
end
addEvent("onRequestEnter", true)
addEventHandler("onRequestEnter", getRootElement(), function(...)
    outputConsole("onRequestEnter event is triggered")
    enterPlayer(...)
end)

function registerPlayer(username, email, password)
	if not validateCredentials(username, email, password) then return end
    if not isValidEmail(email) then
        exports.notyf_system:badnotyShow("Неправильный формат почты!", source)
        return
    end

	local account = getAccount(username)
	if account then
		-- outputChatBox("Аккаунт уже существует", source)
		exports.notyf_system:badnotyShow("Аккаунт уже существует!", source)
	else
		account = addAccount(username, password)
		logIn(source, account, password)
		triggerClientEvent(source, "hideLoginWindow", getRootElement())
		triggerEvent("onPlayerDayZRegister", getRootElement(), username, password, email, source)
		triggerClientEvent(source, "openClothingInterface", getRootElement())
	end
end
addEvent("onRequestRegister", true)
addEventHandler("onRequestRegister", getRootElement(), registerPlayer)

Это код скриптов стороны клиента и сервера с логикой регистрации, авторизации и других мелочей при входе в игру для мультиплеера MTA SAN ANDREAS. Проанализируй код, найди недостатки, ошибки, возможные баги и предложи варианты их исправления.