Related
Script:
local ToolFolder = game:GetService("ServerStorage"):FindFirstChild("SavedItems")
local DataStoreService = game:GetService("DataStoreService")
local SaveData = DataStoreService:GetDataStore("SaveData")
game.Players.PlayerAdded:Connect(function(player)
local ToolData = SaveData:GetAsync(player.UserId)
local BackPack = player:WaitForChild("Backpack")
local StarterGear = player:WaitForChild("StarterGear")
if ToolData ~= nil then
for i, v in pairs(ToolData) do
if ToolFolder:FindFirstChild(v) and BackPack:FindFirstChild(v) == nil and StarterGear:FindFirstChild(v) == nil then
ToolFolder[v]:Clone().Parent = BackPack
ToolFolder[v]:Clone().Parent = StarterGear
end
end
end
player.CharacterRemoving:Connect(function(Character)
Character:WaitForChild("Humanoid"):UnequipTools()
end)
end)
game.Players.PlayerRemoving:Connect(function(player)
local ToolTable = {}
for i,v in pairs(player.Backpack:GetChildren()) do
table.insert(ToolTable, v.Name)
end
if ToolTable ~= nil then
SaveData:SetAsync(player.UserId, ToolTable)
end
end)
Issue:
ServerScriptService.SaveTools:12: attempt to index nil with 'FindFirstChild'
Couldn't find a solution. Appreciate any help. :)
Any time you see "attempt to index nil with x" you need to look at where you are asking for "x" and realize that the object holding "x" doesn't exist. The job then becomes figuring out why that object doesn't exist.
In your case, whatever object that you are calling "FindFirstChild" on line 12 doesn't exist. Sadly, line 12 uses this three times :
if ToolFolder:FindFirstChild(v) and
BackPack:FindFirstChild(v) == nil and
StarterGear:FindFirstChild(v) == nil then
So let's look at where ToolFolder, BackPack, and StarterGear were created and see if that gives any clues.
local ToolFolder = game:GetService("ServerStorage"):FindFirstChild("SavedItems")
...
local BackPack = player:WaitForChild("Backpack")
local StarterGear = player:WaitForChild("StarterGear")
Backpack and StarterGear looks correct, they are both children of the Player, and both look to be spelled correctly.
ToolFolder is probably the culprit, you should make sure there is actually an object named SavedItems in ServerStorage. Double check that the spelling and capitalization are correct.
Ive been racking brains all day tryna fix this and i am new with lua and cant come up with a solution to this problem even though im rather close.
SO
I have a drift leader board in my server. The problem is the script doesnt check for the players [id] in the table. So if player A has a score of 10 and Player B has a score of 5. Player A can take the 2nd position on the leaderboard (table) by getting a score of any int between. so lets say 7.
LEADERBOARD_CLEAR_TIME = 15 -- THIS IS IN MINUTES
local Players = {}
local Leaderboards = {}
RegisterCommand('cleardriftscores', function(source)
if IsPlayerAceAllowed(source, 'drift') then --Ace permissions
clearLeaderboard()
end
end)
RegisterNetEvent("SaveScore")
AddEventHandler("SaveScore", function(client, data)
local identifier = (GetPlayerIdentifier(source, 0))
local playerName = GetPlayerName(source)
if Players[identifier] ~= nil then
if Players[identifier].pb < data.curScore then
-- Personal Best Beat
local oldScore = Players[identifier].pb
Players[identifier] = { pb = data.curScore }
chatMessage(source, string.format("Congrats! You have just beaten your personal best drift chain score of ^2%s^0 with ^2%s^0!", oldScore, data.curScore))
end
else
Players[identifier] = { pb = data.curScore }
end
if #Leaderboards == 0 then
table.insert(Leaderboards, {score = data.curScore, name = playerName, id = identifier})
chatMessage(-1, string.format("^2%s^0 has started off the leaderboard with a score of ^2%s^0!", playerName, data.curScore))
end
for k, v in ipairs(Leaderboards) do
if v.score < data.curScore and checkLeaderboard(identifier) then
-- Outpassed Someone
table.insert(Leaderboards, { score = data.curScore, name = playerName, id = identifier } )
chatMessage(-1, string.format("^2%s^0 has beaten ^2%s's^0 score of ^2%s^0 with ^2%s^0! They are in ^2%s^0 place", playerName, v.name, v.score, data.curScore, GetPlacement(k)))
break
end
end
table.sort(Leaderboards, compare) --Currently bugged, when player 1 is in first and player 2 is in second. Player 1 can take second by getting a score inbetween table entry 1 & 2
end)
function chatMessage(target, msg)
TriggerClientEvent('chat:addMessage', target or -1, {
color = { 255, 0, 0},
multiline = true,
args = {"[Drift] ", msg}
})
end
function checkLeaderboard(identifier)
for k, v in ipairs(Leaderboards) do
if v.id == identifier then
Leaderboards[k] = nil
end
end
return true
end
function compare(a, b) --actual compare funtion. Need to check for player id's somehow...
if a ~= nil and b ~= nil then
return a.score > b.score --I tried adding another compare here but that didnt work.
end
end
function GetPlacement(number) --This doesnt work very well. (12nd place??)
lastDigit = number % 10
local placement = 'th'
if lastDigit == 1 then
placement = 'st'
elseif lastDigit == 2 then
placement = 'nd'
elseif lastDigit == 3 then
placement = 'rd'
end
return number .. placement
end
function clearLeaderboard()
Leaderboards = {}
chatMessage(-1, "The Drift leaderboard has been cleared!")
end
Citizen.CreateThread(function() --Code for timer to reset leaderboard
while true do
while #Leaderboards == 0 do
Citizen.Wait(0)
end
Citizen.Wait((LEADERBOARD_CLEAR_TIME * 1000 * 60) - (60 * 1000 * 5))
chatMessage(-1, "The Drift Leaderboard is clearing in 5 minutes!")
Citizen.Wait(1000 * 60 * 3)
chatMessage(-1, "The Drift Leaderboard is clearing in 2 minutes!")
Citizen.Wait(1000 * 60 * 1)
chatMessage(-1, "The Drift Leaderboard is clearing in 1 minute!")
Citizen.Wait(1000 * 60 * 1)
clearLeaderboard()
end
end)
The problem here is the code that adds to the leaderboards.
If a player is only allowed to be on a leaderboard once, you should check for that!
Here's how I'd do it:
If the player has beaten his/her personal best then attempt to add his new score to the leaderboards.
Start from the lowest score on the leaderboard. For each score:
If the score owner is equal to the player, delete this score (it's outdated).
If the personal best is smaller than the currently inspected score, insert the personal best one below the current inspected score.
Doing so there won't be any duplicates.
Btw, compare function is just fine ;)
But, just a friendly reminder:
a ~= nil and b ~= nil
--This is simpler and faster!
a and b
Remember that nil in Lua evaluates to false. Therefore, if you need to check if something holds value just:
if a then --This checks if a isn't false or nil
--your code goes here
end
And also that line is not really useful in your comparing function.
Hope that helps!
I have a motorized roller blind project.
I am following this instructable: https://www.instructables.com/id/Motorized-WiFi-IKEA-Roller-Blind/ .
I am using the code in the instructable, but I suspect tha fault is in one of these codes:
config.lua
-- file : config.lua
local module = {}
module.SSID = {}
module.SSID["ssid"] = "password"
-- example for local MQTT
--module.MQHOST = "ohab.local"
--module.MQPORT = 1883
--module.MQID = node.chipid()
--module.MQUSR = ""
--module.MQPW = ""
-- example for cloud MQTT
module.MQHOST = "192.***.*.*"
module.MQPORT = 1883
module.MQID = node.chipid()
module.MQUSR = "username"
module.MQPW = "password"
module.MQTLS = 1 -- 0 = unsecured, 1 = TLS/SSL
module.ENDPOINT = "/house/masterbedroom/rollerblind/"
module.ID = "0"
--module.SUB = "set"
module.SUB = {[module.ENDPOINT .. module.ID .. "/set"]=0,[module.ENDPOINT .. "all"]=0}
module.POST = module.ENDPOINT .. module.ID .. "/status"
return module
wifi_setup
-- file: setup.lua
local module = {}
local function wifi_wait_ip()
if wifi.sta.getip()== nil then
print("IP unavailable, Waiting...")
else
tmr.stop(1)
gpio.write(pin_led,1) --off
print("\n================== ==================")
print("ESP8266 mode is: " .. wifi.getmode())
print("MAC address is: " .. wifi.ap.getmac())
print("IP is "..wifi.sta.getip())
print("====================================")
mq.start()
end
end
local function wifi_start(list_aps)
if list_aps then
gpio.write(pin_led,0) --on
for key,value in pairs(list_aps) do
if config.SSID and config.SSID[key] then
wifi.setmode(wifi.STATION);
wifi.sta.config(key,config.SSID[key])
wifi.sta.connect()
print("Connecting to " .. key .. " ...")
--config.SSID = nil -- can save memory
tmr.alarm(1, 2500, 1, wifi_wait_ip)
end
end
else
print("Error getting AP list")
end
end
function module.start()
print("Configuring Wifi ...")
wifi.setmode(wifi.STATION);
wifi.sta.getap(wifi_start)
end
return module
Sadly I cant manage to go beyond step 4. After I have tried to download the code to the ESP8266 I only get this error:
PANIC: unprotected error in call to Lua API (wifi_setup.lua:25: bad argument #1 to 'config' (config table not found!))
I have only changed the stuff the instructable told me to change, and I have tried to look for faults myself but I cant find any... It's the first time I am using Lua, so this is totally new for me.
Hope somebody here can offer some help. Been stuck on this for days...
THIS PART OF THE PROBLEM IS SOLVED. CHECK COMMENTS FOR SOLUTION
Sadly when one problem is solved, a new one rises...
The chip is now successfully connecting to the wifi and mqtt service, but when I try to do the dry run by putting in the command 'step_move(1000,FWD,2)' nothing happens. The motor should rotate...
Also when I press the button I get a new panic error as follows:
PANIC: unprotected error in call to Lua API (button.lua:23: attempt to perform arithmetic on upvalue '?' (a nil value))
button.lua
--file button4.lua
do
-- use pin 1 as the input pulse width counter
local pin=5
local debounce = 150 --ms
local longpress = 2000 --ms
local pulse1, pulse2, du, now, trig = 1, 0, 0, tmr.now, gpio.trig
local prev_int_time, int_time, up_time = 0
local cal_steps = 100000
local cal_steps_dn = 0
local cal_steps_up = 0
local cal_state = 0 -- 0 = not calibration, 1 = calibrating down, 2 = calibrating up
state = 0 -- state: 0 = up, 1 = transition, 2 = down
gpio.mode(pin,gpio.INT)
local function pin4cb(level)
int_time = now() / 1000
if ((int_time - prev_int_time) > debounce) then
if (level == 0) then
up_time = int_time
else
if((int_time - up_time) > longpress) then
print("calibrating")
cal_state = 1
--cur_step = 100000
step_move(cal_steps,FWD,2)
else -- short press
print("short", cal_state)
if (cal_state == 2) then -- calibrated up (done)
print("calibration done")
state = 0 -- up
cur_step = 0
tot_steps = cal_steps - step_stepsleft
print("cal_steps: " .. cal_steps)
print("step_stepsleft: " .. step_stepsleft)
print("tot_steps: " .. tot_steps)
step_stop()
pins_disable()
cal_state = 0
if file.open("cfg_tot_steps.lua", "w+") then
file.write("tot_steps=" .. tot_steps .. '\n')
file.close()
end
elseif (cal_state == 1) then -- calibrated dn (switch direction)
print("calibration low point")
print(cal_steps - step_stepsleft)
step_stop()
step_move(cal_steps,REV,2)
cal_state = 2
elseif (cal_state == 0) then
if (state == 0 and step_stepsleft == 0) then -- i am up, go dowm
rollerblind.down()
-- state = 2
elseif (state == 1) then -- i am moving, do nothing
-- do nothing
elseif (state == 2 and step_stepsleft == 0) then -- i am down, go up
rollerblind.up()
-- state = 0
end
end
end
end
--print (level)
prev_int_time = int_time
end
end
gpio.trig(pin, "both", pin4cb)
end
Here is the code for the stepper.lua:
-- stepper.lua
-- code from: http://www.esp8266.com/viewtopic.php?f=19&t=2326
-- simple stepper driver for controlling a stepper motor with a
-- l293d driver
-- nodemcu pins: 0 5 6 7
stepper_pins = {1,3,2,4} -- (A-)blue, (A+)pink, (B-)yellow, (B+)orange
--stepper_pins = {1,2,3,4}
-- half or full stepping
step_states4 = {
{1,0,0,1},
{1,1,0,0},
{0,1,1,0},
{0,0,1,1}
}
step_states8 = {
{1,0,0,0},
{1,1,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,1,0},
{0,0,1,1},
{0,0,0,1},
{1,0,0,1},
}
step_states = step_states4 -- choose stepping mode
step_numstates = 4 -- change to match number of rows in step_states
step_delay = 10 -- choose speed
step_state = 0 -- updated by step_take-function
step_direction = 1 -- choose step direction -1, 1
step_stepsleft = 0 -- number of steps to move, will de decremented
step_timerid = 4 -- which timer to use for the steps
status_timerid = 2 -- timer id for posing of status messages
-- setup pins
function pins_enable()
for i = 1, 4, 1 do
gpio.mode(stepper_pins[i],gpio.OUTPUT)
end
end
function pins_disable()
-- for i = 1, 4, 1 do -- no power, all pins
for i = 2, 4, 1 do -- no power, all pins except one (to keep it in place)
gpio.mode(stepper_pins[i],gpio.INPUT)
end
end
-- turn off all pins to let motor rest
function step_stopstate()
for i = 1, 4, 1 do
gpio.write(stepper_pins[i], 0)
end
end
-- make stepper take one step
function step_take()
-- jump to the next state in the direction, wrap
step_state = step_state + step_direction
cur_step = cur_step + step_direction * FWD
if step_state > step_numstates then
step_state = 1;
elseif step_state < 1 then
step_state = step_numstates
end
-- write the current state to the pins
pins_enable()
for i = 1, 4, 1 do
gpio.write(stepper_pins[i], step_states[step_state][i])
end
-- might take another step after step_delay
step_stepsleft = step_stepsleft-1
if step_stepsleft > 0 then
-- if cur_step > 0 and cur_step < tot_steps and step_stepsleft > 0 then
tmr.alarm(step_timerid, 10, 0, step_take )
--tmr.alarm(step_timerid, 10, 0, step_take )
else
step_stopstate()
step_stop()
pins_disable()
mq.post_status()
if file.open("cfg_cur_step.lua", "w+") then
file.write("cur_step=" .. cur_step .. '\n')
file.close()
end
end
end
-- public method to start moving number of 'int steps' in 'int direction'
function step_move(steps, direction, delay)
tmr.stop(step_timerid)
step_stepsleft = steps
step_direction = direction
step_delay = delay
step_take()
end
function step_go_to(step, delay)
if step >= cur_step then
steps = step - cur_step
step_move(steps, FWD, delay)
end
if step <= cur_step then
steps = cur_step - step
step_move(steps, REV, delay)
end
end
function percent_go_to(percent, delay)
if(percent >= 0 and percent <= 100) then
step_stop()
tmr.register(status_timerid, 1000, tmr.ALARM_AUTO, function () mq.post_status() end)
tmr.start(status_timerid)
step = percent * tot_steps / 100
step_go_to(step, delay)
end
end
-- public method to cancel moving
function step_stop()
tmr.stop(step_timerid)
tmr.stop(status_timerid)
step_stepsleft = 0
step_stopstate()
end
Let's parse the error message one by one:
unprotected error in call to Lua API (wifi_setup.lua:25: bad argument #1 to 'config' (config table not found!))
Unprotected error means that you did a normal function call, as opposed to a protected call (aka pcall), which is a function call where you expect errors to occur and want to provide means to handle them. Since you did not do a protected call, Lua does not know how to handle the error and terminates right away (which is not a bad thing).
wifi_setup.lua:25 tells you the file and line at which the error occured.
bad argument #1 to 'config' means that the problem is due to the first argument passed to a function named config.
config table not found! is the error message provided by the implementer of that function.
So in summary, there is a problem with the function call wifi.sta.config(key,config.SSID[key]) as it expects a table as first argument, while you provided something different (ie. a string with the BSSID). Indeed checking with the NodeMCU docs shows that you need to pass a table to this function:
wifi.sta.config()
Sets the WiFi station configuration. [...]
Syntax
wifi.sta.config(station_config)
Parameters
station_config table containing configuration data for
station
The expected layout of the table is documented in detail on that page as well.
ComicSansMS provided a great answer the essence of which is that you need to replace
wifi.sta.config(key,config.SSID[key])
with
wifi.sta.config{ssid=key,pwd=config.SSID[key]}
Thus a standalone example could be like this:
--wifi.setmode(wifi.NULLMODE)
config = {}
config.SSID = {}
config.SSID["ssid"] = "password"
function wifi_wait_ip()
if wifi.sta.getip() == nil then
print("IP unavailable, Waiting...")
else
tmr.stop(1)
print("\n====================================")
print("ESP8266 mode is: " .. wifi.getmode())
print("MAC address is: " .. wifi.ap.getmac())
print("IP is " .. wifi.sta.getip())
print("====================================")
end
end
function wifi_start(list_aps)
if list_aps then
for key, value in pairs(list_aps) do
if config.SSID and config.SSID[key] then
wifi.setmode(wifi.STATION);
wifi.sta.config{ssid=key, pwd=config.SSID[key]}
-- wifi.sta.connect() not needed as config() uses auto-connect=true by default
print("Connecting to " .. key .. " ...")
tmr.alarm(1, 2500, 1, wifi_wait_ip)
end
end
else
print("Error getting AP list")
end
end
function start()
print("Configuring Wifi ...")
wifi.setmode(wifi.STATION)
wifi.sta.getap(wifi_start)
end
start()
What is the problem here? This code is supposed to remove texts after I shoot it and at the same time increasing the score. Also, can someone explain what does the other.name actually mean? I don't quite fully understand it..(And yes its the first if statement that has the error)
function wordCollision(e)
if (e.other.name == 'balloonText') then -- error here: attempt to index field 'other'(a nil value)
display.remove(e.other)
e.other = nil
score.text = score.text + 50
score.anchorX = 0
score.anchorY = 0
score.x = 200
score.y = 50
target.text = target.text - 1
else
if (e.other.name == 'balloonTextt') then
display.remove(e.other)
e.other = nil
score.text = score.text + 50
score.anchorX = 0
score.anchorY = 0
score.x = 200
score.y = 50
target.text = target.text - 1
end
end
end
It simply means that there is no entry with key 'other' in the table e.
If if you want to look up something in e.other you'll have to assign a table to that key:
e.other = {}
Using metatables, you could make it go automatically:
mt = {}
mt.__index=function(t,k) if ~rawget(t,k) then t[k]=setmetatable({},mt) end return t[k] end
e={}
e=setmetatable(e,mt)
e.other.name='foo'
Watch out with this though, because any lookup to a non-existant field will create a new table for it, which may or may not be what you want (aside of the fact that this overwrites any existing metatable on e):
for k,v in pairs(e) do print(k,v) end
print(e.bar)
for k,v in pairs(e) do print(k,v) end
Problem could be that you have the e.other = nil in there, but don't reset e.other to something after, so when the wordCollision() gets called again, it is e.other is nil. Could also be that e.other is never initialized in the first place. Verify it is initialized somewhere before wordCollision() is ever called, and verify that it is re-set to something between two calls to wordCollision().
I'm attempting to compare two tables of equal length with a function, since I don't know of any other way to do so. However, with the following function, it fails to register, and I've no clue why. I'm hoping someone can provide insight to this problem or has a better way of comparing the two tables.
The tables are being populated with the following code:
str = "parameters determined by program (all digits)"
tableone = {}
for word in str:gmatch("%d") do table.insert(tableone,word) end
It's identical for both tables, except, of course, the individual table names. The tables are being populated properly, and display properly when I print them. Here are two tables for the sake of this question:
tableone = {}
tabletwo = {}
for i=1,4 do table.insert(tableone, i) end
for i=1,4 do table.insert(tabletwo, i) end
Obviously, these two tables are going to be equal to each other. The function I wrote to compare the index tables is as follows:
function comparetables(t1, t2)
matchct = 0
for i=1,#t1 do
if t1[i] == t2[i] then
matchct = matchct + 1
end
if matchct == #t1 then
return true
end
end
I tried doing
print(comparetables(tableone,tabletwo))
to see if it'll print "true" but no luck. To me, it seems like it should work without a problem. Yet it doesn't. What am I missing? I've tried searching for something like a table.compare function that someone may have already written, but no such luck in finding one. Thanks for any suggestions!
Additional information:
The reason I'm comparing tables is for a mastermaind-type game. That means the following three rules must apply when comparing tables. The function I created was to just get me started, thinking I could work from there.
When comparing the tables, if the numbers match, Ccount increases by 1.
When comparing tables, if the value exists in a different index position, increment Pcount by 1
For example, with a table of values {1, 3, 3, 4} and a guess of {4, 4, 3, 1}, it would return Pcount of 2 (the one 4 and the 1) and a Ccount of 1 (the three in the third position). I think one of the hardest parts is going to be getting the comparison to recognize that the second 4 in the guess should not increment the Pcount at all.
A slight variant on your code that should work is:
function comparetables(t1, t2)
if #t1 ~= #t2 then return false end
for i=1,#t1 do
if t1[i] ~= t2[i] then return false end
end
return true
end
However I use something more like this: It checks the types of the arguments, their metatables, and a few other cases.
-- This is not clever enough to find matching table keys
-- i.e. this will return false
-- recursive_compare( { [{}]:1 }, { [{}]:1 } )
-- but this is unusual enough for me not to care ;)
-- It can also get stuck in infinite loops if you use it on
-- an evil table like this:
-- t = {}
-- t[1] = t
function recursive_compare(t1,t2)
-- Use usual comparison first.
if t1==t2 then return true end
-- We only support non-default behavior for tables
if (type(t1)~="table") then return false end
-- They better have the same metatables
local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if( not recursive_compare(mt1,mt2) ) then return false end
-- Check each key-value pair
-- We have to do this both ways in case we miss some.
-- TODO: Could probably be smarter and not check those we've
-- already checked though!
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if( not recursive_compare(v1,v2) ) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if( not recursive_compare(v1,v2) ) then return false end
end
return true
end
Here's an example of it in use:
print( recursive_compare( {1,2,3,{1,2,1}}, {1,2,3,{1,2,1}} ) ) -- prints true
print( recursive_compare( {1,2,3,{1,2,1}}, {2,2,3,{1,2,3}} ) ) -- prints false
If you're comparing objects that are more objecty than tabley in an Object oriented sense, then I'd look at implementing the functions in the lua OO way.
Something like this should do the trick:
GameState = {}
GameState.mt = {}
GameState.mt.fns = {}
GameState.mt.__index = GameState.mt.fns
function GameState.new(a,b,c,d)
-- TODO: put argument checks here...
local retval = {}
retval[1] = a
retval[2] = b
retval[3] = c
retval[4] = d
setmetatable(retval, GameState.mt)
return retval
end
function GameState.mt.fns.print( self )
print(" GameState: ", self[1], self[2], self[3], self[4] )
end
function GameState.mt.__tostring( self )
return "GameState: "..self[1].." "..self[2].." "..self[3].." "..self[4]
end
function GameState.mt.__eq(self, other)
-- Check it's actually a GameState, and all its bits match
return getmetatable(other)==GameState.mt and
(self[1] == other[1]) and
(self[2] == other[2]) and
(self[3] == other[3]) and
(self[4] == other[4])
end
Then you'd use it like this:
state1 = GameState.new(1,2,3,4)
state2 = GameState.new(1,2,3,4)
print("State 1 is:")
state1:print()
print("State 2 is:")
print(state2)
print( "state1 == state2 : ", state1 == state2 )
print( "Changing state 2")
state2[1]=2
print( "state1 == state2 : ", state1 == state2 )