Skip to content

GERTi API

MajorGeneralRelativity edited this page Nov 28, 2021 · 8 revisions

GERTi (Globally Engineered Routing Technology internal) handles passing messages between computers in a network, and for integration into other networks. GERTi networks connect to other GERTi networks and other GERTe services through a GERTe Gateway which bridges a GERTi network to the GERTe network. This page documents the GERTi API. An example program can be found at https://github.com/GlobalEmpire/GERT/blob/master/GERTi/GERTiTestApplication.lua .

Documentation on how to phsyically set up a GERTi network can be found at https://github.com/GlobalEmpire/GERT/wiki/Setting-up-a-GERT-Network .

Documentation on the GERTi backend can be found at https://github.com/GlobalEmpire/GERT/wiki/Custom-GERTi-Implementations .

Documentation as to how the MNCAPI differs from the standard API can be found at https://github.com/GlobalEmpire/GERT/wiki/MNCAPI

Terms:

GERTc address: Globally Engineered Routing Technology complete: This is the designation for a GERTi network connected to the GERTe internetwork through the GERTiMNC. A GERTc address is a universally unique address specifying a particular GERTi computer on a GERTe endpoint. 0000.1999:0123.0456 would mean GERTi client 0123.0456 on the GERTe endpoint 0000.1999

GERTiAPI: The API is the frontend of GERTi. It, and the backend, resides in GERTiClient.lua, and consists of the functions developers can call.

GERTi MNC: The Master Network Controller. Only one can exist per network, and it controls all the clients, converts address, and can participate in connection pathfinding. It runs GERTiMNC.lua. It also links the GERTi network to the GERTe network if GERTe functionality is enabled.

GERTi Node: Each computer running GERTiClient.lua

Connection ID Connection ID's are functionally equivalent to OC modem ports, or TCP ports. They are named Connection ID's and not ports to avoid confusion with standard OC modem ports.

MNCAPI The MNCAPI can be found at https://github.com/GlobalEmpire/GERT/blob/master/GERTi/Modules/MNCAPI.lua . It is only installed on the MNC and performs a similar function to GERTiClient, and shares API functions.

 

The GERTi API:

For programs and users to interact with GERTi (and GERTe), they will need to use the GERTiClient API in their programs. To do so, merely include GERTiClient.lua in your /lib folder, require the API, and it will automatically load and try to join a network. Documentation of each public function in the API follows:

GERTi.getAddress(): floating point number

This returns the GERTi address of the client. It will be in the format of xxxx.yyyy, with leading 0's deleted. Examples include: 0.1 and 105.4091. Addresses start at 0.1 and increment up to 4095.4095, which allows for over 16 million clients connected to a single GERTi network.

GERTi.getAllServices(): table

This queries the MNC for all network services running on the network. A table is returned with each key being the name of the network service, and each value being the ConnectionID for that network service. Network services run on the MNC (0.0).

GERTi.getConnections():table

This returns a table of all currently open connections on the computer, whether it is the origination or destination. Connections are subtabled based on a Connection Index, which is formed in the manner of Origination.."|"..Destination.."|"..ConnectionID. There are sub indexes of origin, destination, and ID. If the connection's destination is the computer calling getConnection, the order sub index is available, and it can be used to determine how many packets have been received. If the connection's destination is not the computer calling getConnections, nextHop and port sub indexes are available. They describe the modem address and modem port of the next node in the connection path. An example would be:

local myTable = GERTi.getConnections()
local connectionDestination = myTable["0.1|0.2|1"]["order"]

This will get the number of packets received originating from the computer with an address of 0.1, with a destination of the computer with an address of 0.2, and with a connectionID of 1.

GERTi.getDNSCache(): table

This function returns a table of all cached DNS addresses. A table is returned with each key being the DNS Name of a node, and the value being the GERTi address of the node.

GERTi.getEdition(): string

This function returns a string with the edition of GERTiClient running. The standard GERTiClient for OpenOS will return "BasicClient". Different clients for OpenOS and clients for other operating systems can return different strings.

GERTi.getHostname():string

This function returns the hostname of the computer. If a hostname has not been configured or DNS is not enabled, this will return nil.

GERTi.getLoadedModules(): table

This funciton returns a table of all currently enabled modules. This table is generated at client startup and is in the same format as getAllServices.

GERTi.getNeighbors(): table

This returns a table of neighboring GERTi Clients that can be directly contacted. The table is indexed by the GERTi address of each computer, with the subfields being "add" for the modem address, "port" for the port used to connect to that computer (useful for telling whether it is a modem or tunnel connection), and "tier" being the tier of the computer, ranging from 1 to 3 inclusive for other clients. A lower number indicates that it is logically closer to the MNC.

