Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal
  • 0
SavageNoob

OpenSecurity EntityDetector + Door

Question

So I am trying to make a program where if i am in the vicinity the doors will open, and if im not, the doors will close and the alarm will sound. The problem I am having is that if no one is around, it throws an error message that says trying to access a nil field. Here is my code :

 

 

local component = require("component")
local colors = require "colors"
gpu = require("component").gpu
alarm = require("component").os_alarm
door = require("component").os_door
entity = require("component").os_entdetector
local w, h = gpu.getResolution()


local delay -- sleep time
local player -- list of players



  while true do
  os.sleep(2)
     if  entity.scanPlayers(64) == true then
        scan = entity.scanPlayers(64)
  
        name = scan[1].name
    
      if name ~= "sav" then

        door.close()
        alarm.activate()
        print("Hello " .. scan[1].name)
        os.sleep(2)
        alarm.deactivate()

      elseif name == "sav" then
        door.open()
        alarm.deactivate()
        print("Welcome Home Sav")
        os.sleep(2)
      end
  os.sleep(4)
  
  end
 
    
  

end

 

This obviously doesnt work and just freezes the computer, any guidance on how to do something like, if someone is around then either open the door or close it based on who it is, if no one is around, than keep repeating the program until someone is. Any help would be appreciated, 

 

Thank you

Share this post


Link to post
Share on other sites

4 answers to this question

Recommended Posts

  • 0

I am sorry, but unless I'm misreading your code, it can't possibly throw an "attempt to index a nil value" error, with any people in proximity or without. And the question is contradictory, stating both that the program crashes and that it freezes (the latter is correct, however). Actually, I'm unsure what the actual question is. Therefore, I'll do the only thing I can here: that is, explain what your code does wrong and how to fix it.

Before I can proceed to fix the actual issues in the program's logic, I have to get rid of some minor problems. The first such problem is that the code is indented poorly. Of course, Lua doesn't care how you format your code, but it isn't why you do that. Indentation helps you, the program's author, to keep track of what it does, to visually separate chunks of code. And other people benefit from that too, for properly formatted code is much more readable. Without further ado...

local component = require("component")
local colors = require "colors"
gpu = require("component").gpu
alarm = require("component").os_alarm
door = require("component").os_door
entity = require("component").os_entdetector
local w, h = gpu.getResolution()

local delay -- sleep time
local player -- list of players

while true do
  os.sleep(2)
  if entity.scanPlayers(64) == true then
    scan = entity.scanPlayers(64)

    name = scan[1].name

    if name ~= "sav" then
      door.close()
      alarm.activate()
      print("Hello " .. scan[1].name)
      os.sleep(2)
      alarm.deactivate()
    elseif name == "sav" then
      door.open()
      alarm.deactivate()
      print("Welcome Home Sav")
      os.sleep(2)
    end
    os.sleep(4)
  end
end

...And the second thing to take care of is the global variables. Unless you need them such (which rarely happens and this is no exception), make them local. Finally, you've already required component, so there is no reason to do so again; and, while fixing that, let's also remove the unused variables: gpu, colors, delay and player. You can re-add them later yourself if you need.

local component = require("component")
local alarm = component.os_alarm
local door = component.os_door
local entity = component.os_entdetector

local owner = "sav" -- insert your nickname here

while true do
  os.sleep(2)

  if entity.scanPlayers(64) == true then
    local scan = entity.scanPlayers(64)
    local name = scan[1].name

    if name ~= "sav" then
      door.close()
      alarm.activate()
      print("Hello " .. scan[1].name)
      os.sleep(2)
      alarm.deactivate()
    elseif name == "sav" then
      door.open()
      alarm.deactivate()
      print("Welcome Home Sav")
      os.sleep(2)
    end

    os.sleep(4)
  end
end

Great, so now we can start dissecting the code. What does it do?

First, we require the component library, get proxies of the components we need, and proceed into the main loop. There we first sleep for 2 seconds... Why do we sleep for 2 seconds, again? I'm not sure; let's keep it there for now.

Then we perform a scan, calling entity.scanPlayers(64), which returns a table, and compare if it is equal to true. Wait. There is no way a table can be equal to true, not unless you mess with metamethods, which we don't. Very suspicious. If we interpret the code literally, we skip the whole if, and end up at the start of the loop again. It's not interesting, so let's pretend that somehow the if's condition is satisfied to understand what the author's intent was.

And then we perform another scan... hang a minute, another one? Haven't we already done that? Why should we be in dire need of repeating ourselves? Anyway, I think I realized what the code was supposed to do: scan the surroundings and check if there's anyone around. After all, running an alarm without anybody to listen to the marvelous sounds of it is just a waste of energy, isn't it? Let's put it into words in Lua.

...

while true do
  local scan = entity.scanPlayers(64)
  
  if scan and #scan > 0 then
    ...
  else
    door.close()
    alarm.deactivate()
  end
  
  os.sleep(2)
end

We do the scan as soon as we're in the loop. If the scan result is present (neither a false nor a nil), which means the entity detector had no errors, and the length (#) of that table (#scan) is greater than 0 (#scan > 0), then we'll do something about it. The previous sentence is basically how you'd read the code in English.

Note that if scan happens to be empty, we close the door and disable the alarm. And we shouldn't forget to sleep, for the sake of energy conservation.

All right, so let's do something about the scan. scan the variable is a table of tables. In other words, scan is a table that stores other tables. Each "other table", in turn, contains information about one player nearby. We have already checked that at least one player is present, but there may be more people gathered around, totally not planning a siege, and we don't yet know whether you are there. You blindly assume you would be the first player in the returned list. Unfortunately, the entity detector couldn't care less about that, and your entry could easily be the second or the last one. Or not present at all, if you happened to carelessly abandon your fortress.

