]> git.za3k.com Git - za3k.git/commitdiff
Add computercraft folder
authorZachary Vance <za3k@za3k.com>
Sun, 1 Mar 2020 00:08:22 +0000 (16:08 -0800)
committerZachary Vance <za3k@za3k.com>
Sun, 1 Mar 2020 00:08:22 +0000 (16:08 -0800)
computercraft/kv [new file with mode: 0644]

diff --git a/computercraft/kv b/computercraft/kv
new file mode 100644 (file)
index 0000000..2d856b7
--- /dev/null
@@ -0,0 +1,235 @@
+local args = { ... }
+local PROTOCOL = "kv"
+local BSPROTOCOL = "kv-bs"
+local NAMESPACE = "" -- global prefix for protocol, ex: "system2."
+local KEYPREFIX = "" -- local prefix for put/get, ex: "za3k."
+if KEYPREFIX == "" and fs.exists(".kv-prefix") then
+  f = fs.open(".kv-prefix", "r")
+  KEYPREFIX = f.readAll()
+  f.close()
+end
+local SERVER = NAMESPACE .. "kv-server"
+local KVKEY = NAMESPACE .. "kv"
+local REGFILE = ".register"
+local RUNFILE = ".kv-run"
+local shouldRegister = true
+local findModem = function()
+  for _,side in ipairs(rs.getSides()) do
+    if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
+      return side
+    end
+  end
+  print("no modem available")
+end
+
+local printUsage = function()
+  print("kv update")
+  print("  Update the 'kv' program to the lastest version")
+  print("kv get <keyname> [<filename>]")
+  print("  Get a program over wifi")
+  print("kv put [<filename>] <keyname>")
+  print("  Store a program over wifi")
+  print("kv run <keyname> [...]")
+  print("  Immediately run the named program")
+  print("kv host [FILE]")
+  print("  Host a database server.")
+  print("kv host-dump FILE [PREFIX]")
+  print("  (as host) Prints all kv-pairs")
+  print("Edit .kv-prefix to set a global prefix")
+end
+local host = function(dbFile, quineDisable, noImmediate)
+  db = loadDB(dbFile)
+  if db == nil then
+    db = {}
+  else
+    print("loaded "..dbFile)
+  end
+  if not quineDisable then db[KVKEY] = readFile(KVKEY) end
+  local modemSide = findModem()
+  rednet.open(modemSide)
+  rednet.host(PROTOCOL, SERVER)
+  print("Hosting kv database")
+  print("To bootstrap run:")
+  print("  rednet.open(\"back\")")
+  print("  _,b,_=rednet.receive(\""..BSPROTOCOL.."\")")
+  print("  f=fs.open(\""..KVKEY.."\",\"w\")")
+  print("  f.write(b)")
+  print("  f.close()")
+  local dirty = false
+  while true do
+    clientID, message, protocol = rednet.receive(PROTOCOL, 60)
+    if clientID == nil then
+      -- Broadcast a bootstrap periodically
+      if not quineDisable then
+        --print("broadcast bs")
+        local bs = db[KVKEY]
+        rednet.broadcast(bs, BSPROTOCOL)
+      end
+      if dbFile and dirty then
+        print("write "..dbFile)
+        saveDB(dbFile, db)
+        dirty = false
+      end
+    elseif message.action == "get" then
+      print("get "..message.key)
+      local value = db[message.key]
+      response = {
+        ["action"]="getResponse",
+        ["key"]=message.key,
+        ["value"]=value
+      }
+      rednet.send(clientID, response, PROTOCOL)
+    elseif message.action == "put" then
+      print("put "..message.key)
+      db[message.key] = message.value
+      if noImmediate then
+        dirty = true
+      else
+        print("write "..dbFile)
+        saveDB(dbFile, db)
+        dirty = false
+      end
+    end
+  end  
+  rednet.unhost(PROTOCOL, SERVER)
+  rednet.close(modemSide)
+end
+local get = function(k, prefix)
+  local modemSide = findModem()
+  prefix = prefix or KEYPREFIX
+  local key = NAMESPACE .. prefix .. k
+  rednet.open(modemSide)
+  local serverID = rednet.lookup(PROTOCOL, SERVER)
+  local message = {
+    ["action"] = "get",
+    ["key"] = key,
+  }
+  rednet.send(serverID, message, PROTOCOL)
+  local value = nil
+  while true do
+    peerID, message, protocol = rednet.receive(PROTOCOL, 10)
+    if peerID == nil then
+      break
+    elseif peerID == serverID and message.action == "getResponse" and message.key == key then
+      value = message.value
+      break
+    end
+  end
+  rednet.close()
+  return value
+end
+local put = function(k, v, prefix)
+  prefix = prefix or KEYPREFIX
+  local key = NAMESPACE .. prefix .. k
+  local modemSide = findModem()
+  rednet.open(modemSide)
+  local serverID = rednet.lookup(PROTOCOL, SERVER)
+  message = {
+    ["action"] = "put",
+    ["key"] = key,
+    ["value"] = v
+  }
+  rednet.send(serverID, message, PROTOCOL)
+  rednet.close()
+end
+writeFile = function(path, content)
+  local f = fs.open(path, "w")
+  f.write(content)
+  f.close()
+end
+readFile = function(path)
+  local f = fs.open(path, "r")
+  content = f.readAll()
+  f.close()
+  return content
+end
+loadDB = function(path)
+  if path == nil or not fs.exists(path) then
+    return nil
+  else
+    return textutils.unserialise(readFile(path))
+  end
+end
+saveDB = function(path, db)
+  writeFile(path, textutils.serialise(db))
+end
+local run = function(content, ...)
+  writeFile(RUNFILE, content)
+  shell.run(RUNFILE, ...)
+end
+local autoregister = function()
+  if shouldRegister and not fs.exists(REGFILE) then
+    writeFile(REGFILE, "registered: true")
+    local id = os.getComputerID()
+    local label = os.getComputerLabel()
+    put("registration."..id..".label", label)
+    put("registration."..id..".namespace", NAMESPACE)
+  end
+end
+
+if #args < 1 then
+  printUsage(); return
+elseif args[1] == "host" then
+  if #args == 1 then dbpath = nil
+  elseif #args == 2 then dbpath = args[2]
+  else printUsage(); return
+  end
+  host(dbpath)
+elseif args[1] == "get" then
+  autoregister()
+  if #args == 2 then from, to = args[2], args[2]
+  elseif #args == 3 then from, to = args[2], args[3]
+  else printUsage(); return
+  end
+  content = get(from)
+  if content == nil then print("content not found"); return end
+  writeFile(to, content)
+elseif args[1] == "put" then
+  autoregister()
+  if #args == 2 then from, to = args[2], args[2]
+  elseif #args == 3 then from, to = args[2], args[3]
+  else printUsage(); return
+  end
+  if not fs.exists(from) then print("file does not exist"); return end
+  content = readFile(from)
+  put(to, content)    
+elseif args[1] == "run" then
+  autoregister()
+  if #args >= 2 then
+    local from = args[2]
+    table.remove(args, 1)
+    table.remove(args, 1)
+    local runArgs = args
+  else printUsage(); return
+  end
+  content = get(from)
+  if content == nil then print("content not found"); return end
+  run(content, unpack(runArgs))
+elseif args[1] == "update" then
+  autoregister()
+  if #args == 1 then
+    old = readFile(KVKEY)
+    content = get(KVKEY, "")
+    if old == content then
+      print("Already up to date")
+    else
+      print("updating")
+      writeFile(KVKEY, content)
+    end
+  else
+    printUsage(); return
+  end
+elseif args[1] == "host-dump" then
+  if #args == 2 then path, prefix = args[2], nil
+  elseif #args == 3 then path, prefix = args[2], args[3]
+  else printUsage(); return
+  end  
+  db = loadDB(path)
+  if not db then print("DB file not found"); return end
+  for k,v in pairs(db) do
+    if not prefix or (k and string.find(k, prefix, 1, true)==1) then
+      print(k, v)
+    end
+  end 
+else printUsage(); return
+end