-
-
Save Fingercomp/684d2c72faaa941df857 to your computer and use it in GitHub Desktop.
OpenComputers Game Launcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local DEBUG = {} | |
local LOG = "/var/log/launcher.log" | |
local GAMESDIR = "/games/" | |
local W, H = 80, 40 | |
local OWNER = "Fingercomp" | |
local NOINTERRUPT = true | |
-- OpenOS Libs | |
local com = require("component") | |
local event = require("event") | |
local fs = require("filesystem") | |
local inet = require("internet") | |
local term = require("term") | |
local unicode = require("unicode") | |
local comp = require("computer") | |
local kbd = require("keyboard") | |
-- Components | |
local inetcom = com.internet | |
local gpu = com.gpu | |
-- Dynamic libs | |
local function loadLib(libPath, url, exec) | |
if not fs.exists(libPath) then | |
local f = io.open(libPath, "w") | |
local response = inet.request(url) | |
for chunk in response do | |
f:write(chunk) | |
f:flush() | |
end | |
f:close() | |
end | |
if not exec then | |
return loadfile(libPath)() | |
else | |
os.execute(libPath) | |
end | |
end | |
if not fs.exists("/lib/doubleBuffering.lua") then | |
loadLib("/tmp/buffer.lua", "http://pastebin.com/raw/vTM8nbSZ", true) | |
end | |
local buffer = require("doubleBuffering") | |
local json = loadLib("/usr/lib/json.lua", "http://regex.info/code/JSON.lua") | |
local image = require("image") | |
-- Program variables | |
local w, h | |
local games = {} | |
local mode = {"main"} | |
local dbg, log, redrawMode, save, trunc, getRating | |
local noExit = true | |
local popular = {} | |
local liked = {} | |
local rating = {} | |
local searchList = {} | |
-- Pre-actions | |
for game in fs.list(GAMESDIR) do | |
if fs.exists(fs.concat(GAMESDIR, game, "info")) then | |
local key = game:sub(-1, -1, "/") and game:sub(1, -2) or game | |
local f = io.open(fs.concat(GAMESDIR, game, "info")) | |
games[key] = json:decode(f:read("*a")) | |
f:close() | |
end | |
end | |
gpu.setResolution(W, H) | |
w, h = gpu.getResolution() | |
if NOINTERRUPT then | |
event.shouldInterrupt = function() | |
return false | |
end | |
end | |
if com.isAvailable("redstone") then | |
com.redstone.setWakeThreshold(1) | |
end | |
-- Functions | |
local function tblen(tbl) | |
local max = 0 | |
for num, _ in pairs(tbl) do | |
max = max + 1 | |
end | |
return max | |
end | |
local function maxn(tbl) | |
local max = 0 | |
for num, _ in pairs(tbl) do | |
max = math.max(max, num) | |
end | |
return max | |
end | |
local function isin(tbl, value) | |
for num, i in pairs(tbl) do | |
if i == value then | |
return true, num | |
end | |
end | |
return false | |
end | |
local function str2hex(color) | |
local hextbl = {[0]="0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"} | |
local result = 0 | |
for i = color:len(), 1, -1 do | |
local _, pos = isin(hextbl, color:sub(i, i)) | |
dbg("str2hex", color:sub(i, i) .. ", " .. pos .. ", " .. pos * 16^(color:len() - i - 1) .. " | " .. result) | |
result = result + pos * 16^(color:len() - i) | |
end | |
dbg("str2hex", result) | |
return result | |
end | |
local function center(text, width) | |
width = width or w | |
return math.floor(width / 2 - unicode.len(text) / 2) | |
end | |
local function justifyText(text, width) | |
local result = {} | |
for i = 1, unicode.len(text), width do | |
dbg("justify", i .. "/" .. unicode.len(text) .. " @ " .. width .. " (" .. i .. ":" .. i + width - 1 .. ")") | |
table.insert(result, unicode.sub(text, i, i + width - 1)) | |
dbg("justify", unicode.sub(text, i, i + width - 1)) | |
end | |
return result | |
end | |
dbg = function(section, msg) | |
if isin(DEBUG, section) or isin(DEBUG, "all") then | |
local f = io.open(LOG, "a") | |
f:write("[" .. os.date() .. "] [DBG] [" .. section .. "] " .. msg .. "\n") | |
f:close() | |
end | |
end | |
log = function(msg) | |
local f = io.open(LOG, "a") | |
f:write("[" .. os.date() .. "] " .. msg .. "\n") | |
f:close() | |
end | |
local function percRes(axis, perc) | |
axis = axis == "x" and w or h | |
return math.floor(axis / 100 * perc) | |
end | |
local function download(url, path) | |
local response = inet.request(url) | |
if not response then | |
log("Загрузка " .. url .. " в " .. path .. " завершена с ошибкой: response = nil") | |
return false | |
end | |
local f = io.open(path, "w") | |
for chunk in response do | |
f:write(chunk) | |
end | |
f:close() | |
return true | |
end | |
-- Copypasted from https://gist.github.com/Nayruden/427389 | |
local function editDistance(s, t, lim) | |
local s_len, t_len = #s, #t -- Calculate the sizes of the strings or arrays | |
if lim and math.abs(s_len - t_len) >= lim then -- If sizes differ by lim, we can stop here | |
return lim | |
end | |
-- Convert string arguments to arrays of ints (ASCII values) | |
if type(s) == "string" then | |
s = {string.byte(s, 1, s_len)} | |
end | |
if type(t) == "string" then | |
t = {string.byte(t, 1, t_len)} | |
end | |
local min = math.min -- Localize for performance | |
local num_columns = t_len + 1 -- We use this a lot | |
local d = {} | |
for i = 0, s_len do | |
d[i * num_columns] = i -- Initialize cost of deletion | |
end | |
for j = 0, t_len do | |
d[j] = j -- Initialize cost of insertion | |
end | |
for i = 1, s_len do | |
local i_pos = i * num_columns | |
local best = lim -- Check to make sure something in this row will be below the limit | |
for j = 1, t_len do | |
local add_cost = (s[i] ~= t[j] and 1 or 0) | |
local val = min(d[i_pos - num_columns + j] + 1, d[i_pos + j - 1] + 1, d[i_pos - num_columns + j - 1] + add_cost) | |
d[i_pos + j] = val | |
-- Is this eligible for tranposition? | |
if i > 1 and j > 1 and s[i] == t[j - 1] and s[i - 1] == t[j] then | |
d[i_pos + j] = min(val, d[i_pos - num_columns - num_columns + j - 2] + add_cost) | |
end | |
if lim and val < best then | |
best = val | |
end | |
end | |
if lim and best >= lim then | |
return lim | |
end | |
end | |
return d[#d] | |
end | |
local function search(tbl, value, maxResults) | |
maxResults = maxResults or math.huge | |
local subs = {} | |
local dists = {} | |
for _, v in pairs(tbl) do | |
local name = games[v].name | |
if unicode.sub(unicode.lower(name), 1, unicode.len(value)) == unicode.lower(value) then | |
table.insert(subs, v) | |
end | |
local dist = editDistance(unicode.lower(name), unicode.lower(value)) | |
dists[dist] = dists[dist] or {} | |
table.insert(dists[dist], v) | |
end | |
local results = {} | |
for i = 1, #subs, 1 do | |
if #results == maxResults then | |
return results | |
end | |
table.insert(results, subs[i]) | |
end | |
for i = 0, math.min(maxn(dists), 5), 1 do | |
if dists[i] then | |
for _, game in pairs(dists[i]) do | |
if #results == maxResults then | |
return results | |
end | |
if not isin(results, game) then | |
table.insert(results, game) | |
end | |
end | |
end | |
end | |
return results | |
end | |
local function showMenu(title, bg, fg) | |
buffer.square(1, 1, w, h, 0xffffff, 0x000000, " ") | |
buffer.square(1, 1, w, 3, bg, fg, " ") | |
buffer.square(1, 1, 3, 3, 0x000000, fg, " ", 90) | |
buffer.text(2, 2, fg, "≡") | |
buffer.text(5, 2, fg, title) | |
buffer.square(w - 6, 1, 7, 3, 0x000000, fg, " ", 90) | |
buffer.text(w - 5, 2, fg, "Поиск") | |
buffer.draw() | |
end | |
local function getMostPopular() | |
local mostPop = {"", -1} | |
for k,v in pairs(games) do | |
if v.stats.played > mostPop[2] then | |
mostPop = {k, v.stats.played} | |
end | |
end | |
return table.unpack(mostPop) | |
end | |
local function getMostLiked() | |
local mostLiked = {"", -1} | |
for k,v in pairs(games) do | |
if #v.stats.likes > mostLiked[2] then | |
mostLiked = {k, #v.stats.likes} | |
end | |
end | |
return table.unpack(mostLiked) | |
end | |
local function getHighestRating() | |
local highRating = {"", -1} | |
for k, v in pairs(games) do | |
local rating = 0 | |
for _, rateVal in pairs(v.stats.rate) do | |
rating = rating + rateVal | |
end | |
if rating > highRating[2] then | |
highRating = {k, rating} | |
end | |
end | |
return table.unpack(highRating) | |
end | |
local function drawSuggestions() | |
rating = {getHighestRating()} | |
popular = {getMostPopular()} | |
liked = {getMostLiked()} | |
buffer.text(center("Воспользуйтесь поиском или выберите из предложенного"), 6, 0x000000, "Воспользуйтесь поиском или выберите из предложенного") | |
buffer.square(3, 12, 24, 12, 0x108010, 0xffffff, " ") | |
buffer.square(29, 12, 24, 12, 0x20afff, 0xffffff, " ") | |
buffer.square(55, 12, 24, 12, 0xff4380, 0xffffff, " ") | |
buffer.text(5, 14, 0xffffff, "ЛУЧШЕЕ") | |
buffer.text(31, 14, 0xffffff, "ПОПУЛЯРНОЕ") | |
buffer.text(57, 14, 0xffffff, "ПОНРАВИВШЕЕСЯ") | |
buffer.text(5, 22, 0xffffff, rating[1]) | |
buffer.text(31, 22, 0xffffff, popular[1]) | |
buffer.text(57, 22, 0xffffff, liked[1]) | |
buffer.draw() | |
end | |
local function drawGameInfo(game, bg, fg) | |
buffer.square(w - 14, 1, 6, 3, 0x0, 0x0, " ", 90) | |
buffer.square(w - 21, 1, 6, 3, 0x0, 0x0, " ", 90) | |
buffer.square(w - 25, 1, 3, 3, 0x0, 0x0, " ", 90) | |
buffer.square(w - 34, 1, 3, 3, 0x0, 0x0, " ", 90) | |
buffer.text(w - 33, 2, fg, "-") | |
buffer.text(w - 31 + center(trunc(getRating(game, true), "+"):len(), 6), 2, fg, trunc(getRating(game, true), "+")) | |
buffer.text(w - 24, 2, fg, "+") | |
buffer.text(w - 21 + center(trunc(#games[game].stats.likes):len(), 6), 2, fg, "♥" .. trunc(#games[game].stats.likes)) | |
buffer.text(w - 14 + center(trunc(games[game].stats.played):len(), 6), 2, fg, "►" .. trunc(games[game].stats.played)) | |
local justifiedText = justifyText(games[game].description, w - 9) | |
buffer.square(w - 8, 6, 8, 1, bg, fg, " ") | |
buffer.text(w - 7, 6, fg, "Играть") | |
buffer.text(3, 6, 0x0, "Информация об игре \"" .. games[game].name .. "\" [" .. game .. "]") | |
buffer.text(3, 9, 0x0, "Автор: " .. games[game].author) | |
buffer.text(3, 10, 0x0, "Версия: " .. games[game].version) | |
buffer.text(3, 11, 0x0, "Режим: " .. (games[game].mp and "многопользовательский" or "одиночный")) | |
buffer.text(3, 13, 0x0, "Описание:") | |
for line, i in pairs(justifiedText) do | |
dbg("justified", line .. ": " .. i) | |
buffer.text(6, 13 + line, 0x0, i) | |
end | |
buffer.draw() | |
end | |
local function setRating(game, user, rate) | |
if games[game].stats.rate[user] and games[game].stats.rate[user] == rate then | |
games[game].stats.rate[user] = nil | |
else | |
games[game].stats.rate[user] = rate | |
end | |
save() | |
redrawMode() | |
end | |
local function like(game, user) | |
local likedAlready, pos = isin(games[game].stats.likes, user) | |
if likedAlready then | |
table.remove(games[game].stats.likes, pos) | |
else | |
table.insert(games[game].stats.likes, user) | |
end | |
save() | |
redrawMode() | |
end | |
local function incPldCtr(game, user) | |
games[game].stats.played = games[game].stats.played + 1 | |
games[game].stats.players[user] = (games[game].stats.players[user] or 0) + 1 | |
save() | |
end | |
local function play(game, user) | |
log("Запрошен запуск игры " .. game .. " игроком " .. user) | |
local path = fs.concat(GAMESDIR, game, games[game].runfile) | |
log("Путь к runfile: " .. path) | |
buffer.square(1, 1, w, h, 0x000000, 0xffffff, " ") | |
buffer.draw() | |
term.setCursor(1, 1) | |
io.write("Проверка файла... ") | |
if not fs.exists(path) then | |
log("Файл отсутствует, скачивание") | |
print("Файл не существует!") | |
io.write("Скачивание... ") | |
if not games[game].link then | |
log("Не указан URL, завершение") | |
print("Не указан URL!") | |
print("[Нажмите любую клавишу]") | |
event.pull(30, "key_down") | |
redrawMode() | |
return false | |
end | |
local dlRslt = {pcall(download, games[game].link, path)} | |
if dlRslt[1] then | |
print("OK") | |
else | |
print("Ошибка") | |
log("Ошибка скачивания " .. games[game].link) | |
if dlRslt[2] then | |
log(dlRslt[2]) | |
end | |
fs.remove(path) | |
print("[Нажмите любую клавишу]") | |
event.pull(30, "key_down") | |
redrawMode() | |
return false | |
end | |
else | |
print("OK") | |
end | |
io.write("Сборка байт-кода... ") | |
local suc, reason = loadfile(path, _, _, _ENV) | |
if not suc then | |
print("Ошибка! Причина записана в логе.") | |
log("Ошибка при сборке файла " .. path .. " [" .. game .. "]") | |
log(reason) | |
print("[Нажмите любую клавишу]") | |
event.pull(30, "key_down") | |
redrawMode() | |
return false | |
else | |
print("OK") | |
io.write("Запуск игры... ") | |
if not games[game].mp then comp.addUser(user) end | |
local success, rsn = xpcall(suc, debug.traceback) | |
if not success then | |
comp.removeUser(user) | |
print("Ошибка! Причина записана в логе.") | |
log("Ошибка при исполнении файла " .. path .. " [" .. game .. "]") | |
log(rsn) | |
print("[Нажмите любую клавишу]") | |
event.pull(30, "key_down") | |
redrawMode() | |
return false | |
else | |
comp.removeUser(user) | |
gpu.setResolution(w, h) | |
gpu.setForeground(0xffffff) | |
log("Сессия завершена, инкремент счётчика") | |
buffer.square(1, 1, w, h, 0x101010, 0xffffff, " ") | |
buffer.draw() | |
buffer.square(1, 1, w, h, 0x000000, 0xffffff, " ") | |
buffer.draw() | |
term.setCursor(1, 1) | |
print("Сессия завершена") | |
incPldCtr(game, user) | |
end | |
end | |
print("[Нажмите любую клавишу]") | |
event.pull(30, "key_down") | |
redrawMode() | |
save() | |
end | |
local function drawSearch(active, noHint) | |
local color = active and 0x606060 or 0xc3c3c3 | |
buffer.text(3, 6, color, "┌" .. ("─"):rep(w - 8) .. "┐") | |
buffer.text(3, 7, color, "│" .. (" "):rep(w - 8) .. "│") | |
buffer.text(3, 8, color, "└" .. ("─"):rep(w - 8) .. "┘") | |
if not noHint then buffer.text(3, 10, 0x000000, "Воспользуйтесь поиском сверху или выберите игру из списка") end | |
buffer.text(4, 7, 0x0, unicode.sub(mode[3] or "", mode[5], mode[5] + w - 9)) | |
buffer.draw() | |
end | |
trunc = function(num, plus, zeroSign) | |
plus = plus or "" | |
local sign = zeroSign or "" | |
if num < 0 then | |
sign = "-" | |
elseif num > 0 then | |
sign = plus | |
end | |
num = math.abs(num) | |
if num < 10^4 then | |
return sign .. num | |
elseif num >= 10^4 and num < 10^5 then | |
return sign .. math.floor(num / 10^2) / 10 .. "k" | |
elseif num >= 10^5 and num < 10^6 then | |
return sign .. math.floor(num / 10^3) .. "k" | |
elseif num >= 10^6 and num < 10^7 then | |
return sign .. math.floor(num / 10^4) / 100 .. "M" | |
elseif num >= 10^7 and num < 10^8 then | |
return sign .. math.floor(num / 10^50) / 10 .. "M" | |
elseif num >= 10^8 and num < 10^9 then | |
return sign .. math.floor(num / 10^6) .. "M" | |
end | |
end | |
getRating = function(game, noTrunc) | |
local rate = 0 | |
for _, value in pairs(games[game].stats.rate) do | |
rate = rate + value | |
end | |
dbg('rating', rate) | |
if noTrunc then return rate end | |
trRate = trunc(rate, "+", " ") | |
return trRate | |
end | |
local function drawGamesList(gmtbl, startLine) | |
searchList = gmtbl | |
local listHeight = w - startLine | |
local items2show = math.ceil(listHeight / 3) | |
local truncate = listHeight - items2show * 3 | |
for i = 1, items2show, 1 do | |
if gmtbl[i] then | |
local opacity = i % 2 == 1 and 90 or 80 | |
local rateColor = 0x000000 | |
if getRating(gmtbl[i], true) > 0 then | |
rateColor = 0x00ff00 | |
elseif getRating(gmtbl[i], true) < 0 then | |
rateColor = 0xff0000 | |
end | |
buffer.square(1, startLine + (i - 1) * 3, w, 3, 0x000000, 0x000000, " ", opacity) | |
buffer.square(1, startLine + (i - 1) * 3, 2, 3, str2hex(games[gmtbl[i]].color), 0x000000, " ") | |
buffer.text(4, startLine + (i - 1) * 3 + 1, 0x000000, games[gmtbl[i]].name) | |
buffer.square(w - 10, startLine + (i - 1) * 3, 9, 3, 0x000000, 0x000000, " ", 90) | |
buffer.text(w - 9, startLine + (i - 1) * 3, 0xe8a0a0, "♥ " .. trunc(#games[gmtbl[i]].stats.likes)) | |
buffer.text(w - 9, startLine + (i - 1) * 3 + 1, rateColor, " " .. getRating(gmtbl[i])) | |
buffer.text(w - 9, startLine + (i - 1) * 3 + 2, 0x008000, "► " .. trunc(games[gmtbl[i]].stats.played)) | |
end | |
end | |
buffer.draw() | |
end | |
local function getGamesList() | |
local result = {} | |
for k, _ in pairs(games) do | |
table.insert(result, k) | |
end | |
return result | |
end | |
redrawMode = function() | |
if mode[1] == "main" then | |
showMenu("ГЛАВНОЕ МЕНЮ", 0xf0f0f0, 0x000000) | |
drawSuggestions() | |
elseif mode[1] == "search" then | |
showMenu("ПОИСК", 0x10afff, 0xffffff) | |
if not mode[3] or mode[3] == "" then | |
drawSearch(mode[2]) | |
drawGamesList(getGamesList(), 12) | |
else | |
drawSearch(mode[2], true) | |
local results = search(getGamesList(), mode[3]) | |
drawGamesList(results, 10) | |
end | |
elseif mode[1] == "info" then | |
showMenu(unicode.upper(games[mode[2]].name), str2hex(games[mode[2]].color), str2hex(games[mode[2]].text)) | |
drawGameInfo(mode[2], str2hex(games[mode[2]].color), str2hex(games[mode[2]].text)) | |
end | |
end | |
local function isInBox(x, y, w, h, a, b) | |
if a >= x and a <= x + w and b >= y and b <= y + h then | |
return true | |
end | |
return false | |
end | |
local function touchHandle(data) | |
local x, y = data[3], data[4] | |
if isInBox(1, 1, 3, 3, x, y) and mode[1] ~= "main" then | |
mode = {"main"} | |
redrawMode() | |
return | |
end | |
if isInBox(w - 7, 1, 7, 3, x, y) then | |
mode = {"search", false, "", 1, 1} | |
redrawMode() | |
return | |
end | |
if mode[1] == "main" then | |
if isInBox(3, 12, 24, 12, x, y) then | |
-- Rating | |
mode = {"info", rating[1]} | |
redrawMode() | |
elseif isInBox(29, 12, 24, 12, x, y) then | |
-- Popular | |
mode = {"info", popular[1]} | |
redrawMode() | |
elseif isInBox(55, 12, 24, 12, x, y) then | |
-- Liked | |
mode = {"info", liked[1]} | |
redrawMode() | |
elseif isInBox(1, 1, 3, 3, x, y) and data[6] == OWNER then | |
noExit = false | |
end | |
elseif mode[1] == "info" then | |
if isInBox(w - 8, 6, 8, 1, x, y) then | |
play(mode[2], data[6]) | |
elseif isInBox(w - 36, 1, 3, 3, x, y) then | |
setRating(mode[2], data[6], -1) | |
elseif isInBox(w - 25, 1, 3, 3, x, y) then | |
setRating(mode[2], data[6], 1) | |
elseif isInBox(w - 24, 1, 6, 3, x, y) then | |
like(mode[2], data[6]) | |
end | |
elseif mode[1] == "search" then | |
local yOffset = 10 | |
if not mode[3] or mode[3] == "" then | |
yOffset = 12 | |
end | |
local gamePos = math.floor((y - yOffset) / 3 + 1) | |
if gamePos > 0 and searchList[gamePos] then | |
mode = {"info", searchList[gamePos]} | |
redrawMode() | |
end | |
if isInBox(5, 6, w - 10, 3, x, y) then | |
mode = {"search", not mode[2], (mode[3] or ""), mode[4] or 1, mode[5] or 1} | |
redrawMode() | |
end | |
end | |
end | |
local function setCursor(pos, noRedraw) | |
if mode[4] + pos < 1 then | |
mode[4] = 1 | |
mode[5] = 1 | |
return | |
elseif mode[4] + pos > unicode.len(mode[3]) + 1 then | |
mode[4] = unicode.len(mode[3]) + 1 | |
mode[5] = mode[4] | |
return | |
elseif mode[4] + pos < mode[5] then | |
mode[4] = mode[4] + pos | |
mode[5] = mode[4] | |
elseif mode[4] + pos > mode[5] + w - 8 then | |
mode[4] = mode[4] + pos | |
mode[5] = mode[4] + 8 - w | |
else | |
mode[4] = mode[4] + pos | |
end | |
if not noRedraw then redrawMode() end | |
end | |
local function keyHandle(data) | |
if mode[1] == "search" and mode[2] then | |
mode[3] = mode[3] or "" | |
if data[4] == 203 then -- Left | |
setCursor(-1) | |
elseif data[4] == 205 then -- Right | |
setCursor(1) | |
elseif data[4] == 28 then -- Enter | |
mode = {"search", false, mode[3], mode[4], mode[5]} | |
redrawMode() | |
elseif data[4] == 14 then -- Backspace | |
if mode[4] > 1 then | |
mode[3] = unicode.sub(mode[3], 1, mode[4] - 2) .. unicode.sub(mode[3], mode[4], -1) | |
setCursor(-1) | |
end | |
elseif data[4] == 211 then -- Delete | |
if mode[4] <= unicode.len(mode[3]) then | |
mode[3] = unicode.sub(mode[3], 1, mode[4] - 1) .. unicode.sub(mode[3], mode[4] + 1, -1) | |
end | |
elseif data[4] == 207 then -- End | |
setCursor(unicode.len(mode[3]) - mode[4] + 1) | |
elseif data[4] == 199 then -- Home | |
setCursor(-mode[4] + 1) | |
elseif not isin({54, 201, 209, 210, 200, 208}, data[4]) then | |
-- Shift, PgUp, PgDn, Ins, Up, Down | |
if data[3] ~= 0 then | |
local char = kbd.isShiftDown() and unicode.upper(unicode.char(data[3])) or unicode.char(data[3]) | |
mode[3] = unicode.sub(mode[3], 1, mode[4] - 1) .. char .. unicode.sub(mode[3], mode[4], -1) | |
setCursor(1) | |
end | |
end | |
end | |
end | |
save = function() | |
log("Запрошено сохранение") | |
for game, value in pairs(games) do | |
local data = json:encode_pretty(value) -- Just 'cause I can ;P | |
local f = io.open(fs.concat(GAMESDIR, game, "info"), "w") | |
f:write(data) | |
f:close() | |
end | |
log("Сохранено") | |
end | |
local function quit() | |
save() | |
buffer.square(1, 1, w, h, 0x000000, 0xffffff, " ") | |
buffer.draw() | |
term.setCursor(1, 1) | |
end | |
local function main() | |
while noExit do | |
local data = {event.pull()} | |
if data then | |
if data[1] == "key_down" then | |
keyHandle(data) | |
elseif data[1] == "touch" then | |
touchHandle(data) | |
end | |
end | |
end | |
end | |
-- Main | |
log("---------------") | |
log("Program started") | |
redrawMode() | |
main() | |
quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment