== gtRemote == (GUI Turtle Remote)
Description
Here's my take on a turtle remote for ComputerCraft 1.63+ advanced wireless pocket computers. - Turtles running the host program will show up in the remote list automatically when in range and drop of...
Installation
Copy one of these commands into your ComputerCraft terminal:
Pastebin:
pastebin get XQQsRyLX ==_gtremote_==_(gui_turtle_remote)wget:
wget https://pastebin.com/raw/XQQsRyLX ==_gtremote_==_(gui_turtle_remote)Archive:
wget https://cc.shobie.xyz/cc/get/pb-XQQsRyLX ==_gtremote_==_(gui_turtle_remote)
Quick Install:
wget https://cc.shobie.xyz/cc/get/pb-XQQsRyLX == gtRemote == (GUI Turtle Remote)
Usage
Run the program after downloading
Tags
Source
View Original SourceCode Preview
--[[ gtRemote ]]--
--[[ by Dog ]]--
--[[ aka HydrantHunter ]]--
--[[ with assistance ]]--
--[[ from CometWolf ]]--
--[[ pastebin XQQsRyLX ]]--
local gtRver = "2.0.05"
--[[
Tested with/requires:
- Minecraft 1.6.4+
- ComputerCraft 1.63+
- 1 Advanced Wireless Pocket Computer
- gtHost running on one or more Wireless Turtles (standard or advanced)
Special thanks to: Anavrins (pbkdf2/sha256 hashing)
SquidDev (AES encryption/decryption)
Alex Kloss (base64 encoder/decoder)
]]--
--# AUTOMATIC/STATIC CONFIGURATION
--# Default Settings
local termX, termY = term.getSize()
local netSide, runState, tDirection, thisCC = "none", "init", "Forward", tostring(os.getComputerID())
local kernelState, timerDelay, inputting = true, false, false
local netReceive, drawCLI, drawHeader, turtleInventory, turtleControlScreen, userInput, pollTimer, tempState
--# Turtle List
local allTurtles, filteredTurtles = { }, { }
local numPages, pageNum = 1, 1
local fuelFilter, showFuel = false, false
local gtHost, thisTurtle
local colorBurst = {
G = { order = 1, text = "Green", color = colors.green };
S = { order = 2, text = "Sky", color = colors.lightBlue };
B = { order = 3, text = "Blue", color = colors.blue };
P = { order = 4, text = "Purple", color = colors.purple };
R = { order = 5, text = "Red", color = colors.red };
O = { order = 6, text = "Orange", color = colors.orange };
N = { order = 7, text = "Brown", color = colors.brown };
I = { order = 8, text = "Silver", color = colors.lightGray };
}
--# Color Definitions
local white = colors.white
local black = colors.black
local silver = colors.lightGray
local gray = colors.gray
local brown = colors.brown
local yellow = colors.yellow
local orange = colors.orange
local red = colors.red
local magenta = colors.magenta
local purple = colors.purple
local blue = colors.blue
local sky = colors.lightBlue
local cyan = colors.cyan
local lime = colors.lime
local green = colors.green
--# END AUTOMATIC/STATIC CONFIGURATION
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <[email protected]>
-- licensed under the terms of the LGPL2
-- http://lua-users.org/wiki/BaseSixtyFour
-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- encoding
function encode(data)
return ((data:gsub('.', function(x)
local r,b='',x:byte()
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+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return b:sub(c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
-- decoding
function decode(data)
data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(b:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
return string.char(c)
end))
end
-- AES Lua implementation by SquidDev
-- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
local encrypt, decrypt
do
local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
local bit=_W(function(_ENV, ...)
--[[
This bit API is designed to cope with unsigned integers instead of normal integers
To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
These are written in long form because no constant folding.
]]
local floor = math.floor
local lshift, rshift
rshift = function(a,disp)
return floor(a % 4294967296 / 2^disp)
end
lshift = function(a,disp)
return (a * 2^disp) % 4294967296
end
return {
-- bit operations
bnot = bit32 and bit32.bnot or bit.bnot,
band = bit32 and bit32.band or bit.band,
bor = bit32 and bit32.bor or bit.bor,
bxor = bit32 and bit32.bxor or bit.bxor,
rshift = rshift,
lshift = lshift,
}
end)
local gf=_W(function(_ENV, ...)
-- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
local bxor = bit32 and bit32.bxor or bit.bxor
local lshift = bit.lshift
-- private data of gf
local n = 0x100
local ord = 0xff
local irrPolynom = 0x11b
local exp = {}
local log = {}
--
-- add two polynoms (its simply xor)
--
local function add(operand1, operand2)
return bxor(operand1,operand2)
end
--
-- subtract two polynoms (same as addition)
--
local function sub(operand1, operand2)
return bxor(operand1,operand2)
end
--
-- inverts element
-- a^(-1) = g^(order - log(a))
--
local function invert(operand)
-- special case for 1
if (operand == 1) then
return 1
end
-- normal invert
local exponent = ord - log[operand]
return exp[exponent]
end
--
-- multiply two elements using a logarithm table
-- a*b = g^(log(a)+log(b))
--
local function mul(operand1, operand2)
if (operand1 == 0 or operand2 == 0) then
return 0
end
local exponent = log[operand1] + log[operand2]
if (exponent >= ord) then
exponent = exponent - ord
end
return exp[exponent]
end
--
-- divide two elements
-- a/b = g^(log(a)-log(b))
--
local function div(operand1, operand2)
if (operand1 == 0) then
return 0
end
-- TODO: exception if operand2 == 0
local exponent = log[operand1] - log[operand2]
if (exponent < 0) then
exponent = exponent + ord
end
return exp[exponent]
end
--
-- print logarithmic table
--
local function printLog()
for i = 1, n do
print("log(", i-1, ")=", log[i-1])
end
end
--
-- print exponentiation table
--
local function printExp()
for i = 1, n do
print("exp(", i-1, ")=", exp[i-1])
end
end
--
-- calculate logarithmic and exponentiation table
--
local function initMulTable()
local a = 1
for i = 0,ord-1 do
exp[i] = a
log[a] = i
-- multiply with generator x+1 -> left shift + 1
a = bxor(lshift(a, 1), a)
-- if a gets larger than order, reduce modulo irreducible polynom
if a > ord then
a = sub(a, irrPolynom)
end
end
end
initMulTable()
return {
add = add,
sub = sub,
invert = invert,
mul = mul,
div = div,
printLog = printLog,
printExp = printExp,
}
end)
util=_W(function(_ENV, ...)
-- Cache some bit operators
local bxor = bit.bxor
local rshift = bit.rshift
local band = bit.band
local lshift = bit.lshift
local sleepCheckIn
--
-- calculate the parity of one byte
--
local function byteParity(byte)
byte = bxor(byte, rshift(byte, 4))
byte = bxor(byte, rshift(byte, 2))
byte = bxor(byte, rshift(byte, 1))
return band(byte, 1)
end
--
-- get byte at position index
--
local function getByte(number, index)
return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
end
--
-- put number into int at position index
--
local function putByte(number, index)
return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
end
--
-- convert byte array to int array
--
local function bytesToInts(bytes, start, n)
local ints = {}
for i = 0, n - 1 do
ints[i + 1] =
putByte(bytes[start + (i*4)], 3) +
putByte(bytes[start + (i*4) + 1], 2) +
putByte(bytes[start + (i*4) + 2], 1) +
putByte(bytes[start + (i*4) + 3], 0)
if n % 10000 == 0 then sleepCheckIn() end
end
return ints
end
--
-- convert int array to byte array
--
local function intsToBytes(ints, output, outputOffset, n)
n = n or #ints
for i = 0, n - 1 do
for j = 0,3 do
output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
end
if n % 10000 == 0 then sleepCheckIn() end
end
return output
end
--
-- convert bytes to hexString
--
local function bytesToHex(bytes)
local hexBytes = ""
for i,byte in ipairs(bytes) do
hexBytes = hexBytes .. string.format("%02x ", byte)
end
return hexBytes
end
local function hexToBytes(bytes)
local out = {}
for i = 1, #bytes, 2 do
out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
end
return out
end
--
-- convert data to hex string
--
local function toHexString(data)
local type = type(data)
if (type == "number") then
return string.format("%08x",data)
elseif (type == "table") then
return bytesToHex(data)
elseif (type == "string") then
local bytes = {string.byte(data, 1, #data)}
return bytesToHex(bytes)
else
return data
end
end
local function padByteString(data)
local dataLength = #data
local random1 = math.random(0,255)
local random2 = math.random(0,255)
local prefix = string.char(random1,
random2,
random1,
random2,
getByte(dataLength, 3),
getByte(dataLength, 2),
getByte(dataLength, 1),
getByte(dataLength, 0)
)
data = prefix .. data
local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
for i=1,paddingLength do
padding = padding .. string.char(math.random(0,255))
end
return data .. padding
end
local function properlyDecrypted(data)
local random = {string.byte(data,1,4)}
if (random[1] == random[3] and random[2] == random[4]) then
return true
end
return false
end
local function unpadByteString(data)
if (not properlyDecrypted(data)) then
return nil
end
local dataLength = putByte(string.byte(data,5), 3)
+ putByte(string.byte(data,6), 2)
+ putByte(string.byte(data,7), 1)
+ putByte(string.byte(data,8), 0)
return string.sub(data,9,8+dataLength)
end
local function xorIV(data, iv)
for i = 1,16 do
data[i] = bxor(data[i], iv[i])
end
end
local function increment(data)
local i = 16
while true do
local value = data[i] + 1
if value >= 256 then
data[i] = value - 256
i = (i - 2) % 16 + 1
else
data[i] = value
break
end
end
end
-- Called every encryption cycle
local push, pull, time = os.queueEvent, coroutine.yield, os.time
local oldTime = time()
local function sleepCheckIn()
local newTime = time()
if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
oldTime = newTime
push("sleep")
pull("sleep")
end
end
local function getRandomData(bytes)
local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
local result = {}
for i=1,bytes do
insert(result, random(0,255))
if i % 10240 == 0 then sleep() end
end
return result
end
local function getRandomString(bytes)
local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
local result = {}
for i=1,bytes do
insert(result, char(random(0,255)))
if i % 10240 == 0 then sleep() end
end
return table.concat(result)
end
return {
byteParity = byteParity,
getByte = getByte,
putByte = putByte,
bytesToInts = bytesToInts,
intsToBytes = intsToBytes,
bytesToHex = bytesToHex,
hexToBytes = hexToBytes,
toHexString = toHexString,
padByteString = padByteString,
properlyDecrypted = properlyDecrypted,
unpadByteString = unpadByteString,
xorIV = xorIV,
increment = increment,
sleepCheckIn = sleepCheckIn,
getRandomData = getRandomData,
getRandomString = getRandomString,
}
end)
aes=_W(function(_ENV, ...)
-- Implementation of AES with nearly pure lua
-- AES with lua is slow, really slow :-)
local putByte = util.putByte
local getByte = util.getByte
-- some constants
local ROUNDS = 'rounds'
local KEY_TYPE = "type"
local ENCRYPTION_KEY=1
local DECRYPTION_KEY=2
-- aes SBOX
local SBox = {}
local iSBox = {}
-- aes tables
local table0 = {}
local table1 = {}
local table2 = {}
local table3 = {}
local tableInv0 = {}
local tableInv1 = {}
local tableInv2 = {}
local tableInv3 = {}
-- round constants
local rCon = {
0x01000000,
0x02000000,
0x04000000,
0x08000000,
0x10000000,
0x20000000,
0x40000000,
0x80000000,
0x1b000000,
0x36000000,
0x6c000000,
0xd8000000,
0xab000000,
0x4d000000,
0x9a000000,
0x2f000000,
}
--
-- affine transformation for calculating the S-Box of AES
--
local function affinMap(byte)
mask = 0xf8
result = 0
for i = 1,8 do
result = bit.lshift(result,1)
parity = util.byteParity(bit.band(byte,mask))
result = result + parity
-- simulate roll
lastbit = bit.band(mask, 1)
mask = bit.band(bit.rshift(mask, 1),0xff)
mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f)
end
return bit.bxor(result, 0x63)
end
--
-- calculate S-Box and inverse S-Box of AES
-- apply affine transformation to inverse in finite field 2^8
--
local function calcSBox()
for i = 0, 255 do
inverse = i ~= 0 and gf.invert(i) or i
mapped = affinMap(inverse)
SBox[i] = mapped
iSBox[mapped] = i
end
end
--
-- Calculate round tables
-- round tables are used to calculate shiftRow, MixColumn and SubBytes
-- with 4 table lookups and 4 xor operations.
--
local function calcRoundTables()
for x = 0,255 do
byte = SBox[x]
table0[x] = putByte(gf.mul(0x03, byte), 0)
+ putByte( byte , 1)
+ putByte( byte , 2)
+ putByte(gf.mul(0x02, byte), 3)
table1[x] = putByte( byte , 0)
+ putByte( byte , 1)
+ putByte(gf.mul(0x02, byte), 2)
+ putByte(gf.mul(0x03, byte), 3)
table2[x] = putByte( byte , 0)
+ putByte(gf.mul(0x02, byte), 1)
+ putByte(gf.mul(0x03, byte), 2)
+ putByte( byte , 3)
table3[x] = putByte(gf.mul(0x02, byte), 0)
+ putByte(gf.mul(0x03, byte), 1)
+ putByte( byte , 2)
+ putByte( byte , 3)
end
end
--
-- Calculate inverse round tables
-- does the inverse of the normal roundtables for the equivalent
-- decryption algorithm.
--
local function calcInvRoundTables()
for x = 0,255 do
byte = iSBox[x]
tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
+ putByte(gf.mul(0x0d, byte), 1)
+ putByte(gf.mul(0x09, byte), 2)
+ putByte(gf.mul(0x0e, byte), 3)
tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
+ putByte(gf.mul(0x09, byte), 1)
+ putByte(gf.mul(0x0e, byte), 2)
+ putByte(gf.mul(0x0b, byte), 3)
tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
+ putByte(gf.mul(0x0e, byte), 1)
+ putByte(gf.mul(0x0b, byte), 2)
+ putByte(gf.mul(0x0d, byte), 3)
tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
+ putByte(gf.mul(0x0b, byte), 1)
+ putByte(gf.mul(0x0d, byte), 2)
+ putByte(gf.mul(0x09, byte), 3)
end
end
--
-- rotate word: 0xaabbccdd gets 0xbbccddaa
-- used for key schedule
--
local function rotWord(word)
local tmp = bit.band(word,0xff000000)
return (bit.lshift(word,8) + bit.rshift(tmp,24))
end
--
-- replace all bytes in a word with the SBox.
-- used for key schedule
--
local function subWord(word)
return putByte(SBox[getByte(word,0)],0)
+ putByte(SBox[getByte(word,1)],1)
+ putByte(SBox[getByte(word,2)],2)
+ putByte(SBox[getByte(word,3)],3)
end
--
-- generate key schedule for aes encryption
--
-- returns table with all round keys and
-- the necessary number of rounds saved in [ROUNDS]
--
local function expandEncryptionKey(key)
local keySchedule = {}
local keyWords = math.floor(#key / 4)
if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
error("Invalid key size: " .. tostring(keyWords))
return nil
end
keySchedule[ROUNDS] = keyWords + 6
keySchedule[KEY_TYPE] = ENCRYPTION_KEY
for i = 0,keyWords - 1 do
keySchedule[i] = putByte(key[i*4+1], 3)
+ putByte(key[i*4+2], 2)
+ putByte(key[i*4+3], 1)
+ putByte(key[i*4+4], 0)
end
for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
local tmp = keySchedule[i-1]
if ( i % keyWords == 0) then
tmp = rotWord(tmp)
tmp = subWord(tmp)
local index = math.floor(i/keyWords)
tmp = bit.bxor(tmp,rCon[index])
elseif (keyWords > 6 and i % keyWords == 4) then
tmp = subWord(tmp)
end
keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
end
return keySchedule
end
--
-- Inverse mix column
-- used for key schedule of decryption key
--
local function invMixColumnOld(word)
local b0 = getByte(word,3)
local b1 = getByte(word,2)
local b2 = getByte(word,1)
local b3 = getByte(word,0)
return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
gf.mul(0x0d, b2)),
gf.mul(0x09, b3)),
gf.mul(0x0e, b0)),3)
+ putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
gf.mul(0x0d, b3)),
gf.mul(0x09, b0)),
gf.mul(0x0e, b1)),2)
+ putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
gf.mul(0x0d, b0)),
gf.mul(0x09, b1)),
gf.mul(0x0e, b2)),1)
+ putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
gf.mul(0x0d, b1)),
gf.mul(0x09, b2)),
gf.mul(0x0e, b3)),0)
end
--
-- Optimized inverse mix column
-- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
-- TODO: make it work
--
local function invMixColumn(word)
local b0 = getByte(word,3)
local b1 = getByte(word,2)
local b2 = getByte(word,1)
local b3 = getByte(word,0)
local t = bit.bxor(b3,b2)
local u = bit.bxor(b1,b0)
local v = bit.bxor(t,u)
v = bit.bxor(v,gf.mul(0x08,v))
w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
+ putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t )), 1)
+ putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
+ putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u )), 3)
end
--
-- generate key schedule for aes decryption
--
-- uses key schedule for aes encryption and transforms each
-- key by inverse mix column.
--
local function expandDecryptionKey(key)
local keySchedule = expandEncryptionKey(key)
if (keySchedule == nil) then
return nil
end
keySchedule[KEY_TYPE] = DECRYPTION_KEY
for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
keySchedule[i] = invMixColumnOld(keySchedule[i])
end
return keySchedule
end
--
-- xor round key to state
--
local function addRoundKey(state, key, round)
for i = 0, 3 do
state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
end
end
--
-- do encryption round (ShiftRow, SubBytes, MixColumn together)
--
local function doRound(origState, dstState)
dstState[1] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[1],3)],
table1[getByte(origState[2],2)]),
table2[getByte(origState[3],1)]),
table3[getByte(origState[4],0)])
dstState[2] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[2],3)],
table1[getByte(origState[3],2)]),
table2[getByte(origState[4],1)]),
table3[getByte(origState[1],0)])
dstState[3] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[3],3)],
table1[getByte(origState[4],2)]),
table2[getByte(origState[1],1)]),
table3[getByte(origState[2],0)])
dstState[4] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[4],3)],
table1[getByte(origState[1],2)]),
table2[getByte(origState[2],1)]),
table3[getByte(origState[3],0)])
end
--
-- do last encryption round (ShiftRow and SubBytes)
--
local function doLastRound(origState, dstState)
dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
+ putByte(SBox[getByte(origState[2],2)], 2)
+ putByte(SBox[getByte(origState[3],1)], 1)
+ putByte(SBox[getByte(origState[4],0)], 0)
dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
+ putByte(SBox[getByte(origState[3],2)], 2)
+ putByte(SBox[getByte(origState[4],1)], 1)
+ putByte(SBox[getByte(origState[1],0)], 0)
dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
+ putByte(SBox[getByte(origState[4],2)], 2)
+ putByte(SBox[getByte(origState[1],1)], 1)
+ putByte(SBox[getByte(origState[2],0)], 0)
dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
+ putByte(SBox[getByte(origState[1],2)], 2)
+ putByte(SBox[getByte(origState[2],1)], 1)
+ putByte(SBox[getByte(origState[3],0)], 0)
end
--
-- do decryption round
--
local function doInvRound(origState, dstState)
dstState[1] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[1],3)],
tableInv1[getByte(origState[4],2)]),
tableInv2[getByte(origState[3],1)]),
tableInv3[getByte(origState[2],0)])
dstState[2] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[2],3)],
tableInv1[getByte(origState[1],2)]),
tableInv2[getByte(origState[4],1)]),
tableInv3[getByte(origState[3],0)])
dstState[3] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[3],3)],
tableInv1[getByte(origState[2],2)]),
tableInv2[getByte(origState[1],1)]),
tableInv3[getByte(origState[4],0)])
dstState[4] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[4],3)],
tableInv1[getByte(origState[3],2)]),
tableInv2[getByte(origState[2],1)]),
tableInv3[getByte(origState[1],0)])
end
--
-- do last decryption round
--
local function doInvLastRound(origState, dstState)
dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
+ putByte(iSBox[getByte(origState[4],2)], 2)
+ putByte(iSBox[getByte(origState[3],1)], 1)
+ putByte(iSBox[getByte(origState[2],0)], 0)
dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
+ putByte(iSBox[getByte(origState[1],2)], 2)
+ putByte(iSBox[getByte(origState[4],1)], 1)
+ putByte(iSBox[getByte(origState[3],0)], 0)
dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
+ putByte(iSBox[getByte(origState[2],2)], 2)
+ putByte(iSBox[getByte(origState[1],1)], 1)
+ putByte(iSBox[getByte(origState[4],0)], 0)
dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
+ putByte(iSBox[getByte(origState[3],2)], 2)
+ putByte(iSBox[getByte(origState[2],1)], 1)
+ putByte(iSBox[getByte(origState[1],0)], 0)
end
--
-- encrypts 16 Bytes
-- key encryption key schedule
-- input array with input data
-- inputOffset start index for input
-- output array for encrypted data
-- outputOffset start index for output
--
local function encrypt(key, input, inputOffset, output, outputOffset)
--default parameters
inputOffset = inputOffset or 1
output = output or {}
outputOffset = outputOffset or 1
local state = {}
local tmpState = {}
if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
return
end
state = util.bytesToInts(input, inputOffset, 4)
addRoundKey(state, key, 0)
local round = 1
while (round < key[ROUNDS] - 1) do
-- do a double round to save temporary assignments
doRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round + 1
doRound(tmpState, state)
addRoundKey(state, key, round)
round = round + 1
end
doRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round +1
doLastRound(tmpState, state)
addRoundKey(state, key, round)
util.sleepCheckIn()
return util.intsToBytes(state, output, outputOffset)
end
--
-- decrypt 16 bytes
-- key decryption key schedule
-- input array with input data
-- inputOffset start index for input
-- output array for decrypted data
-- outputOffset start index for output
---
local function decrypt(key, input, inputOffset, output, outputOffset)
-- default arguments
inputOffset = inputOffset or 1
output = output or {}
outputOffset = outputOffset or 1
local state = {}
local tmpState = {}
if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
error("No decryption key: " .. tostring(key[KEY_TYPE]))
return
end
state = util.bytesToInts(input, inputOffset, 4)
addRoundKey(state, key, key[ROUNDS])
local round = key[ROUNDS] - 1
while (round > 2) do
-- do a double round to save temporary assignments
doInvRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round - 1
doInvRound(tmpState, state)
addRoundKey(state, key, round)
round = round - 1
end
doInvRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round - 1
doInvLastRound(tmpState, state)
addRoundKey(state, key, round)
util.sleepCheckIn()
return util.intsToBytes(state, output, outputOffset)
end
-- calculate all tables when loading this file
calcSBox()
calcRoundTables()
calcInvRoundTables()
return {
ROUNDS = ROUNDS,
KEY_TYPE = KEY_TYPE,
ENCRYPTION_KEY = ENCRYPTION_KEY,
DECRYPTION_KEY = DECRYPTION_KEY,
expandEncryptionKey = expandEncryptionKey,
expandDecryptionKey = expandDecryptionKey,
encrypt = encrypt,
decrypt = decrypt,
}
end)
local buffer=_W(function(_ENV, ...)
local function new ()
return {}
end
local function addString (stack, s)
table.insert(stack, s)
end
local function toString (stack)
return table.concat(stack)
end
return {
new = new,
addString = addString,
toString = toString,
}
end)
ciphermode=_W(function(_ENV, ...)
local public = {}
--
-- Encrypt strings
-- key - byte array with key
-- string - string to encrypt
-- modefunction - function for cipher mode to use
--
local random, unpack = math.random, unpack or table.unpack
function public.encryptString(key, data, modeFunction, iv)
if iv then
local ivCopy = {}
for i = 1, 16 do ivCopy[i] = iv[i] end
iv = ivCopy
else
iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
end
local keySched = aes.expandEncryptionKey(key)
local encryptedData = buffer.new()
for i = 1, #data/16 do
local offset = (i-1)*16 + 1
local byteData = {string.byte(data,offset,offset +15)}
iv = modeFunction(keySched, byteData, iv)
buffer.addString(encryptedData, string.char(unpack(byteData)))
end
return buffer.toString(encryptedData)
end
--
-- the following 4 functions can be used as
-- modefunction for encryptString
--
-- Electronic code book mode encrypt function
function public.encryptECB(keySched, byteData, iv)
aes.encrypt(keySched, byteData, 1, byteData, 1)
end
-- Cipher block chaining mode encrypt function
function public.encryptCBC(keySched, byteData, iv)
util.xorIV(byteData, iv)
aes.encrypt(keySched, byteData, 1, byteData, 1)
return byteData
end
-- Output feedback mode encrypt function
function public.encryptOFB(keySched, byteData, iv)
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return iv
end
-- Cipher feedback mode encrypt function
function public.encryptCFB(keySched, byteData, iv)
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return byteData
end
function public.encryptCTR(keySched, byteData, iv)
local nextIV = {}
for j = 1, 16 do nextIV[j] = iv[j] end
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
util.increment(nextIV)
return nextIV
end
--
-- Decrypt strings
-- key - byte array with key
-- string - string to decrypt
-- modefunction - function for cipher mode to use
--
function public.decryptString(key, data, modeFunction, iv)
if iv then
local ivCopy = {}
for i = 1, 16 do ivCopy[i] = iv[i] end
iv = ivCopy
else
iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
end
local keySched
if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
keySched = aes.expandEncryptionKey(key)
else
keySched = aes.expandDecryptionKey(key)
end
local decryptedData = buffer.new()
for i = 1, #data/16 do
local offset = (i-1)*16 + 1
local byteData = {string.byte(data,offset,offset +15)}
iv = modeFunction(keySched, byteData, iv)
buffer.addString(decryptedData, string.char(unpack(byteData)))
end
return buffer.toString(decryptedData)
end
--
-- the following 4 functions can be used as
-- modefunction for decryptString
--
-- Electronic code book mode decrypt function
function public.decryptECB(keySched, byteData, iv)
aes.decrypt(keySched, byteData, 1, byteData, 1)
return iv
end
-- Cipher block chaining mode decrypt function
function public.decryptCBC(keySched, byteData, iv)
local nextIV = {}
for j = 1, 16 do nextIV[j] = byteData[j] end
aes.decrypt(keySched, byteData, 1, byteData, 1)
util.xorIV(byteData, iv)
return nextIV
end
-- Output feedback mode decrypt function
function public.decryptOFB(keySched, byteData, iv)
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return iv
end
-- Cipher feedback mode decrypt function
function public.decryptCFB(keySched, byteData, iv)
local nextIV = {}
for j = 1, 16 do nextIV[j] = byteData[j] end
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return nextIV
end
public.decryptCTR = public.encryptCTR
return public
end)
-- Simple API for encrypting strings.
--
AES128 = 16
AES192 = 24
AES256 = 32
ECBMODE = 1
CBCMODE = 2
OFBMODE = 3
CFBMODE = 4
CTRMODE = 4
local function pwToKey(password, keyLength, iv)
local padLength = keyLength
if (keyLength == AES192) then
padLength = 32
end
if (padLength > #password) then
local postfix = ""
for i = 1,padLength - #password do
postfix = postfix .. string.char(0)
end
password = password .. postfix
else
password = string.sub(password, 1, padLength)
end
local pwBytes = {string.byte(password,1,#password)}
password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
password = string.sub(password, 1, keyLength)
return {string.byte(password,1,#password)}
end
--
-- Encrypts string data with password password.
-- password - the encryption key is generated from this string
-- data - string to encrypt (must not be too large)
-- keyLength - length of aes key: 128(default), 192 or 256 Bit
-- mode - mode of encryption: ecb, cbc(default), ofb, cfb
--
-- mode and keyLength must be the same for encryption and decryption.
--
function encrypt(password, data, keyLength, mode, iv)
assert(password ~= nil, "Empty password.")
assert(data ~= nil, "Empty data.")
local mode = mode or CBCMODE
local keyLength = keyLength or AES128
local key = pwToKey(password, keyLength, iv)
local paddedData = util.padByteString(data)
if mode == ECBMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
elseif mode == CBCMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
elseif mode == OFBMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
elseif mode == CFBMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
elseif mode == CTRMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
else
error("Unknown mode", 2)
end
end
--
-- Decrypts string data with password password.
-- password - the decryption key is generated from this string
-- data - string to encrypt
-- keyLength - length of aes key: 128(default), 192 or 256 Bit
-- mode - mode of decryption: ecb, cbc(default), ofb, cfb
--
-- mode and keyLength must be the same for encryption and decryption.
--
function decrypt(password, data, keyLength, mode, iv)
local mode = mode or CBCMODE
local keyLength = keyLength or AES128
local key = pwToKey(password, keyLength, iv)
local plain
if mode == ECBMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
elseif mode == CBCMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
elseif mode == OFBMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
elseif mode == CFBMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
elseif mode == CTRMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
else
error("Unknown mode", 2)
end
result = util.unpadByteString(plain)
if (result == nil) then
return nil
end
return result
end
end
-- SHA-256, HMAC and PBKDF2 functions in ComputerCraft
-- By Anavrins
-- For help and details, you can PM me on the CC forums
-- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins
-- Pastebin: http://pastebin.com/6UV4qfNF
-- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
local digest, hmac, pbkdf2
do
local mod32 = 2^32
local sha_hashlen = 32
local sha_blocksize = 64
local band = bit32 and bit32.band or bit.band
local bnot = bit32 and bit32.bnot or bit.bnot
local bxor = bit32 and bit32.bxor or bit.bxor
local blshift = bit32 and bit32.lshift or bit.blshift
local upack = unpack
local function rrotate(n, b)
local s = n/(2^b)
local f = s%1
return (s-f) + f*mod32
end
local function brshift(int, by) -- Thanks bit32 for bad rshift
local s = int / (2^by)
return s - s%1
end
local H = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
}
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 counter(incr)
local t1, t2 = 0, 0
if 0xFFFFFFFF - t1 < incr then
t2 = t2 + 1
t1 = incr - (0xFFFFFFFF - t1) - 1
else t1 = t1 + incr
end
return t2, t1
end
local function BE_toInt(bs, i)
return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0)
end
local function preprocess(data)
local len = #data
local proc = {}
data[#data+1] = 0x80
while #data%64~=56 do data[#data+1] = 0 end
local blocks = math.ceil(#data/64)
for i = 1, blocks do
proc[i] = {}
for j = 1, 16 do
proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4))
end
end
proc[blocks][15], proc[blocks][16] = counter(len*8)
return proc
end
local function digestblock(w, C)
for j = 17, 64 do
local v = w[j-15]
local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3))
local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10))
w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32
end
local a, b, c, d, e, f, g, h = upack(C)
for j = 1, 64 do
local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25))
local ch = bxor(band(e, f), band(bnot(e), g))
local temp1 = (h + S1 + ch + K[j] + w[j])%mod32
local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22))
local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c))
local temp2 = (S0 + maj)%mod32
h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32
end
C[1] = (C[1] + a)%mod32
C[2] = (C[2] + b)%mod32
C[3] = (C[3] + c)%mod32
C[4] = (C[4] + d)%mod32
C[5] = (C[5] + e)%mod32
C[6] = (C[6] + f)%mod32
C[7] = (C[7] + g)%mod32
C[8] = (C[8] + h)%mod32
return C
end
local mt = {
__tostring = function(a) return string.char(unpack(a)) end,
__index = {
toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end,
isEqual = function(self, t)
if type(t) ~= "table" then return false end
if #self ~= #t then return false end
local ret = 0
for i = 1, #self do
ret = bit32.bor(ret, bxor(self[i], t[i]))
end
return ret == 0
end
}
}
local function toBytes(t, n)
local b = {}
for i = 1, n do
b[(i-1)*4+1] = band(brshift(band(t[i], 0xFF000000), 24), 0xFF)
b[(i-1)*4+2] = band(brshift(band(t[i], 0xFF0000), 16), 0xFF)
b[(i-1)*4+3] = band(brshift(band(t[i], 0xFF00), 8), 0xFF)
b[(i-1)*4+4] = band(t[i], 0xFF)
end
return setmetatable(b, mt)
end
digest = function(data)
data = data or ""
data = type(data) == "string" and {data:byte(1,-1)} or data
data = preprocess(data)
local C = {upack(H)}
for i = 1, #data do C = digestblock(data[i], C) end
return toBytes(C, 8)
end
hmac = function(data, key)
local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)}
local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)}
local blocksize = sha_blocksize
key = #key > blocksize and digest(key) or key
local ipad = {}
local opad = {}
local padded_key = {}
for i = 1, blocksize do
ipad[i] = bxor(0x36, key[i] or 0)
opad[i] = bxor(0x5C, key[i] or 0)
end
for i = 1, #data do
ipad[blocksize+i] = data[i]
end
ipad = digest(ipad)
for i = 1, blocksize do
padded_key[i] = opad[i]
padded_key[blocksize+i] = ipad[i]
end
return digest(padded_key)
end
pbkdf2 = function(pass, salt, iter, dklen)
local out = {}
local hashlen = sha_hashlen
local block = 1
dklen = dklen or 32
while dklen > 0 do
local ikey = {}
local isalt = type(salt) == "table" and {upack(salt)} or {tostring(salt):byte(1,-1)}
local clen = dklen > hashlen and hashlen or dklen
local iCount = #isalt
isalt[iCount+1] = band(brshift(band(block, 0xFF000000), 24), 0xFF)
isalt[iCount+2] = band(brshift(band(block, 0xFF0000), 16), 0xFF)
isalt[iCount+3] = band(brshift(band(block, 0xFF00), 8), 0xFF)
isalt[iCount+4] = band(block, 0xFF)
for j = 1, iter do
isalt = hmac(isalt, pass)
for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end
if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
end
dklen = dklen - clen
block = block+1
for k = 1, clen do out[#out+1] = ikey[k] end
end
return setmetatable(out, mt)
end
end
local function filterTurtleList()
for i = #filteredTurtles, 1, -1 do
filteredTurtles[i] = nil
end
local turtleCount = 0
for i = 1, #allTurtles do
if (type(fuelFilter) == "boolean" and fuelFilter and allTurtles[i].fuelState) or (not fuelFilter) or (type(fuelFilter) == "string" and not allTurtles[i].fuelState) then
turtleCount = turtleCount + 1
filteredTurtles[turtleCount] = { }
for k, v in pairs(allTurtles[i]) do
filteredTurtles[turtleCount][k] = v
end
filteredTurtles[turtleCount].atList = i
end
end
numPages = math.max(1, math.ceil(#filteredTurtles / 24))
pageNum = math.min(pageNum, numPages)
end
do
local function updateTurtleList(newSettings)
local turtleCount = #allTurtles
if turtleCount == 0 then --# the list is empty
allTurtles[1] = { }
for k, v in pairs(newSettings) do --# add this turtle
allTurtles[1][k] = v
end
else
local nsName, filterCount, done = newSettings.name, #filteredTurtles, false
for i = 1, turtleCount do
if gtHost == allTurtles[i].cc then --# we're already on the list
for k, v in pairs(newSettings) do --# update entry with current state of host
allTurtles[i][k] = v
end
for j = 1, filterCount do
if filteredTurtles[j].atList == i then
for k, v in pairs(newSettings) do
filteredTurtles[j][k] = v
end
done = true
break
end
if done then break end
end
return
end
end
for i = 1, turtleCount do
if nsName < allTurtles[i].name then --# alphabetize
table.insert(allTurtles, i, newSettings) --# insert
break
end
if i == turtleCount then --# we've reached the end of the list
turtleCount = turtleCount + 1
allTurtles[turtleCount] = { } --# tack it on
for k, v in pairs(newSettings) do
allTurtles[turtleCount][k] = v
end
break
end
end
end
filterTurtleList()
end
netReceive = function()
local id, success, encKey, message, encryptedMessage, decryptedMessage, decodedMessage
while true do
if not rednet.isOpen(netSide) then rednet.open(netSide) end
id, encryptedMessage = rednet.receive("gtRemote")
if type(encryptedMessage) == "string" then
success, decodedMessage = pcall(decode, encryptedMessage)
if success and type(decodedMessage) == "string" then
encKey = thisCC .. "gt!Remote" .. tostring(id)
success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
if success and type(decryptedMessage) == "string" then
success, message = pcall(textutils.unserialize, decryptedMessage)
if success and type(message) == "table" and message.program and message.program == "gtRemoteHost" then
gtHost = id
updateTurtleList(message)
if (((runState == "Inventory" or runState == "Remote") and gtHost == allTurtles[thisTurtle].cc) or runState == "List") and not inputting then
if runState == "Inventory" then
drawHeader()
turtleInventory()
elseif runState == "Remote" then
turtleControlScreen()
else
drawCLI()
end
end
end
end
end
end
end
end
end
local function netSend(targetHost, cmd1, act1)
local dataPack = textutils.serialize({ program = "gtRemote", cc = tonumber(thisCC), cmd = cmd1, action = act1 })
if not rednet.isOpen(netSide) then rednet.open(netSide) end
if targetHost == "ALL" then
local encKey = thisCC .. "gt!Remote_Broadcast" .. thisCC
local encryptedData = encode(encrypt(encKey, dataPack))
rednet.broadcast(encryptedData, "gtRemote")
else
local encKey = tostring(targetHost) .. "gt!Remote" .. thisCC
local encryptedData = encode(encrypt(encKey, dataPack))
rednet.send(targetHost, encryptedData, "gtRemote")
end
end
local function assignColor(assignment)
return colorBurst[assignment].color or silver
end
local function drawElement(x, y, w, h, txtColor, bgColor, text)
if type(w) == "boolean" then
term.setCursorPos(x, y)
term.setBackgroundColor(w and green or gray)
term.write(" ")
term.setBackgroundColor(w and gray or red)
term.write(" ")
else
text = text and tostring(text) or ""
local txtLen = #text
w = math.max(w, txtLen)
local spacer = (w - txtLen) / 2
local txtLine = string.rep(" ", math.floor(spacer)) .. text .. string.rep(" ", math.ceil(spacer))
if txtColor then term.setTextColor(txtColor) end
if bgColor then term.setBackgroundColor(bgColor) end
if h == 1 then
term.setCursorPos(x, y)
term.write(txtLine)
else
local line, textRow = string.rep(" ", w), y + math.floor(h / 2)
for i = y, y + h - 1 do
term.setCursorPos(x, i)
term.write(i == textRow and txtLine or line) --# Draw one line of the 'element' (box/rectangle/line-seg)
end
end
end
end
drawHeader = function()
local hColor = (runState == "Remote" or runState == "Inventory" or runState == "color") and assignColor(allTurtles[thisTurtle].color) or blue
local hText = runState == "Help" and "gtRemote " .. gtRver or ((runState == "Remote" or runState == "Inventory" or runState == "color") and allTurtles[thisTurtle].name .. " (cc# " .. tostring(allTurtles[thisTurtle].cc) .. ")" or "gtRemote")
drawElement(1, 1, termX, 1, white, hColor, hText) --# title bar
if runState == "List" or runState == "goPage" then
drawElement(1, 1, 3, 1, nil, nil, "[ ]")
local fColor = fuelFilter and green or gray
drawElement(2, 1, 1, 1, type(fuelFilter) == "boolean" and fColor or orange, nil, "F")
drawElement(termX - 2, 1, 3, 1, white, nil, "[ ]")
drawElement(termX - 1, 1, 1, 1, red, nil, "X")
drawElement(1, 2, 1, 1, green, gray, string.rep(" ", math.floor(termX / 4) - 2) .. "All ON" .. string.rep(" ", math.ceil(termX / 4) - 2))
drawElement(math.ceil(termX / 2), 2, 1, 1, red, nil, string.rep(" ", math.floor(termX / 4) - 2) .. "All OFF" .. string.rep(" ", math.ceil(termX / 4) - 2))
elseif runState == "Remote" or runState == "Inventory" or runState == "color" or runState == "Help" then
drawElement(1, 2, termX, 1, red, gray, "Close")
end
end
local function drawColorList(rating)
local color
drawElement(8, 4, 9, 8, nil, gray) --# menu body
for k, v in pairs(colorBurst) do
if rating == k then
color = v.color
drawElement(16, v.order + 3, 1, 1, nil, color, " ") --# selected color pip
else
color = silver
end
drawElement(7, v.order + 3, 1, 1, nil, v.color, " ") --# rating color pips
if rating == "I" and k == "I" then color = white end
drawElement(9, v.order + 3, 1, 1, color, gray, v.text)
end
end
turtleInventory = function()
local currentSlot, selectedSlot, slotContents, isFuel, sCount, txtColor, bgColor = 0, allTurtles[thisTurtle].slot, allTurtles[thisTurtle].contents
for y = 7, 13, 2 do --# y position
for x = 3, 12, 3 do --# x position
currentSlot = currentSlot + 1 --# increment inventory slot
isFuel = allTurtles[thisTurtle].inv[currentSlot].isFuel
sCount = allTurtles[thisTurtle].inv[currentSlot].count
txtColor = currentSlot == selectedSlot and black or (sCount > 0 and sky or silver)
txtColor = (currentSlot ~= selectedSlot and isFuel) and yellow or txtColor
bgColor = currentSlot == selectedSlot and (isFuel and yellow or (sCount > 0 and sky or silver)) or black
drawElement(x, y, 2, 1, txtColor, bgColor, tostring(sCount)) --# write inventory count
end
end
drawElement(8, 16, 1, 1, allTurtles[thisTurtle].inv[selectedSlot].isFuel and yellow or (allTurtles[thisTurtle].inv[selectedSlot].count > 0 and sky or silver), black, slotContents .. string.rep(" ", 18 - #slotContents))
drawElement(13, 17, 1, 1, silver, nil, tDirection .. " ")
end
local function staticTurtleInventory()
drawHeader()
drawElement(2, 4, termX - 2, 1, silver, black, allTurtles[thisTurtle].name .. "'s Inventory")
drawElement(2, 6, 13, 9, nil, gray) --# Inventory 'box'
drawElement(2, 16, 1, 1, gray, black, "Slot:")
drawElement(2, 17, 1, 1, nil, nil, "Direction:")
drawElement(16, 7, 9, 1, black, yellow, " Refuel")
drawElement(16, 9, 9, 1, nil, green, "Place")
drawElement(16, 11, 9, 1, nil, red, "Break")
drawElement(16, 13, 9, 1, nil, silver, "Equip")
drawElement(math.floor(termX / 2) - 9, termY - 1, 1, 1, white, brown, " Refresh Inventory ")
turtleInventory()
end
turtleControlScreen = function()
runState = "Remote"
drawHeader()
if thisTurtle <= #allTurtles then
drawElement(2, 4, 1, 1, assignColor(allTurtles[thisTurtle].color), black, allTurtles[thisTurtle].name) --# Name
drawElement(12, 4, 1, 1, red, black, "Redstone") --# Redstone Output
drawElement(22, 4, allTurtles[thisTurtle].redstone) --# Redstone Output Switch
drawElement(2, 6, 1, 1, silver, black, allTurtles[thisTurtle].note) --# Short description
local dCheck, tFwd, tUp, tDown = allTurtles[thisTurtle].dirCheck, allTurtles[thisTurtle].forward, allTurtles[thisTurtle].up, allTurtles[thisTurtle].down
local bColor = (tFwd and dCheck) and red or black --# Direction buttons
drawElement(5, 8, 2, 1, bColor, white, "/\\") --# Forward
drawElement(5, 9, 2, 1) --#
drawElement(5, 12, 2, 2, black, nil, "\\/") --# Back
drawElement(3, 10, 2, 1, nil, nil, "/ ") --# Left
drawElement(3, 11, 2, 1, nil, nil, "\\ ") --#
drawElement(7, 10, 2, 1, nil, nil, " \\") --# Right
drawElement(7, 11, 2, 1, nil, nil, " /") --#
drawElement(5, 10, 2, 1, white, black, "..") --# Center
drawElement(5, 11, 2, 1, nil, nil, "..") --#
bColor = (tUp and dCheck) and red or black
drawElement(3, 8, 2, 1, bColor, gray, "/\\") --# Up
drawElement(3, 9, 2, 1) --#
bColor = (tDown and dCheck) and red or black
drawElement(3, 12, 2, 2, bColor, gray, "\\/") --# Down
if dCheck then
if (tDirection == "Up" and tUp) or (tDirection == "Down" and tDown) or (tDirection == "Forward" and tFwd) then
drawElement(7, 8, 2, 1, black, gray, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, red, "BR")
else
drawElement(7, 8, 2, 1, black, green, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, gray, "BR")
end
else
drawElement(7, 8, 2, 1, black, gray, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, nil, "BR")
end
drawElement(7, 12, 2, 1, black, dCheck and gray or red, "OB")
drawElement(7, 13, 2, 1, nil, dCheck and green or gray, "CK")
drawElement(12, 8, 1, 1, gray, black, "Fuel") --# Fuel Gauge
local fuelPercent = allTurtles[thisTurtle].fuelPercent
local fuelColor = fuelPercent > 49 and green or orange
drawElement(17, 8, 1, 1, fuelPercent < 10 and red or fuelColor, nil, fuelPercent < 101 and tostring(fuelPercent) or "Unlimited")
if fuelPercent < 101 then
term.setTextColor(gray)
term.write("% ")
drawElement(12, 9, 1, 1, nil, nil, showFuel and "Units " or "Coal ")
term.setTextColor(silver)
term.write(showFuel and (tostring(allTurtles[thisTurtle].fuelAmount) .. " ") or (tostring(allTurtles[thisTurtle].coal) .. " "))
end
drawElement(12, 11, 1, 1, gray, nil, "PLBR") --# Place/Break direction
drawElement(17, 11, 1, 1, silver, nil, tDirection .. " ")
drawElement(12, 12, 1, 1, gray, nil, "Slot") --# Slot selection
local selectedSlot = allTurtles[thisTurtle].slot
drawElement(17, 12, 1, 1, silver, nil, tostring(selectedSlot) .. " | " .. tostring(allTurtles[thisTurtle].inv[selectedSlot].count) .. " ")
drawElement(12, 13, 14, 1, nil, nil, "< >")
drawElement(14, 13, 10, 1, nil, gray)
drawElement(14, 13, 10, 1, allTurtles[thisTurtle].inv[selectedSlot].isFuel and yellow or (allTurtles[thisTurtle].inv[selectedSlot].count > 0 and sky or silver), nil, allTurtles[thisTurtle].contents:sub(1, 10)) --# Selected slot contents
drawElement(16, 15, 9, 1, black, nil, "Refresh") --# Turtle location (Refresh GPS fix button)
drawElement(16, 16, 9, 1, nil, nil, "GPS Fix")
if allTurtles[thisTurtle].loc.x ~= "No GPS Fix" then --# pgps export (export to pocketgps button)
drawElement(16, 17, 9, 1, nil, silver, "Export")
drawElement(16, 18, 9, 1, nil, nil, "to pgps")
end
drawElement(2, 15, 1, 1, silver, black, "Location:") --# Turtle location (x/y/z)
drawElement(2, 16, 1, 1, nil, nil, "x:")
drawElement(2, 17, 1, 1, nil, nil, "y:")
drawElement(2, 18, 1, 1, nil, nil, "z:")
drawElement(5, 16, 1, 1, sky, nil, tostring(allTurtles[thisTurtle].loc.x))
drawElement(5, 17, 1, 1, nil, nil, tostring(allTurtles[thisTurtle].loc.y))
drawElement(5, 18, 1, 1, nil, nil, tostring(allTurtles[thisTurtle].loc.z))
end
end
local function drawHelpUI()
drawElement(1, 3, termX, termY - 2, nil, white)
drawHeader()
if tempState == "List" then
drawElement(2, 4, termX - 1, 1, black, white, "Left click a turtle to")
drawElement(2, 5, termX - 1, 1, nil, nil, "control and manage it")
drawElement(2, 7, termX - 1, 1, nil, nil, "Middle click a turtle to")
drawElement(2, 8, termX - 1, 1, nil, nil, "lock it")
drawElement(2, 11, termX - 1, 1, nil, nil, "'All ON / All OFF'")
drawElement(2, 12, termX - 1, 1, nil, nil, "controls redstone output")
drawElement(2, 13, termX - 1, 1, nil, nil, "on turtles in range")
drawElement(2, 15, termX - 1, 1, nil, nil, "[F] = Fuel Filter")
drawElement(2, 16, termX - 1, 1, nil, nil, "Filters turtles based")
drawElement(2, 17, termX - 1, 1, nil, nil, "on fueled status")
elseif tempState == "Remote" then
drawElement(2, 4, termX - 1, 1, black, white, "Click arrows to drive")
drawElement(2, 5, termX - 1, 1, nil, nil, "or use [W, A, S, D, Q, E]")
drawElement(2, 7, termX - 1, 1, nil, nil, "PL/BR[P] = Place/Break ")
drawElement(2, 8, termX - 1, 1, nil, nil, "OB/CK[O] = Obstacle check")
drawElement(2, 10, termX - 1, 1, nil, nil, "Left click name or note")
drawElement(2, 11, termX - 1, 1, nil, nil, "to edit")
drawElement(2, 12, termX - 1, 1, nil, nil, "Right click name to")
drawElement(2, 13, termX - 1, 1, nil, nil, "change color")
drawElement(2, 15, termX - 1, 1, nil, nil, "Redstone [R] controls")
drawElement(2, 16, termX - 1, 1, nil, nil, "RS output on the front")
drawElement(2, 18, termX - 1, 1, nil, nil, "'Export to pgps' adds")
drawElement(2, 19, termX - 1, 1, nil, nil, "turtle to pgps .places")
elseif tempState == "Inventory" then
drawElement(2, 4, termX - 1, 1, black, white, "Click inventory slots")
drawElement(2, 5, termX - 1, 1, nil, nil, "to select for action")
drawElement(2, 7, termX - 1, 1, nil, nil, "Click action button")
drawElement(2, 8, termX - 1, 1, nil, nil, "to carry out action")
drawElement(2, 10, termX - 1, 1, nil, nil, "Right click 'Place' to")
drawElement(2, 11, termX - 1, 1, nil, nil, "engrave a sign")
drawElement(2, 13, termX - 1, 1, nil, nil, "Right click 'Break' to")
drawElement(2, 14, termX - 1, 1, nil, nil, "attack")
drawElement(2, 16, termX - 1, 1, nil, nil, "Left click the displayed")
drawElement(2, 17, termX - 1, 1, nil, nil, "direction to change the")
drawElement(2, 18, termX - 1, 1, nil, nil, "direction of action")
end
end
local function drawNaviUI()
local pNum = tostring(pageNum)
if pageNum < 100 then pNum = "0" .. pNum end --# Add a "0" before double digit page numbers
if pageNum < 10 then pNum = "0" .. pNum end --# Add another "0" before single digit page numbers
drawElement(math.floor(termX / 2) - 10, termY, 7, 1, gray, black, pageNum > 1 and "<< BACK" or " ")
drawElement(math.floor(termX / 2) + 4, termY, 7, 1, nil, nil, pageNum < numPages and "NEXT >>" or " ")
drawElement(math.floor((termX - #pNum) / 2) + 1, termY, 3, 1, silver, nil, pNum) --# Page Number
end
local function drawMainUI() --# Turtle 'Address Book'
local xPos, yPos, tName, tFuelState, tColor, spacer = 1, 4
local magicNumber = ((pageNum - 1) * 23) + pageNum
for i = magicNumber, math.min(#filteredTurtles, pageNum * 24) do
term.setCursorPos(xPos, yPos)
tFuelState, tColor = filteredTurtles[i].fuelState, filteredTurtles[i].color
if not filteredTurtles[i].lockState then
term.setTextColor(tFuelState and white or assignColor(tColor))
term.setBackgroundColor(tFuelState and assignColor(tColor) or black)
else
term.setTextColor(tFuelState and silver or gray)
term.setBackgroundColor(tFuelState and gray or black)
end
tName = filteredTurtles[i].name
spacer = (8 - #tName) / 2
term.write(string.rep(" ", math.floor(spacer)) .. tName .. string.rep(" ", math.ceil(spacer)))
yPos = yPos + 2
if yPos > 19 then yPos = 4 xPos = xPos + 9 end
end
end
do
local runStates = {
List = function() drawMainUI() drawNaviUI() end;
Remote = function() turtleControlScreen() end;
Inventory = function() staticTurtleInventory() end;
}
drawCLI = function() --# Client Interface 'decider'
if not inputting then
drawHeader()
if runStates[runState] then runStates[runState]() end
end
end
end
local function readInput(cX, cY, cO, bG, limit, mask) --# cursor X, Y, text color, bg color, character limit, character mask
local word, pos = "", 0
local curX, curY = cX, cY
inputting, timerDelay = true, true
os.cancelTimer(pollTimer)
bG = bG or black
limit = (limit and type(limit) == "number" and limit > 0) and limit or 21
term.setCursorPos(curX, curY)
term.setTextColor(cO)
term.setBackgroundColor(bG)
term.setCursorBlink(true)
while true do
local event, data, mX, mY = os.pullEvent()
if event == "key" then
if data == keys.backspace then
if curX > cX then
curX = math.max(cX, curX - 1)
pos = math.max(0, pos - 1)
word = word:sub(1, #word - 1)
end
drawElement(curX, curY, 1, 1, cO, bG, " ")
term.setCursorPos(curX, curY)
elseif data == keys.enter or data == keys.numPadEnter then
break
end
elseif event == "char" and pos < limit then
drawElement(curX, curY, 1, 1, cO, bG, mask or data)
word = word .. data
curX = curX + 1
pos = pos + 1
elseif event == "paste" and pos < limit then
if pos + #data > limit then
data = data:sub(1, limit - pos)
end
drawElement(curX, curY, 1, 1, cO, bG, mask and string.rep(mask, #data) or data)
word = word .. data
curX = curX + #data
pos = pos + #data
elseif event == "mouse_click" then
if mY ~= curY or mX < cX or mX >= cX + limit then
break
end
end
end
term.setCursorBlink(false)
inputting = false
return word
end
do
local function toggleFuelFilter()
if type(fuelFilter) == "boolean" then
fuelFilter = fuelFilter and "noFuel" or true
else
fuelFilter = false
end
filterTurtleList()
drawElement(1, 3, termX, termY - 2, nil, black)
drawCLI()
end
local function execPLBR(check)
if check then
if (tDirection == "Up" and allTurtles[thisTurtle].up) or (tDirection == "Down" and allTurtles[thisTurtle].down) or (tDirection == "Forward" and allTurtles[thisTurtle].forward) then
drawElement(7, 8, 2, 1, white, red, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, nil, "BR")
sleep(0.1)
drawElement(7, 8, 2, 1, black, gray, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, red, "BR")
else
drawElement(7, 8, 2, 1, white, green, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, nil, "BR")
sleep(0.1)
drawElement(7, 8, 2, 1, black, nil, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, gray, "BR")
end
else
drawElement(7, 8, 2, 1, black, white, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, nil, "BR")
sleep(0.1)
drawElement(7, 8, 2, 1, nil, gray, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, nil, "BR")
end
if not check or ((tDirection == "Up" and allTurtles[thisTurtle].up) or (tDirection == "Down" and allTurtles[thisTurtle].down) or (tDirection == "Forward" and allTurtles[thisTurtle].forward)) then
netSend(allTurtles[thisTurtle].cc, "dig", tDirection)
else
netSend(allTurtles[thisTurtle].cc, "put", tDirection)
end
end
local function inputArrow(direction)
if direction == "up" then
drawElement(3, 8, 2, 1, black, white, "/\\") --# Up
drawElement(3, 9, 2, 1)
sleep(0.1)
drawElement(3, 8, 2, 1, black, gray, "/\\") --# Up
drawElement(3, 9, 2, 1)
netSend(allTurtles[thisTurtle].cc, "move", "up")
elseif direction == "down" then
drawElement(3, 12, 2, 2, black, white, "\\/") --# Down
sleep(0.1)
drawElement(3, 12, 2, 2, nil, gray, "\\/") --# Down
netSend(allTurtles[thisTurtle].cc, "move", "down")
elseif direction == "forward" then
drawElement(5, 8, 2, 1, white, gray, "/\\") --# Forward
drawElement(5, 9, 2, 1)
sleep(0.1)
drawElement(5, 8, 2, 1, black, white, "/\\") --# Forward
drawElement(5, 9, 2, 1)
netSend(allTurtles[thisTurtle].cc, "move", "forward")
elseif direction == "back" then
drawElement(5, 12, 2, 2, white, gray, "\\/") --# Back
sleep(0.1)
drawElement(5, 12, 2, 2, black, white, "\\/") --# Back
netSend(allTurtles[thisTurtle].cc, "move", "back")
elseif direction == "left" then
drawElement(3, 10, 2, 1, white, gray, "/ ") --# Left
drawElement(3, 11, 2, 1, nil, nil, "\\ ") --#
sleep(0.1)
drawElement(3, 10, 2, 1, black, white, "/ ") --# Left
drawElement(3, 11, 2, 1, nil, nil, "\\ ") --#
netSend(allTurtles[thisTurtle].cc, "move", "left")
elseif direction == "right" then
drawElement(7, 10, 2, 1, white, gray, " \\") --# Right
drawElement(7, 11, 2, 1, nil, nil, " /") --#
sleep(0.1)
drawElement(7, 10, 2, 1, black, white, " \\") --# Right
drawElement(7, 11, 2, 1, nil, nil, " /") --#
netSend(allTurtles[thisTurtle].cc, "move", "right")
end
end
local directions = {
Up = "Down";
Down = "Forward";
Forward = "Up";
}
local invSelect = {
[3] = { [7] = 1, [9] = 5, [11] = 9, [13] = 13 }; --# [xCoord] = { [yCoord] = slot, [yCoord] = slot, etc. }
[4] = { [7] = 1, [9] = 5, [11] = 9, [13] = 13 };
[6] = { [7] = 2, [9] = 6, [11] = 10, [13] = 14 };
[7] = { [7] = 2, [9] = 6, [11] = 10, [13] = 14 };
[9] = { [7] = 3, [9] = 7, [11] = 11, [13] = 15 };
[10] = { [7] = 3, [9] = 7, [11] = 11, [13] = 15 };
[12] = { [7] = 4, [9] = 8, [11] = 12, [13] = 16 };
[13] = { [7] = 4, [9] = 8, [11] = 12, [13] = 16 };
}
local keyList = {
[16] = function() inputArrow("down") end; --# Q (down)
[17] = function() inputArrow("forward") end; --# W (forward)
[18] = function() inputArrow("up") end; --# E (up)
[19] = function() netSend(allTurtles[thisTurtle].cc, "rds", not allTurtles[thisTurtle].redstone) end; --# R (redstone)
[24] = function() netSend(allTurtles[thisTurtle].cc, "dcToggle", not allTurtles[thisTurtle].dirCheck) end; --# O (Obstacle check)
[25] = function() execPLBR(allTurtles[thisTurtle].dirCheck) end; --# P (Place/Break)
[30] = function() inputArrow("left") end; --# A (left)
[31] = function() inputArrow("back") end; --# S (back)
[32] = function() inputArrow("right") end; --# D (right)
}
local function inputMain()
local event, mButton, mcX, mcY, xPos, yPos, magicNumber
while true do
event, mButton, mcX, mcY = os.pullEvent()
if event == "mouse_click" then
if runState == "Help" then
if mcY == 2 then
drawElement(1, 2, termX, termY - 1, nil, black)
runState = tempState
drawCLI()
end
elseif runState == "Remote" then
if mcY == 2 then --# Exit Remote/Edit Screen
drawElement(2, 2, termX, termY, nil, black)
runState = "List"
drawCLI()
elseif mcY == 4 then
if mcX > 1 and mcX < 10 and mButton ~= 3 then
if mButton == 1 then --# Edit Turtle Name
drawElement(2, 4, 1, 1, gray, black, allTurtles[thisTurtle].name)
runState = "EditName"
else --# Change color assignment
drawColorList(allTurtles[thisTurtle].color)
runState = "color"
end
return
elseif mcX > 21 and mcX < 26 and mButton == 1 then --# Redstone output
netSend(allTurtles[thisTurtle].cc, "rds", not allTurtles[thisTurtle].redstone)
end
elseif mcY == 6 and mButton == 1 then --# Edit Turtle Note
if mcX > 1 then
drawElement(2, 6, 1, 1, gray, black, allTurtles[thisTurtle].note)
term.setBackgroundColor(black)
runState = "EditNote"
return
end
elseif mcY == 8 and mButton == 1 then --# Movement / Actions
if mcX > 2 and mcX < 5 then --# UP
inputArrow("up")
elseif mcX > 4 and mcX < 7 then --# Forward
inputArrow("forward")
elseif mcX > 6 and mcX < 9 then --# Place/Break
execPLBR(allTurtles[thisTurtle].dirCheck)
end
elseif mcY == 9 and mButton == 1 then
if mcX > 2 and mcX < 5 then --# UP
inputArrow("up")
elseif mcX > 4 and mcX < 7 then --# Forward
inputArrow("forward")
elseif mcX > 6 and mcX < 9 then --# Tunnel
execPLBR(allTurtles[thisTurtle].dirCheck)
elseif mcX > 11 and mcX < termX and allTurtles[thisTurtle].fuelPercent < 101 then --# Fuel/Coal filter
showFuel = not showFuel
drawElement(12, 9, 1, 1, gray, black, showFuel and "Units " or "Coal ")
term.setTextColor(silver)
term.write(showFuel and (tostring(allTurtles[thisTurtle].fuelAmount)) or (tostring(allTurtles[thisTurtle].coal) .. " "))
end
elseif mcY == 10 and mButton == 1 then
if mcX > 2 and mcX < 5 then --# Left
inputArrow("left")
elseif mcX > 4 and mcX < 7 then --# Center
runState = "shellCmd"
return
elseif mcX > 6 and mcX < 9 then --# Right
inputArrow("right")
end
elseif mcY == 11 and mButton == 1 then
if mcX > 2 and mcX < 5 then --# Left
inputArrow("left")
elseif mcX > 4 and mcX < 7 then --# Center
runState = "shellCmd"
return
elseif mcX > 6 and mcX < 9 then --# Right
inputArrow("right")
elseif mcX > 11 and mcX < termX - 1 then --# Change direction of action
tDirection = directions[tDirection]
drawElement(17, 11, 1, 1, silver, black, tDirection .. " ")
if allTurtles[thisTurtle].dirCheck then
if (tDirection == "Up" and allTurtles[thisTurtle].up) or (tDirection == "Down" and allTurtles[thisTurtle].down) or (tDirection == "Forward" and allTurtles[thisTurtle].forward) then
drawElement(7, 8, 2, 1, black, gray, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, red, "BR")
else
drawElement(7, 8, 2, 1, black, green, "PL") --# Place/Break
drawElement(7, 9, 2, 1, nil, gray, "BR")
end
end
end
elseif mcY == 12 and mButton == 1 then
if mcX > 2 and mcX < 5 then --# DOWN
inputArrow("down")
elseif mcX > 4 and mcX < 7 then --# Back
inputArrow("back")
elseif mcX > 6 and mcX < 9 then --# Obstacle check
netSend(allTurtles[thisTurtle].cc, "dcToggle", not allTurtles[thisTurtle].dirCheck)
end
elseif mcY == 13 and mButton == 1 then
if mcX > 2 and mcX < 5 then --# DOWN
inputArrow("down")
elseif mcX > 4 and mcX < 7 then --# Back
inputArrow("back")
elseif mcX > 6 and mcX < 9 then --# Obstacle check
netSend(allTurtles[thisTurtle].cc, "dcToggle", not allTurtles[thisTurtle].dirCheck)
elseif mcX == 12 or mcX == termX - 1 then --# Select inventory slot
local selectedSlot = allTurtles[thisTurtle].slot
selectedSlot = mcX == 12 and selectedSlot - 1 or selectedSlot + 1
if selectedSlot < 1 then selectedSlot = 16 end
if selectedSlot > 16 then selectedSlot = 1 end
netSend(allTurtles[thisTurtle].cc, "sel", selectedSlot)
elseif mcX > 13 and mcX < termX - 2 then --# Open inventory screen
netSend(allTurtles[thisTurtle].cc, "inv", "inv")
drawElement(1, 2, termX, termY - 1, nil, black)
runState = "Inventory"
drawCLI()
end
elseif (mcY == 15 or mcY == 16) and mButton == 1 then --# Refresh GPS fix
if mcX > 15 and mcX < 25 then
drawElement(16, 15, 9, 1, white, gray, "Refresh")
drawElement(16, 16, 9, 1, nil, nil, "GPS Fix")
sleep(0.1)
drawElement(16, 15, 9, 1, black, nil, "Refresh")
drawElement(16, 16, 9, 1, nil, nil, "GPS Fix")
netSend(allTurtles[thisTurtle].cc, "gpsLoc")
end
elseif (mcY == 17 or mcY == 18) and mButton == 1 then
if mcX > 15 and mcX < 25 and allTurtles[thisTurtle].loc.x ~= "No GPS Fix" then --# Export to Lyqyd's pocketgps
drawElement(16, 17, 9, 1, white, silver, "Export")
drawElement(16, 18, 9, 1, nil, nil, "to pgps")
sleep(0.1)
drawElement(16, 17, 9, 1, black, nil, "Export")
drawElement(16, 18, 9, 1, nil, nil, "to pgps")
local pgpsPlaces = fs.open("/.places", fs.exists("/.places") and "a" or "w")
pgpsPlaces.writeLine(allTurtles[thisTurtle].loc.x .. ", " .. allTurtles[thisTurtle].loc.y .. ", " .. allTurtles[thisTurtle].loc.z .. ", " .. allTurtles[thisTurtle].name)
pgpsPlaces.close()
end
end
elseif runState == "Inventory" then
if mcY == 2 and mButton == 1 then --# Close
drawElement(1, 2, termX, termY - 1, nil, black)
runState = "Remote"
drawCLI()
elseif mcY == 17 and mButton == 1 then
if mcX > 12 and mcX < 20 then --# Choose direction of action
tDirection = directions[tDirection]
drawElement(13, 17, 1, 1, silver, black, tDirection .. " ")
end
elseif mcY == termY - 1 and mButton == 1 then --# Refresh inventory
drawElement(math.floor(termX / 2) - 9, termY - 1, 1, 1, black, silver, " Refresh Inventory ")
sleep(0.1)
drawElement(math.floor(termX / 2) - 9, termY - 1, 1, 1, white, brown, " Refresh Inventory ")
netSend(allTurtles[thisTurtle].cc, "inv", "inv")
end
if mcX < 15 and mButton == 1 then --# Inventory
if invSelect[mcX] and invSelect[mcX][mcY] then
netSend(allTurtles[thisTurtle].cc, "sel", invSelect[mcX][mcY])
end
elseif mcX > 14 then --# Refuel/Place/Break/Equip
if mcY == 7 then --# Refuel
drawElement(16, 7, 9, 1, white, gray, " Refuel")
sleep(0.1)
drawElement(16, 7, 9, 1, black, yellow, " Refuel")
if mButton == 1 then
netSend(allTurtles[thisTurtle].cc, "refuel", 1)
elseif mButton == 2 then
netSend(allTurtles[thisTurtle].cc, "refuel", 10)
elseif mButton == 3 then
netSend(allTurtles[thisTurtle].cc, "refuel", allTurtles[thisTurtle].inv[allTurtles[thisTurtle].slot].count)
end
elseif mcY == 9 and mButton ~= 3 then --# Place or engrave
drawElement(16, 9, 9, 1, white, gray, "Place")
sleep(0.1)
drawElement(16, 9, 9, 1, black, green, "Place")
if mButton == 1 then --# Place
netSend(allTurtles[thisTurtle].cc, "put", tDirection)
elseif mButton == 2 and allTurtles[thisTurtle].contents == "sign" then --# Engrave
runState = "engrave"
return
end
elseif mcY == 11 and mButton ~= 3 then --# All units, break and attack!
drawElement(16, 11, 9, 1, white, gray, "Break")
sleep(0.1)
drawElement(16, 11, 9, 1, black, red, "Break")
if mButton == 1 then --# Break
netSend(allTurtles[thisTurtle].cc, "dig", tDirection)
elseif mButton == 2 then --# Attack
netSend(allTurtles[thisTurtle].cc, "atk", tDirection)
end
elseif mcY == 13 and mButton ~= 3 then --# Equip
drawElement(16, 13, 9, 1, white, gray, "Equip")
sleep(0.1)
drawElement(16, 13, 9, 1, black, silver, "Equip")
if mButton == 1 then --# Equip left
netSend(allTurtles[thisTurtle].cc, "eqp", "left")
elseif mButton == 2 then --# Equip right
netSend(allTurtles[thisTurtle].cc, "eqp", "right")
end
end
end
elseif runState == "List" then
if mcY == 1 and mButton == 1 then
if mcX > 0 and mcX < 4 then --# Fuel filter
toggleFuelFilter()
elseif mcX > termX - 3 and mcX <= termX then --# Quit
kernelState = false
if rednet.isOpen(netSide) then rednet.close(netSide) end
term.setBackgroundColor(black)
term.setTextColor(white)
term.clear()
term.setCursorPos(1, 1)
return
end
elseif mcY == 2 and mButton == 1 then --# ALL ON/OFF
netSend("ALL", "rds", mcX < termX / 2)
elseif mcY > 3 and mcY < 19 then --# Open a turtle's properties/control screen
magicNumber, xPos, yPos = ((pageNum - 1) * 23) + pageNum, 1, 4
for i = magicNumber, math.min(#filteredTurtles, pageNum * 24) do
if mcX >= xPos and mcX < xPos + 8 and mcY == yPos then
thisTurtle = filteredTurtles[i].atList
if not filteredTurtles[i].lockState then
if mButton == 1 or mButton == 2 then
netSend(allTurtles[thisTurtle].cc, "inv", "inv")
drawElement(1, 2, termX, termY - 1, nil, black)
turtleControlScreen()
elseif mButton == 3 then
netSend(allTurtles[thisTurtle].cc, "lock")
end
break
else
runState = "password"
return
end
end
yPos = yPos + 2
if yPos > 19 then yPos = 4 xPos = xPos + 9 end
end
elseif mcY == termY and numPages > 1 and mButton == 1 then --# Page Navigation via click
if mcX < math.floor(termX / 2) - 3 or mcX > math.floor(termX / 2) + 3 then --# Page Back / Forward
pageNum = mcX < math.floor(termX / 2) - 3 and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
if pageNum == numPages and numPages > 1 then drawElement(2, 4, termX, termY, nil, black) end
drawMainUI()
drawNaviUI()
elseif mcX > math.floor(termX / 2) - 2 and mcX < math.floor(termX / 2) + 2 then --# Page Numbers (Go To Page dialogue)
runState = "goPage"
return
end
end
end
elseif event == "mouse_scroll" and runState == "List" then
pageNum = mButton == 1 and math.min(pageNum + 1, numPages) or math.max(1, pageNum - 1)
if pageNum == numPages and numPages > 1 then drawElement(2, 4, termX, termY, nil, black) end
drawMainUI()
drawNaviUI()
elseif event == "key" then
if mButton == keys.f1 then --# F1 / Help
if runState ~= "Help" then
tempState = runState
runState = "Help"
drawHelpUI()
else
runState = tempState
drawElement(1, 3, termX, termY - 2, nil, black)
drawCLI()
end
end
if runState == "List" then
if mButton == 33 then --# F / Fuel Filter
toggleFuelFilter()
end
elseif runState == "Remote" then
if keyList[mButton] then keyList[mButton]() end
end
end
end
end
local function colorPicker()
local choice = false
local _, button, x, y = os.pullEvent("mouse_click")
if x > 6 and x < 17 and y > 3 and y < 12 and button == 1 then
for colorChoice = 4, 11 do
if y == colorChoice then
for k, v in pairs(colorBurst) do
if (colorChoice - 3) == v.order then
netSend(allTurtles[thisTurtle].cc, "color", k)
choice = true
break
end
end
end
if choice then break end
end
end
drawElement(7, 4, 10, 8, nil, black)
turtleControlScreen()
end
local function editEntry()
if runState == "EditName" then
drawElement(2, 4, 1, 1, gray, black, allTurtles[thisTurtle].name)
local newName = readInput(2, 4, yellow, nil, 8)
if newName ~= "" then
netSend(allTurtles[thisTurtle].cc, "name", newName)
drawElement(2, 4, 8, 1)
else
drawElement(2, 4, 1, 1, assignColor(allTurtles[thisTurtle].color), nil, allTurtles[thisTurtle].name)
end
elseif runState == "EditNote" then
drawElement(2, 6, 1, 1, gray, black, allTurtles[thisTurtle].note)
local newNote = readInput(2, 6, white, nil, 21)
if newNote ~= "" then
netSend(allTurtles[thisTurtle].cc, "note", newNote)
drawElement(2, 6, 21, 1)
else
drawElement(2, 6, 1, 1, silver, nil, allTurtles[thisTurtle].note)
end
end
runState = "Remote"
end
local function enterPassword()
drawElement(math.floor(termX / 2) - 7, 8, 15, 1, white, gray, " Password: ")
drawElement(math.floor(termX / 2) - 7, 9, 15, 2)
drawElement(math.floor(termX / 2) - 6, 9, 13, 1, nil, black) --# input area bg
local pass = readInput(math.floor(termX / 2) - 6, 9, lime, nil, 13, "*")
if pass ~= "" then
local pw = table.concat(pbkdf2(pass, "gt!Remote", 15))
netSend(allTurtles[thisTurtle].cc, "unlock", pw)
end
drawElement(math.floor(termX / 2) - 7, 8, 15, 3)
runState = "List"
drawMainUI()
end
local function goToPage()
drawElement(math.floor(termX / 2) - 3, termY - 3, 7, 1, white, gray, "Page:")
drawElement(math.floor(termX / 2) - 3, termY - 2, 7, 2)
drawElement(math.floor(termX / 2) - 2, termY - 2, 5, 1, nil, black) --# input area bg
local newPage = tonumber(readInput(math.floor(termX / 2) - 1, termY - 2, lime, nil, 3))
pageNum = newPage or pageNum
pageNum = math.max(1, math.min(pageNum, numPages))
if pageNum == numPages and numPages > 1 then
drawElement(1, 4, termX, termY - 3)
else
drawElement(math.floor(termX / 2) - 3, termY - 3, 7, 3)
end
runState = "List"
drawMainUI()
drawNaviUI()
end
local function getSignText()
drawElement(math.floor(termX / 2) - 8, math.floor(termY / 2) - 1, 17, 1, black, silver, "Engrave")
drawElement(math.floor(termX / 2) - 8, math.floor(termY / 2), 17, 2)
drawElement(math.floor(termX / 2) - 7, math.floor(termY / 2), 15, 1, nil, black) --# input area bg
local newText = readInput(math.floor(termX / 2) - 7, math.floor(termY / 2), lime)
if newText ~= "" then
netSend(allTurtles[thisTurtle].cc, "put", newText)
end
drawElement(math.floor(termX / 2) - 8, math.floor(termY / 2) - 1, 17, 3)
runState = "Inventory"
staticTurtleInventory()
end
local function shellCommand()
drawElement(2, 8, termX - 2, 1, white, gray, "Command:")
drawElement(2, 9, termX - 2, 2)
drawElement(3, 9, termX - 4, 1, nil, black) --# input area bg
local sCommand = readInput(3, 9, lime, nil, 22)
if sCommand ~= "" then
netSend(allTurtles[thisTurtle].cc, "sCmd", sCommand)
end
drawElement(2, 8, termX - 2, 3)
turtleControlScreen()
end
local runStates = {
List = function() inputMain() end;
Remote = function() inputMain() end;
Inventory = function() inputMain() end;
Help = function() inputMain() end;
EditName = function() editEntry() end;
EditNote = function() editEntry() end;
password = function() enterPassword() end;
engrave = function() getSignText() end;
shellCmd = function() shellCommand() end;
goPage = function() goToPage() end;
color = function() colorPicker() end;
}
userInput = function()
repeat
if runStates[runState] then runStates[runState]() end
if kernelState and timerDelay and not inputting then
timerDelay = false
pollTimer = os.startTimer(0.1)
end
until not kernelState
end
end
local function dataPoller()
local removal, _, timer = false
while true do
_, timer = os.pullEvent("timer")
if timer == pollTimer then
if runState == "List" then
for i = #allTurtles, 1, -1 do
if allTurtles[i].quietCount > 1 then
table.remove(allTurtles, i)
removal = true
else
allTurtles[i].quietCount = allTurtles[i].quietCount + 1
end
end
if removal then
removal = false
filterTurtleList()
drawElement(1, 4, termX, termY - 3, nil, black)
drawMainUI()
drawNaviUI()
end
end
if not timerDelay then
netSend("ALL", "gtrQRY", "gtrQRY")
pollTimer = os.startTimer(5)
end
end
end
end
term.setBackgroundColor(black)
term.clear()
if not term.isColor() or not pocket then term.setCursorPos(1, 1) error("Advanced Wireless Pocket Computer Required", 0) end
drawElement(2, 2, 1, 1, white, nil, "Initializing . . .")
if not os.getComputerLabel() then os.setComputerLabel("PocketPC.ID#" .. thisCC) end
if peripheral.isPresent("back") and peripheral.getType("back") == "modem" and peripheral.call("back", "isWireless") then
rednet.open("back")
netSide = "back"
else
term.clear()
drawElement(2, 2, 1, 1, red, nil, "No wireless")
drawElement(2, 3, 1, 1, nil, nil, "modem detected!")
drawElement(2, 5, 1, 1, nil, nil, "gtRemote REQUIRES")
drawElement(2, 6, 1, 1, nil, nil, "a wireless modem.")
term.setCursorPos(1, 9)
return
end
runState = "List"
drawElement(2, 4, 1, 1, white, nil, "Searching for hosts . . .")
netSend("ALL", "gtrQRY", "Full")
pollTimer = os.startTimer(5)
term.clear()
drawCLI()
parallel.waitForAny(userInput, netReceive, dataPoller)