mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-09 17:00:30 +00:00
Add guide to using Rednet
This commit is contained in:
parent
eb3e8ba677
commit
e84c9c79bb
381
doc/guides/rednet_guide.md
Normal file
381
doc/guides/rednet_guide.md
Normal file
@ -0,0 +1,381 @@
|
||||
---
|
||||
module: [kind=guide] rednet_guide
|
||||
see: rednet Send and receive messages over modems.
|
||||
---
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# Transferring information wirelessly using Rednet
|
||||
Being able to send data between computers is an important feature for various
|
||||
programs, including turtle controllers and distributed banking systems. The
|
||||
[modem](https://tweaked.cc/peripheral/modem.html) peripheral enables computers
|
||||
and turtles to transmit and receive messages from other computers, and the
|
||||
[Rednet](https://tweaked.cc/module/rednet.html) API allows sending messages
|
||||
directly to computers by ID, as well as adding hostname lookup and message
|
||||
repeating. This guide will show how to use the Rednet API to send and receive
|
||||
data, as well as some tips on usage.
|
||||
|
||||
## Getting started
|
||||
To begin using Rednet, you will need at least two computers (or turtles) with
|
||||
modems attached. There are three different types of modems available:
|
||||
|
||||
- Wireless Modems are the simplest to craft. They have a limited range -
|
||||
computers that are too far from each other won't be able to receive each others'
|
||||
messages. They also don't work across dimensions.
|
||||
- Ender Modems have infinite range and work across dimensions, but they require
|
||||
an Eye of Ender to craft. But because they're infinite, messages sent will be
|
||||
received by every computer on the server, which could be concerning for
|
||||
multiplayer servers where security is required.
|
||||
- Wired Modems transmit messages through a wire. They don't have limited range,
|
||||
but they'll only send messages to computers connected to the same wire. They can
|
||||
also connect remote peripherals, including inventories like chests. There are
|
||||
two variations: the "half width" modems connect to computers directly, while
|
||||
"full block" modems can connect to turtles, and can function as wires as well.
|
||||
|
||||
To attach a modem to a computer block, simply hold shift and right-click on the
|
||||
computer. To attach a modem to a turtle or pocket computer, craft the computer
|
||||
with a wireless modem, or use the `equip` program with the modem's slot
|
||||
selected. If you use a wired modem, you'll also need to right-click the modem to
|
||||
turn it red - this will connect the computer to the network.
|
||||
|
||||
Before using Rednet, your program needs to open it. Opening Rednet will tell the
|
||||
modem to start listening for messages from other computers. The
|
||||
[`rednet.open`](https://tweaked.cc/module/rednet.html#v:open) function takes the
|
||||
side of the modem to open as a string. On pocket computers, this will always be
|
||||
`back`. For example, if your modem is on the left side, you would use this code:
|
||||
|
||||
```lua
|
||||
rednet.open("left")
|
||||
```
|
||||
|
||||
Always run this function before you do anything with Rednet - otherwise, it may
|
||||
not function as expected.
|
||||
|
||||
## Sending messages
|
||||
To send a message to another computer, use the
|
||||
[`rednet.send`](https://tweaked.cc/module/rednet.html#v:send) function. This
|
||||
function takes the ID of the computer to send to (which you can get with the
|
||||
`id` command), the message you want to send, and the protocol to use if required
|
||||
(more on protocols later).
|
||||
|
||||
This line will send the string `"Hello, World!"` to computer ID 3.
|
||||
|
||||
```lua
|
||||
rednet.send(3, "Hello, World!")
|
||||
```
|
||||
|
||||
You can send almost any type of value, including tables. You can also send
|
||||
variables. Here is an example which sends a table of values from a variable:
|
||||
|
||||
```lua
|
||||
local message = {
|
||||
name = "My Message",
|
||||
length = 5,
|
||||
contents = {1, 2, 5, 4, 3}
|
||||
}
|
||||
rednet.send(3, message)
|
||||
```
|
||||
|
||||
You can even send the contents of files using
|
||||
[`fs.open`](https://tweaked.cc/module/fs.html#v:open):
|
||||
|
||||
```lua
|
||||
local file = assert(fs.open("myfile.txt", "r")) -- Opens the file for reading, with assert to error if it fails.
|
||||
local data = file.readAll() -- Read the full file into a variable.
|
||||
file.close() -- Always close files when you're done!
|
||||
rednet.send(3, data)
|
||||
```
|
||||
|
||||
Note that you cannot send the file handle itself - functions can't be sent over
|
||||
Rednet, so the file reading functions will get removed from the table, resulting
|
||||
in sending an empty table.
|
||||
|
||||
If a message needs to be sent to every computer available,
|
||||
[`rednet.broadcast`](https://tweaked.cc/module/rednet.html#v:broadcast) can be
|
||||
used. It takes the message to send, as well as the protocol if desired.
|
||||
|
||||
```lua
|
||||
rednet.broadcast("Emergency message!")
|
||||
```
|
||||
|
||||
## Receiving messages
|
||||
Sending messages isn't really useful unless there's a way to receive them. The
|
||||
[`rednet.receive`](https://tweaked.cc/module/rednet.html#v:receive) function
|
||||
**waits** for a message to be received, and can wait for a certain amount of
|
||||
time, or filter for a specific protocol (again, more about that later). It
|
||||
returns the ID of the computer that sent the message, and then the message (and
|
||||
finally the protocol if specified), as *multiple return values* (NOT a table).
|
||||
|
||||
Here is a simple example that waits for any message without a timeout:
|
||||
|
||||
```lua
|
||||
local id, message = rednet.receive()
|
||||
```
|
||||
|
||||
This example will wait for 5 seconds to receive a message - if nothing is sent
|
||||
in 5 seconds, it will return `nil`.
|
||||
|
||||
```lua
|
||||
local id, message = rednet.receive(5)
|
||||
if id == nil then -- Check if it timed out.
|
||||
error("No message received!")
|
||||
end
|
||||
```
|
||||
|
||||
It's a good idea to check that the message came from the right computer before
|
||||
processing it further. This chunk will check that the message was sent by
|
||||
computer 1, and if so, will print the message sent to the screen:
|
||||
|
||||
```lua
|
||||
local id, message = rednet.receive()
|
||||
if id == 1 then -- Check for the right sender.
|
||||
print("Message name:", message.name)
|
||||
end -- Ignore it if another computer sent it.
|
||||
```
|
||||
|
||||
Usually, a server-like program will combine this with an infinite loop to
|
||||
constantly process requests. Here's a full example of a server with multiple
|
||||
commands that can be triggered:
|
||||
|
||||
```lua
|
||||
while true do -- Repeat forever.
|
||||
local id, message = rednet.receive()
|
||||
if id == 1 then -- Check for the right sender.
|
||||
-- Check the message command, and execute it.
|
||||
if message == "forward" then
|
||||
turtle.forward()
|
||||
elseif message == "back" then
|
||||
turtle.back()
|
||||
elseif message == "left" then
|
||||
turtle.turnLeft()
|
||||
elseif message == "right" then
|
||||
turtle.turnRight()
|
||||
elseif message == "dig" then
|
||||
turtle.dig()
|
||||
elseif message == "quit" then
|
||||
break -- This will break out of the infinite loop, quitting the program.
|
||||
end -- Do nothing on an invalid command.
|
||||
end -- Ignore it if another computer sent it.
|
||||
end
|
||||
```
|
||||
|
||||
## Filtering messages with protocols
|
||||
Checking for messages by computer ID is a quick way to check that a message is
|
||||
intended for this server, but sometimes multiple computers need to send messages
|
||||
to one computer. Protocols allow you to add a special "tag" to a message, which
|
||||
is used to filter messages. This will prevent computers from accidentally
|
||||
receiving messages that weren't meant for them.
|
||||
|
||||
To add a protocol to a message, simply pass it as the third argument to
|
||||
`rednet.send`:
|
||||
|
||||
```lua
|
||||
rednet.send(3, message, "myProtocol")
|
||||
```
|
||||
|
||||
To keep your protocol unique to yourself, while also being identifiable, it's
|
||||
recommended to use a protocol with your name (or a reverse URL) and a program
|
||||
name separated by a dot:
|
||||
|
||||
```lua
|
||||
rednet.send(3, message, "SquidDev.better-chat")
|
||||
-- if you own a relevant URL, use it instead:
|
||||
rednet.send(3, message, "cc.squiddev.better-chat")
|
||||
```
|
||||
|
||||
On the receiving end, messages can be filtered as the last argument to
|
||||
`rednet.receive`:
|
||||
|
||||
```lua
|
||||
local id, message = rednet.receive("cc.squiddev.better-chat")
|
||||
-- if you want a timeout, put it first:
|
||||
local id, message = rednet.receive(5, "cc.squiddev.better-chat")
|
||||
```
|
||||
|
||||
The protocol is returned as an additional third return value, which can be used
|
||||
to do manual filtering if more than one protocol is necessary:
|
||||
|
||||
```lua
|
||||
local id, message, protocol = rednet.receive()
|
||||
if protocol == "cc.squiddev.better-chat.message" then
|
||||
-- ...
|
||||
elseif protocol == "cc.squiddev.better-chat.query" then
|
||||
-- ...
|
||||
end
|
||||
```
|
||||
|
||||
## Dynamic host ID lookup
|
||||
Sometimes, you may not know the exact computer ID to send to - you only know
|
||||
that you need to send a certain protocol. Rednet features a dynamic host lookup
|
||||
procedure, which lets servers advertise that they listen to a certain protocol.
|
||||
Other computers can broadcast a message asking what servers support a protocol,
|
||||
and any computers which "host" it will respond with their ID. These IDs can be
|
||||
used directly in `rednet.send` calls. This makes setting up large networks
|
||||
simpler, as you don't need to hard-code the computer ID into each and every
|
||||
computer. It also allows creating a list of computers that support a protocol -
|
||||
for example, creating a server list for a chat program.
|
||||
|
||||
To advertise a protocol, use the
|
||||
[`rednet.host`](https://tweaked.cc/module/rednet.html#v:host) function. This
|
||||
function takes the protocol to host, as well as a name to call the computer on
|
||||
the network.
|
||||
|
||||
```lua
|
||||
rednet.host("cc.squiddev.better-chat", "super-cool-computer")
|
||||
```
|
||||
|
||||
Once your program finishes, remember to use the
|
||||
[`rednet.unhost`](https://tweaked.cc/module/rednet.html#v:unhost) function to
|
||||
stop advertising the protocol:
|
||||
|
||||
```lua
|
||||
rednet.unhost("cc.squiddev.better-chat")
|
||||
```
|
||||
|
||||
The [`rednet.lookup`](https://tweaked.cc/module/rednet.html#v:lookup) function
|
||||
takes a protocol to search for, and optionally a name to find, and it returns
|
||||
*multiple return values* with each computer ID found, or `nil` if there were no
|
||||
matches. Here's a basic example looking for any computer hosting a protocol:
|
||||
|
||||
```lua
|
||||
local ids = {rednet.lookup("cc.squiddev.better-chat")} -- Wraps the multiple return values into a single table.
|
||||
```
|
||||
|
||||
This will look for a computer with a specific name, erroring if it's not found:
|
||||
|
||||
```lua
|
||||
local id = rednet.lookup("cc.squiddev.better-chat", "super-cool-computer") -- Does not create a table - takes the first return value directly.
|
||||
if id == nil then error("No route to host") end
|
||||
```
|
||||
|
||||
This example will scan for a protocol, and pings each computer which hosts, but
|
||||
errors if there are no computers found.
|
||||
|
||||
```lua
|
||||
local ids = {rednet.lookup("cc.squiddev.better-chat")}
|
||||
if #ids == 0 then -- Checks if the table is empty.
|
||||
error("No computers found!")
|
||||
else
|
||||
for _, id in ipairs(ids) do -- Loop over all IDs in the table.
|
||||
print("Found computer", id) -- Print the ID to the screen.
|
||||
rednet.send(id, "PING!", "cc.squiddev.better-chat") -- Send a message to the computer.
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Security over Rednet
|
||||
One important thing to be aware of, especially on multiplayer servers, is that
|
||||
Rednet is not a secure protocol. Anyone can intercept messages sent over it
|
||||
(unless the message is sent through a wired modem), with the contents available
|
||||
in plain text. Furthermore, the ID of a message can be spoofed, and a protocol
|
||||
doesn't guarantee that the message is in the right format.
|
||||
|
||||
If security and authentication are necessary for your application, you should
|
||||
add encryption to messages. This requires extra code from other people, and is
|
||||
often more complex than simple send/receive message calls. But for things like
|
||||
banking and secure chat, encryption is necessary to keep communications safe.
|
||||
|
||||
The simplest way to use encryption is through a library like [ECNet2], which
|
||||
uses protocols to create two-way encrypted pipes (or "sockets") between a client
|
||||
and server. See the examples there for more info on how to use it.
|
||||
|
||||
You can also use encryption primitives directly. There are various algorithms
|
||||
that each contribute to the security of a message. They have different purposes,
|
||||
so you'll need to pick and choose which ones apply to your use case.
|
||||
|
||||
> [Expert zone][!WARNING]
|
||||
>
|
||||
> These terms may be hard to understand for beginners. Cryptography is a very
|
||||
> complex field, and uses a lot of terms that may be unfamiliar to novices. This
|
||||
> guide attempts to boil it down to more understandable terms, but even so, it
|
||||
> may not be enough for someone new to programming to understand. If in doubt,
|
||||
> use ECNet, or cross your fingers that nobody will peek at your messages.
|
||||
|
||||
- SHA-256 is a hashing algorithm, made popular by its extensive use in Bitcoin.
|
||||
SHA-256 takes a large string of data, and mashes it up in a semi-random way to
|
||||
create a 32-byte string/number that represents the data, called a "hash" (or
|
||||
"digest"). The hash cannot easily be reversed into its original string, but the
|
||||
same string will create the same hash every time. SHA-256 is useful for
|
||||
applications where you need to know whether two strings are the same without
|
||||
storing the actual string, such as checking passwords. SHA-256 is succeeded by
|
||||
SHA-512, the SHA-3 family of algorithms, and the BLAKE3 family of algorithms,
|
||||
which are all more secure and have a larger hash than SHA-256; but SHA-256 is
|
||||
much simpler to implement and faster in CC, as well as still being unbroken, so
|
||||
it remains the most popular choice for hashing algorithms.
|
||||
- HMAC is a message authentication algorithm, used for checking the validity and
|
||||
sender of a message, and is based on a hashing algorithm. HMAC takes a string
|
||||
message, and a secret key, and creates an "authentication tag" from those two.
|
||||
The authentication tag is then sent next to the message, but the key is kept
|
||||
safe and out of the message. On the other end, the receiver can verify the
|
||||
message by creating its own authentication tag from the message and its copy of
|
||||
the key, and compares that with the authentication tag with the message. This
|
||||
ensures that the message wasn't tampered with, and that the message was sent by
|
||||
the original sender, because changing either the message or key will create a
|
||||
different tag on the other side. However, it does not hide the contents of the
|
||||
message. Poly1305 is a more modern and faster alternative that's often used
|
||||
instead of HMAC, and uses a special algorithm instead of hashing.
|
||||
- PBKDF2 is a key generation algorithm, which makes a random encryption key from
|
||||
a password. PBKDF2 takes a password and a random string called a "salt", and
|
||||
generates a key that can be used for encryption. It uses a large number of
|
||||
cycles of an algorithm like HMAC to create a key that's random enough to not be
|
||||
crackable, which also increases the amount of time to try each password. PBKDF2
|
||||
is useful not only for encryption using a password, but also for checking
|
||||
passwords, as it makes it even more difficult to reverse the password hash.
|
||||
- ChaCha20 is a symmetric encryption algorithm, which uses a shared key to hide
|
||||
the contents of a message. It takes the message, a key, and 12 bytes of random
|
||||
"salt" data, and creates a scrambled string that can only be decoded with the
|
||||
same key and salt. The salt should be stored next to the message, but the key
|
||||
should be kept safe and never sent anywhere. AES is a popular alternative to
|
||||
ChaCha20, but it's more complex and slower, which makes it a poor fit for
|
||||
ComputerCraft. ChaCha20 is often paired with Poly1305 to make a type of
|
||||
encryption known as *Authenticated Encryption with Associated Data* (AEAD),
|
||||
which both hides a message *and* authenticates it with the original sender, and
|
||||
it can even authenticate plain text in the same operation.
|
||||
- Curve25519/X25519 is an elliptic curve (ECC) asymmetric cryptographic
|
||||
algorithm, which uses separate publicly shareable and private keys to exchange
|
||||
data in a trusted way. Its most common usage is for Elliptic Curve
|
||||
Diffie-Hellman (ECDH), which is able to create a shared symmetric key by only
|
||||
sending the public keys of each computer. This key can then be used for ChaCha20
|
||||
encryption. X25519 and ECDH are important for encryption because they allow
|
||||
computers to create a secret key, without sending enough data for other
|
||||
computers to see the secret as well. To use ECDH, first generate a public and
|
||||
private keypair on both sides; then send just the *public* key from both
|
||||
computers to each other. Once both computers have each others' public keys, run
|
||||
ECDH using *this* computer's *private* key, and the *other* computer's *public*
|
||||
key. Through the work of magic, both computers will have the same secret key
|
||||
despite never sharing it directly, and no other computer will be able to create
|
||||
it because they don't have a private key.
|
||||
- Ed25519 is a variant of X25519 that allows *signing* messages. Signing is like
|
||||
message authentication, but uses asymmetric public and private keys to create
|
||||
the tag. Signing involves using the private key on the message to create a
|
||||
signature, and verification uses the public key on the signature to check it
|
||||
with the original message. Signing is used heavily in the real world in HTTPS,
|
||||
which uses "certificates" signed by higher powers to encrypt the connection. The
|
||||
signature ensures that the website can be trusted and is who it says it is.
|
||||
Signing is useful anywhere where authenticating the source and validity of a
|
||||
message is important, but it's critical that the receiver can't create its own
|
||||
authentication tags.
|
||||
|
||||
Some popular encryption primitive libraries include
|
||||
[Anavrins's SHA256/HMAC/PBKDF2 library], [PG231's ECC library], and [CCryptoLib]
|
||||
which is used in ECNet2.
|
||||
|
||||
## Conclusion
|
||||
Remote tasks become a lot easier when computers can communicate together. The
|
||||
Rednet API makes it possible to send messages between computers simply.
|
||||
`rednet.send` and `rednet.receive` are the building blocks for transmitting
|
||||
messages over modems. Protocols help smoothen out these functions by filtering
|
||||
out the messages that aren't important. Host lookup makes it possible to send to
|
||||
computers without needing to know their IDs directly. And despite Rednet being
|
||||
an insecure protocol, there are many ways to fortify your connection against
|
||||
unwanted spying and manipulation. These tools are fundamental to any program
|
||||
that relies on remote communication.
|
||||
|
||||
[ECNet2]: https://github.com/migeyel/ecnet/
|
||||
[Anavrins's SHA256/HMAC/PBKDF2 library]: https://pastebin.com/6UV4qfNF
|
||||
[PG231's ECC library]: https://www.computercraft.info/forums2/index.php?/topic/29803-elliptic-curve-cryptography/
|
||||
[CCryptoLib]: https://github.com/migeyel/ccryptolib/
|
Loading…
Reference in New Issue
Block a user