Информация RakCloud — Разработка

#Rin

Известный
Автор темы
Всефорумный модератор
1,214
1,043
rakcloud_logo.png

Компания RINWARES анонсирует уникальную возможность бесплатного получения лицензии на использование нашего проекта RakCloud.
Главное требование - умение писать Lua скрипты с последующим изучением RakCloud Lua API.

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

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

  • Ресурсы сервера распределены между пользователями.
  • Один IP-адрес будет использоваться сразу несколькими пользователями.
  • Одновременно может быть создано не более двух ботов.
Мы также оставляем за собою право выдавать или аннулировать данную лицензию по своему усмотрению.
Обращения рассматриваются вручную, и требуют некоторого времени на обработку.
При обращении указывайте ссылку на свой профиль на данном форуме, а также подтвердите свою причастность к аккаунту. По вашему профилю должно быть очевидно, что вы можете уверено писать на Lua.

Основная тема в рынке: https://www.blast.hk/threads/66666

У проекта есть вики, где можно ознакомиться со всеми возможностями программного интерфейса.

# Среда разработки
Основной средой разработки является редактор кода Visual Studio Code. Для удобства работы рекомендуется установить следующие дополнения:
В скором времени также будет разработано расширение для автокомплита под RakCloud API.

# VSC и FTP
После того как вы установили расширение для работы с FTP, вам следует его настроить:
1. Нажмите Ctrl + Shift + P (или F1).
2. Введите "ftp-simple".
3. Выберите "ftp-simple: Config - FTP connection setting".
Укажите данные полученные при покупке, такие как: host, port, username, password. В поле name указывайте удобное для вас именование. Если у вас несколько FTP серверов, указывайте их через запятую в главной секции, подробнее смотрите на странице расширения.

JSON:
[
    {
        "name": "RakCloud",
        "host": "",
        "port": 21,
        "type": "ftp",
        "username": "",
        "password": "",
        "path": "/RakCloud/",
        "autosave": true,
        "confirm": true,
        "secure": true,
        "secureOptions": {
            "rejectUnauthorized": false,
            "secureProtocol": "TLSv1_2_method"
        }
    }
]
Для открытия директории FTP сервера, вновь нажмите Ctrl + Shift + P (или F1) и выберите "ftp-simple Remote directory open to workspace.".

# Основная функция
Основная функция каждого скрипта имеет название main, она автоматический создается в качестве скриптового потока и запускается после загрузки всех скриптов.
Lua:
function main()
    print("Turbo script by Rin loaded.")
    while true do
        wait(10000)
        print("I am working...")
    end
end

# Скриптовые потоки
Помимо функции main, вы можете создать отдельные потоки с помощью класса Task.
Lua:
-- Функция, выводящая текст каждые 2 секунды.
function loopPrint(str)
    while true do -- Бесконечный цикл.
        print(str) -- Печатаем строку.
        wait(2000) -- Ждем 2 секунды.
    end
end

-- Создаем кооперативный поток и передаем строку в качестве аргумента.
Task.create(loopPrint, "Hello World")
Обязательным условием для каждого бесконечного цикла является использование функции wait хотя бы с нулевой задержкой, чтобы предотвратить блокировку планировщика.

# События
Подробнее можно узнать здесь. События для SA-MP'а устанавливаются следующим образом:
Lua:
samp.on("playerJoin", function(playerId, color, isNpc, nickName)
    print("На сервер зашел игрок:", nickName, "ID:", playerId)
end)

# Концепция ботов
При разработке я придерживался мнения, что ядро бота не должно отравлять на сервер что-то, что не очевидно в скриптах. Единственным исключением из этого правила стали пакеты необходимые для начального входа на сервер, но и их можно перехватить с помощью системы событий. Все остальное нужно отправлять самостоятельно, в частности и пакеты синхронизации.
Отправка синхры:
-- Отправляем пакеты согласно частоте который отправит нам сервер в событии InitGame, смотреть ниже.
local netModeNormalOnfootSendRate = 40
local netModeNormalIncarSendRate = 40

