MoonlightOwl

SmartLock 0.3 by Totoro

Sep 30th, 2014
90
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --                SmartLock 0.3               --
  2. -- advances lock system by Totoro; 10/01/2014 --
  3.  
  4. local event = require('event')
  5. local serial = require('serialization')
  6. local sides = require('sides')
  7. local term = require('term')
  8. local fs = require('filesystem')
  9. local com = require('component')
  10. local comp = require('computer')
  11.  
  12. -- Таблица констант --
  13. OWNER = 'Totoro'
  14. DEFAULT_USER = 'Noname'
  15. DEFAULT_LEVEL = 1
  16. DEFAULT_DOOR = 'Door X'
  17. OPEN_TIME = 1
  18. RED_STRENGTH = 1
  19. PORT = 27
  20. ALLOW = "。◕‿◕。"
  21. WAIT  = "(⊙_◎)"
  22. DENY  = "(¬_¬)"
  23. GLASSX = 240
  24. GLASSY = 55
  25. GLASS_TIME = 2
  26. -- Таблица цветов --
  27. forecolor  = 0xFFFFFF
  28. backcolor  = 0x000000
  29. allowcolor = 0x00FF00
  30. errorcolor = 0xFF0000
  31. warncolor = 0xCCCC00
  32. infocolor = 0x398eb5
  33. --
  34.  
  35. function trytofind(name)
  36.   if com.isAvailable(name) then
  37.     return com.getPrimary(name)
  38.   else
  39.     return nil
  40.   end
  41. end
  42.  
  43. local modem = trytofind('modem')
  44. local glass = trytofind('openperipheral_glassesbridge')
  45. local mgpu  = trytofind('gpu')
  46.  
  47. term.clear()
  48. function errout(message)
  49.   mgpu.setForeground(errorcolor)
  50.   print('[ERROR] '..message)
  51.   mgpu.setForeground(forecolor)
  52. end
  53. function warnout(message)
  54.   mgpu.setForeground(warncolor)
  55.   print('[WARNING] '..message)
  56.   mgpu.setForeground(forecolor)
  57. end
  58. function infout(message)
  59.   mgpu.setForeground(infocolor)
  60.   print('[INFO] '..message)
  61.   mgpu.setForeground(forecolor)
  62. end
  63.  
  64. local agpu = nil
  65. for a,b in com.list('gpu') do
  66.   if a ~= mgpu.address then agpu = com.proxy(a); break end
  67. end
  68. if agpu == nil then
  69.   errout("Не обнаружена вторая видеокарта!")
  70.   return
  71. end
  72.  
  73. if modem ~= nil then infout("Обнаружен модем. Активирован сетевой интерфейс.") end
  74. if glass ~= nil then infout("Вы можете использовать удаленный интерфейс через очки.")
  75. else warnout("Очки не обнаружены. Удаленный интерфейс недоступен.") end
  76.  
  77. width, height = mgpu.getResolution()
  78.  
  79. -- создаем папку для логов
  80. if not fs.exists("logs") then
  81.   fs.makeDirectory("logs")
  82. end
  83.  
  84. -- ======================================== L I S T   M A N A G E R S ======================================== --
  85. function readList(filename)
  86.   if fs.exists(filename) then
  87.     local file = io.open(filename, "r")
  88.     local data = file:read("*a")
  89.     local list = serial.unserialize(data)
  90.     file:close()
  91.     return list
  92.   else
  93.     errout("Файл "..filename.." не найден, создан пустой список!")
  94.     return {}
  95.   end
  96. end
  97. function saveList(list, filename)
  98.   local file = io.open(filename, "w")
  99.   data = serial.serialize(list)
  100.   file:write(data)
  101.   file:close()
  102. end
  103. function toList(list, name, data)
  104.   if data == nil then data = true end
  105.   list[name] = data
  106.   return true
  107. end
  108. function fromList(list, name)
  109.   if list[name] then
  110.     list[name] = nil
  111.     return true
  112.   end
  113.   return false
  114. end
  115. function contains(list, name)
  116.   return list[name] ~= nil
  117. end
  118. function printlist(list)
  119.   if next(list) == nil then print("Таблица пуста.")
  120.   else
  121.     c = 1
  122.     for a,b in pairs(list) do
  123.       print(a, b)
  124.       c = c + 1
  125.       if c > height-3 then
  126.         print("Любая клавиша для продолжения...")
  127.         event.pull('key_down')
  128.         c = 1
  129.       end
  130.     end
  131.   end
  132. end
  133.  
  134. -- ================================================= L O G S ================================================= --
  135. log = {}
  136. log_new = 1
  137.  
  138. function toLog(message)
  139.   table.insert(log, message)
  140.   local name = 'logs/'..string.gsub(os.date('%x'), '/', '_')..'.log'
  141.   local file = nil
  142.   if fs.exists(name) then
  143.     file = io.open(name, 'a')
  144.   else
  145.     file = io.open(name, 'w')
  146.   end
  147.   file:write(message..'\n')
  148.   file:close()
  149. end
  150. function printlog(index)
  151.   if index == nil then
  152.     infout(" Лог событий (целиком):")
  153.   else
  154.     infout(" Лог событий (новое):")
  155.   end
  156.   c = 0
  157.   for i=index or 1, #log do
  158.     print(log[i])
  159.       c = c + 1
  160.       if c > height-3 then
  161.         print("Любая клавиша для продолжения...")
  162.         event.pull('key_down')
  163.         c = 1
  164.       end
  165.   end
  166.   if c == 0 then print("Лог пуст.") end
  167.   log_new = #log + 1
  168. end
  169.  
  170. -- =========================================== P R O C E S S I N G =========================================== --
  171. function trytoopen(data)
  172.   if data == nil then
  173.     if last_door ~= nil then
  174.       ginfout("Открыто: "..doorlist[last_door].name)
  175.       open(last_door)
  176.     else
  177.       gerrout("Уточните код замка.")
  178.     end
  179.   else
  180.     local screen = com.get(data)
  181.     if screen == nil then
  182.       gerrout("Замок "..data.." не найден.")
  183.     else
  184.       if contains(doorlist, screen) then
  185.         open(screen)
  186.         ginfout("Открыто: "..doorlist[screen].name)
  187.       else
  188.         gerrout("Замок отсутствует в таблице.")
  189.       end
  190.     end
  191.   end
  192. end
  193.  
  194. function split(str)
  195.   local result = {}
  196.   for i in string.gmatch(str, "%S+") do
  197.     table.insert(result, i)
  198.   end
  199.   return result
  200. end
  201. function process(command, console)
  202.   -- разбивка строки-команды
  203.   local data = split(command)
  204.  
  205.   -- команда на открытие двери
  206.   if data[1] == 'open' then
  207.     trytoopen(data[2])
  208.   -- добавление нового игрока в таблицу / изменение существующего
  209.   elseif data[1] == 'set' or data[1] == 'user' then
  210.     local name = data[2] or DEFAULT_USER
  211.     local level = tonumber(data[3]) or DEFAULT_LEVEL
  212.     toList(userlist, name, level)
  213.     ginfout("Уровень доступа "..name..": "..level)
  214.   -- добавление новой двери в список / изменение уровня или названия существующей
  215.   elseif data[1] == 'door' then
  216.     local screen = com.get(data[2], 'screen')
  217.     if screen == nil then
  218.       gerrout("Монитор с адресом "..data[2].." не найден.")
  219.     else
  220.       local red = com.get(data[3], 'redstone')
  221.       if red == nil then
  222.         if contains(doorlist, screen) then
  223.           local level = tonumber(data[3])
  224.           if level == nil then
  225.             doorlist[screen].name = data[3]
  226.             ginfout("Название для "..string.sub(screen, 1, 4).." изменено: "..data[3].." ("..doorlist[screen].level..")")
  227.           else
  228.             doorlist[screen].level = level
  229.             ginfout("Уровень изменен: "..doorlist[screen].name.." ("..level..")")
  230.           end
  231.         else
  232.           gerrout("Этот монитор отсутствует в таблице.")
  233.         end
  234.       else
  235.         local name = data[4] or DEFAULT_DOOR
  236.         local level = tonumber(data[5]) or DEFAULT_LEVEL
  237.         toList(doorlist, screen, {name = name, red = red, level = level})
  238.         ginfout("Новый замок: "..name.." ("..level..")")
  239.       end
  240.     end
  241.   -- вывод на экран/очки списков
  242.   elseif data[1] == 'list' then
  243.     if data[2] == 'door' or data[2] == 'doors' then
  244.       if next(doorlist) == nil then ginfout("Таблица пуста.")
  245.       else
  246.         ginfout(" Таблица замков:")
  247.         for a,b in pairs(doorlist) do
  248.           print(string.sub(a, 1, 4), string.sub(b.red, 1, 4), b.name, b.level)
  249.         end
  250.       end
  251.     else
  252.       ginfout(" Таблица пользователей:")
  253.       printlist(userlist)
  254.     end
  255.   elseif data[1] == 'get' then
  256.     if contains(userlist, data[2]) then
  257.       ginfout("Пользователь "..data[2]..", уровень доступа "..userlist[data[2]])
  258.     else
  259.       local screen = com.get(data[2], 'screen')
  260.       if screen ~= nil then
  261.         if contains(doorlist, screen) then
  262.           ginfout("\n"..string.sub(screen, 1, 4), string.sub(doorlist[screen].red, 1, 4), doorlist[screen].name, doorlist[screen].level)
  263.         else
  264.           gerrout("Этот замок в таблице отсутствует.")
  265.         end
  266.       else
  267.         gerrout("Такого не существует.")
  268.       end
  269.     end
  270.   -- удаление двери или игрока из списка
  271.   elseif data[1] == 'delete' or data[1] == 'remove' then
  272.     local screen = com.get(data[2], 'screen')
  273.     if screen == nil then
  274.       if contains(userlist, data[2]) then
  275.         fromList(userlist, data[2])
  276.         ginfout("Игрок "..data[2].." успешно удален из списка.")
  277.       else
  278.         gerrout("Неверное имя/адрес.")
  279.       end
  280.     else
  281.       fromList(doorlist, screen)
  282.       ginfout("Замок по адресу "..data[2].." успешно удален из списка.")
  283.     end
  284.   -- вывод лога
  285.   elseif data[1] == 'log' then
  286.     if data[2] == 'clear' then
  287.       log = {}
  288.       log_new = 1
  289.       ginfout("Лог очищен.")
  290.     elseif data[2] == 'all' then
  291.       printlog()
  292.     else
  293.       printlog(log_new)
  294.     end
  295.   -- выход из программы
  296.   elseif data[1] == 'exit' or data[1] == 'quit' or data[1] == 'q' then return false
  297.   else gerrout("Команда не знакома.") end
  298.   -- успешное выполнение
  299.   return true
  300. end
  301.  
  302. function open(address, flag)
  303.   local red = com.proxy(doorlist[address].red)
  304.   if red == nil then
  305.     gerrout("Проверьте данные для "..doorlist[address].name.." ("..string.sub(address, 1, 4)..")!")
  306.   else
  307.     -- если дверь открывается - показываем смайлик и записываем время
  308.     if not flag then
  309.       show(address, ALLOW)
  310.       doorlist[address].time = comp.uptime()
  311.     end
  312.    
  313.     for i=0, 5 do
  314.       if flag then
  315.         red.setOutput(i, 0)
  316.       else
  317.         red.setOutput(i, RED_STRENGTH)
  318.       end
  319.     end
  320.   end
  321. end
  322.  
  323. -- ============================================ E M O T I C O N S ============================================ --
  324. function show(address, str)
  325.   agpu.bind(address)
  326.   agpu.setResolution(5,1)
  327.   if str == ALLOW then
  328.     agpu.setForeground(allowcolor)
  329.   elseif str == WAIT then
  330.     agpu.setForeground(warncolor)
  331.   else
  332.     agpu.setForeground(errorcolor)
  333.   end
  334.   agpu.set(1, 1, str)
  335. end
  336.  
  337. -- ============================================== G L A S S E S ============================================== --
  338. glass_uptime = 0
  339.  
  340. function glass_clear()
  341.   if glass ~= nil then
  342.     glass.clear()
  343.   end
  344. end
  345. function glass_addText(x, y, text, color)
  346.   if glass ~= nil then
  347.     obj = {}
  348.     obj[0] = glass.addText(x+1, y+1, text, 0x000000)
  349.     obj[1] = glass.addText(x, y, text, color)
  350.     return obj
  351.   end
  352. end
  353. function glass_splash(message, app)
  354.   if glass ~= nil then
  355.     glass_clear()
  356.  
  357.     local width = #message*5.3; title = '[ Message ]'; color = 0xFFFFFF
  358.     x = GLASSX - width/2; y = GLASSY-10
  359.     if app == 0 then
  360.       color = infocolor
  361.       title = '[ INFO ]'
  362.     elseif app == 1 then
  363.       color = warncolor
  364.       title = '[ WARNING ]'
  365.     else
  366.       color = errorcolor
  367.       title = '[ ERROR ]'
  368.     end
  369.     glass.addBox(x, y, width, 20, color, 0.5)
  370.     glass_addText(GLASSX - 17, GLASSY-11, title, 0xFFFFFF)
  371.     glass_addText(x+10, GLASSY, message, 0xFFFFFF)
  372.     glass_uptime = comp.uptime()
  373.   end
  374. end
  375. function ginfout(message)
  376.   infout(message)
  377.   glass_splash(message, 0)
  378. end
  379. function gwarnout(message)
  380.   warnout(message)
  381.   glass_splash(message, 1)
  382. end
  383. function gerrout(message)
  384.   errout(message)
  385.   glass_splash(message, 2)
  386. end
  387.  
  388. -- ======================================== N E T   I N T E R F A C E ======================================== --
  389. function sendPackage(message)
  390.   if modem ~= nil then
  391.     modem.broadcast(PORT, message)
  392.   end
  393. end
  394.  
  395. -- ================================================= M A I N ================================================= --
  396. if glass ~= nil then glass.clear() end
  397. if modem ~= nil then modem.open(PORT) end
  398.  
  399. userlist = readList('userlist.txt')
  400. doorlist = readList('doorlist.txt')
  401.  
  402. history = {}
  403. infout("Нажмите любую кнопку, чтобы перейти в режим ввода команд.")
  404.  
  405. -- Главный цикл - обновление/обработка --
  406. while true do
  407.   -- ждем событие
  408.   name, add, a, b, c, d = event.pull(OPEN_TIME+0.2)
  409.  
  410.   -- чистим очки
  411.   if glass_uptime ~= 0 then
  412.     if (comp.uptime() - glass_uptime) > GLASS_TIME then
  413.       glass_clear()
  414.       glass_uptime = 0
  415.     end
  416.   end
  417.  
  418.   -- закрываем двери
  419.   for a,b in pairs(doorlist) do
  420.     if b.time ~= nil then
  421.       if (comp.uptime() - b.time) > OPEN_TIME then
  422.         open(a, true)
  423.         b.time = nil
  424.       end
  425.     end
  426.   end
  427.  
  428.   -- обработка пользовательских команд с терминала и очков
  429.   if name == 'key_down' then
  430.     term.write("\n>> ")
  431.     command = term.read(history)
  432.     if not process(command, true) then break end
  433.   elseif name == 'chat_command' then
  434.     if not process(add, false) then break end
  435.  
  436.   -- обработка "звонков в двери"
  437.   elseif name == 'touch' then
  438.     if contains(doorlist, add) then
  439.       -- выводим информацию
  440.       print("\n"..d.." у "..doorlist[add].name.." ("..string.sub(add, 1, 4)..")!")
  441.       -- отчет для лога
  442.       local date = os.date()
  443.       log_message = date..' / '..d..' / '..doorlist[add].name.." ("..string.sub(add, 1, 4)..") | "
  444.       net_message = {date = date, player = d, door = doorlist[add].name, address = add}
  445.  
  446.       -- проверка по списку
  447.       if not contains(userlist, d) then
  448.         glass_splash(log_message..'?', 1)
  449.         show(add, WAIT)
  450.         warnout("В списке его нет. Что делать?")
  451.         toLog(log_message..'?')
  452.         last_door = add
  453.        
  454.         net_message.message = '?'
  455.         sendPackage(serial.serialize(net_message))
  456.       else
  457.         if doorlist[add].level > userlist[d] then
  458.           glass_splash(log_message..'X', 2)
  459.           show(add, DENY)
  460.           print("В доступе отказано!")
  461.           toLog(log_message..'X')
  462.          
  463.           net_message.message = 'X'
  464.           sendPackage(serial.serialize(net_message))
  465.         else
  466.           glass_splash(log_message..'Y', 0)
  467.           open(add)
  468.           print("Доступ разрешен.")
  469.           toLog(log_message..'Y')
  470.  
  471.           net_message.message = 'Y'
  472.           sendPackage(serial.serialize(net_message))
  473.         end
  474.       end
  475.     end
  476.  
  477.   -- обрабатывает сообщения по сети
  478.   elseif name == 'modem_message' then
  479.     local data = serial.unserialize(d)
  480.     if data.command == 'open' then
  481.       trytoopen(data.address)
  482.     end
  483.   end
  484. end
  485.  
  486. -- завершение (сохраняем списки, чистим экран)
  487. if modem ~= nil then modem.close(PORT) end
  488. saveList(userlist, 'userlist.txt')
  489. saveList(doorlist, 'doorlist.txt')
  490. term.clear()
RAW Paste Data