MX_Master Posted April 2, 2011 Share Posted April 2, 2011 (edited) Описание: loadfile ( "путь_к_файлу" ) - это функция языка LUA, которая позволяет прочесть любой файл как LUA скрипт и загрузить его в текущий скрипт в виде функции. Внимание: эта функция считается небезопасной с версии 1.1.Параметры: "путь_к_файлу" - строка, содержащая путь к скрипту, начиная с папки, где находится EXEшник сервера, допустим, "mods/deathmatch/resources/имя_ресурса/имя_скрипта.lua", если скрипт лежит в папке ресурса, или "mods/deathmatch/имя_скрипта.lua", если скрипт лежит рядом с файлом настройки сервера. Возвращаемые значения: функцию - если код файла был без ошибокnil и "строку с кодом ошибки" - если в коде файла найдены ошибки [*]loadstring ( "строка_с_кодом" ) - это функция языка LUA, которая позволяет прочесть любую строку как LUA код и загрузить этот код в текущий скрипт в виде функции. Параметры: "строка_с_кодом" - любая строка, желательно с правильным LUA кодом Возвращаемые значения: функцию - если код в строке был без ошибокnil и "строку с кодом ошибки" - если найдены ошибки в коде строки Области применения: Динамическое подключение других скриптов в текущий скрипт без указания их в meta.xml Выполнение кода, который хранится в виде строки, например, в базе данных, XML файле или другом месте Изменение, проверка и запуск ваших скриптов прямо на сервере. Допустим, вы админ и хотите никуда не выходя с сервера изменить какой-то скрипт вашего ресурса: несколько махинаций с GUI и вы можете открывать/изменять/запускать нужные скрипты. Хранение аккаунтов игроков, настроек, языковых файлов и прочего в виде LUA кода, например в виде таблиц Проверка работоспособности кода перед его выполнением с возможностью узнать текст ошибки Начнем с более простой loadstring: -- в строке нет ошибок local sMyCode = 'local str = "qwerty" \n return str' local fGetQwerty = loadstring(sMyCode) outputChatBox( 'fGetQwerty(): "'..fGetQwerty()..'"' ) -- fGetQwerty(): "qwerty" -- код вверху аналогичен этому local function fGetQwerty() local str = "qwerty" return str end outputChatBox( 'fGetQwerty(): "'..fGetQwerty()..'"' ) -- fGetQwerty(): "qwerty" -- а теперь допустим в коде ошибку local sErrorText sMyCode = 'local str = "qwerty \n return str' -- не завершена строка fGetQwerty, sErrorText = loadstring(sMyCode) outputChatBox( tostring(fGetQwerty) ) -- nil outputChatBox(sErrorText) -- [string "local str = "qwerty ..."]:1: unfinished string near '"qwerty' Здесь мы взяли простую строку, которая может содержать правильный или неправильный LUA код. Загрузили эту строку как LUA код и поместили его в новую функцию, которую мы можем вызвать позднее, когда это нужно. Если код в строке был верный, то loadstring вернула нам переменную типа 'function' или проще говоря, новую функцию. Если код в строке был неверный, то первым значением loadstring вернула nil, а вторым - "строку с текстом ошибки". Теперь loadfile: -- допустим наш ресурс называется "myResource" -- в папке это ресурса есть два скрипта "main.lua" и "include.lua" -- в "meta.xml" мы указали только "main.lua" как серверный скрипт -- допустим, текст файла "include.lua" будет такой -------------- local t = {"qwe","rty","uio"} return t -------------------------- -- текст файла "main.lua" ------------------------ -- имя этого ресурса ("myResource") local sThisResourceName = getResourceName( getThisResource() ) -- путь к скрипту local sPathToFile = 'mods/deathmatch/resources/'..sThisResourceName..'/include.lua' -- загружаем файл и создаем из него функцию local fFunction, sErrorText = loadfile(sPathToFile) if not fFunction then -- если в коде файла есть ошибка или файл не существует outputChatBox('Ошибка в коде файла "'..sPathToFile..'":') outputChatBox(' '..sErrorText) else -- если всё пучком local tTableFromFile = fFunction() -- выполним код файла outputChatBox(' tTableFromFile[2] = "'..tTableFromFile[2]..'"') -- tTableFromFile[2] = "rty" end -------------------------- Здесь мы имеем два скрипта в папке вашего ресурса. Вызываем один скрипт из другого, поместив код вызываемого скрипта в функцию, которую можно позднее запустить. Эта новая функция может ничего и не возвращать, а просто выполнять код подключенного файла, когда мы ее вызываем. Хранение данных в виде LUA кода Допустим, вы хотите сделать свою систему аккаунтов для вашего мода/ресурса. Вы можете выбрать XML или другой формат для хранения данных. Но почему бы не выбрать именно формат LUA таблицы ? Файл с LUA кодом легко можно изменить вручную, а также подгрузить его в скрипт с помощью loadfile. Всё, что в этом случае потребуется, только функция, которая из таблицы создает строку правильного LUA кода с определением таблицы. Все остальное у нас уже есть. Допустим файлы аккаунтов будут иметь следующий вид: return { armour = 33.4, money = 423685, password = "123456", health = 74.6 } Такой файл можно смело подгрузить в ваш скрипт в виде новой функции. Потом выполнить ее и результат положить в какую-то ячейку игрока в общей таблице данных игроков. -- общий список данных игроков local tPlayersData = {} -- пытаемся подключить файл аккаунта local fGetAccountData, sError = loadfile("путь/к/файлу/аккаунта") if fGetAccountData then -- если файл есть и загружен -- player тут это какой-то игрок, допустим, который вошел на сервер tPlayersData[player] = fGetAccountData() else -- аккаунт не существует или ошибка в данных файла аккаунта -- можно воспользоваться sError, чтобы определить причину -- или вывести ее в лог end fGetAccountData = nil -- загруженный файл уже далее не нужен sError = nil -- текст ошибки тоже не нужен Прочесть файл аккаунта мы можем, а как же его изменить? Здесь как раз и нужна функция, которая из таблицы с данными аккаунта создаст правильный LUA код с определением этой таблицы. Далее мы к этому определению таблицы спереди добавим "return " и перезапишем весь файл этой строкой. Начнем с функции конверта таблицы в строку правильного LUA кода: -- заменяет все специальные символы в строке на лексемы local tEscapes = { ["\a"] = '\\a', ["\b"] = '\\b', ["\f"] = '\\f', ["\n"] = '\\n', ["\r"] = '\\r', ["\t"] = '\\t', ["\\"] = '\\', ["\""] = '\\"' } local function fSafeString ( s ) for sSearch, sReplace in pairs(tEscapes) do s = string.gsub( s, sSearch, sReplace ) end for n = 1, 31 do s = string.gsub( s, string.char(n), '\\'..string.format('%03d',n) ) end return s end -- конверт таблицы в строку -- если bIndent = true, то сделает и корректные отступы с переносом на новую строку local sQuote = '"' local sIndent = " " local sNewLine = "\r\n" local sLeftBracket, sRightBracket = '[', ']' local sLeftBrace, sRightBrace = '{', '}' local sEqual = '=' local sIndentedEqual = ' = ' local sSeparator = ',' local nRecurLevel = 0 local nIndentLevel = 0 local tRefs = {} local function fTableToString ( t, bIndent ) if type(t) ~= 'table' then return tostring(t) end nRecurLevel = nRecurLevel + 1 nIndentLevel = nIndentLevel + 1 local tResult, sType = {} for k, v in pairs(t) do sType = type(k) if sType == 'string' then k = fSafeString(k) if not k:find('^%a%w*$') then k = sLeftBracket .. sQuote .. k .. sQuote .. sRightBracket end elseif sType == 'number' then k = sLeftBracket .. tostring(k) .. sRightBracket else k = sLeftBracket .. sQuote .. tostring(k) .. sQuote .. sRightBracket end sType = type(v) if sType == 'string' then v = sQuote .. fSafeString(v) .. sQuote elseif sType == 'table' then if tRefs[v] then v = sQuote .. tostring(v) .. sQuote else v = fTableToString(v, bIndent) end tRefs[v] = true elseif sType == 'number' then v = tostring(v) else v = sQuote .. tostring(v) .. sQuote end if bIndent then table.insert( tResult, string.rep(sIndent,nIndentLevel) .. k .. sIndentedEqual .. v ) else table.insert( tResult, k .. sEqual .. v ) end end local sResult if not bIndent then sResult = sLeftBrace .. table.concat(tResult, sSeparator) .. sRightBrace else sResult = sLeftBrace .. sNewLine .. table.concat(tResult, sSeparator..sNewLine) .. sNewLine .. string.rep(sIndent,nIndentLevel-1) .. sRightBrace end if nRecurLevel <= 1 then tRefs = {} end nRecurLevel = nRecurLevel - 1 nIndentLevel = nIndentLevel - 1 return sResult end Теперь мы можем изменить файл аккаунта: local sAccountPath = "путь/к/файлу/аккаунта" -- удаляем файл аккаунта -- по желанию можно делать и бэкапы перед удалением fileDelete(sAccountPath) -- создаем новый файл аккаунта local uFile = fileCreate(sAccountPath) if not uFile then -- если не удалось создать outputChatBox( 'Файла аккаунта не открывается для записи' ) -- что-то еще else -- файл создался -- записываем строку с определением таблицы в файл if not fileWrite( uFile, 'return ' .. fTableToString(tPlayersData[player], true) ) then outputChatBox( 'Строка данных аккаунта не записалась в файл' ) -- что-то еще end fileClose(uFile) -- сохраним файл end Точно таким же образом мы можем хранить и любые другие данные - языковые файлы, настройки и прочее. t, bIndent Edited June 18, 2012 by Guest Link to comment
eAi Posted April 3, 2011 Share Posted April 3, 2011 Loadfile will be disabled in a future release, it is not meant to be enabled at all. Link to comment
MX_Master Posted April 3, 2011 Author Share Posted April 3, 2011 Loadfile will be disabled in a future release, it is not meant to be enabled at all. anyway loadstring still will be working (: Link to comment
Other Languages Moderators Disinterpreter Posted April 4, 2011 Other Languages Moderators Share Posted April 4, 2011 Loadfile will be disabled in a future release, it is not meant to be enabled at all. You want say that loadfile will be disabled, but what this benefit??? Link to comment
lil Toady Posted April 4, 2011 Share Posted April 4, 2011 Loadfile will be disabled in a future release, it is not meant to be enabled at all. You want say that loadfile will be disabled, but what this benefit??? loadfile can break outside mta folder, even though it only expects lua files and can't be too harmful, it's still suggested to disable it. Link to comment
Other Languages Moderators Disinterpreter Posted April 4, 2011 Other Languages Moderators Share Posted April 4, 2011 loadfile can break outside mta folder, even though it only expects lua files and can't be too harmful, it's still suggested to disable it. Mean store by loadfile not secure?? What do you do? Store by SQLite nobody wants . P.S Why we speak in English??? Link to comment
lil Toady Posted April 4, 2011 Share Posted April 4, 2011 Можно написать свой loadfile с функциями файлов и loadstring Link to comment
Other Languages Moderators Disinterpreter Posted April 4, 2011 Other Languages Moderators Share Posted April 4, 2011 А кстати в PHP 5 есть функции для SQLite просто если я свой сайт буду интегрировать с сервером))) Link to comment
Arisu Posted April 4, 2011 Share Posted April 4, 2011 Можно написать свой loadfile с функциями файлов и loadstring но ведь на клиенте нету функций файлов? Link to comment
MX_Master Posted April 4, 2011 Author Share Posted April 4, 2011 в 1,1 есть функции файлов в клиенте ну если отключат обе, придется что-то вроде toJSON / fromJSON юзать + функции файлов для хранения файлов аккаунтов и прочих настроек. Создание/правку/удаление своих .DBшек sqlite еще никто не сделал Link to comment
Other Languages Moderators Disinterpreter Posted April 4, 2011 Other Languages Moderators Share Posted April 4, 2011 DBшек sqlite еще никто не сделал А стандартная система хранения аккаутнов это не SQLite? Link to comment
_Vincent_ Posted April 4, 2011 Share Posted April 4, 2011 Блин, только что читал тему и улыбнуло как 2 русских в русском разделе общались на английском X) 1 Link to comment
MX_Master Posted April 29, 2011 Author Share Posted April 29, 2011 Т.к. mtasa 1.1 уже ругается на использование loadfile, я написал небольшую функцию, которая использует loadstring и файловые функции. Эта функция позволяет подключить в ваш текущий скрипт любой другой скрипт в виде функции (что, сосбсна, loadfile и делал). Это может быть полезно, если вы из разных ресурсов подключаете какой-то класс (таблицу), находящийся в другом ресурсе. Сама функция: function getScriptAsFunction ( sPath ) if type(sPath) ~= 'string' then sPath = tostring(sPath) end if not fileExists(sPath) then return false, "can't find file '"..sPath.."'" end local uFile = fileOpen( sPath, true ) if not uFile then return false, "can't open file '"..sPath.."'" end local nFileSize = fileGetSize(uFile) if not nFileSize then fileClose(uFile) return false, "can't get file '"..sPath.."' size" end local sFileContent = fileRead( uFile, nFileSize ) if not sFileContent or #sFileContent <= 0 then fileClose(uFile) return false, "can't get file '"..sPath.."' text" end fileClose(uFile) -- вырежем BOM, если он есть в начале файла if string.byte( sFileContent, 1 ) == 0xEF then sFileContent = string.sub( sFileContent, 4 ) end local f, sError = loadstring(sFileContent) if type(f) ~= 'function' then return false, sError end return f end Функция подходит для клиента и сервера версии 1.1, а также только для сервера версии 1.0.x. Пример: Допустим, в ресурсе "tools" лежит файл "tableTools.lua", который состоит из ваших дополнительных функций для работы с таблицами, которые собраны в одну таблицу (простейший класс). Вот пример этого класса:local tableTools = {} function tableTools.findValueIndex(...) ... end function tableTools.deleteValue(...) ... end function tableTools.clear(...) ... end return tableTools В meta.xml нашего конечного ресурса добавляемresource="tools" /> чтобы ресурс, где лежит наш файл, запускался перед запуском конечного ресурса. Это важно, допустим, если вы будете подключать один клиентский скрипт из другого - он должен сначала скачаться._ И далее мы должны получить эту таблицу функций в конечный ресурс, чтобы пользоваться этими функциями. Делаем так:local myTableTools local myTableToolsFunction, errorText = getScriptAsFunction(":tools/tableTools.lua") -- если файл успешно подключен как тело функции if type(myTableToolsFunction) == "function" then -- выполним ее и результатом должна быть таблица с нашими функциями myTableTools = myTableToolsFunction() else outputDebugString( "Can't include file with my table tools! Reason: " .. errorText, 3 ) return false -- выход со скрипта end -- если таблица не получена или в ней нет нужных функций if type(myTableTools) ~= "table" or not myTableTools.findValueIndex then outputDebugString( "Can't get table with my table tools!", 3 ) return false -- выход со скрипта end -- здесь мы уже можем юзать нашу новую таблицу myTableTools с функциями внутри -- ... Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now