local myTable = GERTi.getNeighbors()
local node1 = myTable[0.1]
print("Node modem address is "..node1["add"]..", the port is "..node1["port"]..", and the tier is "..node1["tier"])

GERTi.getVersion(): string and formatted string

This returns a string with the version number (Major.Minor.Patch) and a string that designates the version and build number. local versionString, versionNumber = GERTi.getVersion() print("Version String is "..versionString)

The above example will accept both the string and semantic string from the function. The first string will always return with a version formatted in accordance with semantic versioning, while the formatting of the second string can vary depending on version and build status.

GERTi.isServicePresent(name): number

This function queries the MNC for the presence of the named network service. If successful, it will return the connection ID of the specified network service. If unsuccessful, it will return false

GERTi.registerModule(Modulename, port): does not return

This will update the local modules table with the name and connectionID of a new module. This function only updates the local modules table and the information is not relayed across the network.

GERTi.resolveDNS(hostname): number

This function will attempt to resolve the provided hostname. The DNS cache will be checked first, and the MNC will be queried if the cache does not contain the appropriate binding. If DNS is not active on the network or the binding cannot be resolved, the function will return nil. Otherwise, it will return the GERTi address of the provided hostname.

GERTi.removeDNSRecord(hostname): does not return

This function tells the MNC to remove a binding of a hostname to the sending computer's GERTi address. If the binding is found on the MNC, it will be removed. If the binding is either for another computer or is not present, no changes will occur.

GERTi.updateDNSRecord(hostname): does not return

This function tells the MNC to add or update the binding of a hostname to the sending computer's GERTi address. If the binding is new, it will be added. If the binding is present and is bound to the sending computer's GERTi address, it will be updated. If the binding is present and not already bound to the sending computer's GERTi address, no changes will occur.

GERTi.send(): does not return

This function allows you to send messages directly to a neighboring computer. This offers lower latency than opening a socket, but does not offer buffering and only works with computers that can directly speak to each other. The function takes two arguments.

  1. Destination: The GERT address of the receiving computer.
  2. Data: The data you want to send. It must be transmittable over a modem card, so unserialized tables and functions are not supported. To send a table, please serialize it first and then use GERTi.send()

GERTi.send(0.1, "Hello!")

The above example will send a message to the neighboring computer of 0.1, with the connectionID -1, and with the message "Hello." A computer on the receiving end merely needs to listen to GERTData events to see the message. For information on using the event system, please see the "Using Events" section of this page.

GERTi.broadcast(): does not return

This function emulates modem.broadcast() but with GERTi packets. Calling this function will send a GERTData packet to all neighboring computers over the primary modem connection, which is the component returned by calling component.modem.

GERTi.broadcast("Hello")

The above example will broadcast "Hello" to every computer with a modem connection to the sender. The connection ID on the receiving computer will be -1.

GERTi.openSocket(): socket object

This is the main function used with GERTi to open a socket to another computer to read and write data. Further details on how to manage a socket afterwards is covered in the Managing Sockets section of this page. openSocket takes 2 arguments.

  1. The GERT address or DNS name of the destination. The GERT address may either be a GERTc address (e.g. 1865.1731:0.1) if you want to talk across GERTe, or a GERTi address (e.g. 1.105) for talking on the same GERTi network. The DNS name option is only available if DNS is available on the network. If DNS is available, the DNS name is resolved to a GERT address (using the DNS Cache if possible), or a resolution request is made to the DNS Server. If DNS is not available, openSocket will immediately return false.
  2. ConnectionID. Optional. If a specific ID is required, like when a computer needs to open a socket to another computer that has opened a socket first, it may be an integer. If a specific ID is not required, a value need not be passed in. ConnectionIDs are like ports; see Terms for explanation.

Example:

local socket1 = GERTi.openSocket(0.2, 5)
local socket2 = GERTi.openSocket("1095.1095:1096.1096", 1500)
local socket 3 = GERTi.openSocket("TestComputer2", 5)

The first line will open a socket over the local GERTi network to the computer with an address of 0.2, and on Connection ID 5. The second line will open a socket over the GERTe internetwork to GERTe endpoint 1095.1095, and the GERTi Node on that endpoint with the address of 1096.1096. The Connection ID in that connection is 1500. The third line will attempt to open a socket to the computer with the DNS name of "TestComputer2".

GERT events

