Skip to content

Home (Russian)

Pavel Akulichev edited this page Oct 17, 2020 · 3 revisions

Добро пожаловать на вики-страницы SL:NET!

SL:NET (Simple Lua Network) — модуль сетевого интерфейса, работающий на основе библиотеке LuaSocket и использующий в своей работе протокол UDP (User Datagram Protocol), который чаще всего используется при реализации сетевых игр. Помимо стандартного функционала UDP были добавлены некоторые функции, которые присущи TCP (Transmission Control Protocol), но отсутствуют в UDP. Так, например, SL:NET имеет функционал, позволяющий убедиться в том, что тот или иной пакет был доставлен до получателя, а также система приоритетов отправки пакетов. Чем выше приоритет, тем больше попыток сделает ваш сервер или клиент, чтобы отправить пакет в случае, если вторая сторона не отчиталась о его получении. Таким образом на модуле SL:NET можно писать как большие проекты такие как, например, SL:MP - прародитель SL:NET, именно там впервые была использована эта технология, так и маленькие проекты: чаты, мини-игры по вашему желанию (крестики-нолики, шашки, шахматы по сети) и прочее, что взбредет вам в голову, ведь функционал не ограничен.

Есть ли какие-нибудь ограничения по количеству клиентов
Нет, абсолютно никаких ограничений нет, ибо UDP как протокол не имеет функции подключения. Вы можете присоединить столько клиентов, сколько хотите - все эти операции происходят при помощи обмена пакетами. Простой пример: клиент отправляет пакет с запросом на подключение, сервер проверяет не зарегистрирован ли ещё этот клиент в системе и если нет, то происходит регистрации, а после клиенту отправляется пакет о том, что он зарегистрирован.

