mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-15 04:30:29 +00:00
Merge branch 'mc-1.17.x' into mc-1.18.x
This commit is contained in:
commit
901d8d4c3b
@ -176,7 +176,7 @@ dependencies {
|
||||
exclude group: "org.jetbrains", module: "annotations"
|
||||
}
|
||||
|
||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.4'
|
||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.5'
|
||||
}
|
||||
|
||||
// Compile tasks
|
||||
|
@ -2,7 +2,7 @@
|
||||
module: [kind=event] modem_message
|
||||
---
|
||||
|
||||
The @{modem_message} event is fired when a message is received on an open channel on any modem.
|
||||
The @{modem_message} event is fired when a message is received on an open channel on any @{modem}.
|
||||
|
||||
## Return Values
|
||||
1. @{string}: The event name.
|
||||
@ -10,11 +10,15 @@ The @{modem_message} event is fired when a message is received on an open channe
|
||||
3. @{number}: The channel that the message was sent on.
|
||||
4. @{number}: The reply channel set by the sender.
|
||||
5. @{any}: The message as sent by the sender.
|
||||
6. @{number}: The distance between the sender and the receiver, in blocks (decimal).
|
||||
6. @{number}: The distance between the sender and the receiver, in blocks.
|
||||
|
||||
## Example
|
||||
Prints a message when one is sent:
|
||||
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
||||
|
||||
```lua
|
||||
local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||
modem.open(0)
|
||||
|
||||
while true do
|
||||
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
||||
print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(side, channel, replyChannel, distance, tostring(message)))
|
||||
|
83
doc/guides/using_require.md
Normal file
83
doc/guides/using_require.md
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
module: [kind=guide] using_require
|
||||
---
|
||||
|
||||
# Reusing code with require
|
||||
A library is a collection of useful functions and other definitions which is stored separately to your main program. You
|
||||
might want to create a library because you have some functions which are used in multiple programs, or just to split
|
||||
your program into multiple more modular files.
|
||||
|
||||
Let's say we want to create a small library to make working with the @{term|terminal} a little easier. We'll provide two
|
||||
functions: `reset`, which clears the terminal and sets the cursor to (1, 1), and `write_center`, which prints some text
|
||||
in the middle of the screen.
|
||||
|
||||
Start off by creating a file called `more_term.lua`:
|
||||
|
||||
```lua {data-snippet=more_term}
|
||||
local function reset()
|
||||
term.clear()
|
||||
term.setCursorPos(1, 1)
|
||||
end
|
||||
|
||||
local function write_center(text)
|
||||
local x, y = term.getCursorPos()
|
||||
local width, height = term.getSize()
|
||||
term.setCursorPos(math.floor((width - #text) / 2) + 1, y)
|
||||
term.write(text)
|
||||
end
|
||||
|
||||
return { reset = reset, write_center = write_center }
|
||||
```
|
||||
|
||||
Now, what's going on here? We define our two functions as one might expect, and then at the bottom return a table with
|
||||
the two functions. When we require this library, this table is what is returned. With that, we can then call the
|
||||
original functions. Now create a new file, with the following:
|
||||
|
||||
```lua {data-mount=more_term:more_term.lua}
|
||||
local more_term = require("more_term")
|
||||
more_term.reset()
|
||||
more_term.write_center("Hello, world!")
|
||||
```
|
||||
|
||||
When run, this'll clear the screen and print some text in the middle of the first line.
|
||||
|
||||
## require in depth
|
||||
While the previous section is a good introduction to how @{require} operates, there are a couple of remaining points
|
||||
which are worth mentioning for more advanced usage.
|
||||
|
||||
### Libraries can return anything
|
||||
In our above example, we return a table containing the functions we want to expose. However, it's worth pointing out
|
||||
that you can return ''anything'' from your library - a table, a function or even just a string! @{require} treats them
|
||||
all the same, and just returns whatever your library provides.
|
||||
|
||||
### Module resolution and the package path
|
||||
In the above examples, we defined our library in a file, and @{require} read from it. While this is what you'll do most
|
||||
of the time, it is possible to make @{require} look elsewhere for your library, such as downloading from a website or
|
||||
loading from an in-memory library store.
|
||||
|
||||
As a result, the *module name* you pass to @{require} doesn't correspond to a file path. One common mistake is to load
|
||||
code from a sub-directory using `require("folder/library")` or even `require("folder/library.lua")`, neither of which
|
||||
will do quite what you expect.
|
||||
|
||||
When loading libraries (also referred to as *modules*) from files, @{require} searches along the *@{package.path|module
|
||||
path}*. By default, this looks something like:
|
||||
|
||||
* `?.lua`
|
||||
* `?/init.lua`
|
||||
* `/rom/modules/main/?.lua`
|
||||
* etc...
|
||||
|
||||
When you call `require("my_library")`, @{require} replaces the `?` in each element of the path with your module name, and
|
||||
checks if the file exists. In this case, we'd look for `my_library.lua`, `my_library/init.lua`,
|
||||
`/rom/modules/main/my_library.lua` and so on. Note that this works *relative to the current program*, so if your
|
||||
program is actually called `folder/program`, then we'll look for `folder/my_library.lua`, etc...
|
||||
|
||||
One other caveat is loading libraries from sub-directories. For instance, say we have a file
|
||||
`my/fancy/library.lua`. This can be loaded by using `require("my.fancy.library")` - the '.'s are replaced with '/'
|
||||
before we start looking for the library.
|
||||
|
||||
## External links
|
||||
There are several external resources which go into require in a little more detail:
|
||||
|
||||
- The [Lua Module tutorial](http://lua-users.org/wiki/ModulesTutorial) on the Lua wiki.
|
||||
- [Lua's manual section on @{require}](https://www.lua.org/manual/5.1/manual.html#pdf-require).
|
@ -12,16 +12,19 @@ rounded up to the nearest multiple of 0.05 seconds. If you are using coroutines
|
||||
or the @{parallel|parallel API}, it will only pause execution of the current
|
||||
thread, not the whole program.
|
||||
|
||||
**Note** Because sleep internally uses timers, it is a function that yields.
|
||||
This means that you can use it to prevent "Too long without yielding" errors,
|
||||
however, as the minimum sleep time is 0.05 seconds, it will slow your program
|
||||
down.
|
||||
:::tip
|
||||
Because sleep internally uses timers, it is a function that yields. This means
|
||||
that you can use it to prevent "Too long without yielding" errors, however, as
|
||||
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
||||
:::
|
||||
|
||||
**Warning** Internally, this function queues and waits for a timer event (using
|
||||
:::caution
|
||||
Internally, this function queues and waits for a timer event (using
|
||||
@{os.startTimer}), however it does not listen for any other events. This means
|
||||
that any event that occurs while sleeping will be entirely discarded. If you
|
||||
need to receive events while sleeping, consider using @{os.startTimer|timers},
|
||||
or the @{parallel|parallel API}.
|
||||
:::
|
||||
|
||||
@tparam number time The number of seconds to sleep for, rounded up to the
|
||||
nearest multiple of 0.05.
|
||||
|
@ -1,7 +1,7 @@
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
|
||||
# Mod properties
|
||||
mod_version=1.99.1
|
||||
mod_version=1.100.0
|
||||
|
||||
# Minecraft properties (update mods.toml when changing)
|
||||
mc_version=1.18.1
|
||||
|
@ -5,9 +5,8 @@
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.api.lua.LuaValues.*;
|
||||
|
@ -115,7 +115,9 @@ class DfpwmStream implements AudioStream
|
||||
}
|
||||
|
||||
result.flip();
|
||||
return result;
|
||||
|
||||
// This is naughty, but ensures we're not enqueuing empty buffers when the stream is exhausted.
|
||||
return result.remaining() == 0 ? null : result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,4 +125,9 @@ class DfpwmStream implements AudioStream
|
||||
{
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return buffers.isEmpty();
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,20 @@ public class SpeakerInstance
|
||||
|
||||
public synchronized void pushAudio( ByteBuf buffer )
|
||||
{
|
||||
if( currentStream == null ) currentStream = new DfpwmStream();
|
||||
SpeakerSound sound = this.sound;
|
||||
|
||||
DfpwmStream stream = currentStream;
|
||||
if( stream == null ) stream = currentStream = new DfpwmStream();
|
||||
boolean exhausted = stream.isEmpty();
|
||||
currentStream.push( buffer );
|
||||
|
||||
// If we've got nothing left in the buffer, enqueue an additional one just in case.
|
||||
if( exhausted && sound != null && sound.stream == stream && sound.channel != null )
|
||||
{
|
||||
sound.executor.execute( () -> {
|
||||
if( !sound.channel.stopped() ) sound.channel.pumpBuffers( 1 );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public void playAudio( Vec3 position, float volume )
|
||||
|
@ -26,12 +26,14 @@ public class SpeakerManager
|
||||
@SubscribeEvent
|
||||
public static void playStreaming( PlayStreamingSourceEvent event )
|
||||
{
|
||||
if( !(event.getSound() instanceof SpeakerSound) ) return;
|
||||
SpeakerSound sound = (SpeakerSound) event.getSound();
|
||||
if( !(event.getSound() instanceof SpeakerSound sound) ) return;
|
||||
if( sound.stream == null ) return;
|
||||
|
||||
event.getChannel().attachBufferStream( sound.stream );
|
||||
event.getChannel().play();
|
||||
|
||||
sound.channel = event.getChannel();
|
||||
sound.executor = event.getEngine().executor;
|
||||
}
|
||||
|
||||
public static SpeakerInstance getSound( UUID source )
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.client.sound;
|
||||
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
|
||||
import net.minecraft.client.resources.sounds.TickableSoundInstance;
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
@ -13,9 +14,12 @@ import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance
|
||||
{
|
||||
Channel channel;
|
||||
Executor executor;
|
||||
DfpwmStream stream;
|
||||
|
||||
SpeakerSound( ResourceLocation sound, DfpwmStream stream, Vec3 position, float volume, float pitch )
|
||||
|
@ -30,7 +30,36 @@ import java.util.OptionalLong;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The FS API allows you to manipulate files and the filesystem.
|
||||
* The FS API provides access to the computer's files and filesystem, allowing you to manipulate files, directories and
|
||||
* paths. This includes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>**Reading and writing files:** Call {@link #open} to obtain a file "handle", which can be used to read from or
|
||||
* write to a file.</li>
|
||||
* <li>**Path manipulation:** {@link #combine}, {@link #getName} and {@link #getDir} allow you to manipulate file
|
||||
* paths, joining them together or extracting components.</li>
|
||||
* <li>**Querying paths:** For instance, checking if a file exists, or whether it's a directory. See {@link #getSize},
|
||||
* {@link #exists}, {@link #isDir}, {@link #isReadOnly} and {@link #attributes}.</li>
|
||||
* <li>**File and directory manipulation:** For instance, moving or copying files. See {@link #makeDir}, {@link #move},
|
||||
* {@link #copy} and {@link #delete}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* :::note
|
||||
* All functions in the API work on absolute paths, and do not take the @{shell.dir|current directory} into account.
|
||||
* You can use @{shell.resolve} to convert a relative path into an absolute one.
|
||||
* :::
|
||||
*
|
||||
* ## Mounts
|
||||
* While a computer can only have one hard drive and filesystem, other filesystems may be "mounted" inside it. For
|
||||
* instance, the {@link dan200.computercraft.shared.peripheral.diskdrive.DiskDrivePeripheral drive peripheral} mounts
|
||||
* its disk's contents at {@code "disk/"}, {@code "disk1/"}, etc...
|
||||
*
|
||||
* You can see which mount a path belongs to with the {@link #getDrive} function. This returns {@code "hdd"} for the
|
||||
* computer's main filesystem ({@code "/"}), {@code "rom"} for the rom ({@code "rom/"}).
|
||||
*
|
||||
* Most filesystems have a limited capacity, operations which would cause that capacity to be reached (such as writing
|
||||
* an incredibly large file) will fail. You can see a mount's capacity with {@link #getCapacity} and the remaining
|
||||
* space with {@link #getFreeSpace}.
|
||||
*
|
||||
* @cc.module fs
|
||||
*/
|
||||
@ -440,6 +469,12 @@ public class FSAPI implements ILuaAPI
|
||||
* @return The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
* @cc.treturn string The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
||||
* @cc.usage Print the drives of a couple of mounts:
|
||||
*
|
||||
* <pre>{@code
|
||||
* print("/: " .. fs.getDrive("/"))
|
||||
* print("/rom/: " .. fs.getDrive("rom"))
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] getDrive( String path ) throws LuaException
|
||||
|
@ -22,7 +22,7 @@ import dan200.computercraft.core.computer.ComputerSide;
|
||||
* as those from Project:Red. These allow you to send 16 separate on/off signals. Each channel corresponds to a
|
||||
* colour, with the first being @{colors.white} and the last @{colors.black}.
|
||||
*
|
||||
* Whenever a redstone input changes, a {@code redstone} event will be fired. This may be used instead of repeativly
|
||||
* Whenever a redstone input changes, a @{event!redstone} event will be fired. This may be used instead of repeativly
|
||||
* polling.
|
||||
*
|
||||
* This module may also be referred to as {@code rs}. For example, one may call {@code rs.getSides()} instead of
|
||||
|
@ -19,10 +19,10 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* This works with energy storage blocks, as well as generators and machines which consume energy.
|
||||
*
|
||||
* <blockquote>
|
||||
* <strong>Note:</strong> Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. RF
|
||||
* :::note
|
||||
* Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. RF
|
||||
* used/generated per tick).
|
||||
* </blockquote>
|
||||
* :::
|
||||
*
|
||||
* @cc.module energy_storage
|
||||
*/
|
||||
|
@ -21,9 +21,60 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The modem peripheral allows you to send messages between computers.
|
||||
* Modems allow you to send messages between computers over long distances.
|
||||
*
|
||||
* :::tip
|
||||
* Modems provide a fairly basic set of methods, which makes them very flexible but often hard to work with. The
|
||||
* {@literal @}{rednet} API is built on top of modems, and provides a more user-friendly interface.
|
||||
* :::
|
||||
*
|
||||
* ## Sending and receiving messages
|
||||
* Modems operate on a series of channels, a bit like frequencies on a radio. Any modem can send a message on a
|
||||
* particular channel, but only those which have {@link #open opened} the channel and are "listening in" can receive
|
||||
* messages.
|
||||
*
|
||||
* Channels are represented as an integer between 0 and 65535 inclusive. These channels don't have any defined meaning,
|
||||
* though some APIs or programs will assign a meaning to them. For instance, the @{gps} module sends all its messages on
|
||||
* channel 65534 (@{gps.CHANNEL_GPS}), while @{rednet} uses channels equal to the computer's ID.
|
||||
*
|
||||
* - Sending messages is done with the {@link #transmit(int, int, Object)} message.
|
||||
* - Receiving messages is done by listening to the @{modem_message} event.
|
||||
*
|
||||
* ## Types of modem
|
||||
* CC: Tweaked comes with three kinds of modem, with different capabilities.
|
||||
*
|
||||
* <ul>
|
||||
* <li><strong>Wireless modems:</strong> Wireless modems can send messages to any other wireless modem. They can be placed next to a
|
||||
* computer, or equipped as a pocket computer or turtle upgrade.
|
||||
*
|
||||
* Wireless modems have a limited range, only sending messages to modems within 64 blocks. This range increases
|
||||
* linearly once the modem is above y=96, to a maximum of 384 at world height.</li>
|
||||
* <li><strong>Ender modems:</strong> These are upgraded versions of normal wireless modems. They do not have a distance
|
||||
* limit, and can send messages between dimensions.</li>
|
||||
* <li><strong>Wired modems:</strong> These send messages to other any other wired modems connected to the same network
|
||||
* (using <em>Networking Cable</em>). They also can be used to attach additional peripherals to a computer.</li></ul>
|
||||
*
|
||||
* @cc.module modem
|
||||
* @cc.see modem_message Queued when a modem receives a message on an {@link #open(int) open channel}.
|
||||
* @cc.see rednet A networking API built on top of the modem peripheral.
|
||||
* @cc.usage Wrap a modem and a message on channel 15, requesting a response on channel 43. Then wait for a message to
|
||||
* arrive on channel 43 and print it.
|
||||
*
|
||||
* <pre>{@code
|
||||
* local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||
* modem.open(43) -- Open 43 so we can receive replies
|
||||
*
|
||||
* -- Send our message
|
||||
* modem.transmit(15, 43, "Hello, world!")
|
||||
*
|
||||
* -- And wait for a reply
|
||||
* local event, side, channel, replyChannel, message, distance
|
||||
* repeat
|
||||
* event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
||||
* until channel == 43
|
||||
*
|
||||
* print("Received a reply: " .. tostring(message))
|
||||
* }</pre>
|
||||
*/
|
||||
public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPacketReceiver
|
||||
{
|
||||
@ -157,12 +208,23 @@ public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPa
|
||||
* Sends a modem message on a certain channel. Modems listening on the channel will queue a {@code modem_message}
|
||||
* event on adjacent computers.
|
||||
*
|
||||
* <blockquote><strong>Note:</strong> The channel does not need be open to send a message.</blockquote>
|
||||
* :::note
|
||||
* The channel does not need be open to send a message.
|
||||
* :::
|
||||
*
|
||||
* @param channel The channel to send messages on.
|
||||
* @param replyChannel The channel that responses to this message should be sent on.
|
||||
* @param payload The object to send. This can be a boolean, string, number, or table.
|
||||
* @param replyChannel The channel that responses to this message should be sent on. This can be the same as
|
||||
* {@code channel} or entirely different. The channel must have been {@link #open opened} on
|
||||
* the sending computer in order to receive the replies.
|
||||
* @param payload The object to send. This can be any primitive type (boolean, number, string) as well as
|
||||
* tables. Other types (like functions), as well as metatables, will not be transmitted.
|
||||
* @throws LuaException If the channel is out of range.
|
||||
* @cc.usage Wrap a modem and a message on channel 15, requesting a response on channel 43.
|
||||
*
|
||||
* <pre>{@code
|
||||
* local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||
* modem.transmit(15, 43, "Hello, world!")
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void transmit( int channel, int replyChannel, Object payload ) throws LuaException
|
||||
|
@ -80,8 +80,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
* If this computer is attached to the network, it _will not_ be included in
|
||||
* this list.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @return Remote peripheral names on the network.
|
||||
@ -95,8 +96,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Determine if a peripheral is available on this wired network.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@ -112,8 +114,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Get the type of a peripheral is available on this wired network.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@ -132,8 +135,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Check a peripheral is of a particular type.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@ -153,8 +157,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Get all available methods for the remote peripheral with the given name.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@ -174,8 +179,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Call a method on a peripheral on this wired network.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param context The Lua context we're executing in.
|
||||
@ -204,8 +210,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
* may be used by other computers on the network to wrap this computer as a
|
||||
* peripheral.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @return The current computer's name.
|
||||
* @cc.treturn string|nil The current computer's name on the wired network.
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaTable;
|
||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -27,7 +28,7 @@ class DfpwmState
|
||||
* The minimum size of the client's audio buffer. Once we have less than this on the client, we should send another
|
||||
* batch of audio.
|
||||
*/
|
||||
private static final long CLIENT_BUFFER = (long) (SECOND * 1.5);
|
||||
private static final long CLIENT_BUFFER = (long) (SECOND * 0.5);
|
||||
|
||||
private static final int PREC = 10;
|
||||
|
||||
@ -36,7 +37,7 @@ class DfpwmState
|
||||
private boolean previousBit = false;
|
||||
|
||||
private boolean unplayed = true;
|
||||
private long clientEndTime = System.nanoTime();
|
||||
private long clientEndTime = PauseAwareTimer.getTime();
|
||||
private float pendingVolume = 1.0f;
|
||||
private ByteBuffer pendingAudio;
|
||||
|
||||
@ -107,7 +108,7 @@ class DfpwmState
|
||||
|
||||
boolean isPlaying()
|
||||
{
|
||||
return unplayed || clientEndTime >= System.nanoTime();
|
||||
return unplayed || clientEndTime >= PauseAwareTimer.getTime();
|
||||
}
|
||||
|
||||
float getVolume()
|
||||
|
@ -17,6 +17,7 @@ import dan200.computercraft.shared.network.client.SpeakerAudioClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||
import net.minecraft.ResourceLocationException;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.protocol.game.ClientboundCustomSoundPacket;
|
||||
@ -119,7 +120,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
return;
|
||||
}
|
||||
|
||||
long now = System.nanoTime();
|
||||
long now = PauseAwareTimer.getTime();
|
||||
if( sound != null )
|
||||
{
|
||||
lastPlayTime = clock;
|
||||
@ -342,7 +343,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
// TODO: Use ArgumentHelpers instead?
|
||||
int length = audio.length();
|
||||
if( length <= 0 ) throw new LuaException( "Cannot play empty audio" );
|
||||
if( length > 1024 * 16 * 8 ) throw new LuaException( "Audio data is too large" );
|
||||
if( length > 128 * 1024 ) throw new LuaException( "Audio data is too large" );
|
||||
|
||||
DfpwmState state;
|
||||
synchronized( lock )
|
||||
@ -387,17 +388,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
computers.remove( computer );
|
||||
}
|
||||
|
||||
private static final class PendingSound
|
||||
private record PendingSound(ResourceLocation location, float volume, float pitch)
|
||||
{
|
||||
final ResourceLocation location;
|
||||
final float volume;
|
||||
final float pitch;
|
||||
|
||||
private PendingSound( ResourceLocation location, float volume, float pitch )
|
||||
{
|
||||
this.location = location;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
||||
@ -34,6 +33,7 @@ import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
@ -80,53 +80,65 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
PocketUpgrades.getVanillaUpgrades().map( x -> create( -1, null, -1, x ) ).forEach( stacks::add );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entity entity, int slotNum, boolean selected )
|
||||
{
|
||||
if( !world.isClientSide )
|
||||
{
|
||||
// Server side
|
||||
Container inventory = entity instanceof Player ? ((Player) entity).getInventory() : null;
|
||||
PocketServerComputer computer = createServerComputer( world, inventory, entity, stack );
|
||||
if( computer != null )
|
||||
private boolean tick( @Nonnull ItemStack stack, @Nonnull Level world, @Nonnull Entity entity, @Nonnull PocketServerComputer computer )
|
||||
{
|
||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||
|
||||
// Ping computer
|
||||
computer.keepAlive();
|
||||
computer.setLevel( world );
|
||||
computer.updateValues( entity, stack, upgrade );
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
// Sync ID
|
||||
int id = computer.getID();
|
||||
if( id != getComputerID( stack ) )
|
||||
{
|
||||
changed = true;
|
||||
setComputerID( stack, id );
|
||||
if( inventory != null ) inventory.setChanged();
|
||||
}
|
||||
|
||||
// Sync label
|
||||
String label = computer.getLabel();
|
||||
if( !Objects.equal( label, getLabel( stack ) ) )
|
||||
{
|
||||
changed = true;
|
||||
setLabel( stack, label );
|
||||
if( inventory != null ) inventory.setChanged();
|
||||
}
|
||||
|
||||
// Update pocket upgrade
|
||||
if( upgrade != null )
|
||||
if( upgrade != null ) upgrade.update( computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entity entity, int slotNum, boolean selected )
|
||||
{
|
||||
upgrade.update( computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||
}
|
||||
}
|
||||
if( !world.isClientSide )
|
||||
{
|
||||
Container inventory = entity instanceof Player player ? player.getInventory() : null;
|
||||
PocketServerComputer computer = createServerComputer( world, entity, inventory, stack );
|
||||
computer.keepAlive();
|
||||
|
||||
boolean changed = tick( stack, world, entity, computer );
|
||||
if( changed && inventory != null ) inventory.setChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client side
|
||||
createClientComputer( stack );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEntityItemUpdate( ItemStack stack, ItemEntity entity )
|
||||
{
|
||||
if( entity.level.isClientSide ) return false;
|
||||
|
||||
PocketServerComputer computer = getServerComputer( stack );
|
||||
if( computer != null && tick( stack, entity.level, entity, computer ) ) entity.setItem( stack.copy() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use( Level world, Player player, @Nonnull InteractionHand hand )
|
||||
@ -134,22 +146,18 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
ItemStack stack = player.getItemInHand( hand );
|
||||
if( !world.isClientSide )
|
||||
{
|
||||
PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack );
|
||||
|
||||
boolean stop = false;
|
||||
if( computer != null )
|
||||
{
|
||||
PocketServerComputer computer = createServerComputer( world, player, player.getInventory(), stack );
|
||||
computer.turnOn();
|
||||
|
||||
boolean stop = false;
|
||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||
if( upgrade != null )
|
||||
{
|
||||
computer.updateValues( player, stack, upgrade );
|
||||
stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( !stop && computer != null )
|
||||
if( !stop )
|
||||
{
|
||||
boolean isTypingOnly = hand == InteractionHand.OFF_HAND;
|
||||
new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) );
|
||||
@ -207,17 +215,17 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
return super.getCreatorModId( stack );
|
||||
}
|
||||
|
||||
public PocketServerComputer createServerComputer( final Level world, Container inventory, Entity entity, @Nonnull ItemStack stack )
|
||||
@Nonnull
|
||||
public PocketServerComputer createServerComputer( Level world, Entity entity, @Nullable Container inventory, @Nonnull ItemStack stack )
|
||||
{
|
||||
if( world.isClientSide ) return null;
|
||||
if( world.isClientSide ) throw new IllegalStateException( "Cannot call createServerComputer on the client" );
|
||||
|
||||
PocketServerComputer computer;
|
||||
int instanceID = getInstanceID( stack );
|
||||
int sessionID = getSessionID( stack );
|
||||
int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID();
|
||||
|
||||
if( instanceID >= 0 && sessionID == correctSessionID &&
|
||||
ComputerCraft.serverComputerRegistry.contains( instanceID ) )
|
||||
if( instanceID >= 0 && sessionID == correctSessionID && ComputerCraft.serverComputerRegistry.contains( instanceID ) )
|
||||
{
|
||||
computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID );
|
||||
}
|
||||
@ -235,13 +243,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" );
|
||||
setComputerID( stack, computerID );
|
||||
}
|
||||
computer = new PocketServerComputer(
|
||||
world,
|
||||
computerID,
|
||||
getLabel( stack ),
|
||||
instanceID,
|
||||
getFamily()
|
||||
);
|
||||
computer = new PocketServerComputer( world, computerID, getLabel( stack ), instanceID, getFamily() );
|
||||
computer.updateValues( entity, stack, getUpgrade( stack ) );
|
||||
computer.addAPI( new PocketAPI( computer ) );
|
||||
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
|
||||
@ -251,15 +253,17 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
return computer;
|
||||
}
|
||||
|
||||
public static ServerComputer getServerComputer( @Nonnull ItemStack stack )
|
||||
@Nullable
|
||||
public static PocketServerComputer getServerComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int session = getSessionID( stack );
|
||||
if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null;
|
||||
|
||||
int instanceID = getInstanceID( stack );
|
||||
return instanceID >= 0 ? ComputerCraft.serverComputerRegistry.get( instanceID ) : null;
|
||||
return instanceID >= 0 ? (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID ) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ClientComputer createClientComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int instanceID = getInstanceID( stack );
|
||||
@ -274,6 +278,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClientComputer getClientComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int instanceID = getInstanceID( stack );
|
||||
|
@ -22,8 +22,50 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The turtle API allows you to control your turtle.
|
||||
* Turtles are a robotic device, which can break and place blocks, attack mobs, and move about the world. They have
|
||||
* an internal inventory of 16 slots, allowing them to store blocks they have broken or would like to place.
|
||||
*
|
||||
* ## Movement
|
||||
* Turtles are capable of moving throug the world. As turtles are blocks themselves, they are confined to Minecraft's
|
||||
* grid, moving a single block at a time.
|
||||
*
|
||||
* {@literal @}{turtle.forward} and @{turtle.back} move the turtle in the direction it is facing, while @{turtle.up} and
|
||||
* {@literal @}{turtle.down} move it up and down (as one might expect!). In order to move left or right, you first need
|
||||
* to turn the turtle using @{turtle.turnLeft}/@{turtle.turnRight} and then move forward or backwards.
|
||||
*
|
||||
* :::info
|
||||
* The name "turtle" comes from [Turtle graphics], which originated from the Logo programming language. Here you'd move
|
||||
* a turtle with various commands like "move 10" and "turn left", much like ComputerCraft's turtles!
|
||||
* :::
|
||||
*
|
||||
* Moving a turtle (though not turning it) consumes *fuel*. If a turtle does not have any @{turtle.refuel|fuel}, it
|
||||
* won't move, and the movement functions will return @{false}. If your turtle isn't going anywhere, the first thing to
|
||||
* check is if you've fuelled your turtle.
|
||||
*
|
||||
* :::tip Handling errors
|
||||
* Many turtle functions can fail in various ways. For instance, a turtle cannot move forward if there's already a block
|
||||
* there. Instead of erroring, functions which can fail either return @{true} if they succeed, or @{false} and some
|
||||
* error message if they fail.
|
||||
*
|
||||
* Unexpected failures can often lead to strange behaviour. It's often a good idea to check the return values of these
|
||||
* functions, or wrap them in @{assert} (for instance, use `assert(turtle.forward())` rather than `turtle.forward()`),
|
||||
* so the program doesn't misbehave.
|
||||
* :::
|
||||
*
|
||||
* ## Turtle upgrades
|
||||
* While a normal turtle can move about the world and place blocks, its functionality is limited. Thankfully, turtles
|
||||
* can be upgraded with *tools* and @{peripheral|peripherals}. Turtles have two upgrade slots, one on the left and right
|
||||
* sides. Upgrades can be equipped by crafting a turtle with the upgrade, or calling the @{turtle.equipLeft}/@{turtle.equipRight}
|
||||
* functions.
|
||||
*
|
||||
* Turtle tools allow you to break blocks (@{turtle.dig}) and attack entities (@{turtle.attack}). Some tools are more
|
||||
* suitable to a task than others. For instance, a diamond pickaxe can break every block, while a sword does more
|
||||
* damage. Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||
*
|
||||
* Peripherals (such as the @{modem|wireless modem} or @{speaker}) can also be equipped as upgrades. These are then
|
||||
* accessible by accessing the `"left"` or `"right"` peripheral.
|
||||
*
|
||||
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
|
||||
* @cc.module turtle
|
||||
* @cc.since 1.3
|
||||
*/
|
||||
|
@ -31,7 +31,6 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntitySelector;
|
||||
import net.minecraft.world.entity.MoverType;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.level.Level;
|
||||
@ -39,6 +38,7 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
@ -48,6 +48,7 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static dan200.computercraft.shared.common.IColouredItem.NBT_COLOUR;
|
||||
import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED;
|
||||
@ -65,6 +66,8 @@ public class TurtleBrain implements ITurtleAccess
|
||||
|
||||
private static final int ANIM_DURATION = 8;
|
||||
|
||||
public static final Predicate<Entity> PUSHABLE_ENTITY = entity -> !entity.isSpectator() && entity.getPistonPushReaction() != PushReaction.IGNORE;
|
||||
|
||||
private TileTurtle owner;
|
||||
private ComputerProxy proxy;
|
||||
private GameProfile owningPlayer;
|
||||
@ -886,7 +889,7 @@ public class TurtleBrain implements ITurtleAccess
|
||||
}
|
||||
|
||||
AABB aabb = new AABB( minX, minY, minZ, maxX, maxY, maxZ );
|
||||
List<Entity> list = world.getEntitiesOfClass( Entity.class, aabb, EntitySelector.NO_SPECTATORS );
|
||||
List<Entity> list = world.getEntitiesOfClass( Entity.class, aabb, PUSHABLE_ENTITY );
|
||||
if( !list.isEmpty() )
|
||||
{
|
||||
double pushStep = 1.0f / ANIM_DURATION;
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
/**
|
||||
* A monotonically increasing clock which accounts for the game being paused.
|
||||
*/
|
||||
@Mod.EventBusSubscriber( Dist.CLIENT )
|
||||
public final class PauseAwareTimer
|
||||
{
|
||||
private static boolean paused;
|
||||
private static long pauseTime;
|
||||
private static long pauseOffset;
|
||||
|
||||
private PauseAwareTimer()
|
||||
{
|
||||
}
|
||||
|
||||
public static long getTime()
|
||||
{
|
||||
return (paused ? pauseTime : Util.getNanos()) - pauseOffset;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void tick( TickEvent.RenderTickEvent event )
|
||||
{
|
||||
if( event.phase != TickEvent.Phase.START ) return;
|
||||
|
||||
boolean isPaused = Minecraft.getInstance().isPaused();
|
||||
if( isPaused == paused ) return;
|
||||
|
||||
if( isPaused )
|
||||
{
|
||||
pauseTime = Util.getNanos();
|
||||
paused = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pauseOffset += Util.getNanos() - pauseTime;
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,3 +7,7 @@ public net.minecraft.client.gui.components.ChatComponent m_93787_(Lnet/minecraft
|
||||
public net.minecraft.client.gui.components.ChatComponent m_93803_(I)V # removeById
|
||||
# NoTermComputerScreen
|
||||
public net.minecraft.client.Minecraft f_91080_ # screen
|
||||
|
||||
# SpeakerInstance/SpeakerManager
|
||||
public com.mojang.blaze3d.audio.Channel m_83652_(I)V # pumpBuffers
|
||||
public net.minecraft.client.sounds.SoundEngine f_120223_ # executor
|
||||
|
@ -1,16 +1,18 @@
|
||||
--- The Disk API allows you to interact with disk drives.
|
||||
--
|
||||
-- These functions can operate on locally attached or remote disk drives. To use
|
||||
-- a locally attached drive, specify “side” as one of the six sides
|
||||
-- (e.g. `left`); to use a remote disk drive, specify its name as printed when
|
||||
-- enabling its modem (e.g. `drive_0`).
|
||||
--
|
||||
-- **Note:** All computers (except command computers), turtles and pocket
|
||||
-- computers can be placed within a disk drive to access it's internal storage
|
||||
-- like a disk.
|
||||
--
|
||||
-- @module disk
|
||||
-- @since 1.2
|
||||
--[[- The Disk API allows you to interact with disk drives.
|
||||
|
||||
These functions can operate on locally attached or remote disk drives. To use a
|
||||
locally attached drive, specify “side” as one of the six sides (e.g. `left`); to
|
||||
use a remote disk drive, specify its name as printed when enabling its modem
|
||||
(e.g. `drive_0`).
|
||||
|
||||
:::tip
|
||||
All computers (except command computers), turtles and pocket computers can be
|
||||
placed within a disk drive to access it's internal storage like a disk.
|
||||
:::
|
||||
|
||||
@module disk
|
||||
@since 1.2
|
||||
]]
|
||||
|
||||
local function isDrive(name)
|
||||
if type(name) ~= "string" then
|
||||
|
@ -1,27 +1,30 @@
|
||||
--- The GPS API provides a method for turtles and computers to retrieve their
|
||||
-- own locations.
|
||||
--
|
||||
-- It broadcasts a PING message over @{rednet} and wait for responses. In order
|
||||
-- for this system to work, there must be at least 4 computers used as gps hosts
|
||||
-- which will respond and allow trilateration. Three of these hosts should be in
|
||||
-- a plane, and the fourth should be either above or below the other three. The
|
||||
-- three in a plane should not be in a line with each other. You can set up
|
||||
-- hosts using the gps program.
|
||||
--
|
||||
-- **Note**: When entering in the coordinates for the host you need to put in
|
||||
-- the `x`, `y`, and `z` coordinates of the computer, not the modem, as all
|
||||
-- rednet distances are measured from the block the computer is in.
|
||||
--
|
||||
-- Also note that you may choose which axes x, y, or z refers to - so long as
|
||||
-- your systems have the same definition as any GPS servers that're in range, it
|
||||
-- works just the same. For example, you might build a GPS cluster according to
|
||||
-- [this tutorial][1], using z to account for height, or you might use y to
|
||||
-- account for height in the way that Minecraft's debug screen displays.
|
||||
--
|
||||
-- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/
|
||||
--
|
||||
-- @module gps
|
||||
-- @since 1.31
|
||||
--[[- The GPS API provides a method for turtles and computers to retrieve their
|
||||
own locations.
|
||||
|
||||
It broadcasts a PING message over @{rednet} and wait for responses. In order for
|
||||
this system to work, there must be at least 4 computers used as gps hosts which
|
||||
will respond and allow trilateration. Three of these hosts should be in a plane,
|
||||
and the fourth should be either above or below the other three. The three in a
|
||||
plane should not be in a line with each other. You can set up hosts using the
|
||||
gps program.
|
||||
|
||||
:::note
|
||||
When entering in the coordinates for the host you need to put in the `x`, `y`,
|
||||
and `z` coordinates of the computer, not the modem, as all modem distances are
|
||||
measured from the block the computer is in.
|
||||
:::
|
||||
|
||||
Also note that you may choose which axes x, y, or z refers to - so long as your
|
||||
systems have the same definition as any GPS servers that're in range, it works
|
||||
just the same. For example, you might build a GPS cluster according to [this
|
||||
tutorial][1], using z to account for height, or you might use y to account for
|
||||
height in the way that Minecraft's debug screen displays.
|
||||
|
||||
[1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/
|
||||
|
||||
@module gps
|
||||
@since 1.31
|
||||
]]
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
Functions are not actually executed simultaniously, but rather this API will
|
||||
automatically switch between them whenever they yield (eg whenever they call
|
||||
@{coroutine.yield}, or functions that call that - eg `os.pullEvent` - or
|
||||
@{coroutine.yield}, or functions that call that - eg @{os.pullEvent} - or
|
||||
functions that call that, etc - basically, anything that causes the function
|
||||
to "pause").
|
||||
|
||||
Each function executed in "parallel" gets its own copy of the event queue,
|
||||
and so "event consuming" functions (again, mostly anything that causes the
|
||||
script to pause - eg `sleep`, `rednet.receive`, most of the `turtle` API,
|
||||
script to pause - eg @{os.sleep}, @{rednet.receive}, most of the @{turtle} API,
|
||||
etc) can safely be used in one without affecting the event queue accessed by
|
||||
the other.
|
||||
|
||||
|
@ -66,7 +66,7 @@ type.
|
||||
What is a peripheral type though? This is a string which describes what a
|
||||
peripheral is, and so what functions are available on it. For instance, speakers
|
||||
are just called `"speaker"`, and monitors `"monitor"`. Some peripherals might
|
||||
have more than one type; a Minecraft chest is both a `"minecraft:chest"` and
|
||||
have more than one type - a Minecraft chest is both a `"minecraft:chest"` and
|
||||
`"inventory"`.
|
||||
|
||||
You can get all the types a peripheral has with @{peripheral.getType}, and check
|
||||
|
@ -1,21 +1,48 @@
|
||||
--- The Rednet API allows systems to communicate between each other without
|
||||
-- using redstone. It serves as a wrapper for the modem API, offering ease of
|
||||
-- functionality (particularly in regards to repeating signals) with some
|
||||
-- expense of fine control.
|
||||
--
|
||||
-- In order to send and receive data, a modem (either wired, wireless, or ender)
|
||||
-- is required. The data reaches any possible destinations immediately after
|
||||
-- sending it, but is range limited.
|
||||
--
|
||||
-- Rednet also allows you to use a "protocol" - simple string names indicating
|
||||
-- what messages are about. Receiving systems may filter messages according to
|
||||
-- their protocols, thereby automatically ignoring incoming messages which don't
|
||||
-- specify an identical string. It's also possible to @{rednet.lookup|lookup}
|
||||
-- which systems in the area use certain protocols, hence making it easier to
|
||||
-- determine where given messages should be sent in the first place.
|
||||
--
|
||||
-- @module rednet
|
||||
-- @since 1.2
|
||||
--[[- The Rednet API allows computers to communicate between each other by using
|
||||
@{modem|modems}. It provides a layer of abstraction on top of the main @{modem}
|
||||
peripheral, making it slightly easier to use.
|
||||
|
||||
## Basic usage
|
||||
In order to send a message between two computers, each computer must have a
|
||||
modem on one of its sides (or in the case of pocket computers and turtles, the
|
||||
modem must be equipped as an upgrade). The two computers should then call
|
||||
@{rednet.open}, which sets up the modems ready to send and receive messages.
|
||||
|
||||
Once rednet is opened, you can send messages using @{rednet.send} and receive
|
||||
them using @{rednet.receive}. It's also possible to send a message to _every_
|
||||
rednet-using computer using @{rednet.broadcast}.
|
||||
|
||||
:::caution Network security
|
||||
|
||||
While rednet provides a friendly way to send messages to specific computers, it
|
||||
doesn't provide any guarantees about security. Other computers could be
|
||||
listening in to your messages, or even pretending to send messages from other computers!
|
||||
|
||||
If you're playing on a multi-player server (or at least one where you don't
|
||||
trust other players), it's worth encrypting or signing your rednet messages.
|
||||
:::
|
||||
|
||||
## Protocols and hostnames
|
||||
Several rednet messages accept "protocol"s - simple string names describing what
|
||||
a message is about. When sending messages using @{rednet.send} and
|
||||
@{rednet.broadcast}, you can optionally specify a protocol for the message. This
|
||||
same protocol can then be given to @{rednet.receive}, to ignore all messages not
|
||||
using this protocol.
|
||||
|
||||
It's also possible to look-up computers based on protocols, providing a basic
|
||||
system for service discovery and [DNS]. A computer can advertise that it
|
||||
supports a particular protocol with @{rednet.host}, also providing a friendly
|
||||
"hostname". Other computers may then find all computers which support this
|
||||
protocol using @{rednet.lookup}.
|
||||
|
||||
[DNS]: https://en.wikipedia.org/wiki/Domain_Name_System "Domain Name System"
|
||||
|
||||
@module rednet
|
||||
@since 1.2
|
||||
@see rednet_message Queued when a rednet message is received.
|
||||
@see modem Rednet is built on top of the modem peripheral. Modems provide a more
|
||||
bare-bones but flexible interface.
|
||||
]]
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
@ -46,9 +73,17 @@ This will open the modem on two channels: one which has the same
|
||||
|
||||
@tparam string modem The name of the modem to open.
|
||||
@throws If there is no such modem with the given name
|
||||
@usage Open a wireless modem on the back of the computer.
|
||||
@usage Open rednet on the back of the computer, allowing you to send and receive
|
||||
rednet messages using it.
|
||||
|
||||
rednet.open("back")
|
||||
|
||||
@usage Open rednet on all attached modems. This abuses the "filter" argument to
|
||||
@{peripheral.find}.
|
||||
|
||||
peripheral.find("modem", rednet.open)
|
||||
@see rednet.close
|
||||
@see rednet.isOpen
|
||||
]]
|
||||
function open(modem)
|
||||
expect(1, modem, "string")
|
||||
@ -65,6 +100,7 @@ end
|
||||
-- @tparam[opt] string modem The side the modem exists on. If not given, all
|
||||
-- open modems will be closed.
|
||||
-- @throws If there is no such modem with the given name
|
||||
-- @see rednet.open
|
||||
function close(modem)
|
||||
expect(1, modem, "string", "nil")
|
||||
if modem then
|
||||
@ -90,6 +126,7 @@ end
|
||||
-- modems will be checked.
|
||||
-- @treturn boolean If the given modem is open.
|
||||
-- @since 1.31
|
||||
-- @see rednet.open
|
||||
function isOpen(modem)
|
||||
expect(1, modem, "string", "nil")
|
||||
if modem then
|
||||
@ -109,15 +146,17 @@ function isOpen(modem)
|
||||
end
|
||||
|
||||
--[[- Allows a computer or turtle with an attached modem to send a message
|
||||
intended for a system with a specific ID. At least one such modem must first
|
||||
intended for a sycomputer with a specific ID. At least one such modem must first
|
||||
be @{rednet.open|opened} before sending is possible.
|
||||
|
||||
Assuming the target was in range and also had a correctly opened modem, it
|
||||
may then use @{rednet.receive} to collect the message.
|
||||
Assuming the target was in range and also had a correctly opened modem, the
|
||||
target computer may then use @{rednet.receive} to collect the message.
|
||||
|
||||
@tparam number recipient The ID of the receiving computer.
|
||||
@param message The message to send. This should not contain coroutines or
|
||||
functions, as they will be converted to @{nil}.
|
||||
@param message The message to send. Like with @{modem.transmit}, this can
|
||||
contain any primitive type (numbers, booleans and strings) as well as
|
||||
tables. Other types (like functions), as well as metatables, will not be
|
||||
transmitted.
|
||||
@tparam[opt] string protocol The "protocol" to send this message under. When
|
||||
using @{rednet.receive} one can filter to only receive messages sent under a
|
||||
particular protocol.
|
||||
@ -174,16 +213,19 @@ function send(recipient, message, protocol)
|
||||
return sent
|
||||
end
|
||||
|
||||
--- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST}
|
||||
-- channel. The message will be received by every device listening to rednet.
|
||||
--
|
||||
-- @param message The message to send. This should not contain coroutines or
|
||||
-- functions, as they will be converted to @{nil}.
|
||||
-- @tparam[opt] string protocol The "protocol" to send this message under. When
|
||||
-- using @{rednet.receive} one can filter to only receive messages sent under a
|
||||
-- particular protocol.
|
||||
-- @see rednet.receive
|
||||
-- @changed 1.6 Added protocol parameter.
|
||||
--[[- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST}
|
||||
channel. The message will be received by every device listening to rednet.
|
||||
|
||||
@param message The message to send. This should not contain coroutines or
|
||||
functions, as they will be converted to @{nil}. @tparam[opt] string protocol
|
||||
The "protocol" to send this message under. When using @{rednet.receive} one can
|
||||
filter to only receive messages sent under a particular protocol.
|
||||
@see rednet.receive
|
||||
@changed 1.6 Added protocol parameter.
|
||||
@usage Broadcast the words "Hello, world!" to every computer using rednet.
|
||||
|
||||
rednet.broadcast("Hello, world!")
|
||||
]]
|
||||
function broadcast(message, protocol)
|
||||
expect(2, protocol, "string", "nil")
|
||||
send(CHANNEL_BROADCAST, message, protocol)
|
||||
@ -263,25 +305,25 @@ function receive(protocol_filter, timeout)
|
||||
end
|
||||
end
|
||||
|
||||
--- Register the system as "hosting" the desired protocol under the specified
|
||||
-- name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and
|
||||
-- maybe name) on the same network, the registered system will automatically
|
||||
-- respond via a background process, hence providing the system performing the
|
||||
-- lookup with its ID number.
|
||||
--
|
||||
-- Multiple computers may not register themselves on the same network as having
|
||||
-- the same names against the same protocols, and the title `localhost` is
|
||||
-- specifically reserved. They may, however, share names as long as their hosted
|
||||
-- protocols are different, or if they only join a given network after
|
||||
-- "registering" themselves before doing so (eg while offline or part of a
|
||||
-- different network).
|
||||
--
|
||||
-- @tparam string protocol The protocol this computer provides.
|
||||
-- @tparam string hostname The name this protocol exposes for the given protocol.
|
||||
-- @throws If trying to register a hostname which is reserved, or currently in use.
|
||||
-- @see rednet.unhost
|
||||
-- @see rednet.lookup
|
||||
-- @since 1.6
|
||||
--[[- Register the system as "hosting" the desired protocol under the specified
|
||||
name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and
|
||||
maybe name) on the same network, the registered system will automatically
|
||||
respond via a background process, hence providing the system performing the
|
||||
lookup with its ID number.
|
||||
|
||||
Multiple computers may not register themselves on the same network as having the
|
||||
same names against the same protocols, and the title `localhost` is specifically
|
||||
reserved. They may, however, share names as long as their hosted protocols are
|
||||
different, or if they only join a given network after "registering" themselves
|
||||
before doing so (eg while offline or part of a different network).
|
||||
|
||||
@tparam string protocol The protocol this computer provides.
|
||||
@tparam string hostname The name this protocol exposes for the given protocol.
|
||||
@throws If trying to register a hostname which is reserved, or currently in use.
|
||||
@see rednet.unhost
|
||||
@see rednet.lookup
|
||||
@since 1.6
|
||||
]]
|
||||
function host(protocol, hostname)
|
||||
expect(1, protocol, "string")
|
||||
expect(2, hostname, "string")
|
||||
@ -306,21 +348,38 @@ function unhost(protocol)
|
||||
hostnames[protocol] = nil
|
||||
end
|
||||
|
||||
--- Search the local rednet network for systems @{rednet.host|hosting} the
|
||||
-- desired protocol and returns any computer IDs that respond as "registered"
|
||||
-- against it.
|
||||
--
|
||||
-- If a hostname is specified, only one ID will be returned (assuming an exact
|
||||
-- match is found).
|
||||
--
|
||||
-- @tparam string protocol The protocol to search for.
|
||||
-- @tparam[opt] string hostname The hostname to search for.
|
||||
--
|
||||
-- @treturn[1] { number }|nil A list of computer IDs hosting the given
|
||||
-- protocol, or @{nil} if none exist.
|
||||
-- @treturn[2] number|nil The computer ID with the provided hostname and protocol,
|
||||
-- or @{nil} if none exists.
|
||||
-- @since 1.6
|
||||
--[[- Search the local rednet network for systems @{rednet.host|hosting} the
|
||||
desired protocol and returns any computer IDs that respond as "registered"
|
||||
against it.
|
||||
|
||||
If a hostname is specified, only one ID will be returned (assuming an exact
|
||||
match is found).
|
||||
|
||||
@tparam string protocol The protocol to search for.
|
||||
@tparam[opt] string hostname The hostname to search for.
|
||||
|
||||
@treturn[1] number... A list of computer IDs hosting the given protocol.
|
||||
@treturn[2] number|nil The computer ID with the provided hostname and protocol,
|
||||
or @{nil} if none exists.
|
||||
@since 1.6
|
||||
@usage Find all computers which are hosting the `"chat"` protocol.
|
||||
|
||||
local computers = {rednet.lookup("chat")}
|
||||
print(#computers .. " computers available to chat")
|
||||
for _, computer in pairs(computers) do
|
||||
print("Computer #" .. computer)
|
||||
end
|
||||
|
||||
@usage Find a computer hosting the `"chat"` protocol with a hostname of `"my_host"`.
|
||||
|
||||
local id = rednet.lookup("chat", "my_host")
|
||||
if id then
|
||||
print("Found my_host at computer #" .. id)
|
||||
else
|
||||
printError("Cannot find my_host")
|
||||
end
|
||||
|
||||
]]
|
||||
function lookup(protocol, hostname)
|
||||
expect(1, protocol, "string")
|
||||
expect(2, hostname, "string", "nil")
|
||||
|
@ -108,39 +108,48 @@ local function makePagedScroll(_term, _nFreeLines)
|
||||
end
|
||||
end
|
||||
|
||||
--- Prints a given string to the display.
|
||||
--
|
||||
-- If the action can be completed without scrolling, it acts much the same as
|
||||
-- @{print}; otherwise, it will throw up a "Press any key to continue" prompt at
|
||||
-- the bottom of the display. Each press will cause it to scroll down and write
|
||||
-- a single line more before prompting again, if need be.
|
||||
--
|
||||
-- @tparam string _sText The text to print to the screen.
|
||||
-- @tparam[opt] number _nFreeLines The number of lines which will be
|
||||
-- automatically scrolled before the first prompt appears (meaning _nFreeLines +
|
||||
-- 1 lines will be printed). This can be set to the terminal's height - 2 to
|
||||
-- always try to fill the screen. Defaults to 0, meaning only one line is
|
||||
-- displayed before prompting.
|
||||
-- @treturn number The number of lines printed.
|
||||
-- @usage
|
||||
-- local width, height = term.getSize()
|
||||
-- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2)
|
||||
function pagedPrint(_sText, _nFreeLines)
|
||||
expect(2, _nFreeLines, "number", "nil")
|
||||
--[[- Prints a given string to the display.
|
||||
|
||||
If the action can be completed without scrolling, it acts much the same as
|
||||
@{print}; otherwise, it will throw up a "Press any key to continue" prompt at
|
||||
the bottom of the display. Each press will cause it to scroll down and write a
|
||||
single line more before prompting again, if need be.
|
||||
|
||||
@tparam string text The text to print to the screen.
|
||||
@tparam[opt] number free_lines The number of lines which will be
|
||||
automatically scrolled before the first prompt appears (meaning free_lines +
|
||||
1 lines will be printed). This can be set to the cursor's y position - 2 to
|
||||
always try to fill the screen. Defaults to 0, meaning only one line is
|
||||
displayed before prompting.
|
||||
@treturn number The number of lines printed.
|
||||
|
||||
@usage Generates several lines of text and then prints it, paging once the
|
||||
bottom of the terminal is reached.
|
||||
|
||||
local lines = {}
|
||||
for i = 1, 30 do lines[i] = ("This is line #%d"):format(i) end
|
||||
local message = table.concat(lines, "\n")
|
||||
|
||||
local width, height = term.getCursorPos()
|
||||
textutils.pagedPrint(message, height - 2)
|
||||
]]
|
||||
function pagedPrint(text, free_lines)
|
||||
expect(2, free_lines, "number", "nil")
|
||||
-- Setup a redirector
|
||||
local oldTerm = term.current()
|
||||
local newTerm = {}
|
||||
for k, v in pairs(oldTerm) do
|
||||
newTerm[k] = v
|
||||
end
|
||||
newTerm.scroll = makePagedScroll(oldTerm, _nFreeLines)
|
||||
|
||||
newTerm.scroll = makePagedScroll(oldTerm, free_lines)
|
||||
term.redirect(newTerm)
|
||||
|
||||
-- Print the text
|
||||
local result
|
||||
local ok, err = pcall(function()
|
||||
if _sText ~= nil then
|
||||
result = print(_sText)
|
||||
if text ~= nil then
|
||||
result = print(text)
|
||||
else
|
||||
result = print()
|
||||
end
|
||||
@ -214,32 +223,45 @@ local function tabulateCommon(bPaged, ...)
|
||||
end
|
||||
end
|
||||
|
||||
--- Prints tables in a structured form.
|
||||
--
|
||||
-- This accepts multiple arguments, either a table or a number. When
|
||||
-- encountering a table, this will be treated as a table row, with each column
|
||||
-- width being auto-adjusted.
|
||||
--
|
||||
-- When encountering a number, this sets the text color of the subsequent rows to it.
|
||||
--
|
||||
-- @tparam {string...}|number ... The rows and text colors to display.
|
||||
-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" })
|
||||
-- @since 1.3
|
||||
--[[- Prints tables in a structured form.
|
||||
|
||||
This accepts multiple arguments, either a table or a number. When
|
||||
encountering a table, this will be treated as a table row, with each column
|
||||
width being auto-adjusted.
|
||||
|
||||
When encountering a number, this sets the text color of the subsequent rows to it.
|
||||
|
||||
@tparam {string...}|number ... The rows and text colors to display.
|
||||
@since 1.3
|
||||
@usage
|
||||
|
||||
textutils.tabulate(
|
||||
colors.orange, { "1", "2", "3" },
|
||||
colors.lightBlue, { "A", "B", "C" }
|
||||
)
|
||||
]]
|
||||
function tabulate(...)
|
||||
return tabulateCommon(false, ...)
|
||||
end
|
||||
|
||||
--- Prints tables in a structured form, stopping and prompting for input should
|
||||
-- the result not fit on the terminal.
|
||||
--
|
||||
-- This functions identically to @{textutils.tabulate}, but will prompt for user
|
||||
-- input should the whole output not fit on the display.
|
||||
--
|
||||
-- @tparam {string...}|number ... The rows and text colors to display.
|
||||
-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" })
|
||||
-- @see textutils.tabulate
|
||||
-- @see textutils.pagedPrint
|
||||
-- @since 1.3
|
||||
--[[- Prints tables in a structured form, stopping and prompting for input should
|
||||
the result not fit on the terminal.
|
||||
|
||||
This functions identically to @{textutils.tabulate}, but will prompt for user
|
||||
input should the whole output not fit on the display.
|
||||
|
||||
@tparam {string...}|number ... The rows and text colors to display.
|
||||
@see textutils.tabulate
|
||||
@see textutils.pagedPrint
|
||||
@since 1.3
|
||||
|
||||
@usage Generates a long table, tabulates it, and prints it to the screen.
|
||||
|
||||
local rows = {}
|
||||
for i = 1, 30 do rows[i] = {("Row #%d"):format(i), math.random(1, 400)} end
|
||||
|
||||
textutils.tabulate(colors.orange, {"Column", "Value"}, colors.lightBlue, table.unpack(rows))
|
||||
]]
|
||||
function pagedTabulate(...)
|
||||
return tabulateCommon(true, ...)
|
||||
end
|
||||
@ -692,11 +714,11 @@ saving in a file or pretty-printing.
|
||||
@throws If the object contains a value which cannot be
|
||||
serialised. This includes functions and tables which appear multiple
|
||||
times.
|
||||
@see cc.pretty.pretty An alternative way to display a table, often more suitable for
|
||||
pretty printing.
|
||||
@see cc.pretty.pretty_print An alternative way to display a table, often more
|
||||
suitable for pretty printing.
|
||||
@since 1.3
|
||||
@changed 1.97.0 Added `opts` argument.
|
||||
@usage Pretty print a basic table.
|
||||
@usage Serialise a basic table.
|
||||
|
||||
textutils.serialise({ 1, 2, 3, a = 1, ["another key"] = { true } })
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
--- The turtle API allows you to control your turtle.
|
||||
--
|
||||
-- @module turtle
|
||||
--- @module turtle
|
||||
|
||||
if not turtle then
|
||||
error("Cannot load turtle API on computer", 2)
|
||||
@ -8,9 +6,8 @@ end
|
||||
|
||||
--- The builtin turtle API, without any generated helper functions.
|
||||
--
|
||||
-- Generally you should not need to use this table - it only exists for
|
||||
-- backwards compatibility reasons.
|
||||
-- @deprecated
|
||||
-- @deprecated Historically this table behaved differently to the main turtle API, but this is no longer the base. You
|
||||
-- should not need to use it.
|
||||
native = turtle.native or turtle
|
||||
|
||||
local function addCraftMethod(object)
|
||||
|
@ -1,3 +1,15 @@
|
||||
# New features in CC: Tweaked 1.100.0
|
||||
|
||||
* Speakers can now play arbitrary PCM audio.
|
||||
* Add support for encoding and decoding DFPWM streams, with the `cc.audio.dfpwm` module.
|
||||
* Wired modems now only render breaking progress for the part which is being broken.
|
||||
* Various documentation improvements.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix the "repeat" program not repeating broadcast rednet messages.
|
||||
* Fix the drag-and-drop upload functionality writing empty files.
|
||||
* Prevent turtles from pushing non-pushable entities.
|
||||
|
||||
# New features in CC: Tweaked 1.99.1
|
||||
|
||||
* Add package.searchpath to the cc.require API. (MCJack123)
|
||||
|
@ -6,7 +6,7 @@ Uses LuaJ from http://luaj.sourceforge.net/
|
||||
The ComputerCraft 1.76 update was sponsored by MinecraftU and Deep Space.
|
||||
Visit http://www.minecraftu.org and http://www.deepspace.me/space-cadets to find out more.
|
||||
|
||||
Join the ComputerCraft community online at http://www.computercraft.info
|
||||
Join the ComputerCraft community online at https://computercraft.cc
|
||||
Follow @DanTwoHundred on Twitter!
|
||||
|
||||
To help contribute to ComputerCraft, browse the source code at https://github.com/dan200/ComputerCraft.
|
||||
|
@ -1,12 +1,13 @@
|
||||
New features in CC: Tweaked 1.99.1
|
||||
New features in CC: Tweaked 1.100.0
|
||||
|
||||
* Add package.searchpath to the cc.require API. (MCJack123)
|
||||
* Provide a more efficient way for the Java API to consume Lua tables in certain restricted cases.
|
||||
* Speakers can now play arbitrary PCM audio.
|
||||
* Add support for encoding and decoding DFPWM streams, with the `cc.audio.dfpwm` module.
|
||||
* Wired modems now only render breaking progress for the part which is being broken.
|
||||
* Various documentation improvements.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix keys being "sticky" when opening the off-hand pocket computer GUI.
|
||||
* Correctly handle broken coroutine managers resuming Java code with a `nil` event.
|
||||
* Prevent computer buttons stealing focus from the terminal.
|
||||
* Fix a class cast exception when a monitor is malformed in ways I do not quite understand.
|
||||
* Fix the "repeat" program not repeating broadcast rednet messages.
|
||||
* Fix the drag-and-drop upload functionality writing empty files.
|
||||
* Prevent turtles from pushing non-pushable entities.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
@ -36,7 +36,7 @@ local encoder = dfpwm.make_encoder()
|
||||
local decoder = dfpwm.make_decoder()
|
||||
|
||||
local out = fs.open("speedy.dfpwm", "wb")
|
||||
for input in io.lines("my_audio_track.dfpwm", 16 * 1024 * 2) do
|
||||
for input in io.lines("data/example.dfpwm", 16 * 1024 * 2) do
|
||||
local decoded = decoder(input)
|
||||
local output = {}
|
||||
|
||||
|
@ -225,7 +225,7 @@ end
|
||||
--- Display a document on the terminal.
|
||||
--
|
||||
-- @tparam Doc doc The document to render
|
||||
-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
-- @tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
local function write(doc, ribbon_frac)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
expect(2, ribbon_frac, "number", "nil")
|
||||
@ -287,7 +287,7 @@ end
|
||||
--- Display a document on the terminal with a trailing new line.
|
||||
--
|
||||
-- @tparam Doc doc The document to render.
|
||||
-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
-- @tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
local function print(doc, ribbon_frac)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
expect(2, ribbon_frac, "number", "nil")
|
||||
@ -298,9 +298,9 @@ end
|
||||
--- Render a document, converting it into a string.
|
||||
--
|
||||
-- @tparam Doc doc The document to render.
|
||||
-- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to
|
||||
-- fit this width - it is only used for finding the best layout.
|
||||
-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
-- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to fit
|
||||
-- this width - it is only used for finding the best layout.
|
||||
-- @tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
-- @treturn string The rendered document as a string.
|
||||
local function render(doc, width, ribbon_frac)
|
||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||
@ -483,7 +483,7 @@ Controls how various properties are displayed.
|
||||
- `function_args`: Show the arguments to a function if known (`false` by default).
|
||||
- `function_source`: Show where the function was defined, instead of
|
||||
`function: xxxxxxxx` (`false` by default).
|
||||
@tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
@tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||
|
||||
@usage Display a table on the screen.
|
||||
|
||||
|
@ -1,22 +1,24 @@
|
||||
--- This provides a pure Lua implementation of the builtin @{require} function
|
||||
-- and @{package} library.
|
||||
--
|
||||
-- Generally you do not need to use this module - it is injected into the
|
||||
-- every program's environment. However, it may be useful when building a
|
||||
-- custom shell or when running programs yourself.
|
||||
--
|
||||
-- @module cc.require
|
||||
-- @since 1.88.0
|
||||
-- @usage Construct the package and require function, and insert them into a
|
||||
-- custom environment.
|
||||
--
|
||||
-- local r = require "cc.require"
|
||||
-- local env = setmetatable({}, { __index = _ENV })
|
||||
-- env.require, env.package = r.make(env, "/")
|
||||
--
|
||||
-- -- Now we have our own require function, separate to the original.
|
||||
-- local r2 = env.require "cc.require"
|
||||
-- print(r, r2)
|
||||
--[[- This provides a pure Lua implementation of the builtin @{require} function
|
||||
and @{package} library.
|
||||
|
||||
Generally you do not need to use this module - it is injected into the every
|
||||
program's environment. However, it may be useful when building a custom shell or
|
||||
when running programs yourself.
|
||||
|
||||
@module cc.require
|
||||
@since 1.88.0
|
||||
@see using_require For an introduction on how to use @{require}.
|
||||
@usage Construct the package and require function, and insert them into a
|
||||
custom environment.
|
||||
|
||||
local r = require "cc.require"
|
||||
local env = setmetatable({}, { __index = _ENV })
|
||||
env.require, env.package = r.make(env, "/")
|
||||
|
||||
-- Now we have our own require function, separate to the original.
|
||||
local r2 = env.require "cc.require"
|
||||
print(r, r2)
|
||||
]]
|
||||
|
||||
local expect = require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")
|
||||
local expect = expect.expect
|
||||
|
@ -171,7 +171,7 @@ end
|
||||
local current_section = nil
|
||||
local offset = 0
|
||||
|
||||
--- Find the currently visible seciton, or nil if this document has no sections.
|
||||
--- Find the currently visible section, or nil if this document has no sections.
|
||||
--
|
||||
-- This could potentially be a binary search, but right now it's not worth it.
|
||||
local function find_section()
|
||||
|
@ -26,13 +26,14 @@ const Click = (options: { run: () => void }) =>
|
||||
|
||||
type WindowProps = {};
|
||||
|
||||
type WindowState = {
|
||||
visible: boolean,
|
||||
|
||||
example: string,
|
||||
exampleIdx: number,
|
||||
type Example = {
|
||||
files: { [file: string]: string },
|
||||
}
|
||||
|
||||
type WindowState = {
|
||||
exampleIdx: number,
|
||||
} & ({ visible: false, example: null } | { visible: true, example: Example })
|
||||
|
||||
type Touch = { clientX: number, clientY: number };
|
||||
|
||||
class Window extends Component<WindowProps, WindowState> {
|
||||
@ -41,12 +42,14 @@ class Window extends Component<WindowProps, WindowState> {
|
||||
private top: number = 0;
|
||||
private dragging?: { downX: number, downY: number, initialX: number, initialY: number };
|
||||
|
||||
private snippets: { [file: string]: string } = {};
|
||||
|
||||
constructor(props: WindowProps, context: unknown) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
visible: false,
|
||||
example: "",
|
||||
example: null,
|
||||
exampleIdx: 0,
|
||||
}
|
||||
}
|
||||
@ -57,10 +60,16 @@ class Window extends Component<WindowProps, WindowState> {
|
||||
const element = elements[i] as HTMLElement;
|
||||
|
||||
let example = element.innerText;
|
||||
|
||||
const snippet = element.getAttribute("data-snippet");
|
||||
if (snippet) this.snippets[snippet] = example;
|
||||
|
||||
if (element.getAttribute("data-lua-kind") == "expr") {
|
||||
example = exprTemplate.replace("__expr__", example);
|
||||
}
|
||||
render(<Click run={this.runExample(example)} />, element);
|
||||
|
||||
const mount = element.getAttribute("data-mount");
|
||||
render(<Click run={this.runExample(example, mount)} />, element);
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,13 +85,13 @@ class Window extends Component<WindowProps, WindowState> {
|
||||
</div>
|
||||
<div class="computer-container">
|
||||
<Computer key={exampleIdx} files={{
|
||||
"example.lua": example, ...defaultFiles
|
||||
...example!.files, ...defaultFiles
|
||||
}} />
|
||||
</div>
|
||||
</div> : <div class="example-window example-window-hidden" />;
|
||||
}
|
||||
|
||||
private runExample(example: string): () => void {
|
||||
private runExample(example: string, mount: string | null): () => void {
|
||||
return () => {
|
||||
if (!this.positioned) {
|
||||
this.positioned = true;
|
||||
@ -90,9 +99,17 @@ class Window extends Component<WindowProps, WindowState> {
|
||||
this.top = 20;
|
||||
}
|
||||
|
||||
const files: { [file: string]: string } = { "example.lua": example };
|
||||
if (mount !== null) {
|
||||
for (const toMount of mount.split(",")) {
|
||||
const [name, path] = toMount.split(":", 2);
|
||||
files[path] = this.snippets[name] || "";
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(({ exampleIdx }: WindowState) => ({
|
||||
visible: true,
|
||||
example: example,
|
||||
example: { files },
|
||||
exampleIdx: exampleIdx + 1,
|
||||
}));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user