forked from lyqyd/ComputerCraft-LyqydNet
-
Notifications
You must be signed in to change notification settings - Fork 0
Elsys656/ComputerCraft-LyqydNet
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
LyqydNet APIs:
1 - Setup
To install, simply copy the files to their appropriate directories.
Each computer that you want to be on the network should have this added
to its startup (see 3.1 - 3.4 for more information):
shell.run("lyqydnet")
Routers need an additional argument:
shell.run("lyqydnet route")
Use of the "routed" program to activate routing features is deprecated.
2 - APIs
2.1 - Connection API
The connection API is the application-layer rednet interface in
LyqydNet. It provides an easy way to set up client-server applications
rapidly and with minimal effort. This layer abstracts away most tech-
nical aspects of formatting and delivering rednet packets so that the
small details are gotten out of the developer's way.
2.1.1 - connection.new()
The new function takes up to three arguments; a computer identification
string, a remote socket number, and a local socket number. To create a
new connection to a computer named "fileserver" running the filed
program (which listens on port 21), we would use:
local conn = connection.new("fileserver", 21)
This will instantiate a new connection instance, and add the connection
to the master table of connections in the connection API. To use the
connection instance, you must first call its :open() method (see 2.1.2).
2.1.2 - conn:open()
The open function for a given connection can be called with one
argument, an optional timeout in seconds. If no timeout is provided, the
function will wait until a response is received from the server. A time-
out of two to five seconds is usually sufficient to allow a connection
time to initialize. You must call :open on a connection before using
the :listen or :send methods on it. The open function returns the
payload of the response packet from the computer being connected to.
local response = conn:open(2)
2.1.3 - conn:send()
The send function takes two arguments; a message type, and a message to
send. Most communications to a server are sent using the "data" message
type. Note that if you have not opened the connection (see 2.1.2), send
will take no action and return false. For example,
conn:send("data", "Hello, Server!")
Please be aware that if you specify an unrecognized packet type string,
the packet may fail to be sent. If you desire to use a custom packet
type, you should either add your translation entries to the table in the
packet API, or use the short (two character) packet type name here. See
2.2.3 for more details.
2.1.4 - conn:listen()
The listen function takes only one argument, a timeout in seconds. This
function will wait for packets coming in on the connection, based on the
origin, destination and local/remote port numbers. The listen function
returns the entire packet (see <packetAPI>) it received.
local packetReceived = conn:listen()
2.1.5 - conn:close()
The close function takes two optional arguments; a message, and a
'quiet' value. If no message is provided, the message "disconnect" will
automatically be used. If the quiet option is true, no closing packet
will be send to the connection. Normally, one would use the close
function in quiet mode after receiving a connection close message from a
client computer. An example of a client disconnecting:
conn:close()
2.1.6 - conn:isTurtle()
The isTurtle function returns true if the remote computer is a turtle
and false if it is not.
2.1.7 - conn:route()
The route function returns the host table routing number for the remote
computer.
2.1.8 - conn:name()
The name function returns the name of the remote computer.
2.1.9 - connection.listen()
The listen function is used by foreground servers (the computer is only
running the server) to listen for incoming packets and connections. It
takes two arguments; the socket number and an optional timeout duration.
The socket number provided to this call is used to check incoming
connection requests, so it is imperative that they match. The listen
function returns the received packet and a connection. For example, a
file server might use:
local packetReceived, conn = connection.listen(21)
2.2 - Packet API
The Packet API is the basic method of handling data as it is transported
across the network. Every layer of LyqydNet utilizes it to more easily
handle looking up and changing various pieces of the data, and packet
data type values are returned from the connection API listening
functions (2.1.4, 2.1.9).
2.2.1 - packet.new()
This function intializes a new packet, given a two-letter packet type
(see 2.2.3 for translation table), a computer ID destination, a string
of message contents, a destination socket number (unused on RM packets),
and an origin socket number (also unusued on RM packets).
2.2.2 - packet.reconstruct()
Reconstruct takes a received message string, a destination, an origin,
and a distance as arguments and creates a packet value from those pieces
of information.
2.2.3 - packet.types
The types table is a lookup table to convert between the two-letter type
names and the human-readable type names. For example, the response
packet type's entry in the table would be this:
{
SR = "response"
response = "SR"
}
As you can see, simply indexing the table with one type of packet name
will give the matching name of the other type, so packet.types["SR"]
would give the value "response".
2.3 - Net API
The Net API is the layer that the connection API is built on. This layer
handles the sending and receiving of packets, as well as delivering them
to the running server applications, network daemons and client programs.
Full documentation is provided, but many functions of this API are
intended for use only by other APIs.
2.3.1 - net.netInit()
This function takes no arguments. The netInit function is crucial to the
LyqydNet system. It makes it discoverable to routers and other computers
on the network. Each computer must be labeled before it can be added to
the network. Computer labels are used as hostnames. This function sends
out basic information about the computer in a broadcast. This function
is automatically called when the lyqydnet program is run.
2.3.2 - net.daemonAdd()
This function takes three arguments; a daemon name, a function and a
socket number. The function is used to create a coroutine which will be
resumed every time a packet is received for the daemon on that socket.
Daemons are background servers. See 2.1.1, 2.1.7, 2.1.8 for more
information on socket numbers.
2.3.3 - net.daemonRemove()
This function removes a daemon from the daemon table, so that it can no
longer receive packets. It takes one argument; the daemon name. If the
daemon was present, this function returns true; otherwise, it returns
false.
2.3.4 - net.routeFromName()
This function takes the name of a computer as an argument and returns
the route number associated with it.
2.3.5 - net.routeFromCID()
This function takes the ID number of a computer as an argument and
returns the route number associated with it.
2.3.6 - net.nameFromRoute()
This function takes a route number as an argument and returns the name
of the computer associated with it.
2.3.7 - CIDFromRoute()
This function takes a route number as an argument and returns the ID of
the computer associated with it.
2.3.8 - net.send()
This function takes a packet and stringifies it and sends it out.
2.3.9 - net.add_route()
This function takes an ID number, computer type, computer name, gateway
and cost and adds a route to the route table. Do not manually call this
function.
2.3.10 - net.remove_route()
This function takes a route number and removes it from the routing table
and the hosts file. Do not manually call this function.
2.4 - Net File API
The netfile API is used to greatly simplify the exchange of files across
the LyqydNet network.
2.4.1 - netfile.get()
The get function takes four arguments; a connection number, a remote
file to get, a local file to save the remote file to, and a timeout. If
the connection number refers to a connection that doesn't exist, it will
return false. Otherwise, it will request the file and then return with a
call to netfile.receive(). See 2.4.2 in regards to using this in a
server application.
2.4.2 - netfile.receive()
The receive function takes three arguments; a connection number, a local
file to save the incoming data to, and a timeout. It will return false
if the connection number refers to a connection that doesn't exist, or
if it times out before the file is received. Otherwise, it will return
the file header (contains the name of the remote file).
Only foreground servers should use netfile.receive() (and netfile.get(),
since it uses netfile.receive()) for reasons discussed in 2.3.12. Since
they wait for packets without using coroutine.yield, they will not work
properly (or at all) in background servers. Background servers wishing
to receive files should instead catch the fileData message type (saving
each line to a table) and the fileEnd message type (write the table to
the file).
2.4.3 - netfile.put()
The put function takes four arguments; a connection number, a local file
to send, a remote file location to save it to, and a timeout duration.
It sends a packet requesting to upload the file and awaits a response.
If a response allowing the upload is received before the timeout, the
function calls netfile.send() to send the file and returns whatever send
returns. If the local file doesn't exist, or the connection doesn't
exist, or if a bad response is received, put() will return false.
2.4.4 - netfile.send()
The send function takes two arguments; a connection number and a local
file to send. If the connection number refers to a connection that does
not exist, or if the local file does not exist, send() will return
false. Otherwise, it will send a file header packet containing the local
name of the file, the file data one line per packet, and finally a file
end packet. It will then return true.
3 - Programs
3.1 - lyqydnet
This program sets up the environment for lyqydnet to operate, as well as
establishing the network daemon coroutine. The network daemon
establishes and maintains the routing table and responds to network
information packets.
shell.run("lyqydnet")
3.1.1 - lyqydnet route
This argument enables the routing functionality of the network daemon.
It maintains lists of hosts and is the backbone of the network. Each
router will forward packets onto other routers, using the most efficient
known route to pass packets on to their destinations. Routers are used
to create networks larger than the maximum rednet transmission distance
by repeating packets onward.
They send the list of hosts on the network to computers joining the
network. New hosts will then direct all of their traffic through the
router until they discover shorter routes (peer connections cannot be
made until the first peer has rebooted; this is to minimize lag).
Routers can be made to work across great distances by using the longlink
instruction. Longlink routers will hold onto packets forwarded through
them until the next router confirms receipt of the packet. As the chain
of routers is walked past, each router in line comes online. When a
router comes online, longlink routers check whether there are any
packets waiting to be sent to it for forwarding. If so, all packets for
it are sent onward.
This process repeats all the way across the usually-unloaded chunks to
the other end of the network. Longlink is not the default behavior for
routers because it causes twice the packets to be sent each time (one
for data, one for confirmation). Routers that are normally in range of
each other should not be longlink routers.
The routing daemon should be started immediately after the network
daemon, if the computer will be a router.
shell.run("lyqydnet route")
To make a router a longlink router, start it with the longlink option:
shell.run("lyqydnet route longlink")
3.3 - netmon
Netmon is a network monitoring utility. It watches network activity over
a range of frequencies specified when starting it:
netmon 0 7
4 - Examples
4.1 - Example Foreground Server
'------------------------------------------------------------------------
local connections = {}
while true do
--wait for input from client connections on socket 21.
packet, conn = connection.listen(21)
local messType = packet.types[packet.type]
--okay, we have a packet, see if the connection is one we already
--know about.
if connections[conn] and connections[conn].status == "open" then
--we can use additional entries in our 'connections' table
--(which is internal to the server app) to store information
--about client state, among other things.
if messType == "data" then
--handle client input here; use the connection number in
--'conn' to send data back.
print(message)
conn:send(conn, "done", "OK")
--break packet tells the client we are done transmitting
--and are ready for the next command. This is optional. Some
--types of servers wouldn't need to use this, e.g. a chat
--server.
elseif messType == "close" then
--client has closed the connection, so let's close out
--the connection information.
conn:close("disconnect", true)
connections[conn].status = "closed"
connections[conn].name = nil
elseif messType == "instruction" then
--these are local instructions. Often unused.
if message == "stop" then
return true
end
end
elseif messType == "query" then
--a client wants to open a connection
if connections[conn] then
--already have an entry for that connection number,
--reset the information
connections[conn].status = open
connections[conn].name = connection.name(conn)
else
--no entry for this connection, set up a new
--entry.
local connect = {}
connect.status = "open"
connect.name = connection.name(conn)
connections[conn] = connect
end
--either way we set up the connection, let the client know
--it was successful.
conn:send("response", "ok")
end
end
------------------------------------------------------------------------
4.2 - Example background server
Note that this example is very similar to the previous example, the
foreground server. The critical difference here is that we use
connection.listenIdle() to get packets (which uses coroutine.yield()).
We also declare the server as a function, and then use net.daemonAdd to
add it to the daemon table. Please note that the daemon name, "server"
is the same as the server identifier in the previous example, which is
in the connection.listen() call.
------------------------------------------------------------------------
local connections = {}
function serverFunction ()
while true do
--wait for input from client connections on socket 21.
conn, messType, message = connection.listenIdle(21)
--okay, we have a packet, see if the connection is one we
--already know about.
if connections[conn] and connections[conn].status == "open" then
--we can use additional entries in our 'connections' table
--(which is internal to the server app) to store information
--about client state, among other things.
if messType == "data" then
--handle client input here; use the connection number in
--'conn' to send data back.
connection.send(conn, "done", "OK")
--break packet tells the client we are done transmitting
--and are ready for the next command.
elseif messType == "close" then
--client has closed the connection, so let's close out
--the connection information.
connection.close(conn, disconnect, true)
connections[conn].status = "closed"
connections[conn].name = nil
elseif messType == "instruction" then
--these are local instructions. Often unused.
if message == "stop" then
return true
end
end
elseif messType == "query" then
--a client wants to open a connection
if connections[conn] then
--already have an entry for that connection number,
--reset the information
connections[conn].status = open
connections[conn].name = connection.name(conn)
else
--no entry for this connection number, set up a new
--entry.
local connect = {}
connect.status = "open"
connect.name = connection.name(conn)
table.insert(connections, conn, connect)
end
--either way we set up the connection, let the client know
--it was successful.
connection.send(conn, "response", "ok")
end
end
end
--make sure the socket number here matches the socket number used above.
net.addDaemon("server", serverFunction, 21)
------------------------------------------------------------------------
4.3 - Example Client
This client would, when run, open a command prompt. The command open,
with a server name, would open a connection to a server. The command
close would close the connection. The exit command would close the
connection if one was open, then exit the program. All other text would
be sent directly to the server application.
------------------------------------------------------------------------
local tHistory = {}
local serverConnection = false
while true do
write("client> ")
local sLine = read(nil, tHistory)
table.insert( tHistory, sLine )
local commandArgs = {}
for match in string.gmatch(sLine, "[^ \t]+") do
table.insert( commandArgs, match )
end
if commandArgs[1] == "open" then
if commandArgs[2] then
serverConnection = connection.new(commandArgs[2], 21)
response = serverConnection:open()
if not response then
print("Connection Failed!")
else
print("Connected to "..commandArgs[2]..".")
end
else
print("No server specified!")
end
elseif commandArgs[1] == "close" then
if serverConnection:close() then
serverConnection:destroy()
serverConnection = false
print("Connection Closed.")
else
print("Could not close connection!")
end
elseif commandArgs[1] == "exit" then
if serverConnection then serverConnection:close() serverConnection:destroy() end
return
else
serverConnection:send("data", sLine)
local response = nil
while response.type ~= "done" do
response = serverConnection:listen()
if response.type == "close" then
print("Connection closed by server.")
serverConnection:close(nil, true)
serverConnection:destroy()
return
elseif response.type == "data" then
print("Server sent: "..response.payload)
else
end
end
end
end
------------------------------------------------------------------------
About
A set of APIs and scripts for ComputerCraft to establish and operate an in-game computer network with routing.
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published