diff --git a/.gitignore b/.gitignore index aa50e87..983bd14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ repolist.csh -persistence.json +persistence.ltn gitget.lua json \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c6b8ed6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Todd Lange + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 508f9f8..3310231 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ It uses Computercraft's Command Computer, and Plethora Peripherals' Creative Cha The command computer can already do many things, being a programmable command block. Allium takes that power to the next level and expands functionality to allow for plugins to be registered. From there you can add commands that get triggered by players, threads that can do command based things without the need of a command to execute them, and data that can be stored in a persistent file so that things you want to hang onto don't get lost in the event of a crash or restart. -Allium uses **[Raisin](https://github.com/hugeblank/raisin/)**, a next generation thread manager made by me, hugeblank. If you're a prospective Allium plugin developer, you may want to skim the documentation, found in the Readme. +Allium uses **[Raisin](https://github.com/hugeblank/raisin/)**, a next generation thread manager made by me, hugeblank. If you're a prospective Allium plugin developer, you may want to skim the documentation, found on Raisin's [wiki](https://github.com/hugeblank/raisin/wiki/). Allium also uses a chat text formatting API provided by [roger109z](https://github.com/roger109z/). I (hugeblank) personally haven't touched it (even though it is in my repo) since he's created it as it works flawlessly. @@ -13,132 +13,24 @@ To install Allium, run this command, it's that simple! `pastebin run LGwrkjxm` -The installer installs Apemanzilla's [gitget](http://www.computercraft.info/forums2/index.php?/topic/17387-gitget-version-2-release/), a github repository downloader that is necessary to download the bot, and the plugins that can be installed. +The installer installs: -## Base Commands -`!help`: Lists the info entries for the installed plugins +- The Allium Repo - allium.lua, plugins/allium-stem.lua, colors.lua, readme.md -`!plugins`: Lists the plugins that are installed, based on the name of the directory that they're found in. +- The Raisin Repo - raisin/raisin.lua, raisin/readme.md -`!credits`: Links to the github repo for [Allium](https://github.com/hugeblank/Allium), and to the people that made Allium. +- Apemanzilla's [gitget](http://www.computercraft.info/forums2/index.php?/topic/17387-gitget-version-2-release/), a github repository downloader that is necessary to download Allium, and the plugins that can be installed. -## API -Quick reference for plugin developers: +- repolist.csh - A _Craftos SHell_ file, where you can gitget various plugins and utilities and keep them up to date. -### `allium`: -`assert`: Lua's generic assert, with the ability to set the error level -- **Parameters** - - _boolean_: condition to test - - _string_: error message - - _number_: error level -- **Returns** - - _none_ +- startup.lua - startup file that runs the repolist file, then runs Allium. When Allium crashes/exits it will reboot after 3 seconds unless interrupted. -`sanitize`: Sanitize plugin names to meet allium's ID standards -- **Parameters** - - _string_: plugin/command name string -- **Returns** - - _string_: valid allium plugin/command ID +- persistence.ltn - A _Lua Table Notation_ file containing all serialized persistence entries for each plugin. -`tell`: Output colorcode formatted text to the user, using the & and then a hexadecimal symbol. Additional codes are also provided that perform other actions. -- **Parameters** - - _string_: name of user - - _string_: text __OR__ _table_: list of text - - _[string]_: label replacement __OR__ _[boolean]_: hide label -- **Returns** - - _string_: execution results +**Want More?** Check out the [wiki](https://github.com/hugeblank/Allium/wiki/)! -`getPlayers`: Lists all online players -- **Parameters** - - _none_ -- **Returns** - - _table_: list of online players - -`getInfo`: Get information for one or all plugins -- **Parameters** - - _[string]_: allium plugin ID -- **Returns** - - _table_: table of information organized as `table[plugin][command] = information` - -`getName`: Get the human readable name from the plugin ID -- **Parameters** - - _string_: allium plugin ID -- **Returns** - - _string_: human readable plugin name - -`register`: The big boy, Register an Allium plugin -- **Parameters** - - _string_: plugin name, converted to allium plugin ID - - _[string]_: optional manually set human readable plugin name -- **Returns** - - _table_: list of functions to register commands/threads/persistent data (see below) - -### `register`: -`command`: Register a command within this plugin -- **Parameters** - - _string_: command name - - _function_: function to execute - - _string_: information about the command - - _[string]_: command usage formatted as ` [optional | second | arguments]` -- **Returns** - - _none_ - -`thread`: Register a thread within this plugin -- **Parameters** - - _function_: function to turn into a thread -- **Returns** - - _none_ - -`setPersistence`: Sets data that will remain persistent across a reboot of Allium. -- **Parameters** - - _string_: name of the persistent value - - _any_: data to assign to value -- **Returns** - - _none_ - -`getPersistence`: Sets data that will remain persistent across a reboot of Allium. -- **Parameters** - - _string_: name of the persistent value -- **Returns** - - _any_: data that was assigned to that value ---- -## Formatting Codes -The following characters are all valid for formatting in `allium.tell` when prefixxed with an `&`. - -### Colors -r - reset formatting -0 - black -1 - dark blue -2 - dark green -3 - dark aqua -4 - dark red -5 - dark purple -6 - gold -7 - gray -8 - dark gray -9 - blue -a - green -b - aqua -c - red -d - light purple -e - yellow -f - white - -### Actions -g - execute text | `&g(!allium:help)Click for help!&r` -h - hover text | `&h(Hi there :P)Mouse over me!&r` -i - clickable link | `&i(https://google.com)Go to google!&r` -s - suggest text | `&s(I'm in your bar now!)Click on me!&r` - -### Emphases -k - obfuscated -l - bold -m - strikethrough -n - italic -o - underline ---- -## Cloning this repository +## Forking this repository It's worth noting that there are some places you might want to check out after you fork, and before you start testing your code. -1. The pastebin [installer](https://www.pastebin.com/LGwrkjxm). You are free to make your own installer to your fork with this code, simply change the `repo` string to "your github username"/"the name of your Allium repository". -2. The startup file of your fork. There is a line that has a comment along with it. Change that repo name from the official Allium repository to your forked repository, similar to the style shown above. \ No newline at end of file +1. The pastebin [installer](https://www.pastebin.com/LGwrkjxm). You are free to make your own installer to your fork with this code, simply change the `repo` string to "[your github username] [the name of your Allium repository] [the branch you want to clone from] [location]". +2. The startup file of your fork. Change the debug variable, and make sure to replace the marked line with your repo, like above. \ No newline at end of file diff --git a/allium.lua b/allium.lua index ae6c238..8041bcf 100644 --- a/allium.lua +++ b/allium.lua @@ -1,5 +1,10 @@ -- Allium by hugeblank -paintutils.drawImage(paintutils.loadImage("allium.nfp"), 40, 2) -- Draw the Allium image on the side +do -- Allium image setup <3 + local x, y = term.getSize() + paintutils.drawImage(paintutils.loadImage("allium.nfp"), x-7, 2) -- Draw the Allium image on the side + local win = window.create(term.current(), 1, 1, x-9, y, true) + term.redirect(win) +end term.setBackgroundColor(colors.black) -- Reset terminal and cursor term.setTextColor(colors.white) term.setCursorPos(1, 1) @@ -7,34 +12,31 @@ term.setCursorPos(1, 1) print("Loading Allium") print("Initializing API") -local mName = "<&r&dAll&5&h(Hugeblank was here. Hi.)&i(https://www.youtube.com/watch?v=PomZJao7Raw)i&r&dum&r>" --bot title -local raisin = require("raisin.raisin") -local color = require("color") --Sponsored by roger109z +local label = "<&r&dAll&5&h[[Hugeblank was here. Hi.]]&i[[https://www.youtube.com/watch?v=PomZJao7Raw]]i&r&dum&r>" --bot title +local raisin, color = require("raisin.raisin"), require("color") --Sponsored by roger109z local allium = {} -- API table -local group = {thread = raisin.group.add(1), command = raisin.group.add(2)} +local group = {thread = raisin.group.add(1) , command = raisin.group.add(2)} -- threads first, commands second, plugin groups third local plugins = {} -- Plugin table allium.assert = function(condition, message, level) - if not condition then error(message, level) end + if not condition then error(message, level or 3) end end local assert = allium.assert allium.sanitize = function(name) - if name then - return name:lower():gsub(" ", "-") - end + assert(type(name) == "string", "Invalid argument #1 (expected string, got "..type(name)..")") + return name:lower():gsub(" ", "-") end -allium.tell = function(name, message, botname) --allium.tell as documented in README - if not (type(message) == "string" or type(message) == "table") then - return false - end +allium.tell = function(name, message, alt_name) + assert(type(name) == "string", "Invalid argument #1 (expected string, got "..type(name)..")") + assert(type(message) == "string" or type(message) == "table", "Invalid argument #2 (expected string or table, got "..type(message)..")") local test if type(message) == "table" then _, test = commands.tellraw(name, color.format(table.concat(message, "\n"))) else - _, test = commands.tellraw(name, color.format((function(botname) if botname == true then return "" elseif botname then return botname.."&r " else return mName.."&r " end end)(botname)..message)) + _, test = commands.tellraw(name, color.format((function(alt_name) if alt_name == true then return "" elseif alt_name then return alt_name.."&r " else return label.."&r " end end)(alt_name)..message)) end return textutils.serialise(test) end @@ -43,7 +45,7 @@ allium.getPlayers = function() local didexec, input = commands.exec("list") local out = {} if not didexec then - local _, users = commands.testfor("@a") + local _, users = commands.exec("testfor @a") for i = 1, #users do out[#out+1] = string.sub(users[i], 7, -1) end @@ -59,20 +61,54 @@ allium.getPlayers = function() return out end -allium.getInfo = function(plugin) -- Get the information of all plugins, or a single plugin - assert(plugin == nil or type(plugin) == "string", "Invalid argument #1 (string expected, got"..type(plugin)..")", 3) - if plugin then - plugin = allium.sanitize(plugin) +allium.forEachPlayer = function(func) + assert(type(func) == "function", "Invalid argument #1 (function expected, got "..type(func)..")") + local threads = {} + local players = allium.getPlayers() + local mentioned, error = false + for i = 1, #players do + threads[#threads+1] = function() + local suc, err = pcall(func, players[i]) + if not suc and not mentioned then + error = err + mentioned = true + end + end end - assert(command == nil or type(command) == "string", "Invalid argument #2 (string expected, got"..type(command)..")", 3) - if command then - assert(plugin, "Invalid argument #1 (string expected, got"..type(plugin)..")", 3) + parallel.waitForAll(unpack(threads)) + if not mentioned then + return true + else + return false, error end - if plugin then - assert(plugins[plugin], "Invalid argument #1 (plugin "..plugin.." does not exist)", 3) - if command then - assert(plugins[plugin].commands[command], "Invalid argument #2 (command "..command.." does not exist in plugin "..plugin..")", 3) +end + +allium.getPosition = function(name) + assert(type(name) == "nil" or type(name) == "string", "Invalid argument #1 (expected string or nil, got "..type(name)..")") + local position = {} -- Player position values + local suc, tbl + parallel.waitForAll(function() -- Execute tp to player, and value check simultaneously for minimum latency + suc = commands.exec("tp @e[type=minecraft:armor_stand,team=allium_trackers] "..name) + end, function() + _, tbl = commands.exec("tp @e[type=minecraft:armor_stand,team=allium_trackers] ~ ~ ~") + end) + commands.exec("tp @e[type=minecraft:armor_stand,team=allium_trackers] "..table.concat({commands.getBlockPosition()}, " ")) + if suc then + local pos_str = tbl[1]:gsub("Teleported Armor Stand to ", ""):gsub("[,]", "") + for value in pos_str:gmatch("%S+") do + position[#position+1] = value end + else + return false + end + return unpack(position) +end + +allium.getInfo = function(plugin) -- Get the information of all plugins, or a single plugin + assert(plugin == nil or type(plugin) == "string", "Invalid argument #1 (nil or string expected, got"..type(plugin)..")") + if plugin then + plugin = allium.sanitize(plugin) + assert(plugins[plugin], "Invalid argument #1 (plugin "..plugin.." does not exist)") end if plugin then local res = {[plugin] = {}} @@ -93,62 +129,66 @@ allium.getInfo = function(plugin) -- Get the information of all plugins, or a si end allium.getName = function(plugin) - assert(type(plugin) == "string", "Invalid argument #1 (string expected, got "..type(plugin)..")", 3) + assert(type(plugin) == "string", "Invalid argument #1 (string expected, got "..type(plugin)..")") if plugins[plugin] then return plugins[plugin].name end end allium.register = function(p_name, fullname) - assert(type(p_name) == "string", "Invalid argument #1 (string expected, got "..type(p_name)..")", 3) + assert(type(p_name) == "string", "Invalid argument #1 (string expected, got "..type(p_name)..")") local real_name = allium.sanitize(p_name) - assert(plugins[real_name] == nil, "Invalid argument #1 (plugin exists under name "..real_name..")", 3) + assert(plugins[real_name] == nil, "Invalid argument #1 (plugin exists under name "..real_name..")") plugins[real_name] = {threads = {}, commands = {}, name = fullname or p_name} local funcs = {} local this = plugins[real_name] - funcs.command = function(c_name, command, info, usage) -- name: name | command: executing function | info: help information | usage: string for improper inputs - assert(type(c_name) == "string", "Invalid argument #1 (string expected, got "..type(c_name)..")", 3) + funcs.command = function(c_name, command, info, usage) -- name: name | command: executing function | info: help information | usage: table of strings for improper inputs + assert(type(c_name) == "string", "Invalid argument #1 (string expected, got "..type(c_name)..")") local real_name = allium.sanitize(c_name) - assert(type(command) == "function", "Invalid argument #2 (function expected, got "..type(command)..")", 3) - assert(this.commands[real_name] == nil, "Invalid argument #2 (command exists under name "..real_name.." for plugin "..this.name..")", 3) - assert(type(info) == "string", "Invalid argument #3 (string expected, got "..type(info)..")", 3) + assert(type(command) == "function", "Invalid argument #2 (function expected, got "..type(command)..")") + assert(this.commands[real_name] == nil, "Invalid argument #2 (command exists under name "..real_name.." for plugin "..this.name..")") + assert(type(info) == "string" or type(info) == "table" or not info, "Invalid argument #3 (string, table, or nil expected, got "..type(info)..")") + if type(info) == "string" then info = {generic = info} end + assert(info.generic, "Invalid argument #3 ('generic' info expected, none found)") this.commands[real_name] = {command = command, info = info, usage = usage} end funcs.thread = function(thread) - assert(type(thread) == "function", "Invalid argument #1 (function expected, got "..type(thread)..")", 3) - return raisin.thread.add(thread, 0, group.thread) + assert(type(thread) == "function", "Invalid argument #1 (function expected, got "..type(thread)..")") + return raisin.thread.wrap(raisin.thread.add(thread, 0, group.thread), group.thread) end funcs.getPersistence = function(name) - if fs.exists("persistence.json") then - local fper = fs.open("persistence.json", "r") + assert(type(name) ~= "nil", "Invalid argument #1 (expected anything but nil, got "..type(name)..")") + if fs.exists("persistence.ltn") then + local fper = fs.open("persistence.ltn", "r") local tpersist = textutils.unserialize(fper.readAll()) fper.close() - if not tpersist[p_name] then - tpersist[p_name] = {} + if not tpersist[real_name] then + tpersist[real_name] = {} end if type(name) == "string" then - return tpersist[p_name][name] + return tpersist[real_name][name] end end return false end funcs.setPersistence = function(name, data) + assert(type(name) ~= "nil", "Invalid argument #1 (expected anything but nil, got "..type(name)..")") local tpersist - if fs.exists("persistence.json") then - local fper = fs.open("persistence.json", "r") + if fs.exists("persistence.ltn") then + local fper = fs.open("persistence.ltn", "r") tpersist = textutils.unserialize(fper.readAll()) fper.close() end - if not tpersist[p_name] then - tpersist[p_name] = {} + if not tpersist[real_name] then + tpersist[real_name] = {} end if type(name) == "string" then - tpersist[p_name][name] = data - local fpers = fs.open("persistence.json", "w") + tpersist[real_name][name] = data + local fpers = fs.open("persistence.ltn", "w") fpers.write(textutils.serialise(tpersist)) fpers.close() return true @@ -159,8 +199,7 @@ allium.register = function(p_name, fullname) return funcs end --- Finding the chat module -for _, side in pairs(peripheral.getNames()) do +for _, side in pairs(peripheral.getNames()) do -- Finding the chat module if peripheral.getMethods(side) then for _, method in pairs(peripheral.getMethods(side)) do if method == "capture" then @@ -172,22 +211,17 @@ for _, side in pairs(peripheral.getNames()) do end if allium.side then break end end - -if not allium.side then - printError("Cannot find chat module") - return -end +assert(allium.side, "Allium requires a creative chat module in order to operate") _G.allium = allium -- Globalizing Allium API do -- Plugin loading process print("Loading plugins...") - local dir = shell.dir() - if fs.exists(dir.."plugins") then - for _, plugin in pairs(fs.list(dir.."plugins")) do - if not fs.isDir(dir.."plugins/"..plugin) then - local file, err = loadfile(dir.."plugins/"..plugin) + local function scopeDown(dir) + for _, plugin in pairs(fs.list(dir)) do + if (not fs.isDir(dir.."/"..plugin)) and plugin:find(".lua") then + local file, err = loadfile(dir.."/"..plugin) if not file then printError(err) else @@ -196,70 +230,116 @@ do -- Plugin loading process printError(err) end end + elseif fs.isDir(dir.."/"..plugin) then + scopeDown(dir.."/"..plugin) end end end + local dir = shell.dir() + if fs.exists(dir.."/plugins") then + scopeDown(dir.."/plugins") + else + fs.makeDir(dir.."/plugins") + end end -local main = function() +local interpreter = function() -- Main command interpretation thread while true do - local _, message, _, name = os.pullEvent("chat_capture") --Pull chat messages - if string.find(message, "!") == 1 then --are they for allium? + local _, message, _, name = os.pullEvent("chat_capture") -- Pull chat messages + if string.find(message, "!") == 1 then -- Are they for allium? args = {} - for k in string.gmatch(message, "%S+") do --put all arguments spaced out into a table + for k in string.gmatch(message, "%S+") do -- Put all arguments spaced out into a table args[#args+1] = k end local cmd = args[1]:sub(2, -1) -- Strip the ! - table.remove(args, 1) --remove the first parameter given (!command) + table.remove(args, 1) -- Remove the first parameter given (!command) local cmd_exec - if not string.find(cmd, ":") then --did they not specify the plugin source? - for p_name, plugin in pairs(plugins) do --nope... gonna have to find it for them. - for c_name, command in pairs(plugin.commands) do + if not string.find(cmd, ":") then -- Did they not specify the plugin source? + for p_name, plugin in pairs(plugins) do -- Nope... gonna have to find it for them. + for c_name, data in pairs(plugin.commands) do if c_name == cmd then --well I found it, but there may be more... - cmd_exec = {command = command, plugin = p_name} --split into command function, source + cmd_exec = {data = data, plugin = p_name, command = c_name} -- Split into command function, plugin name, and command name break end end if cmd_exec then break end -- Exit this loop, we've found the command we're looking for end - else --hey they did! +1 karma. + else -- Hey they did! +1 karma. local splitat = string.find(cmd, ":") local p_name, c_name = string.sub(cmd, 1, splitat-1), string.sub(cmd, splitat+1, -1) if plugins[p_name] then --check plugin existence if plugins[p_name].commands[c_name] then --check command existence - cmd_exec = {command = plugins[p_name].commands[c_name], plugin = p_name} --split it into the function, and then the source + cmd_exec = {data = plugins[p_name].commands[c_name], plugin = p_name, command = c_name} -- Split it into the function, and then the source end end end - if cmd_exec then --is there really a command? + if cmd_exec then -- Is there really a command? local data = { -- Infrequently used data to pass onto the command being executed - usage = function(name) allium.tell(name, "&c"..cmd.." "..cmd_exec.command.usage) end, - autofill = cmd_exec.command.usage + error = function(text) + local str = "Invalid or missing parameter(s)" + if cmd_exec.data.usage then + str = "!"..cmd.." "..cmd_exec.data.usage + end + allium.tell(name, "&c"..(text or str)) + end, + usage = cmd_exec.data.usage } local function exec_command() - local stat, err = pcall(cmd_exec.command.command, name, args, data) --Let's execute the command in a safe environment that won't kill allium + local stat, err = pcall(cmd_exec.data.command, name, args, data) --Let's execute the command in a safe environment that won't kill allium if stat == false then--it crashed... - allium.tell(name, "&4"..cmd.." crashed! This is likely not your fault, but the developer's. Please contact the developer of &a"..cmd_exec.plugin.."&4. Error:\n&c"..err) + allium.tell(name, "&4"..cmd_exec.command.." crashed! This is likely not your fault, but the developer's. Please contact the developer of &a"..cmd_exec.plugin.."&4. Error:\n&c&h[[Click here to place error into chat prompt, so you may copy it if needed for an issue report]]&s[["..err.."]]"..err.."&r") printError(cmd.." errored. Error:\n"..err) end end raisin.thread.add(exec_command, 0, group.command) else --this isn't even a valid command... - allium.tell(name, "&6Invalid Command, use &c&g(!allium:help)!help&r&6 for assistance.") --bleh! + allium.tell(name, "&6Invalid Command, use &c&g[[!allium:help]]!help&r&6 for assistance.") --bleh! end end end end -raisin.thread.add(main, 0) +local scanner = function() -- Login/out scanner thread + local online = {} + while true do + local cur_players = allium.getPlayers() + local organized = {} + for i = 1, #cur_players do -- Sort players in a way that's useful + organized[cur_players[i]] = cur_players[i] + end + for _, name in pairs(organized) do + if online[name] == nil then + online[name] = name + os.queueEvent("player_join", name) + end + end + for _, name in pairs(online) do + if organized[name] == nil then + online[name] = nil + os.queueEvent("player_quit", name) + end + end + sleep() + end +end + +raisin.thread.add(interpreter, 0) +raisin.thread.add(scanner, 1) -if not fs.exists("persistence.json") then --In the situation that this is a first installation, let's add persistence.json - local fpers = fs.open("persistence.json", "w") +if not fs.exists("persistence.ltn") then --In the situation that this is a first installation, let's do some setup + local fpers = fs.open("persistence.ltn", "w") fpers.write("{}") fpers.close() end +if not commands.exec("testfor @e[r=1,type=minecraft:armor_stand,team=allium_trackers]") then + commands.execAsync("kill @e[type=minecraft:armor_stand,team=allium_trackers]") + commands.execAsync("scoreboard teams add allium_trackers") + commands.execAsync("summon minecraft:armor_stand ~ ~ ~ {Marker:1,NoGravity:1,Invisible:1}") + commands.execAsync("scoreboard teams join allium_trackers @e[r=1,type=minecraft:armor_stand]") +end + print("Allium started.") allium.tell("@a", "&eHello World!") -sleep() raisin.manager.run() +_G.allium = nil \ No newline at end of file diff --git a/allium.nfp b/allium.nfp index 8a09be9..22ec6ce 100644 --- a/allium.nfp +++ b/allium.nfp @@ -8,8 +8,8 @@ aa66a2a 5 d d - d 5 5 d d + d \ No newline at end of file diff --git a/color.lua b/color.lua index 1d92782..45c45b8 100644 --- a/color.lua +++ b/color.lua @@ -51,8 +51,8 @@ local dCurrent = { local function escape(tbl) for k, v in pairs(tbl) do if v[2]:find("\\") == v[2]:len() then - tbl[k] = {v[1], v[2]:sub(1, -1).."&"..tbl[tonumber(k)+1][1]..tbl[tonumber(k)+1][2]} - table.remove(tbl, tonumer(k)+1) + tbl[k] = {v[1], v[2]:sub(1, -2).."&"..tbl[tonumber(k)+1][1]..tbl[tonumber(k)+1][2]} + table.remove(tbl, tonumber(k)+1) local ret = escape(tbl) return ret end @@ -78,46 +78,46 @@ this.format = function(sText, bAction) local seperated = {} sText = "&r"..sText for k in string.gmatch(sText, "[^&]+") do - seperated[#seperated+1] = {string.sub(k, 1, 1), string.sub(k, 2)} + seperated[#seperated+1] = {k:sub(1, 1), k:sub(2)} end local outText = '["",' local prev seperated = escape(seperated) - for k, v in pairs(seperated) do - if cTable[v[1]] ~= nil then - current["color"] = cTable[v[1]] - elseif formats[v[1]] ~= nil then - if current["format"][formats[v[1]]] == false then - current["format"][formats[v[1]]] = true + for _, toParse in pairs(seperated) do + if cTable[toParse[1]] ~= nil then + current["color"] = cTable[toParse[1]] + elseif formats[toParse[1]] ~= nil then + if current["format"][formats[toParse[1]]] == false then + current["format"][formats[toParse[1]]] = true else - current["format"][formats[v[1]]] = false + current["format"][formats[toParse[1]]] = false end - elseif actions[v[1]] ~= nil then - current["action"] = actions[v[1]] - local ind = string.find(v[2], ")") + elseif actions[toParse[1]] ~= nil then + current["action"] = actions[toParse[1]] + local ind, bck = string.find(toParse[2], "%[%[.*%]%]") if ind ~= nil then - current["actionText"] = string.sub(v[2], 2, ind-1) - v[2] = v[2]:sub(ind+1) + current["actionText"] = toParse[2]:sub(ind+2, bck-2) + toParse[2] = toParse[2]:sub(bck+1) else - current["actionText"] = v[2] + current["actionText"] = toParse[2] end - elseif other[v[1]] ~= nil then - if other[v[1]] == "hoverEvent" then + elseif other[toParse[1]] ~= nil then + if other[toParse[1]] == "hoverEvent" then current["hoverEvent"] = true - local ind = string.find(v[2], ")") + local ind, bck = string.find(toParse[2], "%[%[.*%]%]") if ind ~= nil then - current["hoverText"] = this.format(string.sub(v[2], 2, ind-1), false) - v[2] = v[2]:sub(ind+1) + current["hoverText"] = this.format(toParse[2]:sub(ind+2, bck-2), false) + toParse[2] = toParse[2]:sub(bck+1) else - current["hoverText"] = v[2] + current["hoverText"] = toParse[2] end - elseif other[v[1]] == "reset" then + elseif other[toParse[1]] == "reset" then current = copy(dCurrent) end else - v[2] = "&"..v[1]..v[2] + toParse[2] = "&"..toParse[1]..toParse[2] end - outText = outText..'{"text":"'..v[2]..'","color":"'..current["color"]..'"' + outText = outText..'{"text":"'..toParse[2]..'","color":"'..current["color"]..'"' for k, v in pairs(current["format"]) do if v then outText = outText..",\""..k.."\":true" diff --git a/plugins/allium-stem.lua b/plugins/allium-stem.lua index 3b08b08..57fe244 100644 --- a/plugins/allium-stem.lua +++ b/plugins/allium-stem.lua @@ -6,34 +6,25 @@ local help = function(name, args, data) local out_str = "" local next_command = "!allium:help " - if args[1] == nil then - page = 1 - elseif not tonumber(args[1]) then - args[1] = allium.sanitize(args[1]) - if allium.getName(args[1]) then - for cmd_name, entry in pairs((allium.getInfo(args[1]))[args[1]]) do - if entry.usage then - entry.usage = " "..entry.usage - else - entry.usage = "" - end - info[#info+1] = "&c&s(!"..args[1]..":"..cmd_name..")&h(!"..args[1]..":"..cmd_name..entry.usage..")!"..cmd_name.."&r: "..entry.info + local function run() + for i = (cmds_per*(page-1))+1, (cmds_per*page) do + if info[i] then + out_str = out_str..info[i].."\n" end - else - allium.tell(name, "&cPlugin "..args[1].." does not exist") - return end - next_command = next_command..args[1].." " - if tonumber(args[2]) then - page = tonumber(args[2]) + if out_str == "" or page <= 0 then + data.error("Page does not exist.") + return end - elseif tonumber(args[1]) then - page = tonumber(args[1]) - else - meta.usage(name) + out_str = "&2===================&r &dAll&5i&r&dum&e Help Menu&r &2===================&r\n"..out_str + local template = #(" << "..page.."/"..math.ceil(#info/cmds_per).." >> ") + local sides = math.ceil((64/2)-template) + out_str = out_str.."&2"..string.rep("=", sides).."&r &6&l&h[[Previous Page]]&g[["..next_command..(page-1).."]]<<&r&c&l "..page.."/"..math.ceil(#info/cmds_per).." &r&6&l&h[[Next Page]]&g[["..next_command..(page+1).."]]>>&r &2"..string.rep("=", sides).."&r" + allium.tell(name, out_str, true) + return end - if #info == 0 then + if tonumber(args[1]) or not args[1] then -- The first argument is nothing or a page number local data = allium.getInfo() for p_name, commands in pairs(data) do for cmd_name, entry in pairs(commands) do @@ -42,70 +33,105 @@ local help = function(name, args, data) else entry.usage = "" end - info[#info+1] = "&c&s(!"..p_name..":"..cmd_name..")&h(!"..p_name..":"..cmd_name..entry.usage..")!"..p_name..":"..cmd_name.."&r: "..entry.info + info[#info+1] = "&c&s[[!"..p_name..":"..cmd_name.."]]&h[[!"..p_name..":"..cmd_name..entry.usage.."]]!"..p_name..":"..cmd_name.."&r: "..entry.info.generic end end - end - - for i = (cmds_per*(page-1))+1, (cmds_per*page) do - if info[i] then - out_str = out_str..info[i].."\n" + if tonumber(args[1]) then + page = tonumber(args[1]) + end + return run() + else -- The first argument is a plugin/command + args[1] = allium.sanitize(args[1]) + if tonumber(args[2]) then + page = tonumber(args[2]) + end + next_command = next_command..args[1].." " + local cnp = args[1]:find(":") + if cnp then -- This is a command and plugin + local p_name, c_name = args[1]:sub(1, cnp-1), args[1]:sub(cnp+1, -1) + if allium.getName(p_name) then + local c_info = allium.getInfo(p_name)[p_name][c_name] + if c_info then + next_command = next_command..args[1].." " + local function addDetails(i_table, label, pre_str, sug_cmd, include) + if not include then + if i_table.noclick then -- If the parameter is not intended to be clicked (Eg: a username) + info[#info+1] = pre_str.."<&a"..label.."&r>: "..i_table.generic + elseif i_table.optional then -- If the parameter is not required + info[#info+1] = pre_str.."[&a"..label.."&r]: "..i_table.generic + elseif not (i_table.noclick or i_table.optional) then + info[#info+1] = pre_str.."<&a&h[[Add this parameter]]&s[["..sug_cmd.." ]]"..label.."&r>: "..i_table.generic + end + end + for param, param_info in pairs(i_table) do + if param ~= "generic" and param ~= "optional" and param ~= "noclick" then + addDetails(param_info, param, " "..pre_str, sug_cmd.." "..param) + end + end + end + if not c_info.usage then c_info.usage = "" end + info[#info+1] = "&c&s[[!"..p_name..":"..c_name.."]]&h[[!"..c_name..c_info.usage.."]]!"..c_name.."&r: "..c_info.info.generic + addDetails(c_info.info, c_name, "&6-&r ", "!"..p_name..":"..c_name, true) + return run() + else + data.error("Command !"..args[1].." does not exist") + return + end + else + data.error("Plugin "..args[1].." does not exist") + return + end + else -- This is just a plugin + if allium.getName(args[1]) then + for cmd_name, entry in pairs((allium.getInfo(args[1]))[args[1]]) do + if entry.usage then + entry.usage = " "..entry.usage + else + entry.usage = "" + end + info[#info+1] = "&c&s[[!"..args[1]..":"..cmd_name.."]]&g[[!help "..args[1]..":"..cmd_name.."]]&h[[!"..args[1]..":"..cmd_name..entry.usage.."]]!"..cmd_name.."&r: "..entry.info.generic + end + return run() + else + data.error("Plugin "..args[1].." does not exist") + return + end end end - - if out_str == "" or page <= 0 then - allium.tell(name, "&cPage does not exist.") - return - end - - out_str = "&2===================&r &dAll&5i&r&dum&e Help Menu&r &2===================&r\n"..out_str - - local template = #(" << "..page.."/"..math.ceil(#info/cmds_per).." >> ") - local sides = math.ceil((64/2)-template) - - out_str = out_str.."&2"..string.rep("=", sides).."&r &6&l&h(Previous Page)&g("..next_command..(page-1)..")<<&r&c&l "..page.."/"..math.ceil(#info/cmds_per).." &r&6&l&h(Next Page)&g("..next_command..(page+1)..")>>&r &2"..string.rep("=", sides).."&r" - allium.tell(name, out_str, true) end ---[[local function repeatName(name, message) -- Put this in BagelBot! - local prefixes = allium.getPersistence("prefixes") - local nicks = allium.getPersistence("nicknames") - local rank = betaBot.getLevel(name)+1 - if not prefixes then - prefixes = { - user = {}, - ranks = { - "&r[&amember&r]", - "&r[&eVIP&r]", - "&r[&cadmin&r]", - } - } - allium.setPersistence("prefixes", prefixes) - end - if not nicks then - nicks = {} - allium.setPersistence("nicknames", nicks) - end - local nick = nicks[name] or name - local prefix = prefixes.user[name] or prefixes.ranks[rank] or "" - commands.tellraw("@a", color.format(prefix.." &r<"..nick.."&r> "..message)) -end]] - local credits = function(name) - allium.tell(name, "This project was cultivated with love by &a&h(Check out his repo!)&i(https://github.com/hugeblank)hugeblank&r.\nCommand formatting API provided graciously by &1&h(Check out his repo!)&i(https://github.com/roger109z)roger109z&r.\nContribute and report issues to allium here: &9&n&h(Check out where allium is grown!)&ihttps://github.com/hugeblank/allium") + allium.tell(name, "This project was cultivated with love by &a&h[[Check out his repo!]]&i[[https://github.com/hugeblank]]hugeblank&r.\nCommand formatting API provided graciously by &1&h[[Check out his repo!]]&i[[https://github.com/roger109z]]roger109z&r.\nContribute and report issues to allium here: &9&n&h[[Check out where allium is grown!]]&ihttps://github.com/hugeblank/allium") end local plugins = function(name) local pluginlist = {} local str = "" local plugins = allium.getInfo() - for p_name in pairs(plugins) do - pluginlist[#pluginlist+1] = "&h(Tag: "..p_name..")"..allium.getName(p_name) + for p_name in pairs(plugins) do + local p_str = "&h[[Tag: "..p_name.."]]" + if plugins[p_name]["credits"] then + p_str = p_str.."&g[[!"..p_name..":credits]]" + end + print(p_str) + pluginlist[#pluginlist+1] = p_str..allium.getName(p_name) end str = table.concat(pluginlist, "&r, &a") allium.tell(name, "Plugins installed: &a"..str) end -stem.command("help", help, "This command! Helpful.", "[page/plugin name], [page]") -stem.command("plugins", plugins, "Lists the name of all plugins installed on allium") -stem.command("credits", credits, "Provides credits where they are due") \ No newline at end of file +local info = { + help = { + generic = "This command! Helpful." + }, + plugins = { + generic = "Lists the name of all plugins installed on allium" + }, + credits = { + generic = "Provides credits where they are due" + } +} + +stem.command("help", help, info.help, "[page | plugin name], [page]") +stem.command("plugins", plugins, info.plugins) +stem.command("credits", credits, info.credits) \ No newline at end of file diff --git a/raisin b/raisin index 6a48d1b..f165499 160000 --- a/raisin +++ b/raisin @@ -1 +1 @@ -Subproject commit 6a48d1b346bca282f1540753c8ee4109d17c06ba +Subproject commit f165499348374927fdbe65052d9e978dd609cc79 diff --git a/startup.lua b/startup.lua index 35b6c05..2cbc3e8 100644 --- a/startup.lua +++ b/startup.lua @@ -1,18 +1,20 @@ shell.openTab("shell") +local debug = false -- for use when debugging, auto-update script doesn't get triggered -- Checking for a repolist shell executable -if fs.exists("repolist.csh") then - -- Update all plugins and programs on the repolist - local file = fs.open("repolist.csh", "r") - for k in file.readLine do - shell.run(k) +if not debug then + if fs.exists("repolist.csh") then + -- Update all plugins and programs on the repolist + for line in io.lines("repolist.csh") do + shell.run(line) + end + file.close() + else + -- Generate a repolist file + local file = fs.open("repolist.csh", "w") + file.write("gitget hugeblank Allium master /") --Forkers change this to their repository. + file.close() + printError("No valid repo file, default file created") end - file.close() -else - -- Generate a repolist file - local file = fs.open("repolist.csh", "w") - file.write("gitget hugeblank/Allium") --Forkers change this to their repository. - file.close() - printError("No valid repo file, default file created") end -- Clearing the screen term.setBackgroundColor(colors.black) @@ -22,20 +24,18 @@ term.setCursorPos(1, 1) -- Running Allium shell.run("allium.lua") -- Removing all captures -local exit = false -for _, side in pairs(peripheral.getNames()) do +for _, side in pairs(peripheral.getNames()) do -- Finding the chat module if peripheral.getMethods(side) then for _, method in pairs(peripheral.getMethods(side)) do if method == "uncapture" then - peripheral.call(side, method, ".") - exit = true - break + peripheral.call(allium.side, "uncapture", ".") + break end end end - if exit then break end end + -- Rebooting or exiting -print("Rebooting in 3 seconds") +print("Rebooting in 5 seconds") print("Press any key to cancel") -parallel.waitForAny(function() repeat until os.pullEvent("char") end, function() sleep(3) os.reboot() end) \ No newline at end of file +parallel.waitForAny(function() repeat until os.pullEvent("char") end, function() sleep(5) os.reboot() end) \ No newline at end of file