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

Textutils.serialize Always Nil ?

Started by whatxDxDxD, 30 October 2013 - 03:28 PM
whatxDxDxD #1
Posted 30 October 2013 - 04:28 PM
http://pastebin.com/LcqtDt0B
Spoiler

--Info Board by Southp0le
--adjusted for 6x8 Advanced Monitor
--__work in progress
--Main Monitor Coordinates:
----x:=[1,55]
----y:=[1,27]
--Touch Monitor Coordinates:
----x:=[1,80]
----y:=[1,5]

---------------------
-------Options-------
---------------------
os.loadAPI("buttonAPI")
local serverAdmin = "Guracao"
local boardAdmin = "Southp0le"
local mon = peripheral.wrap("monitor_0")
local mon2 = peripheral.wrap("top")
local chat = peripheral.wrap("bottom")
local mod = peripheral.wrap("back")
local det = peripheral.wrap("playerDetector_0")
local filled
local page = 1
local pages = 1
local showed
backgroundColor = colors.lightBlue
textColor = colors.black
textSize = 1.5
mon.setTextColor(textColor)
mon.setBackgroundColor(backgroundColor)
mon.setTextScale(textSize)
mon2.setTextColor(textColor)
mon2.setBackgroundColor(backgroundColor)
mon2.setTextScale(textSize-0.5)

-----------------------
-------Functions-------
-----------------------
----------------
--file control--
----------------
--saves the file
function saveFile(filename)
  local file = fs.open(filename,"w")
  if filename == "list1" then
	file.write(textutils.serialize(list))
print(textutils.serialize(list))
	file.close()
	print("File '"..filename.."' saved.")
	refresh()
	return true
  elseif filename == "numbers" then
	file.write(textutils.serialize(numbers))
	file.close()
	print("File '"..filename.."' saved.")
	refresh()
	return true
  elseif filename == "deathReasons" then
	file.write(textutils.serialize(deathReasons))
	file.close()
	print("File '"..filename.."' saved.")
	refresh()
	return true
  else
	return false
  end
end

--loads the file and returns the file
function loadFile(filename)
  local file = fs.open(filename,"r")
  local data = file.readAll()
  file.close()
  if string.len(data) > 0 then
	print("File '"..filename.."' loaded.")
	return textutils.unserialize(data)
  else
	return {}
  end
end

--checks if theres a file, creates one if not
function checkFile(file)
  if fs.exists(file) == false then
	if saveFile(file) then
	  print("Created a new file: " ..file..".")
	end
  else
	print("Everything fine, file '" ..file.."' exists already.")
  end
end
----------
--Tables--
----------
local list = {}
local numbers = {}
local deathReasons = {}
--creates table "list" with all infomation (overloaded)
function setTable(...)
  local n = length()+1
  local arguments = {select(1,...)}
  local player = arguments[1]
  numbers[n] = player
  deathReasons[getNumber(player)] = {}
  if table.getn(arguments) == 1 then
	list[n] = {}
	list[n]["player"] = arguments[1]
	list[n]["deaths"] = 0
	list[n]["kills"] = 0
	list[n]["ratio"] = "-"
	list[n]["logins"] = 0
	list[n]["messages"] = 0
	list[n]["average"] = 0
	print("Player '" ..player.. "' successfully created.")
  elseif table.getn(arguments) == 7 then
	list[n] = {}
	list[n]["player"] = arguments[1]
	list[n]["deaths"] = arguments[2]
	list[n]["kills"] = arguments[3]
	list[n]["ratio"] = arguments[4]
	list[n]["logins"] = arguments[5]
	list[n]["messages"] = arguments[6]
	list[n]["average"] = arguments[7]
	print("Player '" ..player.. "' successfully created.")
  else
	print("Error: setTable: Wrong parameter passed to function 'setTable'!")
  end
end
--returns lenght of list
function length()
  local n = 0
	if list ~= nil then
	  for k in pairs(list) do
		n = n +1
	  end
	end
  return n
end
--returns related number from player
function getNumber(player)
  if numbers ~= nil then
	for k in ipairs(numbers) do
	  if numbers[k] == player then
		return k
	  end
	end
  else
	print("Error: getNumber: No players!")
  end
