6 записей с тегом "lua" | Блог _AMD_
Перейти к основному содержимому

6 записей с тегом "lua"

Посмотреть все теги

· 7 мин. чтения

InGameShop (IGS) - внутриигровая менюшка для Garry's Mod серверов, через которую игроки донатят их владельцам с помощью gm-donate.net. До недавней поры установка происходила через Steam Workshop. Сейчас готова версия, которая 1 запросом скачивает и выполняет код. Внизу будет реализация

Разработка такой версии изначально казалась задачкой на вечерок под кофеек, но много раз откладывалась из-за каких-то нюансов. Было много идей, как все реализовать, но выжила только одна, о которой пост

· 7 мин. чтения

Я занимаюсь Garry's Mod'ом с 2015 года, начиная с администрирования чужого сервера и до создания своего проекта, где большинство разработок были самописны (не куплены), а сервера проекта в свое время были в топ-10 мира по рейтингу GameTracker.

Инструменты, которые я использую позволили мне ускорить разработку в сравнении с банальным редактированием файлов через FTP в десятки раз. То, что написано ниже требует немного времени на изучение, но это время уже за неделю окупится за счет гораздо большего скорости разработки и удобства в целом.

· 2 мин. чтения

Насчет __mode совсем недавно узнал как работает. Штука очень классная и полезная.

По-простому объясняя, нужно понимать, что сборщик мусора считает мусором те данные, на которые больше нигде не осталось ссылок.

Сами ссылки ты устанавливаешь, когда например добавляешь данные в таблицу. Пример:

local arr = {}
QWE[1] = arr

Теперь на таблицу arr установлена одна сильная ссылка. Если сделать QWE[1] = nil, то ссылок больше не останется и сборщик потом удалит эту табличку

__mode позволяет устанавливать таблицам инструкцию, что закрепленные за ней ключи или значения не будут создавать сильные ссылки (они называются слабыми)

Если мы сделаем setmetatable(QWE, {__mode = "v"}) (тем самым сказав, что значения таблицы QWE не считаются сильными ссылками) и повторим наш QWE[1] = arr, то сборщик мусора зачистит наш arr и при print(QWE[1]) после работы сборщика отпринтит nil

Слабыми можно делать и ключи, тогда нужно указать mode "v". Или и ключи и значения: "kv".

Реальное применение

Я узнал об этом совсем недавно, но успел применить так: в новой версии IGS полностью переписан API framework. Репитер упавших запросов сделан отдельным модулем. Так вот тот репитер повторяет запросы, которые по некоторым причинам провалились, но делает это не более 15 раз.

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

· 4 мин. чтения

Мне потребовалась функция, определяющая, находится ли IP под указанной маской и я захотел в деталях понять, как она работает. Для этого надо было понять, как IP конвертируются в целые числа и наоборот

Преобразования IP в число и обратно это побитовые операции, но я постараюсь объяснить все так, чтобы понятно было тем, кто с ними не знаком или плохо знаком. Подробнее о них можно почитать, например, здесь.

Представление IP

Как вам известно, IP состоит из 4 блоков, где число в блоке \<= 255. Почему? В старые времена компьютеры поддерживали числа только до 32 бит. В 32 битах максимальное число 2^32-1 = 4294967295. В шестнадцатиричной системе (HEX) оно выглядит как FFFFFFFF или 32 ноля в двоичной (BIN).

А теперь разделим FFFFFFFF на 4 кусочка (блока): FF FF FF FF FF HEX = 255 DEC. Отсюда и получается максимально большой ИП: 255.255.255.255.

IP это HEX или BIN кусочки, разделенные точкой

Вид 255.255.255.255 в разных представлениях:

DEC 255 255 255 255
BIN 11111111 11111111 11111111 11111111
HEX FF FF FF FF

Ноли в начале чисел ничего не значат. Тоесть, 000001 == 1, поэтому если в тексте ниже сначала чисел будут ноли, то они добавлены "для красоты"

