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

Prototype for Metatable Error

Started by FellousT, 29 December 2016 - 10:33 PM
FellousT #1
Posted 29 December 2016 - 11:33 PM
Background - I am currently trying to make a personal API for easier handling networks of three or more computers that are constantly communicating with each other. The API lets me create a "network" object with a metatable and then use methods of the "network" metatable to handle the networking between computers.

Latest Version of Code - 12/29/16 @ 5:00 P.M.
Filename: "network"

-- Network API, ComputerCraft Networking API

local tSide = {"left", "right", "front", "back", "top", "bottom"}

local function mapPeripherals() -- returns list of all peripherals with side as key
  local tPeripheral = {}

  for i, side in pairs(tSide) do
    if peripheral.isPresent(side) then
      if peripheral.getType(side) == "modem" then
        if peripheral.wrap(side).isWireless() then
          tPeripheral[side] = peripheral.getType(side) .. "-t"
        else
          tPeripheral[side] = peripheral.getType(side) .. "-f"
        end
      else
      tPeripheral[side] = peripheral.getType(side)
    end
    else
      tPeripheral[side] = nil
    end
  end

  return (tPeripheral)
end

function getKey(pTable, value) -- getKey(table, value) returns key for value in table
  local lTable = {}
  for key, value in pairs(pTable) do
    lTable[value] = key
  end
  return (lTable[value])
end

local function getPeripheral(pass, ...) -- getPeripheral(String peripheral type, number [peripheral index], [boolean wirelessModem)  returns side for specified perihperal
  local tArgs = {...}
  local passCount = tArgs[1]
  local wirelessModem = tArgs[1]
  local pIndex = {}
  local PMAP = mapPeripherals()

  if type(passCount) == "boolean" then
    wirelessModem = passCount
    passCount = 1
  end

  if wirelessModem == nil then wirelessModem = true end
  if passCount == nil then passCount = 1 end

  if pass == "modem" then
    if wirelessModem then
      pass = pass .. "-t"
    else
      pass = pass .. "-f"
    end
  end


  for key, side in pairs(PMAP) do
    if PMAP[key] == pass then
      table.insert(pIndex, key)
    end
  end

  if #pIndex == 0 then
    return false
  elseif #pIndex == 1 then
    return getKey(PMAP, pass)
  elseif passCount > #pIndex then
    return pIndex[#pIndex]
  else
    return pIndex[pCount]
  end
end

local function getPlatform() -- returns {string platform "turtle" "computer", boolean advanced}

  local plat
  local adv

  if turtle then
    platform = "turtle"
  else
    platform = "computer"
  end

  if term.isColor() then
    advanced = true
  else
    advanced = false
  end

  return {platform, advanced}
end

local function getNetworkID(length) -- getNetworkID(number length) returns random networkID of "length" characters

  local available = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
  local final

  for i = 1, length do
    final = final .. available[math.random(36)]
  end

  return final
end

local function getMod() -- returns false if no wireless modem else returns getPeripheral("modem", true)
  if not getPeripheral("modem", true) then
    return false
  else
    return getPeripheral("modem", true)
  end
end

-- The following section defines socket type objects      ASSOCIATE
-- ** Attributed to key:value that only apply when "doAuth" is set to true

local prototype = {                        -- Defines prototype for network
    __index = {
    ["hostname"] = "NET-"..os.getComputerLabel().."@"..os.getComputerID(), 
    ["connections"] = {},                                                            -- Defines index for clients, which holds list of computer/turtle IDs that have access to the network
    ["doAuth"] = true,                                                               -- Defines access type, if false then connections do not need authentication
    ["networkID"] = getNetworkID(10),                                                -- Defines "hopefully" unique networkID or name for referencing a network
    ["authKey"] = nil,                                                               -- **Defines the authentication key for establishing connections
    ["authTimeuot"] = 100,                                                           -- **Defines length of time (seconds) before a connection is removed
    ["open"] = false,                                                                -- Defines the state of the network, whether it is open for connections or not
    ["receiveQue"] = {},                                                             -- Defines a buffer for messages to be processed
    ["sendQue"] = {},                                                                -- Defines a buffer for messages to be sent
    }
  }

local network = {}                                                              -- Used to create network.new method

local function network:new(self, pass)                                          -- Establishes network.new method
  setmetatable(pass, prototype)
  return pass
end

local function network:establish(self)                                          -- Establishes new network based on configured settings
  if not getMod() then
    return "NO_MODEM"
  elseif rednet.lookup(self.networkID) ~= nil then
    return "NETWORK_OCCUPIED"
  else
    rednet.open(getMod())
    self.open = true
  end
  --parallel.waitForAll(network.receiveQue, network.sendQue())
end

local function network:receiveQue(self)                                         -- Appends messages to the recieved que
  while self.open do
    local senderID, message, networkID = rednet.receive()
    self.receiveQue[#self.receiveQue + 1] = {senderID, message, networkID}
  end
end

local function network:sendQue(self)                                            -- Processes and routes messages from the send que
  while self.open do
    if self.sendQue[1] ~= nil then
      que = self.sendQue[1]
      if #que == 3 then
        rednet.send(que.recipient, que.message, que.networkID)
      elseif #que == 2 then
        rednet.broadcast(que.message, que.networkID)
      else
        rednet.broadcast(que.message, self.networkID)
      end
      table.remove(self.sendQue, 1)
    end
  end
end

local function network:queSend(self, que)                                               -- Modifyer method for self.sendQue
  table.insert(self.sendQue, que, #self.sendQue)
end

Question - So the problem I seem to be having is when I call this file from the in game terminal I get an error saying "bios:14: [string "network"]:136: "(" expected". To correct this error all I had to do was take away the ":functionName" from the network table functions but for obvious reasons I need to have those functions names following the table. Maybe I have misunderstood how to put functions into tables but I have searched many forums (both ComputerCraft and LUA forums alike) as well as looked up tutorials, videos, examples, etc. and still can't seem to find my problem. Keep in mind that I haven't even attempted to call any of these function either, I have only tried calling the "network" file from the CC terminal so please point out any other errors you might find. Any help/criticism would be appreciated.

Note - If you have any other questions regarding the code, or if you need clarification please ask.
Bomb Bloke #2
Posted 30 December 2016 - 01:50 AM
You can't localise keys placed within a table. You also need not use "self" in your incoming parameter list when using colon notation for the function declaration.

Eg change this sort of thing:

local function network:sendQue(self)

… to either this:

function network:sendQue()

… or this:

function network.sendQue(self)
Edited on 30 December 2016 - 12:53 AM
KingofGamesYami #3
Posted 30 December 2016 - 01:53 AM
You can't make a table index local.
FellousT #4
Posted 30 December 2016 - 04:55 AM
You can't localise keys placed within a table. You also need not use "self" in your incoming parameter list when using colon notation for the function declaration.

Eg change this sort of thing:

local function network:sendQue(self)

… to either this:

function network:sendQue()

… or this:

function network.sendQue(self)

Thankyou very much! Never occurred to me that local might be affecting the functions. Also, if I use dot notation (object.new) do I need to pass self into the function?
Bomb Bloke #5
Posted 30 December 2016 - 07:34 AM
Also, if I use dot notation (object.new) do I need to pass self into the function?

Yes. This goes whether you're defining or calling your function; if you want "self", you need to specify it if you use a period, but it's implied if you use a colon.