end
--checks if player exists
function checkPlayer(player)
  local x = 0
  if numbers ~= nil then
	for k in pairs(numbers) do
	  if numbers[k] == player then
		x=x+1
	  end
	end
  end
  if x > 0 then
	return true
  else
	return false
  end
end
--removes table-entry
function table.removePlayer(player)
  local key = getNumber(player)
  if checkPlayer(player) then
	table.remove(list, key)
	table.remove(numbers, key)
	if deathReasons[key] ~= nil then
	  table.remove(deathReasons, key)
	end
	saveFile("list1")
	saveFile("numbers")
	saveFile("deathReasons")
	print("Player " ..player.. " successfully removed.")
  else
	print("Error: removePlayer: Player '" ..player.. "' could not be removed. Is the name written correctly?")
  end
end
--creates a dummy-player for comparative
--removes it if no longer needed
function dummy()
  local number = length()
  --setTable("dummy")
  print("Players:")
  if number == 0 then
	setTable("dummy")
  end
  if list ~= nil then
	for k in ipairs(list) do
	  print(list[k]["player"])
	end
  else
	print(" ")
  end
  if number>1 and checkPlayer("dummy") then
	table.removePlayer("dummy")
  end
  print("There're " ..number.. " Players on the server.")
end
--adds a new player to the list
function addPlayer()
  while true do
	event,newcomer = os.pullEvent("player")
	local x = 0
	if checkPlayer(newcomer) then
	  chat.tell(newcomer,"Error: "..newcomer..", you're already listed in the database. Don't you remember, Idiot?")
	elseif checkPlayer(newcomer) == false then
	  chat.tell(newcomer,"Congratulations! Welcome to this great server, now you're listed in the database. Have fun, "..newcomer..".")
	  setTable(newcomer)
	  saveFile("numbers")
	  saveFile("list1")
	else
	  chat.say("Well this shouldn't happen, please report this error to "..serverAdmin.." or " ..infoboardAdmin..".")
	  chat.say("No joke!")
	end
  end
end

--calculates kill/death ratio
function ratio(player)
  if list[player]["deaths"] == 0 then
	list[player]["ratio"] = "-"
  else
	list[player]["ratio"] = tonumber(list[player]["kills"])/tonumber(list[player]["deaths"])
  end
end

--calculates average messagelenght
function average(player, message)
  list[player]["average"] = (tonumber(list[player]["average"]))*(tonumber(list[player]["messages"])-1)
  list[player]["average"] = (tonumber(list[player]["average"]) + string.len(message)) / (tonumber(list[player]["messages"]))
end
function sortByDeathsH(x, y)
  return x.deaths > y.deaths
end
function sortByKillsH(x, y)
  return x.kills > y.kills
end
function sortByLoginsH(x, y)
  return x.logins > y.logins
end
function sortByMessagesH(x, y)
  return x.messages > y.messages
end
function sortByAverageH(x, y)
  return x.average > y.average
end
function sortByRatioH(x,y)
  return x.ratio > y.ratio
end
function sortByDeathsL(x, y)
  return x.deaths < y.deaths
end
function sortByKillsL(x, y)
  return x.kills < y.kills
end
function sortByLoginsL(x, y)
  return x.logins < y.logins
end
function sortByMessagesL(x, y)
  return x.messages < y.messages
end
function sortByAverageL(x, y)
  return x.average < y.average
end
function sortByRatioL(x,y)
  return x.ratio < y.ratio
end
function sortByReasons(x, y)
  return x.k > y.k
end
function sortAll (state)
  if state == "max1" then
	table.sort(list,sortByKillsH)
	buttonAPI.toggleAll(" Max  ")
  elseif state == "min1" then
	table.sort(list,sortByKillsL)
	buttonAPI.toggleAll(" Min  ")
  elseif state == "max2" then
	table.sort(list,sortByDeathsH)
	buttonAPI.toggleAll("Max ")
  elseif state == "min2" then
	table.sort(list,sortByDeathsL)
	buttonAPI.toggleAll("Min ")
  elseif state == "max3" then
	table.sort(list,sortByRatioH)
	buttonAPI.toggleAll(" Max ")
  elseif state == "min3" then
	table.sort(list,sortByRatioL)
	buttonAPI.toggleAll(" Min ")
  elseif state == "max4" then
	table.sort(list,sortByMessagesH)
	buttonAPI.toggleAll("Max")
  elseif state == "min4" then
	table.sort(list,sortByMessagesL)
	buttonAPI.toggleAll("Min")
