This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
MysticT's profile picture

Location System - find your enemy's computers

Started by MysticT, 14 June 2012 - 08:06 PM
MysticT #1
Posted 14 June 2012 - 10:06 PM
After reading a post here about someone trying to locate computers, I decided to make some system to do it. And now it's here…
It works like the gps api, so you need some "host" computers to receive the messages and a "master" computer to process the data.

How it works:
- A host computer receives a message.
- It sends it's location (the host location), the sender id and the distance from it to the master computer.
- The master computer stores that info.
- When the master has enough info to locate a computer it attemps to do it.
- If succesful it saves that location.

Setup:
- Place the host computers with a modem on a side (like you do for gps towers).
- Install the program on them, and run it with the "host" option.
- Place the master computer and a modem on a side.
- Install the program and run it with the "receive" option.

To run a host computer type "programName id x y z", where id is the id of the master computer and x, y and z are the computer's location.
To run the master computer type "programName receive".

Now every time some computer/turtle in range "pings" to get it's location (using gps) you'll get it's location.

Download:
pastebin code: RUFzL779
Code:
Spoiler


-- Location System
-- by MysticT

local tLocations = {}
local tKnownLocations = {}
local bDebug = false

-- Helper functions

local function clear()
term.clear()
term.setCursorPos(1, 1)
end

local function table_size(t)
local i = 0
for _,_ in pairs(t) do
i = i + 1
end
return i
end

local function connect()
print("Starting location system...")
print("Connecting to rednet...")
for _,s in ipairs(rs.getSides()) do
if peripheral.isPresent(s) and peripheral.getType(s) == "modem" then
rednet.open(s)
return true
end
end
print("No modem found")
print("Aborting")
return false
end

local function _print(...)
if bDebug then
print(...)
end
end

local function vector_equal(v1, v2)
return v1.x == v2.x and v1.y == v2.y and v1.z == v2.z
end

-- Load/Save functions

local function save()
_print("Saving locations...")
local file = fs.open("locations.dat", "w")
if file then
for id, loc in pairs(tKnownLocations) do
file.writeLine(tostring(id)..": "..tostring(loc.x)..", "..tostring(loc.y)..", "..tostring(loc.z))
end
file.close()
_print("Locations saved")
return true
end
_print("Error saving locations")
return false
end

local function load()
_print("Loading locations")
local file = fs.open("locations.dat", "r")
if file then
local i = 0
local sLine = file.readLine()
while sLine do
local sId, sX, sY, sZ = string.match(sLine, "(%d+): (%-?%d+), (%-?%d+), (%-?%d+)")
local id, x, y, z = tonumber(sId), tonumber(sX), tonumber(sY), tonumber(sZ)
if id and x and y and z then
tKnownLocations[id] = vector.new(x, y, z)
i = i + 1
end
sLine = file.readLine()
end
file.close()
_print(n, " locations loaded")
return true, i
end
_print("Error loading locations")
return false
end

-- Location functions

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(), result2:round()
if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
return result1, result2
else
return rounded1
end
end
return result:round()
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.05 then
return p1, p2
elseif dist1 < dist2 then
return p1:round()
else
return p2:round()
end
end

function locate(id)
local tFixes = {}
local p1, p2
for _,t in pairs(tLocations[id]) do
table.insert(tFixes, { ["position"] = vector.new(t.x, t.y, t.z), ["distance"] = t.d })
end
local i = 3
while (p1 == nil or p2 ~= nil) and i <= #tFixes do
if not p1 then
p1, p2 = trilaterate(tFixes[1], tFixes[2], tFixes[i])
else
p1, p2 = narrow(p1, p2, tFixes[i])
end
i = i + 1
end
if p1 and p2 then
_print("Ambiguous position")
_print("Could be "..p1.x..","..p1.y..","..p1.z.." or "..p2.x..","..p2.y..","..p2.z)
elseif p1 then
if not tKnownLocations[id] or not vector_equal(tKnownLocations[id], p1) then
print("Location added:")
print(id, ": ", p1)
tKnownLocations[id] = p1
save()
end
else
_print("Couldn't determine location for ", id)
end
end

local function addLocation(id, t)
if not tLocations[t.id] then
tLocations[t.id] = {}
end
local tLoc = {}
tLoc.x = t.x
tLoc.y = t.y
tLoc.z = t.z
tLoc.d = t.d
tLocations[t.id][id] = tLoc
if table_size(tLocations[t.id]) >= 3 then
_print("Trying to locate #", t.id)
locate(t.id)
end
end

-- Event handlers

local function handle_message(id, msg)
local st = string.match(msg, "<LOCATION> (.+)")
if st then
local t = textutils.unserialize(st)
if t and type(t) == "table" then
_print("Location received from ", id, ":")
_print(t.id, ": ", "(", t.x, ", ", t.y, ", ", t.z, ", ", t.d, ")")
addLocation(id, t)
end
end
end

local function handle_char(c)
c = string.lower(c)
if c == "l" then
print("Locations:")
for id, loc in pairs(tKnownLocations) do
print(id, ": ", loc)
end
elseif c == "c" then
clear()
end
end

-- Start program

local function printUsage()
local sName = fs.getName(shell.getRunningProgram())
print("Usage:")
print(sName, " receive [debug]")
print(sName, " host id x y z")
end

local tArgs = {...}
local sAction = tArgs[1]
if not sAction then
printUsage()
return
end