function main()
    while true do
        wait(0)
        if samp.isBotConnected() and samp.isBotSpawned() then -- Если бот подключен и заспавнен.
            if samp.isBotOnFoot() then -- Если бот на ногах.
                samp.sendOnFootData()
                wait(netModeNormalOnfootSendRate)
            elseif samp.isBotDrivingVehicle() then -- Если бот управляет транспортом.
                samp.sendInCarData()
                wait(netModeNormalIncarSendRate)
            elseif samp.isBotSitsAsPassenger() then -- Если бот является пассажиром транспорта.
                samp.sendPassengerData()
                wait(netModeNormalIncarSendRate)
            end
        end
    end
end

samp.on("initGame", function(serverInfo)
    -- Меняем частоту отправки пакетов синхронизации на ту, что  требует сервер.
    netModeNormalOnfootSendRate = serverInfo.netModeNormalOnfootSendRate
    netModeNormalIncarSendRate = serverInfo.netModeNormalIncarSendRate
end)

# Менеджер и боты
Скрипты пишутся отдельно для менеджера и бота. Менеджер фактически является тем, кто управляет всеми ботами, поэтому его функционал никак не завязан на SA-MP'е, а только на общении с внешним миром и самим ботами.

# Взаимодействие ботов
Боты могут посылать менеджеру или друг другу сообщения.

Бот сообщает менеджеру, что с сервера вышел админ и пора запустить еще одного бота.

Скрипт для бота:
samp.on("playerQuit", function(playerId, reason)
    local player = samp.getPlayer(playerId) -- Получаем игрока из пула по его ID.
    if player:getNickName() == "Vladimir_Putin" then
        -- С сервера вышел главный админ, сообщаем менеджеру запустить остальных ботов.
        ipc.send("GuysTheAdminIsOutItsTimeToWork")
    end
end)

Обработчик менеджера, на запрос бота запустить еще ботов.
Скрипт для менеджера:
ipc.on("GuysTheAdminIsOutItsTimeToWork", function()
    local bot = SampBot.new("TurboBot") -- Создаем нового бота.
    bot:start() -- Запускаем его.
end)

# Управление
Для управления используется кроссплатформенный клиент который уже доступен на операционных системах Windows и Linux, и архитектурах x86, x64. Также возможно собрать под macOS (Кому-то реально понадобится? Мне нужен мак для этих целей...). Код клиента будет опубликован на GitHub после проведения рефакторинга.

1618323518076.png 1618323615043.png 1618323668115.png
По скриншотам видно, что клиент местами сыроват.

# Будущее
У меня достаточно много идей, реализация их всех могла бы занять вечность, а проект и так в разработке более чем полтора года. Ниже список некоторых из них:
  • Добавить в Lua функционал для работы с HTTP запросами.
  • Добавить систему модулей для Lua.
  • Подумать про обходы на сервера с лаунчерами.
  • Возможность распространять приватные скрипты без получения исходного кода.
  • Реализовать управление с помощью ботов в ТГ и ВК.
  • Создать клиент для мобильных устройств, а также внутриигровой клиент.
  • Реализовать в менеджере функционал для создания веб-сервера.
  • Ручное управление ботами прямо из игры.
  • И многое другое, что я в данный момент не могу вспомнить.
Вступайте в наш Discord канал.
Связь со мною: Telegram
 
Последнее редактирование:

#Kai-

Известный
705
292
Менеджеры, фермы, боты, разве в сампе боты не легчайши палятся, слишком много пафоса для сампа как по мне.
Может я чего-то не знаю и фермеры миллионы зарабатывают, но верится с трудом.

Ты еще не подумал?
Почему, подумал когда текст составлял...

Кругом обман, кругом обман... пойду в банан, пойду в банааааан...
 

Shamanije

Известный
Друг
959
923
Может я чего-то не знаю и фермеры миллионы зарабатывают, но верится с трудом.
Если все делать по уму, то можно зарабатывать неплохие деньги, однако об этом никто никогда не напишет на каком то форуме. Все действительно прибыльные боты находятся в приватном пользовании и человек в здравом уме не полетит делать тему в разделе Рынок, и уж тем более в разделе Читы.
 

#Rin