elseif state == "max5" then
	table.sort(list,sortByMessagesH)
	buttonAPI.toggleAll("Max ")
  elseif state == "min5" then
	table.sort(list,sortByMessagesL)
	buttonAPI.toggleAll("Min ")
  elseif state == "max6" then
	table.sort(list,sortByLoginsH)
	buttonAPI.toggleAll("Max")
  elseif state == "min6" then
	table.sort(list,sortByLoginsL)
	buttonAPI.toggleAll("Min")
  end
  saveFile("list1")
  refresh()
end

-----------
--Monitor--
-----------
function firstToUpper(str)
	return (str:gsub("^%l", string.upper))
end
function refresh()
  mon.clear()
  mon2.clear()
  screen()
  buttonAPI.screen()
end
--creates labels
function label(xpos,ypos, text)
  mon.setCursorPos(xpos, ypos)
  mon.write(text)
end
--prints everything on the monitor
function printAll()
  mon2.clear()
  local y = 4
  if filled == "login" then
	label(2,2,"Player:")
	label(27,2, "Logins:")
	for k in pairs(list) do
	  mon.setCursorPos(3,y)
	  mon.write(list[k]["player"])
	  mon.setCursorPos(29,y)
	  mon.write(tostring(list[k]["logins"]))
	  y=y+1
	end
  elseif filled == "sortKill" then
	label(2,2,"Player:")
	label(15,2, "Kills:")
	label(30,2, "Deaths:")
	label(45,2, "Ratio:")
	for k in pairs(list) do
	  mon.setCursorPos(3,y)
	  mon.write(list[k]["player"])
	  mon.setCursorPos(16,y)
	  mon.write(tostring(list[k]["kills"]))
	  mon.setCursorPos(31,y)
	  mon.write(tostring(list[k]["deaths"]))
	  mon.setCursorPos(46,y)
	  mon.write(tostring(list[k]["ratio"]))
	  y=y+1
	end
  elseif filled == "sortMessage" then
	label(2,2,"Player:")
	label(22,2, "Messages:")
	label(37,2, "average Length:")
	for k in pairs(list) do
	  mon.setCursorPos(3,y)
	  mon.write(list[k]["player"])
	  mon.setCursorPos(23,y)
	  mon.write(tostring(list[k]["messages"]))
	  mon.setCursorPos(38,y)
	  mon.write(tostring(list[k]["average"]))
	  y=y+1
	end
  elseif filled == "sortReasons" then
	n = getNumber(showed)
	label(2,2, tostring(showed))
	label(20,4, "Reasons:")
	label(45,4, "Deaths:")
	y=y+2
	if deathReasons[n] ~= nil then
	  if #deathReasons[n] > 0 then
		for k in pairs(deathReasons) do
		  mon.setCursorPos(3,y)
		  mon.write(tostring(deathReasons[n][k]))
		  mon.setCursorPos(21,y)
		  k=k+1
		  mon.write(tostring(deathReasons[n][k]))
		  y=y+1
		end
	  end
	end
  end
  label(55,27,"by Southp0le")
end

function screen()
  if filled ~= "button" then
	printAll()
  end
end
function toggleScreen(change)
  buttonAPI.toggleAll(change)
  refresh()
end

function nextPage()
   if page+1 <= pages then
	 page = page+1
   end
   fillReasons()
end

function prevPage()
   if page-1 >= 1 then
	 page = page-1
   end
   fillReasons()
end
function goBack()
  fillReasons()
end

function fillButton()
  buttonAPI.clearTable()
  buttonAPI.setTable("Kill/Death", fillSortKill,"", 2, 15, 2, 4)
  buttonAPI.setTable("Death Reasons", fillReasons,"", 18, 34, 2, 4)
  buttonAPI.setTable("Messages", fillSortMessages,"", 37, 48, 2, 4)
  buttonAPI.setTable("Time", time,"", 70,79,2,4)
  filled = "button"
  refresh()