Подходит ли модуль для каких-либо площадок кроме MoonLoader'a
Да, SL:NET без каких-либо проблем запускается при помощи LuaJIT вне игры, а тот, как известно, поддерживается и OC Windows и различными сборками Linux, соответственно можно собрать сервер и поставить его на VPS/VDS - нет никаких ограничений в этом плане. При этом SL:NET совместим с MoonLoader`ом.

Примеры использования SL:NET в своих проектах (версия < 1.0.0)
Ниже представлены две версии кода: один для сервера, второй для клиента - вместе они работают отлично и обмениваются пакетами. Обратите внимание на комментарии, в них содержится полезная информация, которая может вам пригодиться при написании своего клиента и/или сервера.

-- Пример использования SL:NET в качестве клиента
-- Примечание: SLNetLoop рекомендуется выполнять асинхронно, если возможно
-- Можно также выполнять в основном цикле, но возможны некоторые задержки

-- Нужно для корректной работы примера, можно убрать если есть папка с библиотеками
package.path = package.path .. ';../?.lua;../?.luac;../../?.lua;../../?.luac'
package.cpath = package.cpath .. ';../?.dll;../../?.dll'
-- Нужно для корректной работы примера, можно убрать если есть папка с библиотеками

require 'SLNet' -- подключаем модуль SL:NET для дальнейшего использования

netHandle = SLNetInit() -- инициализируем модуль SL:Net, нужно делать при каждой смене сервера
-- то есть, если вы хотите подключиться к другому серверу, сначала инициализируйте SL:NET
netHandle:connect('127.0.0.1', 6666) -- подключаемся к серверу по набору IP:PORT
-- можно также использовать функцию SLNetConnect(netHandle, IP, Port)
netHandle:setPrefix('EXMPL') -- устанавливает проверочный префик в начало пакета
-- сервер и все клиенты должны иметь один и тот же префикс, иначе пакет не обработается
-- можно также использовать функцию SLNetSetPrefix(netHandle, Prefix)


-- в данной callback-функции мы будем обрабатывать приходящие пакеты
function onReceiveData(packetID, bitStream)
  print('New Incoming Message => ' .. bitStream:export())

  if packetID == 1 then -- если нам пришел первый PacketID
    -- собираем те параметры, которые нужно получить от сервера
    local a, b = bitStream:read(UINT8), bitStream:read(STRING, bitStream:read(UINT8))
    print('Message Content: ' .. a .. ', ' .. b)
  end

  local testString = 'Hello SL:NET World!'

  local BSNew = BitStream:new() -- создадим BitStream и запишем в него данные
  BSNew:write(UINT8, 128):write(UINT8, #testString):write(STRING, testString)

  SLNetSend(netHandle, 1, BSNew, 5) -- отправляем сообщение с PacketID 1 и приоритетом 5
  -- приоритет помогает увеличить шанс доставки пакета, по сути он будет отправляться до
  -- тех пор, пока не придет подтверждение получения, а количество повторов зависит от
  -- значения четвертого аргумента, не может быть больше 255 повторений - это лимит
  -- пакеты не имеющие сильного значения лучше отправлять с приоритетом ноль
end

netHandle:setHook(onReceiveData) -- ставим хук на входящие пакеты
-- можно также использовать функцию SLNetSetHook(netHandle, Callback)

SLNetSend(netHandle, 1, nil, 5) -- отправить можно и пустой пакет, зачем - не знаю
while true do
  netHandle:loop() -- обязательно вызывать в цикле, желательно в начале
  -- этот метод отвечает за прогон основных потоков получения и отправки данных
  -- вызывать только после того, как произошел bind/connect, иначе вернет FALSE
  -- метод также имеет зеркальную функцию: SLNetLoop(netHandle)
end
-- Пример использования SL:NET в качестве сервера
-- Примечание: SLNetLoop рекомендуется выполнять асинхронно, если возможно
-- Можно также выполнять в основном цикле, но возможны некоторые задержки

-- Нужно для корректной работы примера, можно убрать если есть папка с библиотеками
package.path = package.path .. ';../?.lua;../?.luac;../../?.lua;../../?.luac'
package.cpath = package.cpath .. ';../?.dll;../../?.dll'
-- Нужно для корректной работы примера, можно убрать если есть папка с библиотеками

require 'SLNet'

netHandle = SLNetInit() -- инициализируем модуль SL:Net, вызывать при каждой смене IP:PORT
netHandle:bind('*', 6666) -- занимаем локальный IP и порт 6666, на него будут подключаться клиеты
-- можно также использовать функцию SLNetBind(netHandle, IP, Port)
netHandle:setPrefix('EXMPL') -- устанавливает проверочный префик в начало пакета
-- сервер и все клиенты должны иметь один и тот же префикс, иначе пакет не обработается
-- можно также использовать функцию SLNetSetPrefix(netHandle, Prefix)

-- в данной callback-функции мы будем обрабатывать приходящие пакеты
function onReceiveData(packetID, bitStream, addr, port)
  print('Message from ' .. addr .. ':' .. port)

  if packetID == 1 then -- если нам пришел первый PacketID
    -- собираем те параметры, которые нужно получить от сервера
    local a, b = bitStream:read(UINT8), bitStream:read(STRING, bitStream:read(UINT8))
    print('Message Content: ' .. a .. ', ' .. b)
  end

  local testString = 'Hello SL:NET World!'

  local BSNew = BitStream:new() -- создадим BitStream и запишем в него данные
  BSNew:write(UINT8, 128):write(UINT8, #testString):write(STRING, testString)

  SLNetSend(netHandle, 1, BSNew, addr, port, 5) -- отправляем сообщение с PacketID 1 и приоритетом 5
  -- приоритет помогает увеличить шанс доставки пакета, по сути он будет отправляться до
  -- тех пор, пока не придет подтверждение получения, а количество повторов зависит от
  -- значения четвертого аргумента, не может быть больше 255 повторений - это лимит
  -- пакеты не имеющие сильного значения лучше отправлять с приоритетом ноль
end

netHandle:setHook(onReceiveData) -- ставим хук на входящие пакеты
-- можно также использовать функцию SLNetSetHook(netHandle, Callback)

while true do
  netHandle:loop() -- обязательно вызывать в цикле, желательно в начале
  -- этот метод отвечает за прогон основных потоков получения и отправки данных
  -- вызывать только после того, как произошел bind/connect, иначе вернет FALSE
  -- метод также имеет зеркальную функцию: SLNetLoop(netHandle)
end

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

Можно ли использовать модуль в первоначальном / измененном виде в коммерческих проектах
Да, можно, но при этом в вашем проекте обязательно должны иметься видимые упоминания об использовании модуля, а также о его разработчике. Запрещается менять название модуля при использовании, пытаться скрыть его использование копируя куски кода или повторяя алгоритмы работы. Использование в бесплатных / некоммерческих проектах этот вопрос также регулируется, должно быть упоминание об использовании SL:NET.