if sAction == "receive" then
if #tArgs > 1 then
if string.lower(tArgs[2]) == "debug" then
bDebug = true
else
printUsage()
return
end
end
clear()
if not connect() then
return
end
local ok, n = load()
if ok then
print("Loaded ", n, " locations")
end
print("Waiting for messages...")
print("Press l to list located computers")
print("Press c to clear the screen")
while true do
local evt, arg1, arg2 = os.pullEvent()
if evt == "rednet_message" then
handle_message(arg1, arg2)
elseif evt == "char" then
handle_char(arg1)
end
end
elseif sAction == "host" then
local nMasterID = tonumber(tArgs[2])
local tLoc = {}
tLoc.x = tonumber(tArgs[3])
tLoc.y = tonumber(tArgs[4])
tLoc.z = tonumber(tArgs[5])
if not nMasterID or not tLoc.x or not tLoc.y or not tLoc.z then
printUsage()
return
end
clear()
if not connect() then
return
end
print("Waiting for messages...")
while true do
local id, msg, dist = rednet.receive()
print("Message received from ", id, " at ", dist, " meters.")
tLoc.id = id
tLoc.d = dist
rednet.send(nMasterID, "<LOCATION> "..textutils.serialize(tLoc))
end
else
printUsage()
end

Any comments and suggestions are welcome.
cant_delete_account #2
Posted 14 June 2012 - 10:26 PM
I kinda get it but kinda don't, will it only give the location to the master computer if the computer you want the location of is running the host program? Or does it give the location of computers near the hosts?
MysticT #3
Posted 14 June 2012 - 10:28 PM
It gives you the location of computers in range (modem range) of the hosts.
So, you have 3/4 hosts (3 is the minimum to locate a computer), and they receive a message from some computer, they will send the info to the master computer and it will locate the computer that sent the message.
It could be usefull to locate computers spamming rednet, since they broadcast messages.
Leo Verto #4
Posted 15 June 2012 - 12:09 AM
Hmm, this is pretty awesome, when there wasn't any rednet, I thought about locating a broadcast by having a turtle flying around and locating the broadcaster by using the rednet range limit.

Of course this is a lot easier.

Now to set it up as an anti turtle system, adding automatic controlled bomber turtles would complete this….
KaoS #5
Posted 15 June 2012 - 07:18 AM
100% awesome! I was busy making my own GPS program as I don't have the latest computercraft or internet at home (I would rather experiment and learn to create it all myself than go through the whole download and transfer process), I still want to make it myself as then I can tweak it. but great idea there. genius
KaoS #6
Posted 17 June 2012 - 07:58 AM
now that I think about it I realise that it is just a normal GPS satellite that doesn't check if the detected PC is asking for GPS cds and sends the info to you rather than to the detected PC, what I would do now is set that up in my base (with a few spread out, not just 1 so I have better coverage) that send the detected PCs ID, cds and what they were sending to a large monitor display so you are aware of all rednet activity in a large area, you can make it sort by location/cds range etc. so many possibilities :(/>/>
Sxw #7
Posted 10 July 2012 - 05:13 PM
Awesome. Except you cant see rednet.sends… Unless I modify the api to send to me too… :L
FuzzyPurp #8
Posted 11 August 2012 - 11:05 AM
Nice work.
Cranium #9
Posted 25 September 2012 - 11:49 PM

local function connect()
print("Starting location system...")
print("Connecting to rednet...")
for _,s in ipairs(rs.getSides()) do
  if peripheral.isPresent(s) and peripheral.getType(s) == "modem" then
   rednet.open(s)
   return true
  end
end
print("No modem found")
print("Aborting")
return false
end
I'm no expert, but this function here seems to catch my eye. If I am reading this right, it will check for modems on all sides, and then turn on that modem, right? But I see that after turning on the modem, it will print "No modem found, aborting" after connecting. Is this supposed to happen, or what?
MysticT #10
Posted 25 September 2012 - 11:56 PM

local function connect()
print("Starting location system...")
print("Connecting to rednet...")
for _,s in ipairs(rs.getSides()) do
  if peripheral.isPresent(s) and peripheral.getType(s) == "modem" then
   rednet.open(s)
   return true
  end
end
print("No modem found")
print("Aborting")
return false
end
I'm no expert, but this function here seems to catch my eye. If I am reading this right, it will check for modems on all sides, and then turn on that modem, right? But I see that after turning on the modem, it will print "No modem found, aborting" after connecting. Is this supposed to happen, or what?
When it finds a modem, it opens rednet on that side and returns true, ending the function, so the print below won't be run.
Cranium #11
Posted 26 September 2012 - 12:04 AM
Derp….totally missed the return true command…. :P/>/>
pruby #12
Posted 27 September 2012 - 07:27 AM
Very nice. Perhaps this could be adapted to a turtle which keeps moving and records the distance and its position each time it receives a message. As soon as it gets a certain number of consistent reports (4 or 5?) it could guess that the target is still in a particular place and go for it.
Jojo.bacon #13
Posted 18 October 2012 - 04:54 PM
Wardialing in Minecraft! Achievement get!
ChunLing #14
Posted 18 October 2012 - 07:49 PM
heheh, nice.
darkrising #15
Posted 19 October 2012 - 04:30 AM
Epic, love it :P/>/>
Heracles421 #16
Posted 22 October 2012 - 05:42 AM
Now I can send a bunch if turtles to my enemies' base and destroy it, brilliant!
It's actually really nice