GERT uses 2 events to allow for asynchronous networking and message processing.

  1. GERTConnectionID (eventName, originAddress, connectionID) This event is fired whenever a connection is opened to the computer. In addition to the event name, it provides the originAddress and connectionID parameters, which identify what computer open the connection and what connectionID they used. This can be useful for a server providing services, as it can wait for a client to open a connection, and then open a socket to the client with the same connectionID.
local newSocket
local function waitForSocket(eventName, originAddress, connectionID)
  print("Received an incoming connection from computer: "..originAddress.." with connectionID: "..connectionID)
  newSocket = GERTi.openSocket(originAddress, true, connectionID)
end
event.listen("GERTConnectionID", waitForSocket)
  1. GERTData (eventName, originAddress, connectionID, [data]) This event is fired whenever a Data packet is delivered to this computer. It only fires on the endpoints of a connection, not any intermediary relays. If the packet is part of a connection between two computers, the last data argument is omitted, and the message must be read by a socket connected to the origin computer over the same address.
local function waitForData(eventName, originAddress, connectionID)
  print("Received and incoming data packet from computer: "..originAddress.." for connectionID: "..connectionID)
  local data = socket1:read()
  print(data[1])
end
event.listen("GERTData", waitForData)

This function will fire whenever a data packet is received by the computer. A more advanced function can incorporate address and connectionID sorting so that only the correct socket gets read. After the function is called, it prints the origin address and connectionID, then reads the socket. The data is then printed to the screen. For more details on operating sockets, please see the following section.

local function waitForData(eventName, originAddress, connectionID, data)
  print("Received and incoming data packet from computer: "..originAddress.." for connectionID: "..connectionID)
  print("The received data is: "..data)
end
event.listen("GERTData", waitForData)

This function will also fire whenever a data packet is received by the computer, but it assumes that the incoming data packet was sent by calling GERTi.send() on the transmitting computer. As GERTi.send() does not rely on sockets, the data is embedded into the event as the 4th argument. The function retrieves that and prints the data directly. GERTi.send() data packets always have a Connection ID of -1, so they can be filtered accordingly.

  1. GERTConnectionClose (eventName, originAddress, destinationAddress, ID) This event is fired whenever a connection is closed, with either the origination or destination being this node. Programs can monitor this event to ensure that they can react appropriately to unexpected connection closures. It will return 4 arguments, the first being the event name, the second being the GERTi address where the connection is originating from, the third being the GERTi address where the connection is made to, and the fourth being the connection ID. Either the origination or destination addresses will be the node getting the event.

Managing a GERT Socket

For the purposes of this section, we will assume you have a socket variable named "socket," with a destination of address 0.2, and an origination of address 0.1. The connection ID for this socket is 1. After opening the socket, you interact with the data stream by calling functions provided by the socket. A GERT socket supports 3 simple operations.

  1. socket:read(["flag flag ..."]) This function is to read data from the socket. If no data packets are available or there is no incoming connection from the other end yet, an empty table is returned. Otherwise, a table of data packets with numerical indices from 1 to 20 is returned. If 20 stored packets are present in the socket buffer when another one arrives, the oldest packet will be dropped and the others will be pushed forward in the queue. GERTi supports message re-ordering, and if a message is received and determined to be out of order, the buffer will be re-ordered accordingly. If maximum reliability is desired, a slight delay before destructively reading the buffer to allow for all messages to be received and ordered is recommended.

Flags:

-k: This flag tells the socket to avoid clearing the buffer after the data is returned. Therefore, multiple read operations can be performed to retrieve the same data from the buffer

-f: This flag "flattens" the table. Normally packets with more than one element (i.e. from socket:write operations where more than one parameter was passed) have a subtable with each element in numerical order. This would require it to be accessed like with socket:read()[1][2] to get the second element of the first data packet. -f flattens the table and expands multi-element packets to instead take up multiple indices in the main data table. For example, the previous table could also be accessed by calling socket:read("-f")[2]

local data = socket:read("-k")
print("The first data packet is: "..data[1])
local data = socket:read()
print("The queue is now flushed, and the first data packet is: "..data[1])
  1. socket:write(data, ...) This function is to write data to the socket. It will accept any data that can be sent over a regular modem message, but tables must be serialized first and functions are not accepted. Multiple variables can be pushed at once, and they will be written in the same data packet
local data = "My program works."
local myNumber = 1
socket:write(data, myNumber)

This will transmit the string to the computer on the other end of the socket, where it can be read and processed.

  1. socket:close() This function will close the socket. Closing a socket before your program ends is recommended for housekeeping. GERT will automatically close a socket when a computer shuts down, but if the computer runs out of power or the case button is pushed, proper shutdown is not possible. Calling socket:close() prevents the possibility of any unusual and undefined behavior.
Clone this wiki locally