Известный
Автор темы
Всефорумный модератор
1,214
1,043
Добавлена система модулей а также обширный пакет модулей установленных по умолчанию. С полным списком можно познакомиться на вики: https://wiki.rinwares.com/ru/rakcloud/libraries

Центром всех новшеств стал luv/luvit, которые предоставляют функционал асинхронной работы с файлами и сетью. Под капотом используется та же библиотека что и в node.js.
luv — Предоставляет низкоуровневые функции, и вам врядли потребуется обращаться к ним. А если и потребуется, то скорее всего для работы с TCP/UDP сокетами.
luvit — Комплекс библиотек использующих luv для реализации HTTP сервера/клиента, более удобной работы с файлами и много другого.
Теперь в RakCloud'е стало возможно написать даже сайт, например свою собственную панель по ботам или веб API для получения информации с SA-MP сервера.

Вот несколько примеров кода:
HTTP GET запрос на сайт:
local http = require("http")

local req = http.request("http://rinwares.com", function(res)
    assert(res.statusCode == 200)

    res:on("data", function(chunk)
        print("Data:", chunk)
    end)
    res:on("end", function()
        print("Stream ended")
    end)
end)
req:done()

Чтение файла:
local fs = require("fs")

fs.readFile("shared/data/text.txt", function(err, data)
    assert(not err, err)
    print(data)
end)

HTTP сервер:
local http = require("http")
 
http.createServer(function(req, res)
    local body = "Hello world\n"
    res:setHeader("Content-Type", "text/plain")
    res:setHeader("Content-Length", #body)
    res:finish(body)
end):listen(8080)

С другими примерами можно ознакомиться на GitHub'e:
 

#Rin

Известный
Автор темы
Всефорумный модератор
1,214
1,043
Планировщик задач теперь имеет аналог async/await из JS, позволяющий вызывать асинхронные функции как синхронные, что как правило гораздо удобнее.


Пример:
Lua:
function main()
    -- Неудобно если таких вложенных вызов очень много.
    asyncFunction("World", function(res)
        print(res)
        asyncFunction("Father", function(res)
            print(res)
        end)
    end)

    -- А так гораздо удобнее.
    print(asyncFunctionInSyncStyle("World"))
    print(asyncFunctionInSyncStyle("Rinat"))
end

-- Якобы асинхронная функция. Здесь мог быть сетевой запрос или чтение файла.
function asyncFunction(req, callback)
    Task.create(function()
        wait(1000) -- Типо бурная деятельность...
        callback("Hello " .. req .. "!")
    end)
end

-- Обертка над асинхронной функции.
function asyncFunctionInSyncStyle(req)
    -- Получаем текущий кооперативный поток.
    local thisTask = Task.this()

    -- Проверяем что вызов происходит из корутины.
    assert(thisTask, "attempt to yield across C-call boundary")

    -- Вызываем нашу асинхронную функцию.
    asyncFunction(req, function(res)
        -- Возвращаем ответ.
        thisTask:await(res)
    end)

    -- Блокируем текущий кооперативный поток и ждем ответа.
    return thisTask:await()
end

Реальный пример на HTTP Get запросе.
Lua:
local http, https = require("http"), require("https")

function httpGetRequest(url, callback)
    local thisTask
    if not callback then
        thisTask = Task.this()
        assert(thisTask, "attempt to yield across C-call boundary")
    end
    local req = (http.parseUrl(url).protocol == "https" and https or http).get(url, function(res)
        local body = ""
        res:on("data", function(chunk)
            body = body .. chunk
        end)
        res:on("end", function()
            if callback then
                callback(body)
            else
                thisTask:await(body)
            end
        end)
    end)
    req:done()
    if callback then
        return req
    else
        return thisTask:await(), req
    end
end

Универсальная функция для создания оберток над асинхронными функциями, колбек в параметрах исходной функции всегда должен быть последним.
Lua:
function main()
    local func = promisify(asyncFunction)
    print(func("World"))
end

function promisify(func)
    return function(...)
        local thisTask = Task.this()
        assert(thisTask, "attempt to yield across C-call boundary")
        func(..., function(...)
            thisTask:await(...)
        end)
        return thisTask:await()
    end
end
 
Последнее редактирование: