N1kS

Members
  • Content Count

    200
  • Joined

  • Last visited

Community Reputation

10 Decent

About N1kS

  • Rank
    Chump

Details

  • Gang
    DNR
  • Location
    Donetsk

Recent Profile Visitors

493 profile views
  1. Vim - очень классный редактор для кода с поддержкой расширений/плагинов. Подсветка синтаксиса, предпросмотр картинок, команды, snippet. Поддержка проектов, миникарта. Этакий комбайн все в одном. при этом минималистически выглядит и достаточно удобный.
  2. Для получения информации от сервера используется UDP ASE порт, по стандарту он игровой+123, то есть если игровой 22003, то ASE порт 22126. Спецификацию ASE порта можно найти тут: https://github.com/multitheftauto/mtasa-blue/blob/master/Server/mods/deathmatch/logic/ASE.cpp#L246-L341
  3. Предположим(по примеру), что мне необходимо получить id, name, surname, age, adminLevel конкретного игрока, зачем мне указывать конкретные поля для выборки, если * - это и есть все необходимые поля? Другой вопрос, если при создании таблицы создаются дополнительные колонки, например created_at, updated_at, которые нам знать не нужно. В таком случае - да, это замедлит скорость запроса, да и мы забьем память совершенно не нужной нам информацией. SELECT * FROM `players` WHERE id=1 SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` WHERE id=1 Совет совершенно верный, но лишь для частных случаев. Всегда нужно понимать, какие поля необходимы в конечном результате запроса. Чуть позже обновлю тему и допишу данный совет.
  4. Нет, не ошибаетесь, не совсем то написал. Ресурсы запущены всегда, просто нужно будет установить порядок, какой запуститься первым, а какой второй. Далее с помощью события onClientResourceStart уже отслеживать.
  5. Самый простой вариант - запускать основной ресурс, назовем его для примера Core, далее после запуска основного ресурса запускаются уже вспомогательные ресурсы из основного ресурса(его нужно будет добавить в ACL с правами админа), Vehilces, Weapons, Skins, опять же для примера. Соответственно пишем, что качаются вспомогательные ресурсы, отслеживаем состояние того или иного вспомогательного ресурса, после его запуска убираем текст о том, что качается тот или иной ресурс.
  6. Данный урок предполагает, что вы уже знаете, что такое MySQL, как это работает и зачем это нужно, если же нет - ознакомитесь с данным уроком и вернитесь сюда! Цель данного урока - донести до вас некоторые вещи, которые вы, возможно, не понимаете или даже не задумывались о них. Немного теории MySQL - свободная реляционная система управления базами данных. Для работы с базой используется язык структурированных запросов(он же SQL). Более подробную и интересующую Вас информацию об SQL, Вы сможете найти на просторах интернета. Мы же поговорим о ключевых моментах работы с MySQL. SQL имеет 4 основных оператора для манипуляции с данными. SELECT - выбор данных, удовлетворяющих заданные условия. INSERT - добавление новых данных. UPDATE - изменение существующих данных. DELETE - удаление указанных данных Я не буду рассказывать вам о существующих типах данных MySQL, как создавать или удалять таблицы, я лишь хочу поделиться с вами ключевыми вещами, которые помогут вам оптимизировать работу вашего сервера и уменьшить нагрузку на него. Рассмотрим на основе примера В качестве примера для данного урока будет использоваться таблица с названием players, имеющая следующие столбцы: `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `surname` varchar(255) NOT NULL, `age` tinyint(3) NOT NULL, `adminLevel` tinyint(3) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) Тут я думаю все понятно, никаких проблем быть не должно. Для примера рассмотрим несколько запросов. Получаем всех игроков: SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` Получаем игрока с конкретным id: SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` WHERE id=117 Получаем всех игроков, имя которых равно какому-то конкретному значению: SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` WHERE name="Bobby" Получаем первых 10 игроков, возраст которых равен 19 лет: SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` WHERE age=19 LIMIT 10 Получаем всех игроков, возраст которых больше или равен 18 годам: SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` WHERE age >= 18 Ну тут все просто, пришло время рассмотреть все на основе конкретных примеров и указать ключевые особенности. Неправильный выбор функции для формирования запроса Это первая и наиболее часто встречающаяся ошибка, которую я вижу в коде различных разработчиков. Для формирования запроса к MySQL в МТА существует 2 функции, а именно: dbExec и dbQuery. Обе функции выполняют запрос, но их главная особенность заключается в том, что dbQuery возвращает какой-либо результат и тем самым занимает оперативную память сервера, а dbExec нет. Очень часто разработчики забывают использовать dbFree после запроса, для создания, редактирования или удаления какой-то записи. Рассмотрим на основе простого примера создания нового пользователя: local Name = "Bobby" local Surname = "Raily" local Age = 23 dbExec(dbConnection, "INSERT INTO `players` (name, surname, age) VALUES(?, ?, ?)", Name, Surname, Age) Опять же, все просто, сформировали простой запрос, создали новую запись в таблице players с заданными значениями. Можно, конечно, реализовать это с помощью dbQuery, но обязательно не стоит забывать про dbFree! Казалось бы, а что такого? Но все дело в том, что если у вас на сервере зарегистрируется большое количество игроков, то это порядком забьет память ненужной ерундой, проще говоря мусором, который хранить на сервере нет никакой необходимости. local Name = "Bobby" local Surname = "Raily" local Age = 23 local Query = dbQuery(dbConnection, "INSERT INTO `players` (name, surname, age) VALUES(?, ?, ?)", Name, Surname, Age) dbFree(Query) Я надеюсь, что вы четко и ясно уяснили для себя этот пункт, на это стоит обратить особое внимание. Некорректное использование dbQuery для получения результата запроса Очень часто вам необходимо получить какие-то данные из базы данных. В этом нам помогают dbQuery и dbPoll. Если внимательно посмотреть на синтаксис функции dbQuery, то вы увидите, что в качестве первого необязательного аргумента она принимает callback функцию. Что же это такое и зачем это нужно? Я настоятельно рекомендую вам как можно внимательнее и ответственнее подойти к этому вопросу, т.к. многие разработчики до сих пор не до конца уловили суть данного аргумента и не понимают из-за чего происходят "фризы" во время работы того или иного скрипта. Предположим, что у нас зарегистрировано 100 000 уникальных игроков(для крупного проекта - вполне себе реально, но мы рассматриваем все в теории). Нам необходимо получить список всех имеющихся игроков. Вы должны понимать, что это не произойдет моментально, в любом случае на данную операцию будет затрачено какое-то количество времени. Рассмотрим два примера, использование запроса без callback функции и с ней соответственно: local Players = dbPoll(dbQuery(dbConnection, "SELECT * FROM `players`"), -1) for Index, PlayerData in ipairs(Players) do print(PlayerData.name) end function CallbackFunction(qh, tag, score) local Players = dbPoll(qh, 0) for Index, PlayerData in ipairs(Players) do print(PlayerData.name) end end dbQuery(CallbackFunction, dbConnection, "SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players`") Время, занявшее на выполнение данного запроса, составило 489 мс. Так в чем же характерная особенность между первым и вторым способом? Особенность заключается в том, что пока выполняется получение данных из базы первым способом, то работа нашего сервера приостановиться на время, которое необходимо для выполнения данного запроса, что может негативно повлиять на всех игроков, в результате могут возникнуть такие вещи как фризы, рассинхронизация и т.д. Поэтому вы четко должны понимать для себя нужно вам это или нет. Если же вам необходимо, чтобы работа сервера была приостановлена на время выполнения запроса - используйте первый вариант, если же нет - второй. Если же вы до этого имели опыт с другими ЯП, то вероятнее всего понимаете, что такое синхронное и асинхронное выполнение. Первый вариант - это синхронное выполнение, второй - асинхронное. Выполнение лишней работы посредством Lua Так же очень часто наблюдаю картину, когда многие разработчики выполняют лишнюю работу, а именно предпринимают лишние действия, которые можно было сформировать в результате SQL запроса. SQL существует довольно большое количество времени и за это время овладел всеми необходимыми функциями, операторами и прочими вещами для формирование корректного запроса. К примеру мы хотим получить id всех игроков, adminLevel которых выше или равен 2. Как видно из запроса - мы сразу же получаем список игроков, уровень которых равен или больше 2 function CallbackFunction(qh, tag, score) local Players = dbPoll(qh, 0) for Index, PlayerData in ipairs(Players) do print(PlayerData.id) end end dbQuery(CallbackFunction, dbConnection, "SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players` WHERE adminLevel >= 2") Но иногда мне приходится видеть, как некоторые разработчики получают список всех игроков и начинают проверь их adminLevel посредством Lua, что само по себе не имеет никакого смысла, т.к это выполнение лишней работы. function CallbackFunction(qh, tag, score) local Players = dbPoll(qh, 0) for Index, PlayerData in ipairs(Players) do if PlayerData.adminLevel >= 2 then print(PlayerData.id) end end end dbQuery(CallbackFunction, dbConnection, "SELECT `id`, `name`, `surname`, `age`, `adminLevel` FROM `players`") Заключение Надеюсь, что вы не зря потратили свое время и подчеркнули для себя какие-то полезные вещи, которые в последствии помогут вам при разработке собственного игрового режима или мода. Если же у вас остались какие то вопросы или вам что-то непонятно - вы можете оставлять свои вопросы в данной теме.
  7. Ну делай тогда все в одному ресурсе, ты создаешь из мухи слона, на самом то деле.
  8. @fabervox, я говорил, что скрипт сложный? Что за идиотская причина просто трепать языком? Напиши скрипт, запусти у себя, сделай замеры по времени и выложи исходный код сюда.
  9. @Essle, ну реализовать то можно, но это будет громоздко и будет занимать не малое кол-во времени, да и зачем? Куда проще запустить стандартный editor, полетать, удалить необходимые тебе мировые объекты, сохранить это все и просто в последствии запускать отдельным ресурсом при необходимости.
  10. @Jonathan.P, а что на счет файла utils.lua и type="shared"? Как по мне - самый адекватный вариант подключения необходимых функций для работы, это во-первых. Во-вторых, ты серьезно хочешь сказать, что ты используешь хотя бы 20-25% своего "набора некоторых функций" в каждом ресурсе? Не думаю. Используй необходимые тебе функции для работы конкретного ресурса, зачем что-то усложнять?
  11. Это не прога, а скрипт. Дое......ся Ты горишь?
  12. Почему вы поворачиваете стрелку на скорость авто, а не угол? Если скорость выше, чем макс. скорость на спидометре - ваша стрелка уйдет за отсечку. Используйте функцию ниже, чтобы стрелка не уходила куда не нужно. function math.clamp(val, lower, upper) if lower > upper then lower, upper = upper, lower end return math.max(lower, math.min(upper, val)) end
  13. Works for me: function fileSave( sFile, sData ) if fileExists( sFile ) then fileDelete( sFile ); end local pFile = fileCreate( sFile ); fileWrite( pFile, sData ); fileClose( pFile ); end function fileLoad( sFile ) local pFile = fileOpen( sFile, true ); local sData = fileRead( pFile, fileGetSize( pFile ) ); fileClose( pFile ); return sData; end local FROM="example.lua" local TO="compiled.luac" fetchRemote( "http://luac.mtasa.com/?compile=1&debug=0&obfuscate=0", function(data) print( tostring( data ) ) fileSave( TO, data ) end, fileLoad( FROM ), true );