end
function fillReasons()
  buttonAPI.clearTable()
  players = length(numbers)
  local npp = 3 --names per page
  local x = 0
  pages = math.ceil(players/npp)
  local currName = 0
  for k in pairs(numbers) do
	currName = currName + 1
	if currName > npp*(page-1) and currName < npp*page+1 then
	  buttonAPI.setTable(numbers[k], fillSortReasons, numbers[k], 28+x, 38+x ,2 ,4)
	  x=x+10
	end
  end
  buttonAPI.setTable("Home", fillButton, "", 2, 7, 2, 4)
  buttonAPI.setTable("Next Page", nextPage, "", 10, 20, 1, 2)
  buttonAPI.setTable("Prev Page", prevPage, "", 10, 20, 4, 5)
  buttonAPI.label(70,3, "Page: ")
  filled = "reasons"
  refresh()
end
function fillSortKill()
  buttonAPI.clearTable()
  buttonAPI.setTable(" Min  ", sortAll, "min1", 22, 28, 4, 5)
  buttonAPI.setTable(" Max  ", sortAll, "max1", 22, 28, 1, 2)
  buttonAPI.setTable("Min ", sortAll, "min2", 44, 50, 4, 5)
  buttonAPI.setTable("Max ", sortAll, "max2", 44, 50, 1, 2)
  buttonAPI.setTable(" Min ", sortAll, "min3", 66, 72, 4, 5)
  buttonAPI.setTable(" Max ", sortAll, "max3", 66, 72, 1, 2)
  buttonAPI.setTable("Home", fillButton, "", 2, 7, 2, 4)
  filled = "sortKill"
  refresh()
end
function fillSortReasons(display)
  buttonAPI.clearTable()
  showed = display
  buttonAPI.setTable("Back", goBack, "", 9, 14, 2, 4)
  buttonAPI.setTable("Home", fillButton, "", 2, 7, 2, 4)
  filled = "sortReasons"
  refresh()
end
function fillSortMessages()
  buttonAPI.clearTable()
  buttonAPI.setTable("Min",sortAll, "min4", 35, 41, 4, 5)
  buttonAPI.setTable("Max",sortAll, "max4", 35, 41, 1, 2)
  buttonAPI.setTable("Min ",sortAll, "min5", 60, 66, 4, 5)
  buttonAPI.setTable("Max ",sortAll, "max5", 60, 66, 1, 2)
  buttonAPI.setTable("Home", fillButton, "", 2, 7, 2, 4)
  filled = "sortMessage"
  refresh()
end
function fillSortLogins()
  buttonAPI.clearTable()
  buttonAPI.setTable("Min",sortAll, "min6", 26, 32, 4, 5)
  buttonAPI.setTable("Max",sortAll, "max6", 26, 32, 1, 2)
  buttonAPI.setTable("Home", fillButton, "", 2, 7, 2, 4)
  filled = "sortLogins"
  refresh()
end

------------------------
--while true functions--
------------------------
--counts deaths, kills and deathreasons
function deathcount()
  while true do
	event,dead,killer,cause = os.pullEvent("chat_death")
	number1 = getNumber(dead)
	number2 = getNumber(killer)
	list[number1]["deaths"] = tonumber(list[number1]["deaths"])+1
	ratio(number1)
	if killer~=nil then
	  for k in pairs(list) do
		if killer == list[k]["player"] then
		  list[number2]["kills"] = tonumber(list[number2]["kills"])+1
		  ratio(number2)
		end
	  end
	  local x = 0
	  local i
	  for i=1, table.getn(deathReasons), 1 do
		for j=1, table.getn(deathReasons[number1]), 1 do
		  if deathReasons[i][j] == killer then
			deathReasons[i][j+1] = deathReasons[i][j] + 1
			x = x+1
		  end
		end
	  end
	  if x == 0 then
		number = getNumber(dead)
		deathReasons[number1][table.getn(deathReasons[number1])+1] = killer
		deathReasons[number1][table.getn(deathReasons[number1])+1] = 1
	  end
	end
	chat.tell(dead, "You died " ..list[number1]["deaths"].. " times.")
	table.sort(deathReasons[number1], sortByReasons)
	saveFile("deathReasons")
	saveFile("list1")
  end