Еще пример. Возьмем любой другой ИП, например 37.230.210.51. Разделим его на кусочки: 37 230 210 51. Конвертируем каждый из этих кусочков в HEX и BIN:

DEC 037 230 210 051
BIN 00100101 11100110 11010010 00110011
HEX 25 E6 D2 33

Конвертер десятичных числел в двоичные и HEX здесь

Склеиваем, получается HEX 25E6D233 или BIN 00100101 11100110 11010010 00110011. Теперь можете конвертировать это в DEC и получите IP в виде числа, которым его удобно хранить в БД

Функция конвертации IP в число

local function ipToInt(ip) -- ip = 37.230.210.51
local int = 0

-- 31 230 210 51
local p1, p2, p3, p4 = ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")

-- BIN | HEX
int = int + bit.lshift(p1,24) -- 00100101 00000000 00000000 00000000 | 25 00 00 00
int = int + bit.lshift(p2,16) -- 00100101 11100110 00000000 00000000 | 25 E6 00 00
int = int + bit.lshift(p3,8) -- 00100101 11100110 11010010 00000000 | 25 E6 D2 00
int = int + p4 -- 00100101 11100110 11010010 00110011 | 25 E6 D2 33

return int -- 635884083
end

Функция bit.lshift(a, b) смещает число a на b бит влево (Добавляя ноли справа). Тоесть 11010111 после смещения на 3 бита влево превратится в 11010111000. После этого смещение на 3 бита вправо вернет число в прежнее состояние, убрав ноли справа

Что происходит в функции? Она помещает каждый кусочек IP на свое место в битовом представлении и на выхлопе получается нужное число. Функция делает то же самое, что было описано ранее, только за счет математики

Конвертация числа в IP

Итак, у нас есть IP в виде DEC числа и нам нужно сделать из него глазу понятный IP. Алгоритм человеческим языком:

  1. Представляем DEC в виде BIN
  2. Вырываем с этого BIN 4 кусочка
  3. Разделяем их точкой

Lua функция:

-- Маски из примера ниже
-- FF 00 00 00 == 11111111 00000000 00000000 00000000
-- 00 FF 00 00 == 00000000 11111111 00000000 00000000
-- 00 00 FF 00 == 00000000 00000000 11111111 00000000
-- 00 00 00 FF == 00000000 00000000 00000000 11111111

local function intToIp(int) -- 635884083 (00100101 11100110 11010010 00110011)
local a = bit.band(int, 0xFF000000) -- 00100101 00000000 00000000 00000000 | 25 00 00 00 | 037.000.000.000
local b = bit.band(int, 0x00FF0000) -- 00000000 11100110 00000000 00000000 | 00 E6 00 00 | 000.230.000.000
local c = bit.band(int, 0x0000FF00) -- 00000000 00000000 11010010 00000000 | 00 00 D2 00 | 000.000.210.000
local d = bit.band(int, 0x000000FF) -- 00000000 00000000 00000000 00110011 | 00 00 00 33 | 000.000.000.051

a = bit.rshift(a, 24) -- помещаем в самое начало
b = bit.rshift(b, 16) -- на второе место
c = bit.rshift(c, 8) -- на третье
-- четвертое уже на своем

local ip = a .. "." .. b .. "." .. c .. "." .. d
return ip -- 37.230.210.51
end

print( intToIp(635884083) )

Функция bit.band(a, b) заменяет побитовый оператор AND (и). Он возвращает число, в котором только те биты, которые установлены и в a, и в b. Тоесть bit.band(0x00FF, 0xFF00) вернет 0xFFFF. Это эквивалентно bit.band(0000000011111111, 1111111100000000) == 1111111111111111.

rshift это противоположность lshift, о котором я писал выше

Бонус

Я начал изучение этого вопроса с функции, говорящей, входит ли IP в маску, поэтому делюсь и ею: lua-function-mask-has-ip

Ссылки