Софт MoonBot - внутриигровые боты для Moonloader

moonbot logo.png

Actual verison: 1.12
Немного лирики
Это мой первый серьезный проект на C++. Вероятно, многие опытные разработчики заметят дикие костыли (увы я не склонен бегать к каждому в ЛС задавать постоянно вопросы, а предпочитаю найти всё сам). Буду рад конструктивной доброжелательной критике, а также предложениям, как можно что-либо улучшить. До начала разработки я делал исключительно пару SF плагинов, многое наверстывал в процессе). По правде говоря я искренне хотел доделать этот проект и как видите, что-то у меня вроде вышло :)

MoonBot - модуль для Moonloader, позволяющая создавать внутриигровых ботов, вроде тех, что вы могли лицезреть в RakSAMP, RakBot, Overlight. Вы ограничиваетесь исключительно сервером.
У каждого бота имеется свой индекс, по которому к нему можно обращаться. По разумным причинам, индексы не объединены у всех скриптов. То есть у каждого скрипта может быть бот с 1-ым индексом, но доступа к чужому боту с таким же индексом у них нет.
Давайте сразу договоримся, что переменная mb будет значить это:
Lua:
local mb = require('MoonBot') -- Имя аргумента может быть любое, в дальнейшем для удобства будет использоваться это.
Примечание
То что в фигурных скобках {} - опциональный аргумент, то что в квадратных скобках [] - обязательный аргумент.
Название методаОписание
local bot = mb.add({Имя})Регистрирует бота. В ответ вы получаете его хендл
mb.remove([Индекс бота])Удаляет бота по его индексу
mb.registerIncomingRPC([ID RPC])Регистрирует отлавливание указанного RPC. Для приема используйте функцию onBotRPC(botHandle, rpcId, bitStream)
mb.disconnectAfterUnload([bool status])Устанавливает то, будут ли боты авто-удалятся при oтpaбaтывaнии функции ниже
mb.unload()Выгружает все хуки/ботов (касаемо последних, если не указан аргумент выше на true)
mb.updateCallbacks()Обновляет хуки. Я пытался сделать без этого костыля, но особо не вышло :/
local bots = mb.getBots()
Возвращает таблицу с хендлами ботов​
local bot = mb.getBotHandleByIndex([Индекс бота])Возвращает хендл бота по индексу
local bitStream = mb.getBitStream()Возвращает объект BitStream. Необходимо удалять!
local data = mb.getPlayerData()Возвращает объект PlayerData. Удаляется сам.
local data = mb.getIncarData()Возвращает объект IncarData. Удаляется сам.
local data = mb.getPassengerData()Возвращает объект PassengerData. Удаляется сам.
local data = mb.getUnoccupiedData()
Возвращает объект UnoccupiedData. Удаляется сам.​
local data = mb.getTrailerData()Возвращает объект TrailerData. Удаляется сам.
local data = mb.getBulletData()Возвращает объект BulletData. Удаляется сам.
local data = mb.getSpectatorData()Возвращает объект SpectatorData. Удаляется сам.
local data = mb.getAimData()
Возвращает объект AimData. Удаляется сам.​
Примечание
То что в фигурных скобках {} - опциональный аргумент, то что в квадратных скобках [] - обязательный аргумент.
Название метода / аргументаОписание
local index = bot.indexПолучает индекс бота. Доступно только для чтения
local name = bot.nameПолучает имя бота. Доступно только для чтения
local connected = bot.connectedПолучает статус подключенности бота (bool). Доступно только для чтения
local playerId = bot.playerIDПолучает серверный ID бота. Доступно только для чтения
local hp = bot.hp
bot.hp = 100
Получает/устанавливает здоровье бота
local armor = bot.armor
bot.armor = 100
Получает/устанавливает броню бота
local x, y, z = bot.position.x, bot.position.y, bot.position.z
bot.position.x, bot.position.y, bot.position.z = 0, 0, 0
Получает/устанавливает позицию бота
local password = bot.password
bot.password = '123123'
Получает/устанавливает пароль для сервера с паролем. Пустая строка - пароля нет.
bot:connect({IP}, {Port})Подключает бота к серверу
bot:changeName([Name])Меняет игровой ник боту. Учтите, что происходит переподключение к серверу
bot:connectAsNPC([bool state])Устанавливает то, будет ли бот подключаться как NPC. Полезно для обхода античита
bot:disconnect()Отключает бота от сервера
bot:sendRPC([rpcId], [BitStream])Отправляет указанный RPC от лица бота
bot:sendBitStream([BitStream])Отправляет указанный bitStream от лица бота
bot:sendPlayerData([data])Отправляет пакет ID_ONFOOT_SYNC от лица бота
bot:sendVehicleData([data])Отправляет пакет ID_DRIVER_SYNC от лица бота
bot:sendPassengerData([data)]Отправляет пакет ID_PASSENGER_SYNC от лица бота
bot:sendUnoccupiedData([data])Отправляет пакет ID_UNOCCUPIED_SYNC от лица бота
bot:sendTrailerData([data])Отправляет пакет ID_TRAILER_SYNC от лица бота
bot:sendSpectatorData([data])Отправляет пакет ID_SPECTATOR_SYNC от лица бота
bot:sendBulletData([data])Отправляет пакет ID_BULLET_SYNC от лица бота
bot:sendAimData([data])Отправляет пакет ID_AIM_SYNC от лица бота
bot:sendRequestClass([classId])Отправляет RPC RequestClass от лица бота
bot:sendEnterVehicle([vehicleId], [isPassenger])Отправляет RPC EnterVehicle от лица бота
bot:sendClickPlayer([playerID], [source])Отправляет RPC ClickPlayer от лица бота
bot:sendCommand([команда с /])Отправляет команду от лица бота
bot:sendChat([message])Отправляет сообщение от лица бота
bot:sendSpawn()Отправляет RPC Spawn от лица бота
bot:sendDeathNotification([reason], [killerId])Отправляет RPC смерти от лица бота
bot:sendDialogResponse([dialogId], [buttonId], [listboxId], [input])Отправляет ответ на диалог от лица бота
bot:sendClickTextdraw([textdrawId])Отправляет клик на textDraw от лица бота
bot:sendInteriorChange([interiorId])Отправляет смену интерьера от лица бота
bot:sendRequestSpawn()Отправляет RPC RequestSpawn от лица бота
bot:sendPickedUpPickup([pickupId])Поднимает пикап от лица бота
bot:sendExitVehicle([vehicleId])Отправляет RPC ExitVehicle от лица бота
bot:setReconnectTime([time in ms])Устанавливает время автореконнекта в мс
bot:setDelay([time in ms])Устанавливает задержку между синхрой в мс. От этого зависит пинг. Будьте аккуратны с этой опцией, ибо прокси пока нет! Изначальная формула расчета: 5 * (Индекс бота)
bot:giveDamage([damage])Наносит урон боту. Эта функция используется при выстреле в бота кем-то.
Примечание
То что в фигурных скобках {} - опциональный аргумент, то что в квадратных скобках [] - обязательный аргумент.
Большинство определений позаимствованы из вики мунлоадера, чтобы дать более грамотное описание
Название методаОписание
bs:remove()Удаляет битстрим. Необходимо вызывать после работы с ним, если вы получили его с помощью mb.getBitStream()
bs:setReadOffset([offset])Устанавливает смещение для последующего чтения битстрима (в битах)
bs:setWriteOffset([offset])Устанавливает смещение для последующей записи в битстрим (в битах)
bs:resetReadPointer()Сбрасывает указатель чтения битстрима
bs:resetWritePointer()Сбрасывает указатель записи битстрима
bs:ignoreBits([bits])Осуществляет пропуск битов в указателе чтения/записи битстрима
local bits = bs:getNumberOfBitsUsed()Возвращает количество записанных битов в битстриме
local bits = bs:getNumberOfUnreadBits()Возвращает количество непрочитанных битов в битстриме
local bool = bs:readBool()Читает значение типа boolean из BitStream
local int = bs:readInt8()Читает значение типа byte (1 байт) из BitStream
local int = bs:readInt16()Читает значение типа short (2 байта) из BitStream
local int = bs:readInt32()Читает значение типа integer (4 байта) из BitStream
local float = bs:readFloat()Читает значение типа float из BitStream
local string = bs:readString8()Читает строку, размером в 1 байт из BitStream
local string = bs:readString16()Читает строку, размером в 2 байта из BitStream
local string = bs:readString32()Читает строку, размером в 4 байта из BitStream
bs:writeBool([bool])Записывает значение типа boolean в BitStream
bs:writeInt8([int])Записывает значение типа byte (1 байт) в BitStream
bs:writeInt8([int])Записывает значение типа short (2 байта) в BitStream
bs:writeInt8([int])Записывает значение типа integer (4 байта) в BitStream
bs:writeFloat([float])Записывает значение типа float в BitStream
bs:writeString8([string])Записывает строку, размером в 1 байт из BitStream
bs:writeString16([string])Записывает строку, размером в 2 байта из BitStream
bs:writeString32([string])Записывает строку, размером в 4 байта из BitStream
local string = bs:decodeString([maxCharsToWrite])Декриптует строку из BitStream`a и записывает её в буфер
Примечания
1. Векторы (x, y, z, w) пока нельзя передавать в качестве массива. Возможно позже исправлю, пока имеет то, что имеем.
2. Структуры готовых пакетов идентичны тем, что описаны в библиотеке Samp.Lua
  • data.leftRightKeys
  • data.upDownKeys
  • data.keysData
  • data.position.x, data.position.y, data.position.z
  • data.quaternion.x, data.quaternion.y, data.quaternion.z, data.quaternion.w
  • data.health
  • data.armor
  • data.weapon
  • data.specialAction
  • data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
  • data.surfingOffsets.x, data.surfingOffsets.y, data.surfingOffsets.z
  • data.surfingVehicleId
  • data.animationId
  • data.animationFlags
  • data.vehicleId
  • data.leftRightKeys
  • data.upDownKeys
  • data.keysData
  • data.position.x, data.position.y, data.position.z
  • data.quaternion.x, data.quaternion.y, data.quaternion.z, data.quaternion.w
  • data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
  • data.vehicleHealth
  • data.playerHealth
  • data.armor
  • data.currentWeapon
  • data.siren
  • data.landingGearState
  • data.trailerId
  • data.trainSpeed
  • data.vehicleId
  • data.seatId
  • data.currentWeapon
  • data.health
  • data.armor
  • data.leftRightKeys
  • data.upDownKeys
  • data.keysData
  • data.position.x, data.position.y, data.position.z
  • data.vehicleId
  • data.seatId
  • data.position.x, data.position.y, data.position.z
  • data.roll.x, data.roll.y, data.roll.z
  • data.direction.x, data.direction.y, data.direction.z
  • data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
  • data.turnSpeed.x, data.turnSpeed.y, data.turnSpeed.z
  • data.vehicleHealth
  • data.trailerId
  • data.position.x, data.position.y, data.position.z
  • data.roll.x, data.roll.y, data.roll.z
  • data.direction.x, data.direction.y, data.direction.z
  • data.speed.x, data.speed.y, data.speed.z
  • data.unk
  • data.leftRightKeys
  • data.upDownKeys
  • data.keysData
  • data.position.x, data.position.y, data.position.z
  • data.targetType
  • data.targetId
  • data.origin.x, data.origin.y, data.origin.z
  • data.target.x, data.target.y, data.target.z
  • data.center.x, data.center.y, data.center.z
  • data.weaponId
  • data.camMode
  • data.camFront.x, data.camFront.y, data.camFront.z
  • data.camPos.x, data.camPos.y, data.camPos.z
  • data.aimZ
  • data.camExtZoom
  • data.weaponState
  • data.unknown
Первое, что мы делаем, подключаем библиотеку:
Lua:
local mb = require('MoonBot') -- Имя аргумента может быть любое, в дальнейшем для удобства будет использоваться это.
В конец кода или куда угодно добавляем следующее (или если у вас уже есть onScriptTerminate, то дописываете):
Lua:
function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end
Если мы хотим отлавливать пакеты, RPC, то:
  1. Добавляем в бесконечный цикл метод mb.updateCallbacks()
    Lua:
    function main()-- code
    while true do -- Важно! Если у вас уже есть беск. цикл, то не нужно делать второй! Просто добавьте туда вызов этого метода!
        wait(0) -- Задержка может быть любой, но лучше до 100 мс
        mb.updateCallbacks()
    end
  2. Объявляем функции: если хотите перелавливать входящие RPC, то:
    Lua:
    function onBotIncomingRPC(bot, rpcId, bs) -- bot - хендл бота, rpcId - id полученного rpc, bs - битстрим (с ним нельзя работать как в мунлоадеровским! Как им пользоваться описывалось ранее)
    -- code
    end
    Важно! В случае с RPC в function main еще нужно указать id RPC, которые вы намерены ловить! Делается это так:
    Lua:
    function main()    -- code
        mb.registerIncomingRPC(12) -- Регистрируем RPC SetPlayerPos для примера.
        while true do
            wait(0)
            mb.updateCallbacks()
        end
    end
    Если хотите перелавливать входящие пакеты, то:
    Lua:
    function onBotIncomingPacket(bot, packetId, bs) -- bot - хендл бота, packetId -- ид пакета, bs - битстрим (с ним нельзя работать как в мунлоадеровским! Как им пользоваться описывалось ранее)
    -- code
    end
    Важно! При работе с исходящей синхрой, не забывайте пользоваться сбрасыванием указателя.
    Если хотите перелавливать исходящие RPC, то:
    Lua:
    function onBotOutcomingRPC(bot, rpcId, bs) -- bot - хендл бота, rpcId - id полученного rpc, bs - битстрим (с ним нельзя работать как в мунлоадеровским! Как им пользоваться описывалось ранее)
    -- code
    end
    Если хотите перелавливать исходящие пакеты, то:
    Lua:
    function onBotOutcomingPacket(bot, packetId, bs) -- bot - хендл бота, packetId -- ид пакета, bs - битстрим (с ним нельзя работать как в мунлоадеровским! Как им пользоваться описывалось ранее)
    -- code
    end
Подготовительные работы успешно выполнены! Давайте создадим команду, которая будет подключать нам бота. Также сразу добавим команду на его удаление
Lua:
function main()
    -- code
    sampRegisterChatCommand('bot.add', addCommand) -- Можно не создавать отдельно функцию, сделал так для наглядности
    sampRegisterChatCommand('bot.remove', removeCommand)
    -- code
end

function addCommand(arg) -- Функция, вызываемая при команде /bot.add
    local bot = mb.add(arg) -- Создаём бота. Если бы ник был пустой, то модуль сам бы назвал бота в формате UnnamedИНДЕКС
    bot:connect() -- Подключаем бота. Как видите, айпи и порт не указан: бот возьмёт их сам. Мы бы могли их указать сами, но щас это не нужно
    sampAddChatMessage(string.format('Connecting %s', bot.name), -1) -- Выводим сообщение с ником бота
end

function removeCommand(arg) -- Функция, вызываемая при команде /bot.remove
    if arg:match('%d+') then -- Проверяем, что переданный аргумент - число
        local bot = mb.getBotHandleByIndex(tonumber(arg)) -- Получаем хендл бота по индексу (не рекомендуется хранить хендлы в таблицах)
        if bot ~= nil then -- Проверяем, что бот с таким индексом действительно есть
            sampAddChatMessage(string.format('Deleting %s', bot.name), -1) -- Выводим сообщение об удалении заранее (ибо после бот будет удалён и если мы попытаемся что-то прочитать, то нас крашнет)
            mb.remove(tonumber(arg)) -- Удаляем бота
        end
    end
end
По итогу должно получиться так:
Lua:
local mb = require('MoonBot')

function onBotRPC(bot, rpcId, bs)
    sampAddChatMessage(string.format('Bot %s got RPC: %d', bot.name, rpcId), -1)
end

function main()
    mb.registerIncomingRPC(12)
    sampRegisterChatCommand('bot.add', addCommand)
    sampRegisterChatCommand('bot.remove', removeCommand)
    while true do
        wait(0)
        mb.updateCallbacks()
    end
end

function addCommand(arg)
    local bot = mb.add(arg)
    bot:connect()
    sampAddChatMessage(string.format('Connecting %s', bot.name), -1)
end

function removeCommand(arg)
    if arg:match('%d+') then
        local bot = mb.getBotHandleByIndex(tonumber(arg))
        if bot ~= nil then
            sampAddChatMessage(string.format('Deleting %s', bot.name), -1)
            mb.remove(tonumber(arg))
        end
    end
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end
Заходим в игру проверять команды! При добавлении и удалении (кроме строчки от сервера о подключении) должно получиться так:
1624440829296.png

Всё остальное вы можете пощупать уже самостоятельно, либо посмотреть в примерах.
Lua:
local mb = require('MoonBot')

function main()
    repeat wait(0) until isSampAvailable()
    sampRegisterChatCommand('cv.add', function(param)
        local bot = mb.add(param)
        bot:connect()
    end)
    sampRegisterChatCommand('cv.remove', function(param)
        if param:match('%d+') then
            mb.remove(tonumber(param))
        end
    end)
    while true do
        wait(0)
        mb.updateCallbacks()
    end
end

function onBotOutcomingRPC(bot, rpcId, bs)
    if rpcId == 25 then
        bs:resetReadPointer()
        local ver = bs:readInt32()
        local mod = bs:readInt8()
        local nick = bs:readString8()
        local challengeResponse = bs:readInt32()
        local authkey = bs:readString8()
        local clientVer = bs:readString8()
        local unk = bs:readInt32()
        bs:resetWritePointer()
        bs:writeInt32(ver)
        bs:writeInt8(mod)
        bs:writeString8(nick)
        bs:writeInt32(challengeResponse)
        bs:writeString8(authkey)
        bs:writeString8('MOONBOT')
        bs:writeInt32(unk)
        printStringNow('client version changed!', 1000)
    end
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end
Примечание
Примеры ниже рассчитаны на MoonBot v1.0! В них используются устаревшие механики вроде: onBotPacket (а не onBotIncomingPacket), собственноручное отслеживание позиции и т.п. Рассматривайте их как пример, а не готовый код!
Примечание
С коробки у вас может не заработать. На вашей нубо-аризоне могут либо быть выключены боты, либо стоять другие текстдравы.
Также не считайте этот код образцовым, это просто пример, что можно сделать.
Lua:
local mb = require('MoonBot')
local ev = require('lib.samp.events')

local password = '123123'
local raznos = false

local botData = {}

function main()
    repeat wait(0) until isSampAvailable()
    --mb.disconnectAfterUnload(false)
    mb.registerIncomingRPC(61) -- dialog
    mb.registerIncomingRPC(68) -- spawnInfo
    mb.registerIncomingRPC(134) -- textdraw
    sampRegisterChatCommand('abot.add', function(param)
        if param:len() >= 3 then
            local bot = mb.add(param)
            table.insert(botData, {
                index = bot.index,
                logined = false,
                rvanka = false
            })
            bot:connectAsNPC(true)
            bot:connect()
            sampAddChatMessage(string.format('Connecting %s (Index: %d)', bot.name, bot.index), -1)
        end
    end)
    sampRegisterChatCommand('abot.remove', function(param)
        if param:match('%d+') then
            local index = tonumber(param)
            for k, bot in pairs(mb.getBots()) do
                if bot.index == index then
                    sampAddChatMessage(string.format('Removed %s (Index: %d)', bot.name, bot.index), -1)
                    for j, lBot in pairs(botData) do
                        if lBot.index == bot.index then
                            table.remove(botData, j)
                            break
                        end
                    end
                    mb.remove(index)
                    break
                end
            end
        end
    end)
    sampRegisterChatCommand('abot.raznos', function()
        raznos = not raznos
        for k, lBot in pairs(botData) do
            local bot = mb.getBotHandleByIndex(lBot.index)
            if bot ~= nil then
                if not bot.connected and lBot.logined then
                    lBot.rvanka = false
                end
            end
        end
        sampAddChatMessage('raznos mode ' .. (raznos and 'ON' or 'OFF'), -1)
    end)
    while true do
        wait(50)
        mb.updateCallbacks()
        local ped, playerId = nil, -1
        if raznos then
            local x, y, z = getCharCoordinates(PLAYER_PED)
            _, ped = findAllRandomCharsInSphere(x, y, z, 100, true, true)
            _, playerId = sampGetPlayerIdByCharHandle(ped)
            if _ and not sampIsPlayerPaused(playerId) and not sampIsPlayerNpc(playerId) then
                -- fine :3
            else
                ped = nil
            end
        end
        for k, lBot in pairs(botData) do
            local bot = mb.getBotHandleByIndex(lBot.index)
            if bot ~= nil then
                if not bot.connected and lBot.logined then
                    lBot.logined = false
                end
                if raznos then
                    if lBot.logined and ped ~= nil then
                        local angle = getCharHeading(ped)
                        local data = mb.getPlayerData()
                        local multiplier = 10
                        local x, y, z = getCharCoordinates(ped)
                        data.health = getCharHealth(PLAYER_PED)
                        data.armor = 0
                        data.weapon = 0
                        data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z = math.sin(-math.rad(angle)) * multiplier, math.cos(-math.rad(angle)) * multiplier, multiplier
                        data.position.x, data.position.y, data.position.z = x - math.sin(-math.rad(angle)), y - math.cos(-math.rad(angle)), z + (isCharOnFoot(ped) and 0.5 or -1)
                        bot:sendPlayerData(data)
                        lBot.rvanka = true
                    else
                        lBot.rvanka = false
                    end
                else
                    lBot.rvanka = false
                end
            end
        end
        if ped ~= nil and raznos then
            printStringNow(string.format('Fisting for ~r~%s[%d]', sampGetPlayerNickname(playerId), playerId), 500)
        end
    end
end

function onBotIncomingPacket(bot, packetId, bs)
    --sampfuncsLog(string.format('%s [%d] got packet: %d', bot.name, bot.index, packetId))
    if packetId == 41 then
        sampAddChatMessage(string.format('%s [%d] successfully connected (PlayerID: %d)', bot.name, bot.index, bot.playerID), -1)
    end
end

function onBotIncomingRPC(bot, rpcId, bs)
    --sampAddChatMessage('got rpc: ' .. rpcId, -1)
    if rpcId == 61 then
        local dialogId = bs:readInt16()
        local style = bs:readInt8()
        local title = bs:readString8()
        local btn1 = bs:readString8()
        local btn2 = bs:readString8()
        if title:find('Авторизация') then
            sampAddChatMessage(string.format('%s [%d] logining...', bot.name, bot.index), -1)
            bot:sendDialogResponse(dialogId, 1, 0, password)
        end
        if title:find('%(1/4%)') then
            sampAddChatMessage(string.format('%s [%d] registering...', bot.name, bot.index), -1)
            bot:sendDialogResponse(dialogId, 1, 0, password)
        end
        if title:find('%[2/5%]') or title:find('%[3/5%]') or title:find('%[3/4%]') then
            bot:sendDialogResponse(dialogId, 1, 0, '')
            sampAddChatMessage(string.format('%s [%d] skipped dialog (title: %s)', bot.name, bot.index, title), -1)
        end
    end
    if rpcId == 68 then
        for k, lBot in pairs(botData) do
            if lBot.index == bot.index and not lBot.logined then
                lBot.logined = true
                bot:sendRequestSpawn()
                bot:sendSpawn()
                sampAddChatMessage(string.format('%s [%d] sent request spawn', bot.name, bot.index), -1)
            end
        end
    end
    if rpcId == 134 then
        local tdId = bs:readInt16()
        if tdId == 254 then
            bot:sendClickTextdraw(242)
            sampAddChatMessage(string.format('%s [%d] selected skin', bot.name, bot.index), -1)
            for k, lBot in pairs(botData) do
                if lBot.index == bot.index then
                    lBot.logined = true
                end
            end
        end
    end
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end

function ev.onSendPlayerSync(data)
    local offset = 0
    for k, lBot in pairs(botData) do
        if lBot.logined and lBot ~= nil and not lBot.rvanka then
            offset = offset + 1
            local angle = getCharHeading(PLAYER_PED) - 90
            local sync = mb.getPlayerData()
            sync.position.x, sync.position.y, sync.position.z = data.position.x + math.sin(-math.rad(angle)) * offset, data.position.y + math.cos(-math.rad(angle)) * offset, data.position.z
            sync.health = data.health
            sync.armor = 0
            sync.quaternion.w, sync.quaternion.x, sync.quaternion.y, sync.quaternion.z = data.quaternion[0], data.quaternion[1], data.quaternion[2], data.quaternion[3]
            sync.moveSpeed.x, sync.moveSpeed.y, sync.moveSpeed.z = data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
            sync.weapon = 0
            local bot = mb.getBotHandleByIndex(lBot.index)
            bot:sendPlayerData(sync)
        end
    end
end
Примечание
Не считайте этот код образцовым, это просто пример, что можно сделать.
Также он может быть чучуть устаревшим, 27 числа я наверное выложу более крутую версию.
Lua:
local mb = require('MoonBot')
local ev = require('lib.samp.events')

local bots = {}
local time = 5
local password = '123123'

local targetId = -1
local dist = 10
local hi = false

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end

function onBotIncomingPacket(bot, packetId, bs)
    printStringNow('got packet: ' .. packetId, 500)
    if packetId == 41 then
        sampAddChatMessage('connected: ' .. bot.playerID, -1)
    end
end

function onBotRPC(bot, rpcId, bs)
    if rpcId == 134 then
        local tdId = bs:readInt16()
        sampfuncsLog('td: ' .. tdId)
        if tdId == 637 then
            bot:sendClickTextdraw(637)
            sampAddChatMessage('skin selected', -1)
            bot:sendRequestSpawn()
            bot:sendSpawn()
        end
    end
    if rpcId == 93 then
        local color = bs:readInt32()
        local msg = bs:readString32()
        sampfuncsLog('message: ' .. msg)
        if msg == 'Поздравляем вас с успешной регистрацией!' or msg == '{FFCC00}Авторизация прошла успешно.' or msg == '{DFCFCF}[Подсказка] {DC4747}Вы можете задать вопрос в нашу техническую поддержку /report.' or msg == '[Подсказка] {FFFFFF}Благодарим вас за регистрацию на нашем сервере' then
            bot:sendRequestSpawn()
            bot:sendSpawn()
            sampAddChatMessage(string.format('[Bot #%d]: Spawned', bot.index), -1)
            if msg == '{DFCFCF}[Подсказка] {DC4747}Вы можете задать вопрос в нашу техническую поддержку /report.' then
                for k, botData in pairs(bots) do
                    if botData.index == bot.index then
                        botData.logined = true
                        sampAddChatMessage(string.format('[Bot #%d]: Logined', bot.index), -1)
                        break
                    end
                end
            end
        end
    end
    if rpcId == 61 then
        local dialogId = bs:readInt16()
        local style = bs:readInt8()
        local title = bs:readString8()
        local btn1 = bs:readString8()
        local btn2 = bs:readString8()
        sampAddChatMessage('dialogID: ' .. dialogId, -1)
        if (dialogId == 1 and title:find('Пароль')) or dialogId == 2 then
            bot:sendDialogResponse(dialogId, 1, 0, password)
            sampAddChatMessage(string.format('[Bot #%d]: Logining / Registering...', bot.index), -1)
        end
        if dialogId == 1 and not title:find('Пароль') then
            bot:sendDialogResponse(dialogId, 1, 0, '')
            sampAddChatMessage(string.format('[Bot #%d]: Skipping dialogs...', bot.index), -1)
        end
    end
    if rpcId == 12 then
        local x, y, z = bs:readFloat(), bs:readFloat(), bs:readFloat()
        for k, botData in pairs(bots) do
            if botData.index == bot.index then
                botData.position = {x = x, y = y, z = z}
                pcall(sampAddChatMessage, string.format('Position: %f, %f, %f', botData.position.x, botData.position.y, botData.position.z), -1)
            end
        end
    end
    if rpcId == 68 then
        local team = bs:readInt8()
        local skin = bs:readInt32()
        local unk = bs:readInt8()
        local x, y, z = bs:readFloat(), bs:readFloat(), bs:readFloat()
        for k, botData in pairs(bots) do
            if botData.index == bot.index then
                botData.position = {x = x, y = y, z = z}
            end
        end
    end
end

function getDistanceToPlayer(playerId)
    local _, ped = sampGetCharHandleBySampPlayerId(playerId)
    if _ then
        local mx, my, mz = getCharCoordinates(PLAYER_PED)
        local x, y, z = getCharCoordinates(ped)
        return getDistanceBetweenCoords3d(x, y, z, mx, my, mz)
    else
        return 9999
    end
end

function main()
    repeat wait(0) until isSampAvailable()
    mb.registerIncomingRPC(134)
    mb.registerIncomingRPC(93)
    mb.registerIncomingRPC(61)
    mb.registerIncomingRPC(12)
    mb.registerIncomingRPC(68)
    sampRegisterChatCommand('arizona.add', function(param)
        if param ~= nil then
            local ip, port = sampGetCurrentServerAddress()
            local bot = mb.add(param, ip, port)
            bot:setReconnectTime(10000)
            table.insert(bots, {
                name = bot.name,
                index = bot.index,
                logined = false,
                position = {
                    x = 0,
                    y = 0,
                    z = 0
                },
                timer = 0,
                incar = false,
                seatId = -1,
                hiTimer = 0,
            })
            sampAddChatMessage(string.format('Connecting %s... (index: %d)', param, bot.index), -1)
        end
    end)
    sampRegisterChatCommand('arizona.remove', function(param)
        if param:match('%d+') then
            local i = 0
            for k, botData in pairs(bots) do
                i = i + 1
                if botData.index == tonumber(param) then
                    table.remove(bots, i)
                    mb.remove(tonumber(param))
                    sampAddChatMessage('deleted ' .. param, -1)
                    break
                end
            end
        end
    end)
    sampRegisterChatCommand('arizona.hi', function(param)
        if hi then hi = false return sampAddChatMessage('off', -1) end
        if param:match('%d+') then
            targetId = tonumber(param)
            local _, ped = sampGetCharHandleBySampPlayerId(targetId)
            if _ then
                if isCharOnFoot(ped) and not sampIsPlayerNpc(targetId) then
                    if getDistanceToPlayer(targetId) <= dist then
                        hi = true
                        sampAddChatMessage('on', -1)
                    else
                        sampAddChatMessage('too far', -1)
                    end
                else
                    sampAddChatMessage('player onfoot/npc', -1)
                end
            else
                sampAddChatMessage('incorrect player', -1)
            end
        end
    end)
    while true do
        wait(0)
        mb.updateCallbacks()
        local offset = 0
        for k, botData in pairs(bots) do
            wait(10)
            if botData.logined then
                offset = offset + 1
                if botData.timer > 0 then
                    botData.timer = botData.timer - 1
                end
                if hi and botData.timer <= 0 then
                    local _, ped = sampGetCharHandleBySampPlayerId(targetId)
                    if _ then
                        if isCharOnFoot(ped) and not sampIsPlayerNpc(targetId) then
                            if getDistanceToPlayer(targetId) <= dist then
                                botData.hiTimer = botData.hiTimer + 1
                                local x, y, z = getCharCoordinates(ped)
                                local angle = getCharHeading(ped)

                                x = x + (math.sin(-math.rad(angle)) / 1)
                                y = y + (math.cos(-math.rad(angle)) / 1)

                                angle = angle + 180
                                local b = 0 * math.pi / 360.0
                                local h = 0 * math.pi / 360.0
                                local a = angle * math.pi / 360.0
  
                                local c1, c2, c3 = math.cos(h), math.cos(a), math.cos(b)
                                local s1, s2, s3 = math.sin(h), math.sin(a), math.sin(b)
                  
                                local sync = mb.getPlayerData()

                                sync.health = getCharHealth(PLAYER_PED)
                                sync.armor = 0
                                sync.quaternion.w = c1 * c2 * c3 - s1 * s2 * s3
                                sync.quaternion.z = -( c1 * s2 * c3 - s1 * c2 * s3 )
                                sync.position.x, sync.position.y, sync.position.z = x, y, z
                                sync.moveSpeed.x, sync.moveSpeed.y = 0.01, 0.01

                                local bot = mb.getBotHandleByIndex(botData.index)
                                bot:sendPlayerData(sync)
                                botData.position = {x = x, y = y, z = z}
                                --sync:remove()
                                --printStringNow('sent', 500)

                                printStringNow(botData.hiTimer, 500)
                                if botData.hiTimer >= 50 then
                      
                                    botData.hiTimer = 0
                                    bot:sendCommand('/hi ' .. targetId)
                                end
                            else
                                hi = false
                                sampAddChatMessage('too far', -1)
                            end
                        else
                            hi = false
                            sampAddChatMessage('player incar/npc', -1)
                        end
                    else
                        hi = false
                        sampAddChatMessage('player disappeared', -1)
                    end
      
                end
            end
        end
    end
end

function ev.onSendPlayerSync(data)
    local offset = 0
    for k, botData in pairs(bots) do
        if botData.logined and not hi and getDistanceBetweenCoords3d(botData.position.x, botData.position.y, botData.position.z, data.position.x, data.position.y, data.position.z) <= dist then
            local angle = getCharHeading(PLAYER_PED) + 90
            if botData.timer <= 0 and not stop then
                local bot = mb.getBotHandleByIndex(botData.index)
                offset = offset + 1
                botData.timer = time + offset
                local sync = mb.getPlayerData()
                sync.leftRightKeys = data.leftRightKeys
                sync.upDownKeys = data.upDownKeys
                sync.keysData = data.keysData
                sync.position.x, sync.position.y, sync.position.z = data.position.x + (math.sin(-math.rad(angle)) * offset), data.position.y + (math.cos(-math.rad(angle)) * offset), data.position.z
                sync.moveSpeed.x, sync.moveSpeed.y, sync.moveSpeed.z = data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
                sync.quaternion.w, sync.quaternion.x, sync.quaternion.y, sync.quaternion.z = data.quaternion[0], data.quaternion[1], data.quaternion[2], data.quaternion[3]
                sync.health = data.health
                sync.armor = data.armor
                sync.weapon = data.weapon
                sync.specialAction = data.specialAction
                sync.surfingOffsets.x, sync.surfingOffsets.y, sync.surfingOffsets.z = data.surfingOffsets.x, data.surfingOffsets.y, data.surfingOffsets.z
                sync.surfingVehicleId = data.surfingVehicleId
                sync.animationId = data.animationId
                sync.animationFlags = data.animationFlags
                bot:sendPlayerData(sync)
                botData.position = {x = data.position.x, y = data.position.y, z = data.position.z}
                botData.incar = false
                botData.seatId = -1
                --sync:remove()
            end
        end
    end
end
  • Присутствует ли поддержка прокси?
    • На текущий момент нет. Основная причина: отсутствие у меня подходящих прокси с нормальным пингом.
  • На чем создана библиотека?
    • C++ с применением sol2, а также RakNet (позаимствовано из RakSAMP).
  • Будет ли работать на Arizona, Evolve, Diamond?
    • Боты подключаться будут, а всё остальное зависит от вас.
  • Сколько велась разработка первой версии?
    • Чуть больше месяца. Большинство сил было потрачено на то, чтобы не использовать updateCallbacks(). Но, как видите, он все же используется.
  • На каких версиях проверялось?
    • SA:MP 0.3.7 R1, касаемо мобильных серверов пока ничего не смогу сказать
  • Будет ли поддержка модуля?
    • Смотря, будет ли на нее спрос. Лично я горю этим, так что буду стараться поддерживать.
  • Что на нем вообще можно сделать?
    • Начиная от Join флудера, заканчивая вашей фантазией. Ну условно еще можно сделать ботов-разносчиков, ботов-фармилок
  • При перезагрузке скрипта боты отключаются и их нужно заново подключать. Что с этим можно сделать?
    • Используйте mb.disconnectAfterUnload(false). Однако с ним временно есть проблемы при выходе из игры
1.0:
  • Релиз
1.01:
  • Теперь индексы ботов начинаются с 1, а не с 0
  • Фикс Vehicle (Incar) синхры, теперь она работает корректно
1.1:
  • Добавлена возможность ставить пароль
  • Пофикшены просады ФПС
  • Добавлена возможность собственноручно указывать задержку ботам
  • Теперь позиция, здоровье, броня хранится ботом. С этими значениями можете работать вы, а также сервер
  • Добавлена возможность редактировать исходящую синхронизацию. Будьте осторожны с ней! Пример добавлен в тему.
  • В связи с добавлением исходящей синхры, onBotPacket теперь следует называть onBotIncomingPacket, а onBotRPC следует называть onBotIncomingRPC. На текущий момент скрипты не сломаются, будет уведомление от библиотеки. Однако в дальнейших версиях поддержка старого написания хуков может быть удалено.
  • Бот может получать урон от пуль. Вы также можете сами наносить урон боту.
1.11:
  • Пофикшены просады ФПС, если бот не мог подключиться к серверу
  • Возращена предыдущая система задержки.
1.12
  • Убрано мое "гениальное" решение с синхрой. Теперь не должно отключать от сервера спустя время
  • Теперь bot:disconnect() не приводит к вылету игры
  • Пофиксить зависание игры с включенным mb.disconnectAfterUnload(false)
  • Добавить прокси (???)
  • Перевести векторы (x, y, z (w)) на массивы
Благодарности:
  • @damag за небольшой патч для серверов с React
  • @Lolendor за тесты
Установка: dll из архива закинуть в moonloader/lib.
О багах можете сообщать в эту тему, только пишите подробно, пожалуйста, что и как вы делали, при каких обстоятельствах.
Надеюсь на вашу поддержку! Для меня это серьезный шаг ☺️
 

Вложения

  • MoonBot v1.1.rar
    307.2 KB · Просмотры: 2,429
  • MoonBot 1.12.rar
    256.4 KB · Просмотры: 6,669
Последнее редактирование:

ThunderGD

Новичок
1
0
2 вопроса. 1) Бот может эмулировать нажатие клавиш? 2) Бот может считывать инфу с диалогов?
 

Ya Zaregalsya

Известный
333
112
2 вопроса. 1) Бот может эмулировать нажатие клавиш? 2) Бот может считывать инфу с диалогов?
Мунбот ни че го не эмулирует, здесь это просто не нужно. Это инструмент для отправки и обработки синхры от имени несуществующего игрока.
 

Marya_Hellix

Новичок
16
4
Код:
local mb = require('MoonBot')
se = require ('samp.events')
bots = {}

local connectbot = false

function onBotRPC(bot, rpcId, bs)
sampAddChatMessage(string.format('Bot %s got RPC: %d', bot.name, rpcId), -1)
    lua_thread.create(function()
    if rpcId == 68 then
        bs:ignoreBits(48)
        bot:sendRequestSpawn()
        bot:sendSpawn()
    else
        if rpcId == 61 then
            local dialogId = bs:readInt16()
            local style = bs:readInt8()
            local title = bs:readString8()
            local btn1 = bs:readString8()
            local btn2 = bs:readString8()
                if title:find("Регистрация") or title:find("Авторизация") then
                    sampAddChatMessage("--------------", -1)
                    sampAddChatMessage(title, -1)
                    bot:sendDialogResponse(dialogId, 1, 0, "Kakashka331")
                    sampAddChatMessage("Ввёл пароль", -1)
                    sampAddChatMessage("--------------", -1)
                    wait(3000)
                    bot:sendRequestClass(0)
                else
                    if title:find("Электронная почта") then
                        sampAddChatMessage("--------------", -1)
                        sampAddChatMessage(title, -1)
                        local maile = mail() .. "@gmail.com"
                        bot:sendDialogResponse(dialogId, 1, 0, maile)
                        sampAddChatMessage("Ввёл почту "..maile, -1)
                        sampAddChatMessage("--------------", -1)
                    else
                        if title:find("Реферальная система") then
                            sampAddChatMessage("--------------", -1)
                            sampAddChatMessage(title, -1)
                            bot:sendDialogResponse(dialogId, 0, 0, "")
                            sampAddChatMessage("Пропустил рефералку", -1)
                            sampAddChatMessage("--------------", -1)
                        else
                            if title:find("Выберите пол персонажа") then
                                sampAddChatMessage("--------------")
                                sampAddChatMessage(title, -1)
                                bot:sendDialogResponse(dialogId, 0, 0, "")
                                sampAddChatMessage("Выбрал мужской пол", -1)
                                sampAddChatMessage("--------------", -1)
                            else
                                if title:find("Откуда Вы узнали о") then
                                    sampAddChatMessage("--------------", -1)
                                    sampAddChatMessage(title, -1)
                                    bot:sendDialogResponse(dialogId, 0, 0, "")
                                    sampAddChatMessage("Выбрал откуда узнал о сервере", -1)
                                    sampAddChatMessage("--------------", -1)
                                    wait(3000)
                                    bot:sendRequestClass(0)
                                end
                            end
                        end
                    end
                end
            end
        end
    end)
end
              

function onBotIncomingPacket(bot, id, bs)
    if id == 32 then
        sampAddChatMessage("Бот ("..bot.name..") был кикнут сервером", -1)
    end
end

function main()
    mb.registerIncomingRPC(12)
    sampRegisterChatCommand('addflinbot', addCommand)
    sampRegisterChatCommand("DelAllBot", delbots)
    mb.registerIncomingRPC(61)
    mb.registerIncomingRPC(68) -- spawnInfo
    mb.registerIncomingRPC(134)
    while true do
        wait(50)
        mb.updateCallbacks()
    end
end

function delbots()
mb.unload()
sampAddChatMessage("Боты выключены", -1)
end

function addCommand(nick)
local bot = mb.add(nick)
table.insert(bots, {botnick = nick})
bot:connect()
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end

function mail()
    local email = ''
    math.randomseed(os.time()^5)
    for i = 1, 8 do
          email = email .. string.char(math.random(97,122))
    end
    return email
end

Бот когда подключается, после вводит данные данные авторизации, не появляется и стоит в АФК, а если зарегестрированый бот, то его вообще кикает

UPD:С Киком бота разобрался, но он не появляется и стоит в АФК где то в ебенях

UPD2: Нашел этого бота, оказывается он стоит на кординатах 0 0 0, но он стоит в афк! Как исправить?
ZcFFhd2.png
 
Последнее редактирование:

BARRY BRADLEY

Известный
714
175
Код:
local mb = require('MoonBot')
se = require ('samp.events')
bots = {}

local connectbot = false

function onBotRPC(bot, rpcId, bs)
sampAddChatMessage(string.format('Bot %s got RPC: %d', bot.name, rpcId), -1)
    lua_thread.create(function()
    if rpcId == 68 then
        bs:ignoreBits(48)
        bot:sendRequestSpawn()
        bot:sendSpawn()
    else
        if rpcId == 61 then
            local dialogId = bs:readInt16()
            local style = bs:readInt8()
            local title = bs:readString8()
            local btn1 = bs:readString8()
            local btn2 = bs:readString8()
                if title:find("Регистрация") or title:find("Авторизация") then
                    sampAddChatMessage("--------------", -1)
                    sampAddChatMessage(title, -1)
                    bot:sendDialogResponse(dialogId, 1, 0, "Kakashka331")
                    sampAddChatMessage("Ввёл пароль", -1)
                    sampAddChatMessage("--------------", -1)
                    wait(3000)
                    bot:sendRequestClass(0)
                else
                    if title:find("Электронная почта") then
                        sampAddChatMessage("--------------", -1)
                        sampAddChatMessage(title, -1)
                        local maile = mail() .. "@gmail.com"
                        bot:sendDialogResponse(dialogId, 1, 0, maile)
                        sampAddChatMessage("Ввёл почту "..maile, -1)
                        sampAddChatMessage("--------------", -1)
                    else
                        if title:find("Реферальная система") then
                            sampAddChatMessage("--------------", -1)
                            sampAddChatMessage(title, -1)
                            bot:sendDialogResponse(dialogId, 0, 0, "")
                            sampAddChatMessage("Пропустил рефералку", -1)
                            sampAddChatMessage("--------------", -1)
                        else
                            if title:find("Выберите пол персонажа") then
                                sampAddChatMessage("--------------")
                                sampAddChatMessage(title, -1)
                                bot:sendDialogResponse(dialogId, 0, 0, "")
                                sampAddChatMessage("Выбрал мужской пол", -1)
                                sampAddChatMessage("--------------", -1)
                            else
                                if title:find("Откуда Вы узнали о") then
                                    sampAddChatMessage("--------------", -1)
                                    sampAddChatMessage(title, -1)
                                    bot:sendDialogResponse(dialogId, 0, 0, "")
                                    sampAddChatMessage("Выбрал откуда узнал о сервере", -1)
                                    sampAddChatMessage("--------------", -1)
                                    wait(3000)
                                    bot:sendRequestClass(0)
                                end
                            end
                        end
                    end
                end
            end
        end
    end)
end
              

function onBotIncomingPacket(bot, id, bs)
    if id == 32 then
        sampAddChatMessage("Бот ("..bot.name..") был кикнут сервером", -1)
    end
end

function main()
    mb.registerIncomingRPC(12)
    sampRegisterChatCommand('addflinbot', addCommand)
    sampRegisterChatCommand("DelAllBot", delbots)
    mb.registerIncomingRPC(61)
    mb.registerIncomingRPC(68) -- spawnInfo
    mb.registerIncomingRPC(134)
    while true do
        wait(50)
        mb.updateCallbacks()
    end
end

function delbots()
mb.unload()
sampAddChatMessage("Боты выключены", -1)
end

function addCommand(nick)
local bot = mb.add(nick)
table.insert(bots, {botnick = nick})
bot:connect()
end

function onScriptTerminate(script, quitGame)
    if script == thisScript() then
        mb.unload()
    end
end

function mail()
    local email = ''
    math.randomseed(os.time()^5)
    for i = 1, 8 do
          email = email .. string.char(math.random(97,122))
    end
    return email
end

Бот когда подключается, после вводит данные данные авторизации, не появляется и стоит в АФК, а если зарегестрированый бот, то его вообще кикает

UPD:С Киком бота разобрался, но он не появляется и стоит в АФК где то в ебенях

UPD2: Нашел этого бота, оказывается он стоит на кординатах 0 0 0, но он стоит в афк! Как исправить?
ZcFFhd2.png
Ты не отправляешь пакеты серверу и он просто тебя отключает в АФК ибо не получает от тебя информацию о игроке. В пример есть код:
Lua:
function ev.onSendPlayerSync(data)
    local offset = 0
    for k, lBot in pairs(botData) do
        if lBot.logined and lBot ~= nil and not lBot.rvanka then
            offset = offset + 1
            local angle = getCharHeading(PLAYER_PED) - 90
            local sync = mb.getPlayerData()
            sync.position.x, sync.position.y, sync.position.z = data.position.x + math.sin(-math.rad(angle)) * offset, data.position.y + math.cos(-math.rad(angle)) * offset, data.position.z
            sync.health = data.health
            sync.armor = 0
            sync.quaternion.w, sync.quaternion.x, sync.quaternion.y, sync.quaternion.z = data.quaternion[0], data.quaternion[1], data.quaternion[2], data.quaternion[3]
            sync.moveSpeed.x, sync.moveSpeed.y, sync.moveSpeed.z = data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
            sync.weapon = 0
            local bot = mb.getBotHandleByIndex(lBot.index)
            bot:sendPlayerData(sync)
        end
    end
end
 
  • Нравится
Реакции: MrCreepTon

Marya_Hellix

Новичок
16
4
Ты не отправляешь пакеты серверу и он просто тебя отключает в АФК ибо не получает от тебя информацию о игроке. В пример есть код:
Lua:
function ev.onSendPlayerSync(data)
    local offset = 0
    for k, lBot in pairs(botData) do
        if lBot.logined and lBot ~= nil and not lBot.rvanka then
            offset = offset + 1
            local angle = getCharHeading(PLAYER_PED) - 90
            local sync = mb.getPlayerData()
            sync.position.x, sync.position.y, sync.position.z = data.position.x + math.sin(-math.rad(angle)) * offset, data.position.y + math.cos(-math.rad(angle)) * offset, data.position.z
            sync.health = data.health
            sync.armor = 0
            sync.quaternion.w, sync.quaternion.x, sync.quaternion.y, sync.quaternion.z = data.quaternion[0], data.quaternion[1], data.quaternion[2], data.quaternion[3]
            sync.moveSpeed.x, sync.moveSpeed.y, sync.moveSpeed.z = data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
            sync.weapon = 0
            local bot = mb.getBotHandleByIndex(lBot.index)
            bot:sendPlayerData(sync)
        end
    end
end
Хмм, сейчас попробую

Ты не отправляешь пакеты серверу и он просто тебя отключает в АФК ибо не получает от тебя информацию о игроке. В пример есть код:
Lua:
function ev.onSendPlayerSync(data)
    local offset = 0
    for k, lBot in pairs(botData) do
        if lBot.logined and lBot ~= nil and not lBot.rvanka then
            offset = offset + 1
            local angle = getCharHeading(PLAYER_PED) - 90
            local sync = mb.getPlayerData()
            sync.position.x, sync.position.y, sync.position.z = data.position.x + math.sin(-math.rad(angle)) * offset, data.position.y + math.cos(-math.rad(angle)) * offset, data.position.z
            sync.health = data.health
            sync.armor = 0
            sync.quaternion.w, sync.quaternion.x, sync.quaternion.y, sync.quaternion.z = data.quaternion[0], data.quaternion[1], data.quaternion[2], data.quaternion[3]
            sync.moveSpeed.x, sync.moveSpeed.y, sync.moveSpeed.z = data.moveSpeed.x, data.moveSpeed.y, data.moveSpeed.z
            sync.weapon = 0
            local bot = mb.getBotHandleByIndex(lBot.index)
            bot:sendPlayerData(sync)
        end
    end
end
ХахА, сработало, ебался с этим 3 часа, спс
 
Последнее редактирование:
  • Нравится
Реакции: BARRY BRADLEY

LoManuL Corporation

Потрачен
162
14
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Была у меня крутая идея, и, возможно, нечто подобное позволит это реализовать.
Делюсь идеей на тысячу $, уверен, это будет актуально.
Представьте, что у вас есть личный швейцарский бомж, который может делать всю грязную работу за вас. Например, вас протаранил клоун с ковшом и уезжает, вы пишите команду /фас 337 и просто следуете за недоброжелателем. В этот момент к серверу подключается бот, летит на координаты клоуна (они ему известны, тк потенциальный труп находится непосредственно в вашей зоне стрима), и выкидывает 3 сочных плюхи с пустынного пистолета. Клоун в могиле, к вам претензий ноль, у вас даже пушки с собой не было :)) Мог бы я такое написать, самп для меня заиграл бы новыми красками..
Я хотел бы что-то подобное написать, чтобы заходила армия ботов, если ударить человека, то скрипт рассчитает, сколько ботов нужно, чтобы его убить, они бы зашли, вместе с 15 БОМЖами разведчикам, нашли бы точные координаты, отслеживали бы интерьер, куда зашёл и когда он выйдет, он телепортируются к нему, окружат и расстреляют.
 

why ega

Известный
Проверенный
2,011
1,465
Я хотел бы что-то подобное написать, чтобы заходила армия ботов, если ударить человека, то скрипт рассчитает, сколько ботов нужно, чтобы его убить, они бы зашли, вместе с 15 БОМЖами разведчикам, нашли бы точные координаты, отслеживали бы интерьер, куда зашёл и когда он выйдет, он телепортируются к нему, окружат и расстреляют.
Не везде можно много ботов подключать, обычно от 3+ акков с 1 ip кикает
 

why ega

Известный
Проверенный
2,011
1,465
мне кажется было бы удобнее, если бы названия данных в структурах были такие же как в самп луа, а то хрень пойми шо это (если не сюрфить тему)
Lua:
MoonBotAimSync.unknown = sampluaAimSync.aspectRatio
 
  • Нравится
Реакции: MrCreepTon

MrCreepTon

Неизвестный
Автор темы
Всефорумный модератор
1,962
4,159
мне кажется было бы удобнее, если бы названия данных в структурах были такие же как в самп луа, а то хрень пойми шо это (если не сюрфить тему)
Lua:
MoonBotAimSync.unknown = sampluaAimSync.aspectRatio
Инфа для мунбота бралась с старой версии samp.lua, которую я использовал во время реализации мунбота. Там не все переменные были расшифрованы
 
  • Нравится
Реакции: why ega