end

--counts numbers of messages
--includes maintenance-options
function messagecount()
  while true do
	event, player, message = os.pullEvent("chat")
	local number = getNumber(player)
	list[number]["messages"] = tonumber(list[number]["messages"])+1
	average(number,tostring(message))
	saveFile("list1")
	if player == boardAdmin and message == "createPlayer" then
	  chat.tell(boardAdmin,"Waiting for information to create player: Name, Kills, Deaths, Ratio, Logins, Messages, Average")
	  print("Waiting for information to create player: Name, Kills, Deaths, Ratio, Logins, Messages, Average")
	  event, player, message = os.pullEvent("chat")
	  if player == boardAdmin then
		local string = message
		local x = 0
		for word in string.gmatch(string, "%w+") do
		  if x == 0 then
			name = word
			print("Player: " ..word)
		  elseif x == 1 then
			kills = tonumber(word)
			print("Kills: " ..word)
		  elseif x == 2 then
			deaths = tonumber(word)
			print("Deaths: " ..word)
		 elseif x == 3 then
			ratio = tonumber(word)
			print("Ratio: "..word)
		  elseif x == 4 then
			logins = tonumber(word)
			print("Logins: " ..word)
		  elseif x == 5 then
			messages = tonumber(word)
			print("Messages: " ..word)
		  elseif x == 6 then
			average = tonumber(word)
			print("Average Lenght: " ..word)
		  else
		  print("Error: messagecount: createPlayer: String-message not matching")
		  end
		  x=x+1
		end
	  end
	  setTable(name,deaths,kills,ratio,logins,messages,average)
	  saveFile("list1")
	  saveFile("numbers")
	  saveFile("deathReasons")
	elseif player == boardAdmin and message == "deletePlayer"then
	  chat.tell(boardAdmin,"Waiting for information to delete player: Name")
	  print("Waiting for information to delete player: Name")
	  event, player, message = os.pullEvent("chat")
	  table.removePlayer(tostring(message))
	  saveFile("list1")
	  saveFile("numbers")
	  saveFile("deathReasons")
	end  
  end
end
--checks for monitortouch
function getClick()
  while true do
	event,side,x,y = os.pullEvent("monitor_touch")
	if side == "top" then
	  buttonAPI.checkxy(x,y)
	end
  end
end
--function with all pullEvents
function mainProcess()
	parallel.waitForAny(deathcount, messagecount, addPlayer, getClick)
end
-----------
--Process--
-----------
--lifeControl()
checkFile("list1")
checkFile("numbers")
checkFile("deathReasons")
if loadFile("list1") == nil then
   list = {}
else
  list = loadFile("list1")
end
if loadFile("numbers") == nil then
  numbers = {}
else
  numbers = loadFile("numbers")
end
if loadFile("deathReasons") == nil then
  deathReasons = {}
else
  deathReasons = loadFile("deathReasons")
end
fillButton()
dummy()
refresh()
mainProcess()

So I ran into a weird problem, I don't get it. Maybe I just looked at it for too long. So the program is mostly done but somehow the saving is broken.
The program needs to save three tables. While I was working on the code for the sorting it seemed to work (not sure if it did). Then in a testing round I got a problem with the saveFile/loadFIle (I think). The program runs normally, I can add some fake players with messagecoun() and myself with the player detector. The program saves the tables and everything works fine. When I terminate the program and "edit" the files to see if everything worked fine, there's always "nil" written in the file. So I'm a little bit confused, because the tables weren't nil when the program saved them (at this moment the players and their stats were in the tables).
I hope someone can help me :D/> thanking you in anticipation.
LBPHacker #2
Posted 30 October 2013 - 04:35 PM
That's because you declare the function before declaring the variables you're trying to serialize. When you declare the function, Lua doesn't know that those variables are local, so it treats them as global variables. And e.g. list cannot be found in the global scope, of course; hence the error. The solution is telling Lua that list is local (only once!):
local list

-- delcare functions which use list

list = {} -- don't use local here
whatxDxDxD #3
Posted 30 October 2013 - 05:09 PM
oh.. :blink:/> didn't think about that. not even a second xD thank you so much :D/>
I really hope one day, I can return the favour. ^^