commit 5b23ea99fd15d110c9e69a8a23869b4e4cb4f494 Author: Andrew-71 Date: Sun Mar 10 23:45:44 2024 +0300 initialize diff --git a/Dash/dash.lua b/Dash/dash.lua new file mode 100644 index 0000000..110e0c8 --- /dev/null +++ b/Dash/dash.lua @@ -0,0 +1,8 @@ +local m = peripheral.find("monitor") +m.setTextScale(0.5) +local pixelbox = require("./util/pixelbox_lite") +local screen = pixelbox.new(m) +screen:set_pixel(3, 2, 1) +screen:render() + +print(screen:get_size()) \ No newline at end of file diff --git a/Dash/util/pixelbox_lite.lua b/Dash/util/pixelbox_lite.lua new file mode 100644 index 0000000..1fdc365 --- /dev/null +++ b/Dash/util/pixelbox_lite.lua @@ -0,0 +1,184 @@ +--[[ + Made by O. Caha (9551-Dev), licensed under MIT + + Modified by Andrew_7_1 +]] + +local PIXELBOX = {} +local OBJECT = {} + +local t_sort,t_cat,s_char = table.sort,table.concat,string.char +local function sort(a,b) return a[2] > b[2] end + +local distances = { + {5,256,16,8,64,32}, + {4,16,16384,256,128}, + [4] ={4,64,1024,256,128}, + [8] ={4,512,2048,256,1}, + [16] ={4,2,16384,256,1}, + [32] ={4,8192,4096,256,1}, + [64] ={4,4,1024,256,1}, + [128] ={6,32768,256,1024,2048,4096,16384}, + [256] ={6,1,128,2,512,4,8192}, + [512] ={4,8,2048,256,128}, + [1024] ={4,4,64,128,32768}, + [2048] ={4,512,8,128,32768}, + [4096] ={4,8192,32,128,32768}, + [8192] ={3,32,4096,256128}, + [16384]={4,2,16,128,32768}, + [32768]={5,128,1024,2048,4096,16384} +} + +local to_blit = {} +for i = 0, 15 do + to_blit[2^i] = ("%x"):format(i) +end + + +function PIXELBOX.RESTORE(BOX,color) + local bc = {} + + for y=1,BOX.height*3 do + for x=1,BOX.width*2 do + if not bc[y] then bc[y] = {} end + bc[y][x] = color + end + end + + BOX.CANVAS = bc +end + +local function build_drawing_char(a,b,c,d,e,f) + local arr = {a,b,c,d,e,f} + local c_types = {} + local sortable = {} + local ind = 0 + for i=1,6 do + local c = arr[i] + if not c_types[c] then + ind = ind + 1 + c_types[c] = {0,ind} + end + + local t = c_types[c] + local t1 = t[1] + 1 + + t[1] = t1 + sortable[t[2]] = {c,t1} + end + local n = #sortable + while n > 2 do + t_sort(sortable,sort) + local bit6 = distances[sortable[n][1]] + local index,run = 1,false + local nm1 = n - 1 + for i=2,bit6[1] do + if run then break end + local tab = bit6[i] + for j=1,nm1 do + if sortable[j][1] == tab then + index = j + run = true + break + end + end + end + local from,to = sortable[n][1],sortable[index][1] + for i=1,6 do + if arr[i] == from then + arr[i] = to + local sindex = sortable[index] + sindex[2] = sindex[2] + 1 + end + end + + sortable[n] = nil + n = n - 1 + end + + local n = 128 + local a6 = arr[6] + + if arr[1] ~= a6 then n = n + 1 end + if arr[2] ~= a6 then n = n + 2 end + if arr[3] ~= a6 then n = n + 4 end + if arr[4] ~= a6 then n = n + 8 end + if arr[5] ~= a6 then n = n + 16 end + + if sortable[1][1] == arr[6] then + return s_char(n),sortable[2][1],arr[6] + else + return s_char(n),sortable[1][1],arr[6] + end +end + +function OBJECT:render() + local t = self.term + local blit_line,set_cursor = t.blit,t.setCursorPos + + local w_double = self.width*2 + local canv = self.CANVAS + + local sy = 0 + for y=1,self.height*3,3 do + sy = sy + 1 + local layer_1 = canv[y] + local layer_2 = canv[y+1] + local layer_3 = canv[y+2] + local char_line,fg_line,bg_line = {},{},{} + local n = 0 + for x=1,w_double,2 do + local xp1 = x+1 + local b11,b21,b12,b22,b13,b23 = + layer_1[x],layer_1[xp1], + layer_2[x],layer_2[xp1], + layer_3[x],layer_3[xp1] + + local char,fg,bg = " ",1,b11 + if not (b21 == b11 + and b12 == b11 + and b22 == b11 + and b13 == b11 + and b23 == b11) then + char,fg,bg = build_drawing_char(b11,b21,b12,b22,b13,b23) + end + n = n + 1 + char_line[n] = char + fg_line [n] = to_blit[fg] + bg_line [n] = to_blit[bg] + end + + set_cursor(1,sy) + blit_line( + t_cat(char_line,""), + t_cat(fg_line,""), + t_cat(bg_line,"") + ) + end +end + +function OBJECT:clear(color) + PIXELBOX.RESTORE(self,color) +end + +function OBJECT:set_pixel(x,y,color) + self.CANVAS[y][x] = color +end + +function OBJECT:get_size() + return #(self.CANVAS), #(self.CANVAS[1]) +end + +function PIXELBOX.new(terminal,bg) + local bg = bg or terminal.getBackgroundColor() or colors.black + local BOX = {} + local w,h = terminal.getSize() + BOX.term = terminal + setmetatable(BOX,{__index = OBJECT}) + BOX.width = w + BOX.height = h + PIXELBOX.RESTORE(BOX,bg) + return BOX +end + +return PIXELBOX \ No newline at end of file diff --git a/Dash/widgets/ART.md b/Dash/widgets/ART.md new file mode 100644 index 0000000..b6533ce --- /dev/null +++ b/Dash/widgets/ART.md @@ -0,0 +1,19 @@ + + +Changes through time: +* Morning +* Day +* Evening +* Night + +Changes with weather: +* Sunny +* Cloudy +* Rainy +* Stormy (diff from rain?) +* Snowy - alternative to rainy + +Changes with dates: + +Fireworks +Lowered flag diff --git a/Dash/widgets/README.md b/Dash/widgets/README.md new file mode 100644 index 0000000..0e6823d --- /dev/null +++ b/Dash/widgets/README.md @@ -0,0 +1,10 @@ +# Widgets I'll need +target: 5x3 monitors big (200x114 pixels allegedly) + +### Essential +* Time (with zones?) +* Weather +* Some kind of feed? + +### Cool +* Dynamic cityscape that changes with time and weather \ No newline at end of file diff --git a/GLONASS/README.md b/GLONASS/README.md new file mode 100644 index 0000000..e5917b2 --- /dev/null +++ b/GLONASS/README.md @@ -0,0 +1 @@ +I originally made this project in 2022 as a proof of concept. \ No newline at end of file diff --git a/GLONASS/UNS.lua b/GLONASS/UNS.lua new file mode 100644 index 0000000..3ec8a7e --- /dev/null +++ b/GLONASS/UNS.lua @@ -0,0 +1,63 @@ +--[[ +Unitary Navigational System v0.0 + +This basic script attempts to consolidate a GPS constellation into a single computer. Because using 4 is plain wasteful! +It was made out of project "GLONASS Tower" I made in 2022 after discovering CC + +TODO: Bring back cool logging! +]] + +-- Modems declaration. Edit this +local modems = { + { + id = "modem_id_here", + pos = { + x = 1, + y = 1, + z = 1 + }, + modem = nil, + primary = true -- Highest modem, to increase coverage ever so slightly + } +} + +-- Prepare all modems +local primary_modem = nil +for i = 1 .. #modems do + modems[i].modem = peripheral.wrap(modems[i].id) + if modems[i].primary then + primary_modem = modems[i].modem + end +end +if primary_modem == nil then + primary_modem = modems[0].modem +end +primary_modem.open(gps.CHANNEL_GPS) -- Only *listen* on 1 modem to avoid repeats + +-- Simple logging. It's not ideal but will do as a "temporary" solution +local function log(message) + term.write(message) + term.setCursorPos(1, select(2, term.getCursorPos()) + 1) + if select(2, term.getCursorPos()) >= select(2, term.getSize()) then + term.scroll(1) + term.setCursorPos(1, select(2, term.getCursorPos()) - 1) + end +end +term.clear() +term.setCursorPos(1, 1) +log("UNS v0.0 starting...") + +while true do + -- Wait for a GPS query + local event, side, channel, replyChannel, message, distance + repeat + event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + until channel == gps.CHANNEL_GPS and message == "PING" + log("Recieved request, distance: " .. distance) + + -- Send data from all modems + for _, modem in ipairs(modems) do + modem.transmit(channel, replyChannel, modem.pos) + log("Modem ID <" .. modem.id .. "> transmitted location") + end +end \ No newline at end of file diff --git a/MusicPlayer/Client/client.lua b/MusicPlayer/Client/client.lua new file mode 100644 index 0000000..6954eb9 --- /dev/null +++ b/MusicPlayer/Client/client.lua @@ -0,0 +1,95 @@ +local graphics = require("graphics") + +local modem = peripheral.find('modem') +if not modem then error('No modem found') end +modem.open(7101) + +local dfpwm = require("cc.audio.dfpwm") +local speaker = peripheral.find("speaker") +if not speaker then error('No speaker found') end +local decoder = dfpwm.make_decoder() + +local status_vars = { + is_playing = true, + is_paused = false, + current_second = 0, + track_index = 1, + start_index = 1, +} + +local cfg_vars = { + forward_skip = 5, + backward_skip = 5, + port = 7101, + chunk_size = 0.5 +} + +local function get_catalogue() + modem.transmit(7101, 7101, {type = 'query'}) + local event, sides, channel, replyChannel, message, distance = os.pullEvent('modem_message') + return message.audio_catalogue +end +local audio_catalogue = get_catalogue() + +local function music_loop() + local play_music = require("playback") + while true do + status_vars.current_second = play_music(status_vars, cfg_vars, audio_catalogue, modem) + sleep() + end +end + +local function draw_ui_loop() + while true do + if not status_vars.is_playing then + graphics.draw_track_list(1, 1, audio_catalogue, status_vars.start_index, select(2, term.getSize()) - 1, term) + else + graphics.draw_track_list(1, 1, audio_catalogue, status_vars.start_index, select(2, term.getSize()) - 4, term) + end + sleep(1) + end +end + +local function control_loop() + while true do + local event, button, x, y = os.pullEvent("mouse_click") + + local btn_actions = { + pause = { + x = x + math.floor((select(1, term.getSize()) - 11) / 2) + 5, + y = select(2, term.getSize()) - 1, + func = function () + status_vars.is_paused = not status_vars.is_paused + end + }, + forward = { + x = math.floor((select(1, term.getSize()) - 11) / 2) + 9, + y = select(2, term.getSize()) - 1, + func = function () + status_vars.current_second = status_vars.current_second + cfg_vars.forward_skip + end + }, + backward = { + x = math.floor((select(1, term.getSize()) - 11) / 2) + 1, + y = select(2, term.getSize()) - 1, + func = function () + status_vars.current_second = status_vars.current_second - cfg_vars.backward_skip + end + } + } + if x == 3 and y == 1 then return end + + term.setCursorPos(btn_actions.pause.x, btn_actions.pause.y) + term.write('%') + + for i, v in pairs(btn_actions) do + if x == v.x and y == v.y then + v.func() + break + end + end + end +end + +term.clear() -- Clear terminal +parallel.waitForAny(music_loop, draw_ui_loop, control_loop) -- Start loops \ No newline at end of file diff --git a/MusicPlayer/Client/graphics.lua b/MusicPlayer/Client/graphics.lua new file mode 100644 index 0000000..66021a8 --- /dev/null +++ b/MusicPlayer/Client/graphics.lua @@ -0,0 +1,167 @@ +local api = {} + +api.symbols = { + audio_controls = { + play = "\16", + pause = "\19", + forward = "\16", + backward = "\17", + full_back = "\171" + }, + app_controls = { + up = "\30", + down = "\31", + download = "\25", + play = "\16" + }, + misc = { + music = "\15", + } +} + +-- Draw a spinner loop at a specified position. Useful for waitForAny. +function api.spinner_loop(x, y, spin_monitor) + local spin_chars = { + "\139", + "\138", + "\142", + "\140", + "\141", + "\133", + "\135", + "\131" + } + local index = 1 + while true do + spin_monitor.setCursorPos(x, y) + spin_monitor.write(spin_chars[index]) + index = index + 1 + if index > #spin_chars then index = index - #spin_chars end + sleep(0.1) + end +end + +--[[ + Function to create simple progress bars of any length. + size - length of the bar in pixels, taken, max - ratio of the bar that should be full. + + There is an ability to set custom symbols for full and empty pixels, + however it is not necessary and by default the program uses '#' and '.' for these +]] +function api.make_progressbar(size, taken, max, char_full, char_empty) + --[[ + Maths behind the function are quite simple: + taken/max = x/size + taken * size = x * max + x = taken * size / max + ]] + local full_symbol, empty_symbol = char_full or '#', char_empty or '.' + size = size - 2 -- make space for "[" and "]" + local progress = math.floor(size * taken / max) + local hash = (full_symbol):rep(progress) + local dots = (empty_symbol):rep(size - progress) + return ("[" .. hash .. dots .. "]") +end + +local function seconds_to_string(seconds) + -- int to mm:ss + local minutes = math.floor(seconds / 60) + local seconds = seconds - minutes * 60 + return string.format("%02d:%02d", minutes, seconds) +end + +function api.draw_audio_bar(x, y, progress_seconds, track_info, is_paused, monitor) + --[[ + Example: + | Among us soundtrack lol | + | [<] [####........[<]............] [>] | + | 12:43/14:31 | + ]] + local x_max, y_max = monitor.getSize() + + -- Title + monitor.setCursorPos(x, y) + local title = track_info.title + if #title > x_max - 2 then + title = title:sub(1, x_max - 4) .. ".." + end + monitor.write((' '):rep(math.floor((x_max - #title) / 2)) .. title .. (' '):rep(math.ceil((x_max - #title) / 2))) + + -- Progress bar + monitor.setCursorPos(x, y + 1) + local progress_bar = api.make_progressbar(x_max - 16, progress_seconds, track_info.length) + --monitor.blit(' ' .. seconds_to_string(progress_seconds) .. ' ' .. progress_bar .. ' ' .. seconds_to_string(track_info.length), 'f00000f0' .. ('3'):rep(x_max - 16) .. '0f00000f', ('f'):rep(x_max)) + monitor.write(' ' .. seconds_to_string(progress_seconds) .. ' ' .. progress_bar .. ' ' .. seconds_to_string(track_info.length)) + + -- Controls + local controls = { + api.symbols.audio_controls.backward, + is_paused and api.symbols.audio_controls.play or api.symbols.audio_controls.pause, + api.symbols.audio_controls.forward + } + for i, control in ipairs(controls) do + monitor.setCursorPos(x + math.floor((x_max - 11) / 2) + (i - 1) * 4, y + 1) + monitor.write('[' .. control .. ']') + end +end + +-- Copy of draw_audio_bar, but for when no track is played - just a blank bar. +function api.draw_track_list(x, y, audio_catalogue, start_index, height, monitor) + height = height or select(2, monitor.getSize()) + local x_max, y_max = monitor.getSize() + + --[[ + top bar: + "[X] 21:00:01 Tue 12" + + -button to close the app + -time and date + + bottom bar: + "[up_btn] [down_btn] Showing [current_max_index - list_size]-[current_max_index] of [#audio_catalogue] | [REFRESH]" + -buttons to go up and down the list + -current index and total number of tracks + -button to refresh the list + + list items: + "[index] [title] [length] [play_btn] [download_btn]" + -index - number of the track in the list + -title - title of the track + -length - length of the track + -play_btn - button to play the track + -download_btn - button to download the track (not implemented yet) + ]] + + -- top bar + monitor.setCursorPos(x, y) + local current_time = os.date("%H:%M:%S %b %d") + local exit_btn = "[X]" + + monitor.blit(' ' .. exit_btn .. ' ' ..current_time, 'f0e0f00000000f000f00', 'ffffffffffffffffffff') + + -- bottom bar + monitor.setCursorPos(x, y + height - 1) + local bottom_btns = '[' .. api.symbols.app_controls.up .. '] [' .. api.symbols.app_controls.down .. ']' + local showing = 'Showing ' .. tostring(start_index) .. '-' .. tostring(start_index + height - 2) .. ' of ' .. tostring(#audio_catalogue) + local refresh_btn = '[REFRESH]' + monitor.blit(' ' .. bottom_btns .. ' ' .. showing .. ' | ' .. refresh_btn, 'f0e0f0b0f' .. ('0'):rep(#showing) .. "f0f033333330", ('f'):rep(#bottom_btns + #showing + #refresh_btn + 5)) + -- monitor.write(' ' .. bottom_btns .. ' ' .. showing .. ' | ' .. refresh_btn) + + -- list items + -- i should be current_max_index - height - 2 or 1 if current_max_index - height - 2 < 1 + for i = start_index, math.min(start_index + height - 2, #audio_catalogue) do + if (y + 1 + i >= y + height - 1) or i > #audio_catalogue then break end + + local track = audio_catalogue[i] + local index = tostring(i) + local title = track.title + local length = seconds_to_string(track.length) + local play_btn = api.symbols.audio_controls.play + local download_btn = api.symbols.app_controls.download + local list_item = ' ' .. index .. ' ' .. title .. ' ' .. length .. ' ' .. play_btn .. ' ' .. download_btn + monitor.setCursorPos(x, y + 1 + i) + monitor.write(list_item) + end +end + +return api \ No newline at end of file diff --git a/MusicPlayer/Client/playback.lua b/MusicPlayer/Client/playback.lua new file mode 100644 index 0000000..0b187a8 --- /dev/null +++ b/MusicPlayer/Client/playback.lua @@ -0,0 +1,27 @@ +local dfpwm = require("cc.audio.dfpwm") +local speaker = peripheral.find("speaker") +local decoder = dfpwm.make_decoder() + +local graphics = require("graphics") + + +local function play_music(status_vars, cfg_vars, audio_catalogue, modem) + if (not status_vars.is_playing) or status_vars.is_paused then return 0 end + + modem.transmit(7101, 7101, {type = 'track_chunk', + index = status_vars.track_index, + starting_second = status_vars.current_second, + length = cfg_vars.chunk_size}) + local event, sides, channel, replyChannel, message, distance = os.pullEvent('modem_message') + + local buffer = decoder(message.track) + + while not speaker.playAudio(buffer) do + os.pullEvent("speaker_audio_empty") + graphics.draw_audio_bar(1, select(2, term.getSize()) - 2, status_vars.current_second + cfg_vars.chunk_size, audio_catalogue[status_vars.track_index], status_vars.is_paused, term) + end + + return status_vars.current_second + cfg_vars.chunk_size +end + +return play_music \ No newline at end of file diff --git a/MusicPlayer/Client/touch_processing.lua b/MusicPlayer/Client/touch_processing.lua new file mode 100644 index 0000000..0de19c0 --- /dev/null +++ b/MusicPlayer/Client/touch_processing.lua @@ -0,0 +1,59 @@ +local touch_processer = {} + +touch_processer.init = function () + local self = {} + + + + function self.process_minimised_controls() + local x, y = e[3], e[4] + local btn_actions = { + pause = { + x = pause_btn_coords.x, + y = pause_btn_coords.y, + func = function() + status_vars.is_paused = not status_vars.is_paused + end + }, + forward = { + x = forward_btn_coords.x, + y = forward_btn_coords.y, + func = function() + status_vars.current_second = math.min(status_vars.current_second + cfg_vars.forward_skip, status_vars.max_seconds) + end + }, + backward = { + x = backward_btn_coords.x, + y = backward_btn_coords.y, + func = function() + status_vars.current_second = math.max(status_vars.current_second - cfg_vars.backward_skip, 0) + end + }, + exit = { + x = exit_btn_coords.x, + y = exit_btn_coords.y, + func = function() + status_vars.is_playing = false + return + end + }, + minimise = { + x = minimise_btn_coords.x, + y = minimise_btn_coords.y, + func = function() + status_vars.is_minimised = not status_vars.is_minimised + end + }, + } + for k, v in pairs(btn_actions) do + if x == v.x and y == v.y then + v.func() + end + end + end + + return self +end + +local function key_to_func() +end \ No newline at end of file diff --git a/MusicPlayer/Server/audio_manager.lua b/MusicPlayer/Server/audio_manager.lua new file mode 100644 index 0000000..d23930b --- /dev/null +++ b/MusicPlayer/Server/audio_manager.lua @@ -0,0 +1,98 @@ +--[[ + Class to manage ComputerCraft (CC) audio tracks on different disks + + Written by Andrew_7_1 +]] + +local audio_manager = {} +audio_manager.init = function (dirs_file) + local self = {} + + -- Load audio directories from file where directories are seperated by newlines" + local function load_audio_dirs(file_location) + local audio_dirs = {} + local file = io.open(file_location, 'r') + if file then + for line in file:lines() do + table.insert(audio_dirs, line) + end + file:close() + end + return audio_dirs + end + local audio_dirs = load_audio_dirs(dirs_file or 'audio_dirs.txt') + + --[[ + Iterate over all audio directories and load all audio files into a catalogue. + Audio files have extensions of .dfpwm and rarely .wav + Save them to a table of tables like this: + { + ["filepath"] = absolute file path + ["length"] = file size / 6000 + ["title"] = file name without extension and path, e.g. "title.dfpwm" -> "title" + ["extension"] = file extension, e.g. "dfpwm" + } + ]] + local function load_audio_catalogue() + local audio_catalogue = {} + for _, dir in pairs(audio_dirs) do + for _, file in pairs(fs.list(dir)) do + if file:find('.dfpwm') or file:find('.wav') then + local file_path = dir .. '/' .. file + local file_size = fs.getSize(file_path) + local file_title = file:sub(1, #file - #file:match('.*%.(.*)$')) + local file_extension = file:match('.*%.(.*)$') + table.insert(audio_catalogue, { + ["filepath"] = file_path, + ["length"] = file_size / 6000, + ["title"] = file_title, + ["extension"] = file_extension + }) + end + end + end + return audio_catalogue + end + self.audio_catalogue = load_audio_catalogue() + + -- Update audio catalogue + function self.update_catalogue() + self.audio_catalogue = load_audio_catalogue() + end + + function self.get_catalogue() + return self.audio_catalogue + end + + + -- Get part of an audio file in the catalogue + function self.get_track_chunk(index, starting_second, length) + if index > #self.audio_catalogue then + return false + end + local track = self.audio_catalogue[index] + + local file = io.open(track.filepath, 'rb') + if not file then + return false + end + + file:seek('set', starting_second * 6000) + local chunk = file:read(length * 6000) + file:close() + + return chunk + end + + -- More convenient way to get track info than using entire catalogue + function self.get_track_details(index) + if index > #self.audio_catalogue then + return false + end + return self.audio_catalogue[index] + end + + return self +end + +return audio_manager \ No newline at end of file diff --git a/MusicPlayer/Server/logger.lua b/MusicPlayer/Server/logger.lua new file mode 100644 index 0000000..fec109c --- /dev/null +++ b/MusicPlayer/Server/logger.lua @@ -0,0 +1,60 @@ + + +local logger = {} +logger.init = function(monitor_wrap, scale) + local self = {} + + local monitor = monitor_wrap or peripheral.find("monitor") + if not monitor then + monitor = term + else monitor.setTextScale(scale or 0.5) + end + local log_colours = { + ['log'] = colours.white, + ['info'] = colours.lightBlue, + ['warn'] = colours.yellow, + ['err'] = colours.red, + ['success'] = colours.lime + } + + local function move_line() + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1) + if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then + monitor.scroll(1) + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1) + end + end + + local function write_msg(message, msg_colour) + local time_str = os.date('%Y-%b-%d %H:%M:%S') + local msg_formatted = "[" .. time_str .. "] " .. message + + monitor.setTextColour(msg_colour) + monitor.write(msg_formatted) + move_line() + end + + function self.log(msg) + write_msg(msg, log_colours['log']) + end + + function self.info(msg) + write_msg('INFO: ' .. msg, log_colours['info']) + end + + function self.warning(msg) + write_msg('WARNING: ' .. msg, log_colours['warn']) + end + + function self.error(msg) + write_msg('ERROR: ' .. msg, log_colours['err']) + end + + function self.success(msg) + write_msg('SUCCESS: ' .. msg, log_colours['success']) + end + + return self +end + +return logger \ No newline at end of file diff --git a/MusicPlayer/Server/server.lua b/MusicPlayer/Server/server.lua new file mode 100644 index 0000000..9e47d90 --- /dev/null +++ b/MusicPlayer/Server/server.lua @@ -0,0 +1,60 @@ +--[[ + Audio server for ComputerCraft (CC) + + Written by Andrew_7_1 +]] + +local server = {} +server.init = function (port, audio_dir) + local self = {} + + local logger = require('logger').init() + local audio_manager = require('audio_manager').init(audio_dir or 'audio_dirs.txt') + + local port = port or 7101 + local modem = peripheral.find('modem') + if not modem then error('No modem found') end + modem.open(port) + logger.info('Audio server started on port ' .. port .. ' with audio directories from ' .. audio_dir) + + local message_types = { + query = function (replyChannel, msg) + modem.transmit(replyChannel, port, {code = 200, audio_catalogue = audio_manager.get_catalogue()}) + logger.success('Query sent') + end, + refresh = function (replyChannel, msg) + audio_manager.update_catalogue() + modem.transmit(replyChannel, port, {code = 200}) + logger.success('Catalogue updated') + end, + + track_info = function (replyChannel, msg) + local track = audio_manager.get_track_details(msg.index) + if not track then + modem.transmit(replyChannel, port, {code = 404}) + logger.error('Track not found (id: ' .. msg.index .. ')') + else + modem.transmit(replyChannel, port, {code = 200, track = track}) + logger.success('Track details sent (id: ' .. msg.index .. ')') + end + end, + track_chunk = function (replyChannel, msg) + local track = audio_manager.get_track_chunk(msg.index, msg.starting_second, msg.length) + if not track then + modem.transmit(replyChannel, port, {code = 404}) + logger.error('Track not found (id: ' .. msg.index .. ')') + else + modem.transmit(replyChannel, port, {code = 200, track = track}) + logger.success('Part of a track sent (id: ' .. msg.index .. ', part:' .. msg.starting_second .. '-' .. msg.starting_second + msg.length .. ')') + end + end + } + + function self.process_transmission(event, side, channel, replyChannel, message, distance) + message_types[message.type](replyChannel, message) + end + + return self +end + +return server \ No newline at end of file diff --git a/MusicPlayer/Server/startup.lua b/MusicPlayer/Server/startup.lua new file mode 100644 index 0000000..d452422 --- /dev/null +++ b/MusicPlayer/Server/startup.lua @@ -0,0 +1,14 @@ +local config = { + port = 7101, + audio_dir = 'audio_dirs.txt' +} + +local server = require("server").init(config.port, config.audio_dir) +local modem = peripheral.find("modem") +if not modem then error("No modem found") end +modem.open(config.port) + +while true do + local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + server.process_transmission(event, side, channel, replyChannel, message, distance) +end \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf1e1af --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# CC Projects +Collection of all my ComputerCraft projects, as none of them are big or finished enough to warrant separate repositories. + +If you think that this is a disorganized mess then you are 100% correct. \ No newline at end of file diff --git a/RSS/feeds.lua b/RSS/feeds.lua new file mode 100644 index 0000000..3cdc405 --- /dev/null +++ b/RSS/feeds.lua @@ -0,0 +1,128 @@ +local FEEDS = { + feeds = {} +} + +local FEEDS_PATH = "./feeds" +local SLAXML = require('slaxml') +local LOG = require('log') + +-- Retrieve feeds from config files and prepare them for use +function FEEDS:Load() + self.feeds = {} + + local files = fs.find(FEEDS_PATH .. "/*.json") + for _, v in ipairs(files) do + local file = fs.open(v, "r") + local contents = file.readAll() + local feed = textutils.unserialiseJSON(contents) + + local response, err, _ = http.get(feed.link) + if response == nil then + LOG:Error("HTTP Error loading feed (" .. v .. "): " .. err) + else + local xml = response.readAll() + response.close() + local element + local in_item = false + local function startElement(name, nsURI, nsPrefix) + -- Track the current element name + element = name + if element == "item" then + in_item = true + end + end + local function text(text, cdata) + -- Store the text content based on the current element name + if element == 'title' and not feed.title then + feed.title = text + elseif in_item and not feed.last and element == 'guid' then + feed.last = text + end + end + local parser = SLAXML:parser{ + startElement = startElement, + text = text + } + parser:parse(xml) + + if feed.title_override then + feed.title = feed.title_override + end + + table.insert(self.feeds, feed) + LOG:Info("Loaded feed " .. feed.title) + end + end + if #self.feeds == 0 then + LOG:Info("No feeds loaded, nothing to do") + return true + end + return nil +end + +function FEEDS:ParseLatest() + local output = {} + for _, feed in ipairs(self.feeds) do + local response, err, _ = http.get(feed.link) + if response == nil then + LOG:Error("HTTP Error fetching feed: " .. err) + else + local xml = response.readAll() + response.close() + + local element + local new_last + local done = false + local current_item = { + colour = feed.colour, + source = feed.title + } + local in_item = false + local function startElement(name, nsURI, nsPrefix) + if done then return end + + -- Track the current element name + element = name + if element == "item" then + in_item = true + current_item = { + colour = feed.colour, + source = feed.title + } + end + end + local function closeElement(name, nsURI, nsPrefix) + if done then return end + + if name == 'item' and not done then + table.insert(output, current_item) + end + end + local function text(text, cdata) + if done or not in_item then return end + + -- Store the text content based on the current element name + if element == 'title' then + current_item.title = text + elseif in_item and element == 'guid' then + if not new_last then + new_last = text + end + if feed.last == text then + done = true + end + end + end + local parser = SLAXML:parser{ + startElement = startElement, + closeElement = closeElement, + text = text + } + parser:parse(xml) + feed.last = new_last + end + end + return output +end + +return FEEDS \ No newline at end of file diff --git a/RSS/feeds/nytimes.json b/RSS/feeds/nytimes.json new file mode 100644 index 0000000..b45184c --- /dev/null +++ b/RSS/feeds/nytimes.json @@ -0,0 +1,5 @@ +{ + "link": "https://rss.nytimes.com/services/xml/rss/nyt/Technology.xml", + "title_override": "NYT", + "colour": "9" +} \ No newline at end of file diff --git a/RSS/feeds/tass.json b/RSS/feeds/tass.json new file mode 100644 index 0000000..f0ef522 --- /dev/null +++ b/RSS/feeds/tass.json @@ -0,0 +1,5 @@ +{ + "link": "https://tass.ru/rss/v2.xml", + "title_override": "TACC", + "colour": "b" +} \ No newline at end of file diff --git a/RSS/feeds/youtube_generic.json b/RSS/feeds/youtube_generic.json new file mode 100644 index 0000000..1a2f4b7 --- /dev/null +++ b/RSS/feeds/youtube_generic.json @@ -0,0 +1,5 @@ +{ + "link": "https://www.youtube.com/@tag", + "title_override": "Youtuber name", + "colour": "e" +} \ No newline at end of file diff --git a/RSS/graphics.lua b/RSS/graphics.lua new file mode 100644 index 0000000..1f4279d --- /dev/null +++ b/RSS/graphics.lua @@ -0,0 +1,71 @@ +local GRAPHICS = { + target = nil, + contents = {} +} + +local LOG = require('log') + +-- Set target's colorscheme to gruvbox for better visuals +function GRAPHICS:SetColours() + if self.target == nil then + return + end + self.target.setPaletteColour(colours.red, 0xfb4934) + self.target.setPaletteColour(colours.blue, 0x83a598) + self.target.setPaletteColour(colours.green, 0xb8bb26) + self.target.setPaletteColour(colours.purple, 0xd3869b) + self.target.setPaletteColour(colours.cyan, 0x8ec07c) + self.target.setPaletteColour(colours.white, 0xf9f5d7) + self.target.setPaletteColour(colours.black, 0x1d2021) +end + +-- Set target output for our graphics +function GRAPHICS:SetTarget(target) + self.target = target + if self.target == nil then + LOG:Error("Nil display target, items will not display") + end + target.clear() + target.setTextScale(0.5) + self:Refresh(true) +end + +function GRAPHICS:Refresh(full) + if self.target == nil then + return + end + local _, y = self.target.getSize() + while #self.contents > y do + table.remove(self.contents, 1) + end + + if full then + self.target.scroll(#self.contents) + for k, entry in ipairs(self.contents) do + self.target.setCursorPos(1, y - k + 1) + local output = "[" .. entry.source .. "] " .. entry.title + local fg_blit = "0" .. (entry.colour):rep(#entry.source) .. ("0"):rep(#entry.title + 2) + self.target.blit(output, fg_blit, ("f"):rep(#output)) + end + else + self.target.scroll(1) + self.target.setCursorPos(1, y) + local entry = self.contents[#self.contents] + local output = "[" .. entry.source .. "] " .. entry.title + local fg_blit = "0" .. (entry.colour):rep(#entry.source) .. ("0"):rep(#entry.title + 2) + self.target.blit(output, fg_blit, ("f"):rep(#output)) + end +end + +function GRAPHICS:AddEntry(entry) + table.insert(self.contents, entry) + self:Refresh() +end + +function GRAPHICS:Add(entries) + for _, entry in ipairs(entries) do + self:AddEntry(entry) + end +end + +return GRAPHICS \ No newline at end of file diff --git a/RSS/log.lua b/RSS/log.lua new file mode 100644 index 0000000..981e24a --- /dev/null +++ b/RSS/log.lua @@ -0,0 +1,98 @@ +local LOG = { + target = term, + history = {}, + history_cutoff = 50, + log_path = "./logs/", + debug = false +} + +-- Set target's colorscheme to gruvbox for better visuals +function LOG:SetColours() + self.target.setPaletteColour(colours.red, 0xfb4934) + self.target.setPaletteColour(colours.blue, 0x83a598) + self.target.setPaletteColour(colours.green, 0xb8bb26) + self.target.setPaletteColour(colours.purple, 0xd3869b) + self.target.setPaletteColour(colours.cyan, 0x8ec07c) + self.target.setPaletteColour(colours.white, 0xf9f5d7) + self.target.setPaletteColour(colours.black, 0x1d2021) +end + +-- Set target output for our log +function LOG:SetTarget(target) + self.target = target + target.clear() + target.setTextScale(0.5) +end + +function LOG:SetDebug(value) + self.debug = value +end + +-- Utility function to output some kind of log message +local function genericLog(log, level, message, col) + if #level ~= 4 or #col ~= 1 then + LOG:Error("Incorrect generic log format") + return + end + + local target = log.target + target.scroll(1) + local _, y = target.getSize() + target.setCursorPos(1, y) + + local time = os.date("%T", os.epoch("local") / 1000) + local output = "[" .. time .. "|" .. level .. "] " .. message + local fg_blit = ("0"):rep(#time + 2) .. (col):rep(4) .. ("0"):rep(#message + 2) + target.blit(output, fg_blit, ("f"):rep(#output)) + + table.insert(log.history, output) + if #log.history > log.history_cutoff then + table.remove(log.history, 1) + end +end + +function LOG:Info(message) + genericLog(self, "INFO", message, "b") +end + +function LOG:Success(message) + genericLog(self, "SCCS", message, "d") +end + +function LOG:Debug(message) + if not self.debug then return end + genericLog(self, "DBUG", message, "8") +end + +function LOG:Warning(message) + genericLog(self, "WARN", message, "4") +end + +function LOG:Error(message) + genericLog(self, "ERRO", message, "e") +end + +function LOG:Critical(message) + genericLog(self, "CRIT", message, "e") + self:Dump() + error("critical exception occured, read above") +end + +function LOG:Clear() + self.history = {} +end + +-- Put all recent log messages into a file for later study +function LOG:Dump() + local out = "" + for _, v in ipairs(self.history) do + out = out + v .. "\n" + end + local time = os.date("%F-%T", os.epoch("local") / 1000) + local file = fs.open(string.format("%s/dump_%s.log", self.log_path, time), "w") + file.write(out) + file.close() + return out +end + +return LOG \ No newline at end of file diff --git a/RSS/main.lua b/RSS/main.lua new file mode 100644 index 0000000..650641d --- /dev/null +++ b/RSS/main.lua @@ -0,0 +1,28 @@ +local LOG = require('log') +local GRAPHICS = require('graphics') +local FEEDS = require('feeds') + +local REFRESH_INTERVAL = 8 +local DEBUG = true +local MONITOR = assert(peripheral.find('monitor'), "No monitor detected") + +local function main() + LOG:Info("RSS System init") + LOG:SetDebug(DEBUG) + LOG:SetTarget(term) + LOG:SetColours() + GRAPHICS:SetTarget(MONITOR) + GRAPHICS:SetColours() + local err = FEEDS:Load() + if err ~= nil then + return + end + LOG:Success("System fully loaded, beginning RSS processing") + + while true do + GRAPHICS:Add(FEEDS:ParseLatest()) + sleep(REFRESH_INTERVAL) + end +end + +main() \ No newline at end of file diff --git a/RSS/slaxml.lua b/RSS/slaxml.lua new file mode 100644 index 0000000..98e9819 --- /dev/null +++ b/RSS/slaxml.lua @@ -0,0 +1,259 @@ +--[=====================================================================[ +v0.8 Copyright © 2013-2018 Gavin Kistner ; MIT Licensed +See http://github.com/Phrogz/SLAXML for details. +--]=====================================================================] +local SLAXML = { + VERSION = "0.8", + _call = { + pi = function(target,content) + print(string.format("",target,content)) + end, + comment = function(content) + print(string.format("",content)) + end, + startElement = function(name,nsURI,nsPrefix) + io.write("<") + if nsPrefix then io.write(nsPrefix,":") end + io.write(name) + if nsURI then io.write(" (ns='",nsURI,"')") end + print(">") + end, + attribute = function(name,value,nsURI,nsPrefix) + io.write(' ') + if nsPrefix then io.write(nsPrefix,":") end + io.write(name,'=',string.format('%q',value)) + if nsURI then io.write(" (ns='",nsURI,"')") end + io.write("\n") + end, + text = function(text,cdata) + print(string.format(" %s: %q",cdata and 'cdata' or 'text',text)) + end, + closeElement = function(name,nsURI,nsPrefix) + io.write("") + end, + } +} + +function SLAXML:parser(callbacks) + return { _call=callbacks or self._call, parse=SLAXML.parse } +end + +function SLAXML:parse(xml,options) + if not options then options = { stripWhitespace=false } end + + -- Cache references for maximum speed + local find, sub, gsub, char, push, pop, concat = string.find, string.sub, string.gsub, string.char, table.insert, table.remove, table.concat + local first, last, match1, match2, match3, pos2, nsURI + local unpack = unpack or table.unpack + local pos = 1 + local state = "text" + local textStart = 1 + local currentElement={} + local currentAttributes={} + local currentAttributeCt -- manually track length since the table is re-used + local nsStack = {} + local anyElement = false + + local utf8markers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} } + local function utf8(decimal) -- convert unicode code point to utf-8 encoded character string + if decimal<128 then return char(decimal) end + local charbytes = {} + for bytes,vals in ipairs(utf8markers) do + if decimal<=vals[1] then + for b=bytes+1,2,-1 do + local mod = decimal%64 + decimal = (decimal-mod)/64 + charbytes[b] = char(128+mod) + end + charbytes[1] = char(vals[2]+decimal) + return concat(charbytes) + end + end + end + local entityMap = { ["lt"]="<", ["gt"]=">", ["amp"]="&", ["quot"]='"', ["apos"]="'" } + local entitySwap = function(orig,n,s) return entityMap[s] or n=="#" and utf8(tonumber('0'..s)) or orig end + local function unescape(str) return gsub( str, '(&(#?)([%d%a]+);)', entitySwap ) end + + local function finishText() + if first>textStart and self._call.text then + local text = sub(xml,textStart,first-1) + if options.stripWhitespace then + text = gsub(text,'^%s+','') + text = gsub(text,'%s+$','') + if #text==0 then text=nil end + end + if text then self._call.text(unescape(text),false) end + end + end + + local function findPI() + first, last, match1, match2 = find( xml, '^<%?([:%a_][:%w_.-]*) ?(.-)%?>', pos ) + if first then + finishText() + if self._call.pi then self._call.pi(match1,match2) end + pos = last+1 + textStart = pos + return true + end + end + + local function findComment() + first, last, match1 = find( xml, '^', pos ) + if first then + finishText() + if self._call.comment then self._call.comment(match1) end + pos = last+1 + textStart = pos + return true + end + end + + local function nsForPrefix(prefix) + if prefix=='xml' then return 'http://www.w3.org/XML/1998/namespace' end -- http://www.w3.org/TR/xml-names/#ns-decl + for i=#nsStack,1,-1 do if nsStack[i][prefix] then return nsStack[i][prefix] end end + error(("Cannot find namespace for prefix %s"):format(prefix)) + end + + local function startElement() + anyElement = true + first, last, match1 = find( xml, '^<([%a_][%w_.-]*)', pos ) + if first then + currentElement[2] = nil -- reset the nsURI, since this table is re-used + currentElement[3] = nil -- reset the nsPrefix, since this table is re-used + finishText() + pos = last+1 + first,last,match2 = find(xml, '^:([%a_][%w_.-]*)', pos ) + if first then + currentElement[1] = match2 + currentElement[3] = match1 -- Save the prefix for later resolution + match1 = match2 + pos = last+1 + else + currentElement[1] = match1 + for i=#nsStack,1,-1 do if nsStack[i]['!'] then currentElement[2] = nsStack[i]['!']; break end end + end + currentAttributeCt = 0 + push(nsStack,{}) + return true + end + end + + local function findAttribute() + first, last, match1 = find( xml, '^%s+([:%a_][:%w_.-]*)%s*=%s*', pos ) + if first then + pos2 = last+1 + first, last, match2 = find( xml, '^"([^<"]*)"', pos2 ) -- FIXME: disallow non-entity ampersands + if first then + pos = last+1 + match2 = unescape(match2) + else + first, last, match2 = find( xml, "^'([^<']*)'", pos2 ) -- FIXME: disallow non-entity ampersands + if first then + pos = last+1 + match2 = unescape(match2) + end + end + end + if match1 and match2 then + local currentAttribute = {match1,match2} + local prefix,name = string.match(match1,'^([^:]+):([^:]+)$') + if prefix then + if prefix=='xmlns' then + nsStack[#nsStack][name] = match2 + else + currentAttribute[1] = name + currentAttribute[4] = prefix + end + else + if match1=='xmlns' then + nsStack[#nsStack]['!'] = match2 + currentElement[2] = match2 + end + end + currentAttributeCt = currentAttributeCt + 1 + currentAttributes[currentAttributeCt] = currentAttribute + return true + end + end + + local function findCDATA() + first, last, match1 = find( xml, '^', pos ) + if first then + finishText() + if self._call.text then self._call.text(match1,true) end + pos = last+1 + textStart = pos + return true + end + end + + local function closeElement() + first, last, match1 = find( xml, '^%s*(/?)>', pos ) + if first then + state = "text" + pos = last+1 + textStart = pos + + -- Resolve namespace prefixes AFTER all new/redefined prefixes have been parsed + if currentElement[3] then currentElement[2] = nsForPrefix(currentElement[3]) end + if self._call.startElement then self._call.startElement(unpack(currentElement)) end + if self._call.attribute then + for i=1,currentAttributeCt do + if currentAttributes[i][4] then currentAttributes[i][3] = nsForPrefix(currentAttributes[i][4]) end + self._call.attribute(unpack(currentAttributes[i])) + end + end + + if match1=="/" then + pop(nsStack) + if self._call.closeElement then self._call.closeElement(unpack(currentElement)) end + end + return true + end + end + + local function findElementClose() + first, last, match1, match2 = find( xml, '^', pos ) + if first then + nsURI = nil + for i=#nsStack,1,-1 do if nsStack[i]['!'] then nsURI = nsStack[i]['!']; break end end + else + first, last, match2, match1 = find( xml, '^', pos ) + if first then nsURI = nsForPrefix(match2) end + end + if first then + finishText() + if self._call.closeElement then self._call.closeElement(match1,nsURI) end + pos = last+1 + textStart = pos + pop(nsStack) + return true + end + end + + while pos<#xml do + if state=="text" then + if not (findPI() or findComment() or findCDATA() or findElementClose()) then + if startElement() then + state = "attributes" + else + first, last = find( xml, '^[^<]+', pos ) + pos = (first and last or pos) + 1 + end + end + elseif state=="attributes" then + if not findAttribute() then + if not closeElement() then + error("Was in an element and couldn't find attributes or the close.") + end + end + end + end + + if not anyElement then error("Parsing did not discover any elements") end + if #nsStack > 0 then error("Parsing ended with unclosed elements") end +end + +return SLAXML diff --git a/RSS/todo.txt b/RSS/todo.txt new file mode 100644 index 0000000..eabac24 --- /dev/null +++ b/RSS/todo.txt @@ -0,0 +1,3 @@ +* Add test file that checks entire system/functions before startup? or at least improve error checking +* Enhance customisation per-feed e.g. define 14 colours and set which color each char in feed title is +* Pre-Processing for russian language \ No newline at end of file diff --git a/TARDIM/NavDash.lua b/TARDIM/NavDash.lua new file mode 100644 index 0000000..dd0dabd --- /dev/null +++ b/TARDIM/NavDash.lua @@ -0,0 +1,96 @@ +local tardim = peripheral.find("digital_tardim_interface") +local screen = peripheral.find("monitor") + +screen.clear() +screen.setCursorBlink(false) +screen.setTextScale(0.5) + +-- 15x24 +--[[ +~ Current pos + X + Y + Z + Dimension + Facing +~ Destination + X + Y + Z + Dimension + Facing +~ Fuel + Remaining + Required +]] + +-- This changes the colors to be like the gruvbox theme +local function set_colorscheme(screen) + screen.setPaletteColour(colours.red, 0xfb4934) + screen.setPaletteColour(colours.blue, 0x83a598) + screen.setPaletteColour(colours.green, 0xb8bb26) + screen.setPaletteColour(colours.purple, 0xd3869b) + screen.setPaletteColour(colours.cyan, 0x8ec07c) + + screen.setPaletteColour(colours.white, 0xf9f5d7) + screen.setPaletteColour(colours.black, 0x1d2021) +end + +local function dim_char(dimension_str) + + if dimension_str == "minecraft:overworld" then + return "OWR" + elseif dimension_str == "minecraft:the_nether" then + return "NETH" + elseif dimension_str == "minecraft:the_end" then + return "END" + else + return "???" + end + +end + +local function drawPos(screen, pos, screen_y) + screen.setCursorPos(1, screen_y) + screen.blit("X: " .. pos.pos.x, "e" .. string.rep("0", #tostring(pos.pos.x) + 2), string.rep("f", #tostring(pos.pos.x) + 3)) + screen.setCursorPos(1, screen_y + 1) + screen.blit("Y: " .. pos.pos.y, "d" .. string.rep("0", #tostring(pos.pos.y) + 2), string.rep("f", #tostring(pos.pos.y) + 3)) + screen.setCursorPos(1, screen_y + 2) + screen.blit("Z: " .. pos.pos.z, "b" .. string.rep("0", #tostring(pos.pos.z) + 2), string.rep("f", #tostring(pos.pos.z) + 3)) + screen.setCursorPos(1, screen_y + 3) + screen.write("Dimension: " .. dim_char(pos.dimension), "00000000000111", "ffffffffffffff") + screen.setCursorPos(1, screen_y + 4) + screen.write("Facing: " .. pos.facing) +end + +set_colorscheme(screen) +screen.setCursorPos(1, 1) +screen.blit("> TARDIM NAV", "009999990999", "ffffffffffff") + +while true do + screen.setCursorPos(1, 3) + + local pos = tardim.getCurrentLocation() + screen.write("> Current pos") + drawPos(screen, pos, 4) + + local dest = tardim.getTravelLocation() + screen.setCursorPos(1, 10) + screen.write("> Destination") + drawPos(screen, dest, 11) + + local fuel = tardim.getFuel() + screen.setCursorPos(1, 17) + screen.write("> Fuel") + screen.setCursorPos(1, 18) + screen.write("REM.: " .. fuel .. "/100") + screen.setCursorPos(1, 19) + local Required = tardim.calculateFuelForJourney() + screen.write("REQ.: " .. Required) + screen.setCursorPos(1, 20) + screen.blit("ENOUGH: " .. (fuel >= Required and "YES" or "NO "), "00000000" .. (fuel >= Required and "ddd" or "eee"), "ffffffff" .. (fuel >= Required and "fff" or "fff")) + + local inFlight = tardim.isInFlight() + screen.setCursorPos(1, 22) + screen.blit("IN FLIGHT: " .. (inFlight and "YES" or "NO "), "00000000000" .. (inFlight and "ddd" or "eef"), "fffffffffff" .. (inFlight and "fff" or "fff")) +end \ No newline at end of file diff --git a/TARDIM/NavDashTwo.lua b/TARDIM/NavDashTwo.lua new file mode 100644 index 0000000..420da18 --- /dev/null +++ b/TARDIM/NavDashTwo.lua @@ -0,0 +1,85 @@ +local tardim = peripheral.find("digital_tardim_interface") +local screen = peripheral.find("monitor") + + +-- This changes the colors to be like the gruvbox theme +local function SetColourscheme(screen, scheme) + local schemes = { + gruvbox = { + red = 0xfb4934, + blue = 0x83a598, + green = 0xb8bb26, + purple = 0xd3869b, + cyan = 0x8ec07c, + white = 0xf9f5d7, + black = 0x1d2021 + }, + starfield = { + red = 0xc72138, + blue = 0x304c7a, + yellow = 0xd7a64b, + white = 0xf4f5f7, + orange = 0xe06236, + black = 0x121212 + } + } + + for i, _ in pairs(schemes[scheme]) do + screen.setPaletteColour(colours[i], schemes[scheme][i]) + end +end + +local function DrawStripes(screen) + local stripes = { + [1] = 'b', + [2] = '4', + [3] = '1', + [4] = 'e' + } + local _, y = screen.getSize() + for h=1,y do + for w=1,4 do + screen.setCursorPos(w, h) + screen.blit(' ', '0', stripes[w]) + end + end +end + +local function DrawFuel(screen, fuel) + local bar_colours = { + [75] = 'd', -- green + [60] = '4', -- orange + [30] = '1', -- yellow + [1] = 'e' -- red + } + fuel = math.floor(fuel) + local col = bar_colours[1] + for i, _ in pairs(bar_colours) do + if fuel > i then + col = bar_colours[i] + break + end + end + + local x, y = screen.getSize() + + screen.setCursorPos(8, y - 2) + screen.write('FUEL - ' .. tostring(fuel)) + screen.setCursorPos(8, y - 1) + screen.blit(string.rep(' ', x - 16), string.rep('8', x - 16), string.rep('8', x - 16)) + screen.setCursorPos(8, y - 1) + screen.blit(string.rep(' ', x - 25), string.rep('8', x - 25), string.rep(col, x - 25)) +end + +SetColourscheme(screen, 'starfield') +screen.setCursorBlink(false) +screen.setTextScale(0.5) +screen.setTextColour(colours.black) +screen.setBackgroundColour(colours.white) +screen.clear() +DrawStripes(screen) + +screen.setCursorPos(10, 5) +screen.write("Hello Universe!") + +DrawFuel(screen, tardim.getFuel()) \ No newline at end of file diff --git a/TUI71/TUI71-time.lua b/TUI71/TUI71-time.lua new file mode 100644 index 0000000..3ac6519 --- /dev/null +++ b/TUI71/TUI71-time.lua @@ -0,0 +1,148 @@ +--[[ + Time module for TUI71 + + by Andrew-7-1 + MIT License +]] + +local time_api = {} + +-- ASCII-art digits for bigger numbers +local ascii_digits = { + { + " 0000 ", + "00 00", + "00 00", + "00 00", + " 0000 " + }, + { + "1111 ", + " 11 ", + " 11 ", + " 11 ", + "111111" + }, + { + " 2222 ", + "22 22", + " 22 ", + " 22 ", + "222222", + }, + { + " 3333 ", + "33 33", + " 333", + "33 33", + " 3333 ", + }, + { + "44 44", + "44 44", + "444444", + " 44", + " 44" + }, + { + "555555", + "55 ", + "55555 ", + " 55", + "55555 " + }, + { + " 6666 ", + "66 ", + "66666 ", + "66 66", + " 6666 " + }, + { + "777777", + " 77 ", + " 77 ", + " 77 ", + "77 " + }, + { + " 8888 ", + "88 88", + " 8888 ", + "88 88", + " 8888 " + }, + { + " 9999 ", + "99 99", + " 99999", + " 99", + " 9999 " + } +} +-- Seperator for time e.g. "22:01" +local seperator = { + " ", + " # ", + " ", + " # ", + " ", +} + + +-- Draw a big digit +function time_api.draw_digit(number, x, y, monitor, colour) + local digit = ascii_digits[number] + monitor.setCursorPos(x, y) + monitor.setTextColor(colour) + for i = 1, 5 do + monitor.write(digit[i]) + monitor.setCursorPos(x, y + i) + end +end + + +-- Draw a big ascii-art clock type HH:MM. +function time_api.draw_time(x, y, monitor) + -- Get the current time + local h, m = tostring(os.date("%H")), tostring(os.date("%M")) + local h1, h2 = tonumber(string.sub(h, 1, 1)), tonumber(string.sub(h, 2, 2)) + local m1, m2 = tonumber(string.sub(m, 1, 1)), tonumber(string.sub(m, 2, 2)) + + local s = os.date("%S") + + monitor.setCursorPos(x, y) -- Set cursor position + + -- Draw digits layer by layer + for i = 1, 5 do + monitor.setCursorPos(x, select(2, monitor.getCursorPos()) + 1) + monitor.write(ascii_digits[h1 + 1][i] .. " " .. ascii_digits[h2 + 1][i] .. seperator[i] .. ascii_digits[m1 + 1][i] .. " " .. ascii_digits[m2 + 1][i]) + end + end + +local function draw_timezone(x, y, tz_name, utc_offset, tz_monitor) + --[[ + /SYD\ + |22:34| + \_ _/ + ]] + if #tz_name ~= 3 then error("Invalid timezone name (not 3 letters)") end + local utc = os.date("!*t") + local tz_hour = utc.hour + utc_offset + if tz_hour > 23 then + tz_hour = -24 + tz_hour end + + local day_part = { + ["day"] = "☼", + ["night"] = "☾" + } + local tz_symbol + if (tz_hour > 20 or tz_hour < 7) then tz_symbol = day_part["night"] else tz_symbol = day_part["day"] end + + tz_monitor.setCursorPos(x, y) + tz_monitor.blit(" /" .. tz_name .. "\\", "f04440", "ffffff") + tz_monitor.setCursorPos(x, y + 1) + tz_monitor.write("|" .. tostring(utc.hour + utc_offset) .. ":" .. tostring(utc.min) .. "|") + tz_monitor.setCursorPos(x, y + 2) + tz_monitor.blit(" \\_" .. tz_symbol .. "_/ ", "f00400f", "fffffff") +end \ No newline at end of file diff --git a/TUI71/TUI71.lua b/TUI71/TUI71.lua new file mode 100644 index 0000000..6114df9 --- /dev/null +++ b/TUI71/TUI71.lua @@ -0,0 +1,9 @@ +--[[ + TUI-71 - An overly simple graphics library for CC computers. + + by Andrew-7-1 + MIT License +]] + +local api = {} + diff --git a/apricot/README.md b/apricot/README.md new file mode 100644 index 0000000..b827ea1 --- /dev/null +++ b/apricot/README.md @@ -0,0 +1,3 @@ +If this ever gets worked on again, this was intended to log **all** messages on **all** possible channels, reverse tria- wait no there's a different word for this. Basically global spy monitoring station + +If you're wondering why it's named apricot, I literally had no idea and randomly thought about throwing apricots at people in Hitman 3. **Yes.** \ No newline at end of file diff --git a/apricot/node/logger.lua b/apricot/node/logger.lua new file mode 100644 index 0000000..599a615 --- /dev/null +++ b/apricot/node/logger.lua @@ -0,0 +1,58 @@ +local logger = {} +logger.init = function(monitor_wrap, scale) + local self = {} + + local monitor = monitor_wrap or peripheral.find("monitor") + if not monitor then + monitor = term + else monitor.setTextScale(scale or 0.5) + end + local log_colours = { + ['log'] = colours.white, + ['info'] = colours.lightBlue, + ['warn'] = colours.yellow, + ['err'] = colours.red, + ['success'] = colours.lime + } + + local function move_line() + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1) + if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then + monitor.scroll(1) + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1) + end + end + + local function write_msg(message, msg_colour) + local time_str = os.date('%Y-%b-%d %H:%M:%S') + local msg_formatted = "[" .. time_str .. "] " .. message + + monitor.setTextColour(msg_colour) + monitor.write(msg_formatted) + move_line() + end + + function self.log(msg) + write_msg(msg, log_colours['log']) + end + + function self.info(msg) + write_msg('INFO: ' .. msg, log_colours['info']) + end + + function self.warning(msg) + write_msg('WARNING: ' .. msg, log_colours['warn']) + end + + function self.error(msg) + write_msg('ERROR: ' .. msg, log_colours['err']) + end + + function self.success(msg) + write_msg('SUCCESS: ' .. msg, log_colours['success']) + end + + return self +end + +return logger \ No newline at end of file diff --git a/apricot/node/modems.lua b/apricot/node/modems.lua new file mode 100644 index 0000000..410d376 --- /dev/null +++ b/apricot/node/modems.lua @@ -0,0 +1,19 @@ + +local modems = {peripheral.find("modem")} +local logger = require('logger').init() + +logger.info("Powering up!") +for modem_i, modem in pairs(modems) do + modem.closeAll() + modem.open(1) + -- for i = (128 * (modem_i - 1)),((128 * modem_i) - 1) do + -- modem.open(i) + -- end + logger.success("Modem " .. modem_i .. " opened ports " .. (128 * (modem_i - 1)) .. " - " .. ((128 * modem_i) - 1)) +end + +local event, side, channel, replyChannel, message, distance +while true do + event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + logger.info(channel .. " " .. replyChannel .. " " .. tostring(message) .. " " .. distance ) +end \ No newline at end of file diff --git a/apricot/scripts/receiver.lua b/apricot/scripts/receiver.lua new file mode 100644 index 0000000..5d04258 --- /dev/null +++ b/apricot/scripts/receiver.lua @@ -0,0 +1,12 @@ +local modem = peripheral.find("modem") or error("No modem attached", 0) +local logger = require('logger').init() + +for i = 1,125 do + modem.open(i) +end + +local event, side, channel, replyChannel, message, distance +while true do + event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + logger.info(channel .. " " .. replyChannel .. " " .. tostring(message) .. " " .. distance ) +end \ No newline at end of file diff --git a/apricot/scripts/sender.lua b/apricot/scripts/sender.lua new file mode 100644 index 0000000..51dde7e --- /dev/null +++ b/apricot/scripts/sender.lua @@ -0,0 +1,6 @@ + +local modem = peripheral.find("modem") or error("No modem attached", 0) + +modem.transmit(15, 43, "Hello, world!") +modem.transmit(25, 2, {message=1}) +modem.transmit(1, 1, "What if I put you in an infinite loop?") diff --git a/char_reference.png b/char_reference.png new file mode 100644 index 0000000..391908e Binary files /dev/null and b/char_reference.png differ diff --git a/chest_monitor.lua b/chest_monitor.lua new file mode 100644 index 0000000..cbd384a --- /dev/null +++ b/chest_monitor.lua @@ -0,0 +1,86 @@ +-- There are a lot of chest monitors beyond this one +-- Each branching into countless features of their own +-- But this monitor is being preserved because it's special +-- It's special, because despite being so basic... +-- it was the first practical CC program I ever wrote + +local chests = {peripheral.find("minecraft:chest")} +local data_monitor = peripheral.find("monitor") +local sleep_time = sleep(#(chests)/20) + +local function move_line() + data_monitor.setCursorPos(1, select(2, data_monitor.getCursorPos()) + 1) + if select(2, data_monitor.getCursorPos()) >= select(2, data_monitor.getSize()) then + data_monitor.scroll(1) + data_monitor.setCursorPos(1, select(2, data_monitor.getCursorPos()) - 1) + end +end + +local function get_chest_data(chest) + local count = 0 + local slots = 0 + local calls = {} + local max_storage = 0 + for i, item in pairs(chest.list()) do + table.insert(calls,function() + slots = slots + 1 + count = count + item.count + max_storage = chest.getItemDetail(i).maxCount + max_storage + end) + end + parallel.waitForAll(table.unpack(calls)) + return count, max_storage + (chest.size() - slots) * 64 +end + +local function make_progressbar(size, taken, max) + size = size - 2 -- make space for "[" and "]" + --[[ + taken/max = x/size + taken * size = x * max + x = taken * size / max + ]] + local progress = math.floor(size * taken / max) + local hash = ("#"):rep(progress) + local dots = ("."):rep(size - progress) + return ("[" .. hash .. dots .. "]") +end + +local function write_progressbar(taken, max) + -- Progressbar + local percent = 100 - math.floor(100 * taken/max) + local taken_space = #tostring(percent) + #tostring(taken) + #tostring(max) + 11 -- " taken/free [PROGRESSBAR] percent% free " + local size = select(1, data_monitor.getSize()) - taken_space + + data_monitor.write(" " .. tostring(taken) .. "/" .. tostring(max) .. " " .. make_progressbar(size, taken, max) .. " ".. percent .. "% free") + move_line() +end + +local function write_chest_data(index, taken, max) + local start = " Chest " .. index .. (" "):rep(8 - #tostring(index) - #tostring(taken)) .. tostring(taken) .. "/" .. tostring(max) .. (" "):rep(5 - #tostring(max)) + data_monitor.write(start .. make_progressbar(20, taken, max)) +end + +while true do + local max_storage = 0 + local taken_storage = 0 + local chests_list = {} + + for i, chest in pairs(chests) do + local taken, max = get_chest_data(chest) + + taken_storage = taken_storage + taken + max_storage = max_storage + max + + table.insert(chests_list, {["taken"] = taken, ["max"] = max}) + end + + data_monitor.clear() + data_monitor.setCursorPos(1, 1) + write_progressbar(taken_storage, max_storage) + for i, chest in pairs(chests_list) do + write_chest_data(i, chest["taken"], chest["max"]) + move_line() + end + + sleep(sleep_time) +end \ No newline at end of file diff --git a/dash_old/modules/clock.lua b/dash_old/modules/clock.lua new file mode 100644 index 0000000..ab3d7da --- /dev/null +++ b/dash_old/modules/clock.lua @@ -0,0 +1,140 @@ +local ascii_digits = { + { + " 0000 ", + "00 00", + "00 00", + "00 00", + " 0000 " + }, + { + "1111 ", + " 11 ", + " 11 ", + " 11 ", + "111111" + }, + { + " 2222 ", + "22 22", + " 22 ", + " 22 ", + "222222", + }, + { + " 3333 ", + "33 33", + " 333", + "33 33", + " 3333 ", + }, + { + "44 44", + "44 44", + "444444", + " 44", + " 44" + }, + { + "555555", + "55 ", + "55555 ", + " 55", + "55555" + }, + { + " 6666 ", + "66 ", + "66666 ", + "66 66", + " 6666 " + }, + { + "777777", + " 77 ", + " 77 ", + " 77 ", + "77 " + }, + { + " 8888 ", + "88 88", + " 8888 ", + "88 88", + " 8888 " + }, + { + " 9999 ", + "99 99", + " 99999", + " 99", + " 9999 " + } +} +local seperator = { + " ", + " # ", + " ", + " # ", + " ", +} + +local function draw_time(x, y, monitor) + local date = os.date("%b %d") + local h, m = tostring(os.date("%H")), tostring(os.date("%M")) + local h1, h2 = tonumber(string.sub(h, 1, 1)), tonumber(string.sub(h, 2, 2)) + local m1, m2 = tonumber(string.sub(m, 1, 1)), tonumber(string.sub(m, 2, 2)) + + local s = os.date("%S") + + monitor.setCursorPos(x, y) + monitor.blit(date, "eeef00", "ffffff") + monitor.setCursorPos(x, y + 1) + + for i = 1, 5 do + monitor.setCursorPos(x, select(2, monitor.getCursorPos()) + 1) + monitor.write(ascii_digits[h1 + 1][i] .. " " .. ascii_digits[h2 + 1][i] .. seperator[i] .. ascii_digits[m1 + 1][i] .. " " .. ascii_digits[m2 + 1][i]) + end + end + +local function draw_timezone(x, y, tz_name, utc_offset, tz_monitor) + --[[ + /SYD\ + |22:34| + \_ _/ + ]] + if #tz_name ~= 3 then error("Invalid timezone name (not 3 letters)") end + local utc = os.date("!*t") + local tz_hour = utc.hour + utc_offset + if tz_hour > 23 then + tz_hour = -24 + tz_hour end + + + local day_part = { + ["day"] = "☼", + ["night"] = "☾" + } + local tz_symbol + if (tz_hour > 20 or tz_hour < 7) then tz_symbol = day_part["night"] else tz_symbol = day_part["day"] end + + tz_monitor.setCursorPos(x, y) + tz_monitor.blit(" /" .. tz_name .. "\\", "f04440", "ffffff") + tz_monitor.setCursorPos(x, y + 1) + tz_monitor.write("|" .. tostring(utc.hour + utc_offset) .. ":" .. tostring(utc.min) .. "|") + tz_monitor.setCursorPos(x, y + 2) + tz_monitor.blit(" \\_" .. tz_symbol .. "_/ ", "f00400f", "fffffff") +end + +local clock = {} +clock.new = function (monitor_wrap) + local self = {} + local monitor = monitor_wrap or peripheral.find("monitor") + + function self.update() + draw_time(2, 2, monitor) + draw_timezone(2, 11, "SYD", 10, monitor) + end + + return self +end + +return clock \ No newline at end of file diff --git a/dash_old/modules/rss.lua b/dash_old/modules/rss.lua new file mode 100644 index 0000000..fe4ed50 --- /dev/null +++ b/dash_old/modules/rss.lua @@ -0,0 +1,25 @@ + + +local rss_feed = {} +rss_feed.new = function (monitor_wrap) + local self = {} + + local monitor = monitor_wrap or peripheral.find("monitor") + + local function move_line() + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1) + if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then + monitor.scroll(1) + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1) + end + end + + function self.update() + --monitor.write("[TASS] Joeee biden!") + --move_line() + end + + return self +end + +return rss_feed \ No newline at end of file diff --git a/dash_old/smart_monitor.lua b/dash_old/smart_monitor.lua new file mode 100644 index 0000000..1047a52 --- /dev/null +++ b/dash_old/smart_monitor.lua @@ -0,0 +1,36 @@ +--[[ + Programme for large smart screen in my base + + - RSS feed + - Modular functions + - Time + + Screen is 8 by 4 blocls + 8 + ############# + ############# 4 + ############# + ############# +]] +local monitor_main = peripheral.find("monitor") +local x_max, y_max = monitor_main.getSize() + +local monitor_clock = window.create(monitor_main, 1, 1, x_max / 8 * 3, y_max / 2) +local monitor_rss = window.create(monitor_main, x_max - x_max / 4, 1, x_max, y_max, true) + +local modules = { + require("modules.clock").new(monitor_clock), + require("modules.rss").new(monitor_rss) +} + +while true do + local calls = {} + for i, module in pairs(modules) do + --module.update() + table.insert(calls, module.update()) + end + parallel.waitForAll(table.unpack(calls)) + + sleep(0.05) +end + diff --git a/generator/README.md b/generator/README.md new file mode 100644 index 0000000..51a5516 --- /dev/null +++ b/generator/README.md @@ -0,0 +1,3 @@ +This holds code for the steam generator powering my base on my Minecraft SMP + +It is not designed for compatibility. \ No newline at end of file diff --git a/generator/data.lua b/generator/data.lua new file mode 100644 index 0000000..44a50a7 --- /dev/null +++ b/generator/data.lua @@ -0,0 +1,83 @@ +--[[ +== GENERATOR STATUS ============================= +SU 580000 / 598030 | 96% | 18030 avl +RPM 180 / 256 | 70% +GEN Online + +== ENG ========================================== +ENG1 lvl 9 | watr 100% | powr 80% | size 100% +ENG2 lvl 9 | watr 100% | powr 78% | size 100% +ENG2 lvl 9 | watr 80% | powr 79% | size 100% +LAVA [||||||||| ] 53% | 49mbu + +== BAT ========================================== +BAT1 [||||||||| ] 35% +BAT2 [||||||||||||||| ] 70% +BAT3 [|||||||||||| ] 42% +Total 140 MFE / 270 MFE + +== LOG ========================================== +Time 21:50:52 GMT+3 +21:50:50 energy -3992 FE +21:50:51 energy -539 FE +21:50:52 GEN off -> on +21:50:52 energy +44 FE + +]] + +local RPM_CONTROLLER = peripheral.find("create:speedcontroller") -- TODO: Type +local ENGINES = {peripheral.find("create:engine")} -- TODO: Type +local BATTERIES = {peripheral.find("createcraftsandadditions:accumulator")} -- TODO: Type +local LAVA = peripheral.find("create:fluid_tank") -- TODO: Type + +local GENERATOR_SWITCH = { + set = function (on) + redstone.setOutput("top", on) -- Change side with redstone here! + end +} -- TODO: Type? Redstone???? Side? + +local MONITOR_INFO +local MONITOR_CONTROLS +local monitors = {peripheral.find("monitor")} +for _, monitor in pairs(monitors) do + local x, y = monitor.getSize() + if x < 0 and y < 0 then + MONITOR_CONTROLS = monitor + else + MONITOR_INFO = monitor + end +end + +-- All data, to be updated by relevant functions +local DATA = { + su = { + used = 0, + total = 0 + }, + rpm = 0, + generation = false, + engines = { + + }, + batteries = { + + }, + events = { + + } +} +local function update_data() + +end + +local function change_rpm(rpm) + RPM_CONTROLLER.setTarget(rpm) -- TODO: Real method? +end + +local function set_generation() +end + +local function log(msg) + local current_time = textutils.formatTime(os.time("local"), true) + +end \ No newline at end of file diff --git a/generator/network.lua b/generator/network.lua new file mode 100644 index 0000000..e69de29 diff --git a/generator/startup.lua b/generator/startup.lua new file mode 100644 index 0000000..e69de29 diff --git a/logger.lua b/logger.lua new file mode 100644 index 0000000..401c38b --- /dev/null +++ b/logger.lua @@ -0,0 +1,56 @@ +-- Simple logger for use in any projects + +local logger = {} +logger.new = function(monitor_wrap) + local self = {} + + local monitor = monitor_wrap + local log_colours = { + ['log'] = colours.white, + ['info'] = colours.lightBlue, + ['warn'] = colours.yellow, + ['err'] = colours.red, + ['success'] = colours.lime + } + + local function move_line() + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1) + if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then + monitor.scroll(1) + monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1) + end + end + + local function write_msg(message, msg_colour) + local time_str = os.date('%Y-%b-%d %H:%M:%S') + local msg_formatted = "[" .. time_str .. "] " .. message + + monitor.setTextColour(msg_colour) + monitor.write(msg_formatted) + move_line() + end + + function self.log(msg) + write_msg(msg, log_colours['log']) + end + + function self.info(msg) + write_msg('INFO: ' .. msg, log_colours['info']) + end + + function self.warning(msg) + write_msg('WARNING: ' .. msg, log_colours['warn']) + end + + function self.error(msg) + write_msg('ERROR: ' .. msg, log_colours['err']) + end + + function self.success(msg) + write_msg('SUCCESS: ' .. msg, log_colours['success']) + end + + return self +end + +return logger \ No newline at end of file