Our mini-goal is to walk through all the entries of the table (this is called iterating a table) until your entry is found. If after iterating through all items we have yet to found yours, you're not around. So we close the door and activate the alarm. Otherwise, we do the opposite. This is how you do it:

local ownerNearby = false

for _, entry in ipairs(scan) do
  if entry.name == owner then
    ownerNearby = true
    break
  end
end

if ownerNearby then
  door.open()
  alarm.deactivate()
  print("Welcome home!")
else
  door.close()
  alarm.activate()
  print("Hello, whoever you are.")
end

Something isn't right... The program will keep spamming "Welcome home!" if you're in vicinity, which isn't quite courteous. Let's make it greet you only if you weren't around before that, and do likewise for greeting intruders. To do that, we need to remember the previous state of your program: whether there was nobody, an intruder, or you.

local component = require("component")
local alarm = component.os_alarm
local door = component.os_door
local entity = component.os_entdetector

local owner = "sav" -- insert your nickname here
local previousState = "nobody"

while true do
  local scan = entity.scanPlayers(64)
  
  if scan and #scan > 0 then
    local ownerNearby = false

    for _, entry in ipairs(scan) do
      if entry.name == owner then
        ownerNearby = true
        break
      end
    end

    if ownerNearby and previousState ~= "owner" then
      door.open()
      alarm.deactivate()
      print("Welcome home!")
      previousState = "owner"
    elseif not ownerNearby and previousState ~= "intruder" then
      door.close()
      alarm.activate()
      print("Hello, whoever you are.")
      previousState = "intruder"
    end
  else
    door.close()
    alarm.deactivate()
    previousState = "nobody"
  end

  os.sleep(2)
end

Well, this is the whole program. I didn't test it, so any bugs are yours to find and exterminate.

Share this post


Link to post
Share on other sites
  • 0

Dude, that is the most helpful response i could have expected. Gonna dive into this now, see if i can get it working, and thank you for having patience with such a nub like me. The fact you didnt just correct my crap code but actually explained it to me is awesome. I am trying to learn not just be shoveled code so I have to ask dumb questions on the next program. People should take note, I am sure this will help alot out, not just me.

 

One more dumb question. Could this be easily modified to add multiple people to the allowed list? Like if I team with someone. I play on a very aggressive guns/heli server and we need quick in/out sometimes.

Share this post


Link to post
Share on other sites
  • 0
3 hours ago, SavageNoob said:

Could this be easily modified to add multiple people to the allowed list?

Anything not impossible is possible, and this feature doesn't even require much perseverance to implement.

Right now we're using owner to store the nickname of one player. We are going to replace it with a table, and therefore the name owner is no longer satisfactory. Let's rename it as trustedPeople and populate it with some test names.

local trustedPeople = {"player1", "player2", "player3"}

A table is the universal record type in Lua, that is, the type that stores multiple values. But we still need to choose the correct data structure. The table trustedPeople is a sequence. This means its elements are stored sequentially, without gaps, and can be accessed using integer indeces (trustedPeople[1], trustedPeople[2], etc.). Such a data structure is useful if we need an ordered collection of items to iterate over, for example. However, this is not the case here. What we need is essentially a set, which stores unique elements and can tell us in an efficient way if a certain value exists. In Lua, sets are usually implemented as follows:

local trustedPeople = {
  ["player1"] = true,
  ["player2"] = true,
  ["player3"] = true,
}

The stuff in square brackets is called the key, and what follows after the equals sign is the value. If you have the key, you can quickly determine the associated value, which defaults to nil for nonexistent keys. The most important word is quickly. As for the previous data structure, to find if a table contains a trusted person, we would have to iterate over the sequence of authorized players for every element of the table.

local ownerNearby = false

for _, entry in ipairs(scan) do
  for _, trusted in ipairs(trustedPlayers) do
    if entry.name == trusted then
      ownerNearby = true
      goto out
    end
  end
end

::out::

It's very inefficient and complicated. Now, compare it with the code that we would write if we use a set for trustedPlayers:

local ownerNearby = false

for _, entry in ipairs(scan) do
  if trustedPlayers[entry.name] then
    ownerNearby = true
    break
  end
end

 

However, I like how easy it is to modify the sequence in the code. {"one", "two", "three"} is much more concise than {["one"] = true, ["two"] = true, ["three"] = true}. I'll make it so that we could specify the list of allowed people, and it would be converted to a set by the program.

local trustedPeople = {"player1", "player2", "player3"}

for index, nickname in ipairs(trustedPeople) do
  trustedPeople[nickname] = true
  trustedPeople[index] = nil
end

 

Let's incorporate all of that in the original program.

local component = require("component")
local alarm = component.os_alarm
local door = component.os_door
local entity = component.os_entdetector

local trustedPeople = {"player1", "player2", "player3"}

for index, nickname in ipairs(trustedPeople) do
  trustedPeople[nickname] = true
  trustedPeople[index] = nil
end

local previousState = "nobody"

while true do
  local scan = entity.scanPlayers(64)
  
  if scan and #scan > 0 then
    local shouldOpen = false

    for _, entry in ipairs(scan) do
      if trustedPeople[entry.name] then
        shouldOpen = true
        break
      end
    end

    if shouldOpen and previousState ~= "allowed" then
      door.open()
      alarm.deactivate()
      print("Welcome home!")
      previousState = "allowed"
    elseif not shouldOpen and previousState ~= "intruder" then
      door.close()
      alarm.activate()
      print("Hello, whoever you are.")
      previousState = "intruder"
    end
  else
    door.close()
    alarm.deactivate()
    previousState = "nobody"
  end

  os.sleep(2)
end

I've also changed a couple of names so that they would reflect their purpose.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.