mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-31 01:27:55 +00:00
Merge remote-tracking branch 'CC-Tweaked/mc-1.18.x' into mc-1.18.x/dev
This commit is contained in:
@@ -52,5 +52,6 @@ exclude: |
|
|||||||
src/test/resources/test-rom/data/json-parsing/|
|
src/test/resources/test-rom/data/json-parsing/|
|
||||||
src/test/server-files/|
|
src/test/server-files/|
|
||||||
config/idea/|
|
config/idea/|
|
||||||
.vscode/
|
.vscode/|
|
||||||
|
.*\.dfpwm
|
||||||
)
|
)
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
module: [kind=event] modem_message
|
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
|
## Return Values
|
||||||
1. @{string}: The event name.
|
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.
|
3. @{number}: The channel that the message was sent on.
|
||||||
4. @{number}: The reply channel set by the sender.
|
4. @{number}: The reply channel set by the sender.
|
||||||
5. @{any}: The message as sent 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
|
## Example
|
||||||
Prints a message when one is sent:
|
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||||
|
modem.open(0)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
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)))
|
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)))
|
||||||
|
@@ -12,7 +12,7 @@ see: speaker.playAudio To play audio using the speaker
|
|||||||
This uses @{io.lines} to read audio data in blocks of 16KiB from "example_song.dfpwm", and then attempts to play it
|
This uses @{io.lines} to read audio data in blocks of 16KiB from "example_song.dfpwm", and then attempts to play it
|
||||||
using @{speaker.playAudio}. If the speaker's buffer is full, it waits for an event and tries again.
|
using @{speaker.playAudio}. If the speaker's buffer is full, it waits for an event and tries again.
|
||||||
|
|
||||||
```lua
|
```lua {data-peripheral=speaker}
|
||||||
local dfpwm = require("cc.audio.dfpwm")
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
local speaker = peripheral.find("speaker")
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
|
@@ -145,7 +145,7 @@ adds the sample from one second ago to it.
|
|||||||
For this, we'll need to keep track of the last 48k samples - exactly one seconds worth of audio. We can do this using a
|
For this, we'll need to keep track of the last 48k samples - exactly one seconds worth of audio. We can do this using a
|
||||||
[Ring Buffer], which helps makes things a little more efficient.
|
[Ring Buffer], which helps makes things a little more efficient.
|
||||||
|
|
||||||
```lua
|
```lua {data-peripheral=speaker}
|
||||||
local dfpwm = require("cc.audio.dfpwm")
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
local speaker = peripheral.find("speaker")
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
@@ -157,14 +157,14 @@ for i = 1, samples_n do samples[i] = 0 end
|
|||||||
|
|
||||||
local decoder = dfpwm.make_decoder()
|
local decoder = dfpwm.make_decoder()
|
||||||
for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
||||||
local buffer = decoder(input)
|
local buffer = decoder(chunk)
|
||||||
|
|
||||||
for i = 1, #buffer do
|
for i = 1, #buffer do
|
||||||
local original_value = buffer[i]
|
local original_value = buffer[i]
|
||||||
|
|
||||||
-- Replace this sample with its current amplitude plus the amplitude from one second ago.
|
-- Replace this sample with its current amplitude plus the amplitude from one second ago.
|
||||||
-- We scale both to ensure the resulting value is still between -128 and 127.
|
-- We scale both to ensure the resulting value is still between -128 and 127.
|
||||||
buffer[i] = original_value * 0.7 + samples[samples_i] * 0.3
|
buffer[i] = original_value * 0.6 + samples[samples_i] * 0.4
|
||||||
|
|
||||||
-- Now store the current sample, and move the "head" of our ring buffer forward one place.
|
-- Now store the current sample, and move the "head" of our ring buffer forward one place.
|
||||||
samples[samples_i] = original_value
|
samples[samples_i] = original_value
|
||||||
|
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
|
or the @{parallel|parallel API}, it will only pause execution of the current
|
||||||
thread, not the whole program.
|
thread, not the whole program.
|
||||||
|
|
||||||
**Note** Because sleep internally uses timers, it is a function that yields.
|
:::tip
|
||||||
This means that you can use it to prevent "Too long without yielding" errors,
|
Because sleep internally uses timers, it is a function that yields. This means
|
||||||
however, as the minimum sleep time is 0.05 seconds, it will slow your program
|
that you can use it to prevent "Too long without yielding" errors, however, as
|
||||||
down.
|
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
|
@{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
|
that any event that occurs while sleeping will be entirely discarded. If you
|
||||||
need to receive events while sleeping, consider using @{os.startTimer|timers},
|
need to receive events while sleeping, consider using @{os.startTimer|timers},
|
||||||
or the @{parallel|parallel API}.
|
or the @{parallel|parallel API}.
|
||||||
|
:::
|
||||||
|
|
||||||
@tparam number time The number of seconds to sleep for, rounded up to the
|
@tparam number time The number of seconds to sleep for, rounded up to the
|
||||||
nearest multiple of 0.05.
|
nearest multiple of 0.05.
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
# Done to increase the memory available to gradle.
|
# Done to increase the memory available to gradle.
|
||||||
org.gradle.jvmargs=-Xmx1G
|
org.gradle.jvmargs=-Xmx3G
|
||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
mod_version=1.99.2-alpha1
|
mod_version=1.100.0
|
||||||
|
|
||||||
# Minecraft properties
|
# Minecraft properties
|
||||||
mc_version=1.18.1
|
mc_version=1.18.1
|
||||||
|
@@ -5,9 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.lua;
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static dan200.computercraft.api.lua.LuaValues.*;
|
import static dan200.computercraft.api.lua.LuaValues.*;
|
||||||
|
@@ -173,7 +173,6 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.rewind();
|
|
||||||
toUpload.add( new FileUpload( name, buffer, digest ) );
|
toUpload.add( new FileUpload( name, buffer, digest ) );
|
||||||
}
|
}
|
||||||
catch( IOException e )
|
catch( IOException e )
|
||||||
|
@@ -29,6 +29,7 @@ import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
|
|||||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||||
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
|
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
|
||||||
import dan200.computercraft.shared.util.Config;
|
import dan200.computercraft.shared.util.Config;
|
||||||
|
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
@@ -40,6 +41,7 @@ import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
|
|||||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
|
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
|
||||||
import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry;
|
import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry;
|
||||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
|
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
|
||||||
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||||
import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry;
|
import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
||||||
@@ -62,11 +64,15 @@ public final class ComputerCraftProxyClient implements ClientModInitializer
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
ServerTickEvents.START_SERVER_TICK.register( server -> PauseAwareTimer.tick() );
|
||||||
|
|
||||||
ComputerCraftCustomEvents.CLIENT_UNLOAD_WORLD_EVENT.register( () -> {
|
ComputerCraftCustomEvents.CLIENT_UNLOAD_WORLD_EVENT.register( () -> {
|
||||||
SpeakerManager.reset();
|
SpeakerManager.reset();
|
||||||
ClientMonitor.destroyAll();
|
ClientMonitor.destroyAll();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
ComputerCraftCustomEvents.PLAY_STREAMING_AUDIO_EVENT.register( SpeakerManager::playStreaming );
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
ClientLifecycleEvents.CLIENT_STARTED.register( Config::clientStarted );
|
ClientLifecycleEvents.CLIENT_STARTED.register( Config::clientStarted );
|
||||||
}
|
}
|
||||||
|
@@ -106,7 +106,8 @@ class DfpwmStream implements AudioStream
|
|||||||
if( head == null ) break;
|
if( head == null ) break;
|
||||||
|
|
||||||
int toRead = Math.min( head.remaining(), result.remaining() );
|
int toRead = Math.min( head.remaining(), result.remaining() );
|
||||||
result.put( head.array(), head.position(), toRead ); // TODO: In 1.17 convert this to a ByteBuffer override
|
result.put( result.position(), head, head.position(), toRead );
|
||||||
|
result.position( result.position() + toRead );
|
||||||
head.position( head.position() + toRead );
|
head.position( head.position() + toRead );
|
||||||
|
|
||||||
if( head.hasRemaining() ) break;
|
if( head.hasRemaining() ) break;
|
||||||
@@ -114,7 +115,9 @@ class DfpwmStream implements AudioStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.flip();
|
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
|
@Override
|
||||||
@@ -122,4 +125,9 @@ class DfpwmStream implements AudioStream
|
|||||||
{
|
{
|
||||||
buffers.clear();
|
buffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return buffers.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,20 @@ public class SpeakerInstance
|
|||||||
|
|
||||||
public synchronized void pushAudio( ByteBuf buffer )
|
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 );
|
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 )
|
public void playAudio( Vec3 position, float volume )
|
||||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.client.sound;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.audio.Channel;
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
import net.minecraft.client.resources.sounds.SoundInstance;
|
import net.minecraft.client.resources.sounds.SoundInstance;
|
||||||
|
import net.minecraft.client.sounds.SoundEngine;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -21,14 +22,16 @@ public class SpeakerManager
|
|||||||
private static final Map<UUID, SpeakerInstance> sounds = new ConcurrentHashMap<>();
|
private static final Map<UUID, SpeakerInstance> sounds = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// A return value of true cancels the event
|
// A return value of true cancels the event
|
||||||
public static boolean playStreaming( SoundInstance soundInstance, Channel channel )
|
public static boolean playStreaming( SoundEngine engine, SoundInstance soundInstance, Channel channel )
|
||||||
{
|
{
|
||||||
if( !(soundInstance instanceof SpeakerSound) ) return false;
|
if( !(soundInstance instanceof SpeakerSound sound) ) return false;
|
||||||
SpeakerSound sound = (SpeakerSound) soundInstance;
|
|
||||||
if( sound.stream == null ) return false;
|
if( sound.stream == null ) return false;
|
||||||
|
|
||||||
channel.attachBufferStream( sound.stream );
|
channel.attachBufferStream( sound.stream );
|
||||||
channel.play();
|
channel.play();
|
||||||
|
|
||||||
|
sound.channel = channel;
|
||||||
|
sound.executor = engine.executor;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.client.sound;
|
package dan200.computercraft.client.sound;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
|
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
|
||||||
import net.minecraft.client.resources.sounds.TickableSoundInstance;
|
import net.minecraft.client.resources.sounds.TickableSoundInstance;
|
||||||
import net.minecraft.client.sounds.AudioStream;
|
import net.minecraft.client.sounds.AudioStream;
|
||||||
@@ -13,9 +14,12 @@ import net.minecraft.sounds.SoundSource;
|
|||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance
|
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance
|
||||||
{
|
{
|
||||||
|
Channel channel;
|
||||||
|
Executor executor;
|
||||||
DfpwmStream stream;
|
DfpwmStream stream;
|
||||||
|
|
||||||
SpeakerSound( ResourceLocation sound, DfpwmStream stream, Vec3 position, float volume, float pitch )
|
SpeakerSound( ResourceLocation sound, DfpwmStream stream, Vec3 position, float volume, float pitch )
|
||||||
|
@@ -30,7 +30,36 @@ import java.util.OptionalLong;
|
|||||||
import java.util.function.Function;
|
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
|
* @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.
|
* @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.
|
* @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.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
|
@LuaFunction
|
||||||
public final Object[] getDrive( String path ) throws LuaException
|
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
|
* 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}.
|
* 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.
|
* polling.
|
||||||
*
|
*
|
||||||
* This module may also be referred to as {@code rs}. For example, one may call {@code rs.getSides()} instead of
|
* This module may also be referred to as {@code rs}. For example, one may call {@code rs.getSides()} instead of
|
||||||
|
@@ -5,8 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.fabric.events;
|
package dan200.computercraft.fabric.events;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
import net.fabricmc.fabric.api.event.Event;
|
import net.fabricmc.fabric.api.event.Event;
|
||||||
import net.fabricmc.fabric.api.event.EventFactory;
|
import net.fabricmc.fabric.api.event.EventFactory;
|
||||||
|
import net.minecraft.client.resources.sounds.SoundInstance;
|
||||||
|
import net.minecraft.client.sounds.SoundEngine;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
|
||||||
@@ -28,6 +31,15 @@ public final class ComputerCraftCustomEvents
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
public static final Event<PlayStreamingAudio> PLAY_STREAMING_AUDIO_EVENT = EventFactory.createArrayBacked( PlayStreamingAudio.class,
|
||||||
|
callbacks -> ( engine, soundInstance, channel ) -> {
|
||||||
|
for( PlayStreamingAudio callback : callbacks )
|
||||||
|
{
|
||||||
|
if( callback.onPlayStreamingAudio( engine, soundInstance, channel ) ) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} );
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ClientUnloadWorld
|
public interface ClientUnloadWorld
|
||||||
{
|
{
|
||||||
@@ -39,4 +51,10 @@ public final class ComputerCraftCustomEvents
|
|||||||
{
|
{
|
||||||
void onServerPlayerLoadedChunk( ServerPlayer player, ChunkPos chunkPos );
|
void onServerPlayerLoadedChunk( ServerPlayer player, ChunkPos chunkPos );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface PlayStreamingAudio
|
||||||
|
{
|
||||||
|
boolean onPlayStreamingAudio( SoundEngine engine, SoundInstance soundInstance, Channel channel );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.fabric.mixin;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin( ItemEntity.class )
|
||||||
|
public abstract class MixinItemEntity
|
||||||
|
{
|
||||||
|
@Shadow
|
||||||
|
public abstract ItemStack getItem();
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = "tick",
|
||||||
|
at = @At( "HEAD" ),
|
||||||
|
cancellable = true
|
||||||
|
)
|
||||||
|
private void onTick( CallbackInfo ci )
|
||||||
|
{
|
||||||
|
ItemStack stack = getItem();
|
||||||
|
if( stack.getItem() instanceof IComputerItem item )
|
||||||
|
{
|
||||||
|
if( item.onEntityItemUpdate( stack, (ItemEntity) (Object) this ) ) ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,7 @@
|
|||||||
package dan200.computercraft.fabric.mixin;
|
package dan200.computercraft.fabric.mixin;
|
||||||
|
|
||||||
import com.mojang.blaze3d.audio.Channel;
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
import dan200.computercraft.client.sound.SpeakerManager;
|
import dan200.computercraft.fabric.events.ComputerCraftCustomEvents;
|
||||||
import net.minecraft.client.resources.sounds.SoundInstance;
|
import net.minecraft.client.resources.sounds.SoundInstance;
|
||||||
import net.minecraft.client.sounds.AudioStream;
|
import net.minecraft.client.sounds.AudioStream;
|
||||||
import net.minecraft.client.sounds.SoundEngine;
|
import net.minecraft.client.sounds.SoundEngine;
|
||||||
@@ -19,19 +19,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
@Mixin( SoundEngine.class )
|
@Mixin( SoundEngine.class )
|
||||||
public class MixinSoundEngine
|
public class MixinSoundEngine
|
||||||
{
|
{
|
||||||
// Used to capture the SoundInstance argument passed to SoundEngine#play. Not a thread-safe way to do it but
|
// Used to capture the SoundInstance argument passed to SoundEngine#play and the SoundEngine instance.
|
||||||
// this code is only called from the render thread as far as I can tell.
|
// Not a thread-safe way to do it but this code is only called from the render thread as far as I can tell.
|
||||||
@Unique
|
@Unique private static SoundInstance soundInstanceCapture;
|
||||||
private static SoundInstance onPlaySoundInstanceCapture;
|
@Unique private static SoundEngine thisCapture;
|
||||||
|
|
||||||
@Inject(
|
@Inject(
|
||||||
method = "lambda$play$8",
|
method = "lambda$play$8(Lnet/minecraft/client/sounds/AudioStream;Lcom/mojang/blaze3d/audio/Channel;)V",
|
||||||
at = @At( "HEAD" ),
|
at = @At( "HEAD" ),
|
||||||
cancellable = true
|
cancellable = true
|
||||||
)
|
)
|
||||||
private static void onStreamingSourcePlay( AudioStream audioStream, Channel channel, CallbackInfo ci )
|
private static void onStreamingSourcePlay( AudioStream audioStream, Channel channel, CallbackInfo ci )
|
||||||
{
|
{
|
||||||
if( SpeakerManager.playStreaming( onPlaySoundInstanceCapture, channel ) ) ci.cancel();
|
if( ComputerCraftCustomEvents.PLAY_STREAMING_AUDIO_EVENT.invoker().onPlayStreamingAudio( thisCapture, soundInstanceCapture, channel ) )
|
||||||
|
{
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(
|
@Inject(
|
||||||
@@ -40,6 +43,7 @@ public class MixinSoundEngine
|
|||||||
)
|
)
|
||||||
void onPlay( SoundInstance soundInstance, CallbackInfo ci )
|
void onPlay( SoundInstance soundInstance, CallbackInfo ci )
|
||||||
{
|
{
|
||||||
onPlaySoundInstanceCapture = soundInstance;
|
soundInstanceCapture = soundInstance;
|
||||||
|
thisCapture = (SoundEngine) (Object) this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -56,7 +56,7 @@ public interface IContainerComputer
|
|||||||
void continueUpload( @Nonnull UUID uploadId, @Nonnull List<FileSlice> slices );
|
void continueUpload( @Nonnull UUID uploadId, @Nonnull List<FileSlice> slices );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish off an upload. This either writes the uploaded files or
|
* Finish off an upload. This either writes the uploaded files or informs the user that files will be overwritten.
|
||||||
*
|
*
|
||||||
* @param uploader The player uploading files.
|
* @param uploader The player uploading files.
|
||||||
* @param uploadId The unique ID of this upload.
|
* @param uploadId The unique ID of this upload.
|
||||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.shared.computer.items;
|
|||||||
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@@ -26,6 +27,11 @@ public interface IComputerItem
|
|||||||
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
|
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean onEntityItemUpdate( ItemStack stack, ItemEntity entity )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ComputerFamily getFamily();
|
ComputerFamily getFamily();
|
||||||
|
|
||||||
ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family );
|
ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family );
|
||||||
|
@@ -53,11 +53,6 @@ public class FileSlice
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes.rewind();
|
file.put( offset, bytes, bytes.position(), bytes.remaining() );
|
||||||
file.position( offset );
|
|
||||||
file.put( bytes );
|
|
||||||
file.rewind();
|
|
||||||
|
|
||||||
if( bytes.remaining() != 0 ) throw new IllegalStateException( "Should have read the whole buffer" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -56,7 +56,7 @@ public class FileUpload
|
|||||||
|
|
||||||
public boolean checksumMatches()
|
public boolean checksumMatches()
|
||||||
{
|
{
|
||||||
// This is meant to be a checksum. Doesn't need to be cryptographically secure, hence non constant time.
|
// This is meant to be a checksum. Doesn't need to be cryptographically secure, hence non-constant time.
|
||||||
byte[] digest = getDigest( bytes );
|
byte[] digest = getDigest( bytes );
|
||||||
return digest != null && Arrays.equals( checksum, digest );
|
return digest != null && Arrays.equals( checksum, digest );
|
||||||
}
|
}
|
||||||
@@ -66,9 +66,8 @@ public class FileUpload
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bytes.rewind();
|
|
||||||
MessageDigest digest = MessageDigest.getInstance( "SHA-256" );
|
MessageDigest digest = MessageDigest.getInstance( "SHA-256" );
|
||||||
digest.update( bytes );
|
digest.update( bytes.duplicate() );
|
||||||
return digest.digest();
|
return digest.digest();
|
||||||
}
|
}
|
||||||
catch( NoSuchAlgorithmException e )
|
catch( NoSuchAlgorithmException e )
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.shared.network.server;
|
package dan200.computercraft.shared.network.server;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
|
||||||
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.upload.FileSlice;
|
import dan200.computercraft.shared.computer.upload.FileSlice;
|
||||||
@@ -122,9 +121,9 @@ public class UploadFileMessage extends ComputerServerMessage
|
|||||||
buf.writeByte( slice.getFileId() );
|
buf.writeByte( slice.getFileId() );
|
||||||
buf.writeVarInt( slice.getOffset() );
|
buf.writeVarInt( slice.getOffset() );
|
||||||
|
|
||||||
slice.getBytes().rewind();
|
ByteBuffer bytes = slice.getBytes().duplicate();
|
||||||
buf.writeShort( slice.getBytes().remaining() );
|
buf.writeShort( bytes.remaining() );
|
||||||
buf.writeBytes( slice.getBytes() );
|
buf.writeBytes( bytes );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +157,6 @@ public class UploadFileMessage extends ComputerServerMessage
|
|||||||
|
|
||||||
int canWrite = Math.min( remaining, capacity - currentOffset );
|
int canWrite = Math.min( remaining, capacity - currentOffset );
|
||||||
|
|
||||||
ComputerCraft.log.info( "Adding slice from {} to {}", currentOffset, currentOffset + canWrite - 1 );
|
|
||||||
contents.position( currentOffset ).limit( currentOffset + canWrite );
|
contents.position( currentOffset ).limit( currentOffset + canWrite );
|
||||||
slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) );
|
slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) );
|
||||||
currentOffset += canWrite;
|
currentOffset += canWrite;
|
||||||
|
@@ -21,9 +21,60 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
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.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
|
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}
|
* Sends a modem message on a certain channel. Modems listening on the channel will queue a {@code modem_message}
|
||||||
* event on adjacent computers.
|
* 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 channel The channel to send messages on.
|
||||||
* @param replyChannel The channel that responses to this message should be sent on.
|
* @param replyChannel The channel that responses to this message should be sent on. This can be the same as
|
||||||
* @param payload The object to send. This can be a boolean, string, number, or table.
|
* {@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.
|
* @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
|
@LuaFunction
|
||||||
public final void transmit( int channel, int replyChannel, Object payload ) throws LuaException
|
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
|
* If this computer is attached to the network, it _will not_ be included in
|
||||||
* this list.
|
* this list.
|
||||||
*
|
*
|
||||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @return Remote peripheral names on the network.
|
* @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.
|
* Determine if a peripheral is available on this wired network.
|
||||||
*
|
*
|
||||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @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.
|
* 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}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @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.
|
* Check a peripheral is of a particular type.
|
||||||
*
|
*
|
||||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @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.
|
* 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}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param name The peripheral's name.
|
* @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.
|
* Call a method on a peripheral on this wired network.
|
||||||
*
|
*
|
||||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @param computer The calling computer.
|
* @param computer The calling computer.
|
||||||
* @param context The Lua context we're executing in.
|
* @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
|
* may be used by other computers on the network to wrap this computer as a
|
||||||
* peripheral.
|
* peripheral.
|
||||||
*
|
*
|
||||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
* :::note
|
||||||
* returns false before calling it.</blockquote>
|
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* @return The current computer's name.
|
* @return The current computer's name.
|
||||||
* @cc.treturn string|nil The current computer's name on the wired network.
|
* @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.LuaException;
|
||||||
import dan200.computercraft.api.lua.LuaTable;
|
import dan200.computercraft.api.lua.LuaTable;
|
||||||
|
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
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
|
* 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.
|
* 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;
|
private static final int PREC = 10;
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ class DfpwmState
|
|||||||
private boolean previousBit = false;
|
private boolean previousBit = false;
|
||||||
|
|
||||||
private boolean unplayed = true;
|
private boolean unplayed = true;
|
||||||
private long clientEndTime = System.nanoTime();
|
private long clientEndTime = PauseAwareTimer.getTime();
|
||||||
private float pendingVolume = 1.0f;
|
private float pendingVolume = 1.0f;
|
||||||
private ByteBuffer pendingAudio;
|
private ByteBuffer pendingAudio;
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ class DfpwmState
|
|||||||
|
|
||||||
boolean isPlaying()
|
boolean isPlaying()
|
||||||
{
|
{
|
||||||
return unplayed || clientEndTime >= System.nanoTime();
|
return unplayed || clientEndTime >= PauseAwareTimer.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
float getVolume()
|
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.SpeakerMoveClientMessage;
|
||||||
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
||||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||||
|
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||||
import net.minecraft.ResourceLocationException;
|
import net.minecraft.ResourceLocationException;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.protocol.game.ClientboundCustomSoundPacket;
|
import net.minecraft.network.protocol.game.ClientboundCustomSoundPacket;
|
||||||
@@ -119,7 +120,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long now = System.nanoTime();
|
long now = PauseAwareTimer.getTime();
|
||||||
if( sound != null )
|
if( sound != null )
|
||||||
{
|
{
|
||||||
lastPlayTime = clock;
|
lastPlayTime = clock;
|
||||||
@@ -342,7 +343,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
|||||||
// TODO: Use ArgumentHelpers instead?
|
// TODO: Use ArgumentHelpers instead?
|
||||||
int length = audio.length();
|
int length = audio.length();
|
||||||
if( length <= 0 ) throw new LuaException( "Cannot play empty audio" );
|
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;
|
DfpwmState state;
|
||||||
synchronized( lock )
|
synchronized( lock )
|
||||||
@@ -387,17 +388,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
|||||||
computers.remove( computer );
|
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.ClientComputer;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
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.computer.items.IComputerItem;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
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.InteractionResult;
|
||||||
import net.minecraft.world.InteractionResultHolder;
|
import net.minecraft.world.InteractionResultHolder;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.Item;
|
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 );
|
PocketUpgrades.getVanillaUpgrades().map( x -> create( -1, null, -1, x ) ).forEach( stacks::add );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean tick( @Nonnull ItemStack stack, @Nonnull Level world, @Nonnull Entity entity, @Nonnull PocketServerComputer computer )
|
||||||
|
{
|
||||||
|
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync label
|
||||||
|
String label = computer.getLabel();
|
||||||
|
if( !Objects.equal( label, getLabel( stack ) ) )
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
setLabel( stack, label );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update pocket upgrade
|
||||||
|
if( upgrade != null ) upgrade.update( computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entity entity, int slotNum, boolean selected )
|
public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entity entity, int slotNum, boolean selected )
|
||||||
{
|
{
|
||||||
if( !world.isClientSide )
|
if( !world.isClientSide )
|
||||||
{
|
{
|
||||||
// Server side
|
Container inventory = entity instanceof Player player ? player.getInventory() : null;
|
||||||
Container inventory = entity instanceof Player ? ((Player) entity).getInventory() : null;
|
|
||||||
PocketServerComputer computer = createServerComputer( world, inventory, entity, stack );
|
PocketServerComputer computer = createServerComputer( world, inventory, entity, stack );
|
||||||
if( computer != null )
|
computer.keepAlive();
|
||||||
{
|
|
||||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
|
||||||
|
|
||||||
// Ping computer
|
boolean changed = tick( stack, world, entity, computer );
|
||||||
computer.keepAlive();
|
if( changed && inventory != null ) inventory.setChanged();
|
||||||
computer.setLevel( world );
|
|
||||||
computer.updateValues( entity, stack, upgrade );
|
|
||||||
|
|
||||||
// Sync ID
|
|
||||||
int id = computer.getID();
|
|
||||||
if( id != getComputerID( stack ) )
|
|
||||||
{
|
|
||||||
setComputerID( stack, id );
|
|
||||||
if( inventory != null ) inventory.setChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync label
|
|
||||||
String label = computer.getLabel();
|
|
||||||
if( !Objects.equal( label, getLabel( stack ) ) )
|
|
||||||
{
|
|
||||||
setLabel( stack, label );
|
|
||||||
if( inventory != null ) inventory.setChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update pocket upgrade
|
|
||||||
if( upgrade != null )
|
|
||||||
{
|
|
||||||
upgrade.update( computer, computer.getPeripheral( ComputerSide.BACK ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Client side
|
|
||||||
createClientComputer( stack );
|
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
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public InteractionResultHolder<ItemStack> use( Level world, Player player, @Nonnull InteractionHand hand )
|
public InteractionResultHolder<ItemStack> use( Level world, Player player, @Nonnull InteractionHand hand )
|
||||||
@@ -135,21 +147,17 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
if( !world.isClientSide )
|
if( !world.isClientSide )
|
||||||
{
|
{
|
||||||
PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack );
|
PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack );
|
||||||
|
computer.turnOn();
|
||||||
|
|
||||||
boolean stop = false;
|
boolean stop = false;
|
||||||
if( computer != null )
|
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||||
|
if( upgrade != null )
|
||||||
{
|
{
|
||||||
computer.turnOn();
|
computer.updateValues( player, stack, upgrade );
|
||||||
|
stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||||
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;
|
boolean isTypingOnly = hand == InteractionHand.OFF_HAND;
|
||||||
new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) );
|
new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) );
|
||||||
@@ -193,15 +201,14 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
|
|
||||||
public PocketServerComputer createServerComputer( final Level world, Container inventory, Entity entity, @Nonnull ItemStack stack )
|
public PocketServerComputer createServerComputer( final Level world, Container inventory, Entity entity, @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
if( world.isClientSide ) return null;
|
if( world.isClientSide ) throw new IllegalStateException( "Cannot call createServerComputer on the client" );
|
||||||
|
|
||||||
PocketServerComputer computer;
|
PocketServerComputer computer;
|
||||||
int instanceID = getInstanceID( stack );
|
int instanceID = getInstanceID( stack );
|
||||||
int sessionID = getSessionID( stack );
|
int sessionID = getSessionID( stack );
|
||||||
int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID();
|
int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID();
|
||||||
|
|
||||||
if( instanceID >= 0 && sessionID == correctSessionID &&
|
if( instanceID >= 0 && sessionID == correctSessionID && ComputerCraft.serverComputerRegistry.contains( instanceID ) )
|
||||||
ComputerCraft.serverComputerRegistry.contains( instanceID ) )
|
|
||||||
{
|
{
|
||||||
computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID );
|
computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID );
|
||||||
}
|
}
|
||||||
@@ -219,13 +226,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" );
|
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" );
|
||||||
setComputerID( stack, computerID );
|
setComputerID( stack, computerID );
|
||||||
}
|
}
|
||||||
computer = new PocketServerComputer(
|
computer = new PocketServerComputer( world, computerID, getLabel( stack ), instanceID, getFamily() );
|
||||||
world,
|
|
||||||
computerID,
|
|
||||||
getLabel( stack ),
|
|
||||||
instanceID,
|
|
||||||
getFamily()
|
|
||||||
);
|
|
||||||
computer.updateValues( entity, stack, getUpgrade( stack ) );
|
computer.updateValues( entity, stack, getUpgrade( stack ) );
|
||||||
computer.addAPI( new PocketAPI( computer ) );
|
computer.addAPI( new PocketAPI( computer ) );
|
||||||
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
|
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
|
||||||
@@ -235,15 +236,17 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
return computer;
|
return computer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServerComputer getServerComputer( @Nonnull ItemStack stack )
|
@Nullable
|
||||||
|
public static PocketServerComputer getServerComputer( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
int session = getSessionID( stack );
|
int session = getSessionID( stack );
|
||||||
if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null;
|
if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null;
|
||||||
|
|
||||||
int instanceID = getInstanceID( stack );
|
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 )
|
public static ClientComputer createClientComputer( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
int instanceID = getInstanceID( stack );
|
int instanceID = getInstanceID( stack );
|
||||||
@@ -258,6 +261,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private static ClientComputer getClientComputer( @Nonnull ItemStack stack )
|
private static ClientComputer getClientComputer( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
int instanceID = getInstanceID( stack );
|
int instanceID = getInstanceID( stack );
|
||||||
|
@@ -22,8 +22,50 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
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.module turtle
|
||||||
* @cc.since 1.3
|
* @cc.since 1.3
|
||||||
*/
|
*/
|
||||||
|
@@ -31,7 +31,6 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import net.minecraft.tags.FluidTags;
|
import net.minecraft.tags.FluidTags;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntitySelector;
|
|
||||||
import net.minecraft.world.entity.MoverType;
|
import net.minecraft.world.entity.MoverType;
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
import net.minecraft.world.level.Level;
|
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.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.material.FluidState;
|
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.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
@@ -46,6 +46,7 @@ import javax.annotation.Nonnull;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import static dan200.computercraft.shared.common.IColouredItem.NBT_COLOUR;
|
import static dan200.computercraft.shared.common.IColouredItem.NBT_COLOUR;
|
||||||
import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED;
|
import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED;
|
||||||
@@ -63,6 +64,8 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
|
|
||||||
private static final int ANIM_DURATION = 8;
|
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 TileTurtle owner;
|
||||||
private ComputerProxy proxy;
|
private ComputerProxy proxy;
|
||||||
private GameProfile owningPlayer;
|
private GameProfile owningPlayer;
|
||||||
@@ -876,7 +879,7 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
}
|
}
|
||||||
|
|
||||||
AABB aabb = new AABB( minX, minY, minZ, maxX, maxY, maxZ );
|
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() )
|
if( !list.isEmpty() )
|
||||||
{
|
{
|
||||||
double pushStep = 1.0f / ANIM_DURATION;
|
double pushStep = 1.0f / ANIM_DURATION;
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A monotonically increasing clock which accounts for the game being paused.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick()
|
||||||
|
{
|
||||||
|
boolean isPaused = Minecraft.getInstance().isPaused();
|
||||||
|
if( isPaused == paused ) return;
|
||||||
|
|
||||||
|
if( isPaused )
|
||||||
|
{
|
||||||
|
pauseTime = Util.getNanos();
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pauseOffset += Util.getNanos() - pauseTime;
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,3 +2,7 @@ accessWidener v1 named
|
|||||||
|
|
||||||
accessible class net/minecraft/client/renderer/RenderType$CompositeState
|
accessible class net/minecraft/client/renderer/RenderType$CompositeState
|
||||||
accessible method net/minecraft/client/renderer/RenderType create (Ljava/lang/String;Lcom/mojang/blaze3d/vertex/VertexFormat;Lcom/mojang/blaze3d/vertex/VertexFormat$Mode;IZZLnet/minecraft/client/renderer/RenderType$CompositeState;)Lnet/minecraft/client/renderer/RenderType$CompositeRenderType;
|
accessible method net/minecraft/client/renderer/RenderType create (Ljava/lang/String;Lcom/mojang/blaze3d/vertex/VertexFormat;Lcom/mojang/blaze3d/vertex/VertexFormat$Mode;IZZLnet/minecraft/client/renderer/RenderType$CompositeState;)Lnet/minecraft/client/renderer/RenderType$CompositeRenderType;
|
||||||
|
|
||||||
|
# SpeakerInstance/SpeakerManager
|
||||||
|
accessible method com/mojang/blaze3d/audio/Channel pumpBuffers (I)V
|
||||||
|
accessible field net/minecraft/client/sounds/SoundEngine executor Lnet/minecraft/client/sounds/SoundEngineExecutor;
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
"MixinBlock",
|
"MixinBlock",
|
||||||
"MixinChunkMap",
|
"MixinChunkMap",
|
||||||
"MixinEntity",
|
"MixinEntity",
|
||||||
|
"MixinItemEntity",
|
||||||
"MixinMatrix4f",
|
"MixinMatrix4f",
|
||||||
"MixinServerLevel",
|
"MixinServerLevel",
|
||||||
"MixinServerPlayerGameMode"
|
"MixinServerPlayerGameMode"
|
||||||
|
@@ -1,16 +1,18 @@
|
|||||||
--- The Disk API allows you to interact with disk drives.
|
--[[- The Disk API allows you to interact with disk drives.
|
||||||
--
|
|
||||||
-- These functions can operate on locally attached or remote disk drives. To use
|
These functions can operate on locally attached or remote disk drives. To use a
|
||||||
-- a locally attached drive, specify “side” as one of the six sides
|
locally attached drive, specify “side” as one of the six sides (e.g. `left`); to
|
||||||
-- (e.g. `left`); to use a remote disk drive, specify its name as printed when
|
use a remote disk drive, specify its name as printed when enabling its modem
|
||||||
-- enabling its modem (e.g. `drive_0`).
|
(e.g. `drive_0`).
|
||||||
--
|
|
||||||
-- **Note:** All computers (except command computers), turtles and pocket
|
:::tip
|
||||||
-- computers can be placed within a disk drive to access it's internal storage
|
All computers (except command computers), turtles and pocket computers can be
|
||||||
-- like a disk.
|
placed within a disk drive to access it's internal storage like a disk.
|
||||||
--
|
:::
|
||||||
-- @module disk
|
|
||||||
-- @since 1.2
|
@module disk
|
||||||
|
@since 1.2
|
||||||
|
]]
|
||||||
|
|
||||||
local function isDrive(name)
|
local function isDrive(name)
|
||||||
if type(name) ~= "string" then
|
if type(name) ~= "string" then
|
||||||
|
@@ -1,27 +1,30 @@
|
|||||||
--- The GPS API provides a method for turtles and computers to retrieve their
|
--[[- The GPS API provides a method for turtles and computers to retrieve their
|
||||||
-- own locations.
|
own locations.
|
||||||
--
|
|
||||||
-- It broadcasts a PING message over @{rednet} and wait for responses. In order
|
It broadcasts a PING message over @{rednet} and wait for responses. In order for
|
||||||
-- for this system to work, there must be at least 4 computers used as gps hosts
|
this system to work, there must be at least 4 computers used as gps hosts which
|
||||||
-- which will respond and allow trilateration. Three of these hosts should be in
|
will respond and allow trilateration. Three of these hosts should be in a plane,
|
||||||
-- a plane, and the fourth should be either above or below the other three. The
|
and the fourth should be either above or below the other three. The three in a
|
||||||
-- three in a plane should not be in a line with each other. You can set up
|
plane should not be in a line with each other. You can set up hosts using the
|
||||||
-- hosts using the gps program.
|
gps program.
|
||||||
--
|
|
||||||
-- **Note**: When entering in the coordinates for the host you need to put in
|
:::note
|
||||||
-- the `x`, `y`, and `z` coordinates of the computer, not the modem, as all
|
When entering in the coordinates for the host you need to put in the `x`, `y`,
|
||||||
-- rednet distances are measured from the block the computer is in.
|
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
|
Also note that you may choose which axes x, y, or z refers to - so long as your
|
||||||
-- [this tutorial][1], using z to account for height, or you might use y to
|
systems have the same definition as any GPS servers that're in range, it works
|
||||||
-- account for height in the way that Minecraft's debug screen displays.
|
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
|
||||||
-- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/
|
height in the way that Minecraft's debug screen displays.
|
||||||
--
|
|
||||||
-- @module gps
|
[1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/
|
||||||
-- @since 1.31
|
|
||||||
|
@module gps
|
||||||
|
@since 1.31
|
||||||
|
]]
|
||||||
|
|
||||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
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
|
Functions are not actually executed simultaniously, but rather this API will
|
||||||
automatically switch between them whenever they yield (eg whenever they call
|
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
|
functions that call that, etc - basically, anything that causes the function
|
||||||
to "pause").
|
to "pause").
|
||||||
|
|
||||||
Each function executed in "parallel" gets its own copy of the event queue,
|
Each function executed in "parallel" gets its own copy of the event queue,
|
||||||
and so "event consuming" functions (again, mostly anything that causes the
|
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
|
etc) can safely be used in one without affecting the event queue accessed by
|
||||||
the other.
|
the other.
|
||||||
|
|
||||||
|
@@ -66,7 +66,7 @@ type.
|
|||||||
What is a peripheral type though? This is a string which describes what a
|
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
|
peripheral is, and so what functions are available on it. For instance, speakers
|
||||||
are just called `"speaker"`, and monitors `"monitor"`. Some peripherals might
|
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"`.
|
`"inventory"`.
|
||||||
|
|
||||||
You can get all the types a peripheral has with @{peripheral.getType}, and check
|
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
|
--[[- The Rednet API allows computers to communicate between each other by using
|
||||||
-- using redstone. It serves as a wrapper for the modem API, offering ease of
|
@{modem|modems}. It provides a layer of abstraction on top of the main @{modem}
|
||||||
-- functionality (particularly in regards to repeating signals) with some
|
peripheral, making it slightly easier to use.
|
||||||
-- expense of fine control.
|
|
||||||
--
|
## Basic usage
|
||||||
-- In order to send and receive data, a modem (either wired, wireless, or ender)
|
In order to send a message between two computers, each computer must have a
|
||||||
-- is required. The data reaches any possible destinations immediately after
|
modem on one of its sides (or in the case of pocket computers and turtles, the
|
||||||
-- sending it, but is range limited.
|
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.
|
||||||
-- Rednet also allows you to use a "protocol" - simple string names indicating
|
|
||||||
-- what messages are about. Receiving systems may filter messages according to
|
Once rednet is opened, you can send messages using @{rednet.send} and receive
|
||||||
-- their protocols, thereby automatically ignoring incoming messages which don't
|
them using @{rednet.receive}. It's also possible to send a message to _every_
|
||||||
-- specify an identical string. It's also possible to @{rednet.lookup|lookup}
|
rednet-using computer using @{rednet.broadcast}.
|
||||||
-- which systems in the area use certain protocols, hence making it easier to
|
|
||||||
-- determine where given messages should be sent in the first place.
|
:::caution Network security
|
||||||
--
|
|
||||||
-- @module rednet
|
While rednet provides a friendly way to send messages to specific computers, it
|
||||||
-- @since 1.2
|
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
|
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.
|
@tparam string modem The name of the modem to open.
|
||||||
@throws If there is no such modem with the given name
|
@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")
|
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)
|
function open(modem)
|
||||||
expect(1, modem, "string")
|
expect(1, modem, "string")
|
||||||
@@ -65,6 +100,7 @@ end
|
|||||||
-- @tparam[opt] string modem The side the modem exists on. If not given, all
|
-- @tparam[opt] string modem The side the modem exists on. If not given, all
|
||||||
-- open modems will be closed.
|
-- open modems will be closed.
|
||||||
-- @throws If there is no such modem with the given name
|
-- @throws If there is no such modem with the given name
|
||||||
|
-- @see rednet.open
|
||||||
function close(modem)
|
function close(modem)
|
||||||
expect(1, modem, "string", "nil")
|
expect(1, modem, "string", "nil")
|
||||||
if modem then
|
if modem then
|
||||||
@@ -90,6 +126,7 @@ end
|
|||||||
-- modems will be checked.
|
-- modems will be checked.
|
||||||
-- @treturn boolean If the given modem is open.
|
-- @treturn boolean If the given modem is open.
|
||||||
-- @since 1.31
|
-- @since 1.31
|
||||||
|
-- @see rednet.open
|
||||||
function isOpen(modem)
|
function isOpen(modem)
|
||||||
expect(1, modem, "string", "nil")
|
expect(1, modem, "string", "nil")
|
||||||
if modem then
|
if modem then
|
||||||
@@ -109,15 +146,17 @@ function isOpen(modem)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--[[- Allows a computer or turtle with an attached modem to send a message
|
--[[- 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.
|
be @{rednet.open|opened} before sending is possible.
|
||||||
|
|
||||||
Assuming the target was in range and also had a correctly opened modem, it
|
Assuming the target was in range and also had a correctly opened modem, the
|
||||||
may then use @{rednet.receive} to collect the message.
|
target computer may then use @{rednet.receive} to collect the message.
|
||||||
|
|
||||||
@tparam number recipient The ID of the receiving computer.
|
@tparam number recipient The ID of the receiving computer.
|
||||||
@param message The message to send. This should not contain coroutines or
|
@param message The message to send. Like with @{modem.transmit}, this can
|
||||||
functions, as they will be converted to @{nil}.
|
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
|
@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
|
using @{rednet.receive} one can filter to only receive messages sent under a
|
||||||
particular protocol.
|
particular protocol.
|
||||||
@@ -174,16 +213,19 @@ function send(recipient, message, protocol)
|
|||||||
return sent
|
return sent
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST}
|
--[[- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST}
|
||||||
-- channel. The message will be received by every device listening to rednet.
|
channel. The message will be received by every device listening to rednet.
|
||||||
--
|
|
||||||
-- @param message The message to send. This should not contain coroutines or
|
@param message The message to send. This should not contain coroutines or
|
||||||
-- functions, as they will be converted to @{nil}.
|
functions, as they will be converted to @{nil}. @tparam[opt] string protocol
|
||||||
-- @tparam[opt] string protocol The "protocol" to send this message under. When
|
The "protocol" to send this message under. When using @{rednet.receive} one can
|
||||||
-- using @{rednet.receive} one can filter to only receive messages sent under a
|
filter to only receive messages sent under a particular protocol.
|
||||||
-- particular protocol.
|
@see rednet.receive
|
||||||
-- @see rednet.receive
|
@changed 1.6 Added protocol parameter.
|
||||||
-- @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)
|
function broadcast(message, protocol)
|
||||||
expect(2, protocol, "string", "nil")
|
expect(2, protocol, "string", "nil")
|
||||||
send(CHANNEL_BROADCAST, message, protocol)
|
send(CHANNEL_BROADCAST, message, protocol)
|
||||||
@@ -263,25 +305,25 @@ function receive(protocol_filter, timeout)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Register the system as "hosting" the desired protocol under the specified
|
--[[- Register the system as "hosting" the desired protocol under the specified
|
||||||
-- name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and
|
name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and
|
||||||
-- maybe name) on the same network, the registered system will automatically
|
maybe name) on the same network, the registered system will automatically
|
||||||
-- respond via a background process, hence providing the system performing the
|
respond via a background process, hence providing the system performing the
|
||||||
-- lookup with its ID number.
|
lookup with its ID number.
|
||||||
--
|
|
||||||
-- Multiple computers may not register themselves on the same network as having
|
Multiple computers may not register themselves on the same network as having the
|
||||||
-- the same names against the same protocols, and the title `localhost` is
|
same names against the same protocols, and the title `localhost` is specifically
|
||||||
-- specifically reserved. They may, however, share names as long as their hosted
|
reserved. They may, however, share names as long as their hosted protocols are
|
||||||
-- protocols are different, or if they only join a given network after
|
different, or if they only join a given network after "registering" themselves
|
||||||
-- "registering" themselves before doing so (eg while offline or part of a
|
before doing so (eg while offline or part of a different network).
|
||||||
-- different network).
|
|
||||||
--
|
@tparam string protocol The protocol this computer provides.
|
||||||
-- @tparam string protocol The protocol this computer provides.
|
@tparam string hostname The name this protocol exposes for the given protocol.
|
||||||
-- @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.
|
||||||
-- @throws If trying to register a hostname which is reserved, or currently in use.
|
@see rednet.unhost
|
||||||
-- @see rednet.unhost
|
@see rednet.lookup
|
||||||
-- @see rednet.lookup
|
@since 1.6
|
||||||
-- @since 1.6
|
]]
|
||||||
function host(protocol, hostname)
|
function host(protocol, hostname)
|
||||||
expect(1, protocol, "string")
|
expect(1, protocol, "string")
|
||||||
expect(2, hostname, "string")
|
expect(2, hostname, "string")
|
||||||
@@ -306,21 +348,38 @@ function unhost(protocol)
|
|||||||
hostnames[protocol] = nil
|
hostnames[protocol] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Search the local rednet network for systems @{rednet.host|hosting} the
|
--[[- Search the local rednet network for systems @{rednet.host|hosting} the
|
||||||
-- desired protocol and returns any computer IDs that respond as "registered"
|
desired protocol and returns any computer IDs that respond as "registered"
|
||||||
-- against it.
|
against it.
|
||||||
--
|
|
||||||
-- If a hostname is specified, only one ID will be returned (assuming an exact
|
If a hostname is specified, only one ID will be returned (assuming an exact
|
||||||
-- match is found).
|
match is found).
|
||||||
--
|
|
||||||
-- @tparam string protocol The protocol to search for.
|
@tparam string protocol The protocol to search for.
|
||||||
-- @tparam[opt] string hostname The hostname to search for.
|
@tparam[opt] string hostname The hostname to search for.
|
||||||
--
|
|
||||||
-- @treturn[1] { number }|nil A list of computer IDs hosting the given
|
@treturn[1] number... A list of computer IDs hosting the given protocol.
|
||||||
-- protocol, or @{nil} if none exist.
|
@treturn[2] number|nil The computer ID with the provided hostname and protocol,
|
||||||
-- @treturn[2] number|nil The computer ID with the provided hostname and protocol,
|
or @{nil} if none exists.
|
||||||
-- or @{nil} if none exists.
|
@since 1.6
|
||||||
-- @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)
|
function lookup(protocol, hostname)
|
||||||
expect(1, protocol, "string")
|
expect(1, protocol, "string")
|
||||||
expect(2, hostname, "string", "nil")
|
expect(2, hostname, "string", "nil")
|
||||||
|
@@ -108,39 +108,48 @@ local function makePagedScroll(_term, _nFreeLines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Prints a given string to the display.
|
--[[- Prints a given string to the display.
|
||||||
--
|
|
||||||
-- If the action can be completed without scrolling, it acts much the same as
|
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
|
@{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
|
the bottom of the display. Each press will cause it to scroll down and write a
|
||||||
-- a single line more before prompting again, if need be.
|
single line more before prompting again, if need be.
|
||||||
--
|
|
||||||
-- @tparam string _sText The text to print to the screen.
|
@tparam string text The text to print to the screen.
|
||||||
-- @tparam[opt] number _nFreeLines The number of lines which will be
|
@tparam[opt] number free_lines The number of lines which will be
|
||||||
-- automatically scrolled before the first prompt appears (meaning _nFreeLines +
|
automatically scrolled before the first prompt appears (meaning free_lines +
|
||||||
-- 1 lines will be printed). This can be set to the terminal's height - 2 to
|
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
|
always try to fill the screen. Defaults to 0, meaning only one line is
|
||||||
-- displayed before prompting.
|
displayed before prompting.
|
||||||
-- @treturn number The number of lines printed.
|
@treturn number The number of lines printed.
|
||||||
-- @usage
|
|
||||||
-- local width, height = term.getSize()
|
@usage Generates several lines of text and then prints it, paging once the
|
||||||
-- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2)
|
bottom of the terminal is reached.
|
||||||
function pagedPrint(_sText, _nFreeLines)
|
|
||||||
expect(2, _nFreeLines, "number", "nil")
|
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
|
-- Setup a redirector
|
||||||
local oldTerm = term.current()
|
local oldTerm = term.current()
|
||||||
local newTerm = {}
|
local newTerm = {}
|
||||||
for k, v in pairs(oldTerm) do
|
for k, v in pairs(oldTerm) do
|
||||||
newTerm[k] = v
|
newTerm[k] = v
|
||||||
end
|
end
|
||||||
newTerm.scroll = makePagedScroll(oldTerm, _nFreeLines)
|
|
||||||
|
newTerm.scroll = makePagedScroll(oldTerm, free_lines)
|
||||||
term.redirect(newTerm)
|
term.redirect(newTerm)
|
||||||
|
|
||||||
-- Print the text
|
-- Print the text
|
||||||
local result
|
local result
|
||||||
local ok, err = pcall(function()
|
local ok, err = pcall(function()
|
||||||
if _sText ~= nil then
|
if text ~= nil then
|
||||||
result = print(_sText)
|
result = print(text)
|
||||||
else
|
else
|
||||||
result = print()
|
result = print()
|
||||||
end
|
end
|
||||||
@@ -214,32 +223,45 @@ local function tabulateCommon(bPaged, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Prints tables in a structured form.
|
--[[- Prints tables in a structured form.
|
||||||
--
|
|
||||||
-- This accepts multiple arguments, either a table or a number. When
|
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
|
encountering a table, this will be treated as a table row, with each column
|
||||||
-- width being auto-adjusted.
|
width being auto-adjusted.
|
||||||
--
|
|
||||||
-- When encountering a number, this sets the text color of the subsequent rows to it.
|
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.
|
@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
|
||||||
-- @since 1.3
|
@usage
|
||||||
|
|
||||||
|
textutils.tabulate(
|
||||||
|
colors.orange, { "1", "2", "3" },
|
||||||
|
colors.lightBlue, { "A", "B", "C" }
|
||||||
|
)
|
||||||
|
]]
|
||||||
function tabulate(...)
|
function tabulate(...)
|
||||||
return tabulateCommon(false, ...)
|
return tabulateCommon(false, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Prints tables in a structured form, stopping and prompting for input should
|
--[[- Prints tables in a structured form, stopping and prompting for input should
|
||||||
-- the result not fit on the terminal.
|
the result not fit on the terminal.
|
||||||
--
|
|
||||||
-- This functions identically to @{textutils.tabulate}, but will prompt for user
|
This functions identically to @{textutils.tabulate}, but will prompt for user
|
||||||
-- input should the whole output not fit on the display.
|
input should the whole output not fit on the display.
|
||||||
--
|
|
||||||
-- @tparam {string...}|number ... The rows and text colors to 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.tabulate
|
@see textutils.pagedPrint
|
||||||
-- @see textutils.pagedPrint
|
@since 1.3
|
||||||
-- @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(...)
|
function pagedTabulate(...)
|
||||||
return tabulateCommon(true, ...)
|
return tabulateCommon(true, ...)
|
||||||
end
|
end
|
||||||
@@ -692,11 +714,11 @@ saving in a file or pretty-printing.
|
|||||||
@throws If the object contains a value which cannot be
|
@throws If the object contains a value which cannot be
|
||||||
serialised. This includes functions and tables which appear multiple
|
serialised. This includes functions and tables which appear multiple
|
||||||
times.
|
times.
|
||||||
@see cc.pretty.pretty An alternative way to display a table, often more suitable for
|
@see cc.pretty.pretty_print An alternative way to display a table, often more
|
||||||
pretty printing.
|
suitable for pretty printing.
|
||||||
@since 1.3
|
@since 1.3
|
||||||
@changed 1.97.0 Added `opts` argument.
|
@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 } })
|
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
|
if not turtle then
|
||||||
error("Cannot load turtle API on computer", 2)
|
error("Cannot load turtle API on computer", 2)
|
||||||
@@ -8,9 +6,8 @@ end
|
|||||||
|
|
||||||
--- The builtin turtle API, without any generated helper functions.
|
--- The builtin turtle API, without any generated helper functions.
|
||||||
--
|
--
|
||||||
-- Generally you should not need to use this table - it only exists for
|
-- @deprecated Historically this table behaved differently to the main turtle API, but this is no longer the base. You
|
||||||
-- backwards compatibility reasons.
|
-- should not need to use it.
|
||||||
-- @deprecated
|
|
||||||
native = turtle.native or turtle
|
native = turtle.native or turtle
|
||||||
|
|
||||||
local function addCraftMethod(object)
|
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
|
# New features in CC: Tweaked 1.99.1
|
||||||
|
|
||||||
* Add package.searchpath to the cc.require API. (MCJack123)
|
* 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.
|
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.
|
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!
|
Follow @DanTwoHundred on Twitter!
|
||||||
|
|
||||||
To help contribute to ComputerCraft, browse the source code at https://github.com/dan200/ComputerCraft.
|
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)
|
* Speakers can now play arbitrary PCM audio.
|
||||||
* Provide a more efficient way for the Java API to consume Lua tables in certain restricted cases.
|
* 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:
|
Several bug fixes:
|
||||||
* Fix keys being "sticky" when opening the off-hand pocket computer GUI.
|
* Fix the "repeat" program not repeating broadcast rednet messages.
|
||||||
* Correctly handle broken coroutine managers resuming Java code with a `nil` event.
|
* Fix the drag-and-drop upload functionality writing empty files.
|
||||||
* Prevent computer buttons stealing focus from the terminal.
|
* Prevent turtles from pushing non-pushable entities.
|
||||||
* Fix a class cast exception when a monitor is malformed in ways I do not quite understand.
|
|
||||||
|
|
||||||
Type "help changelog" to see the full version history.
|
Type "help changelog" to see the full version history.
|
||||||
|
@@ -36,7 +36,7 @@ local encoder = dfpwm.make_encoder()
|
|||||||
local decoder = dfpwm.make_decoder()
|
local decoder = dfpwm.make_decoder()
|
||||||
|
|
||||||
local out = fs.open("speedy.dfpwm", "wb")
|
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 decoded = decoder(input)
|
||||||
local output = {}
|
local output = {}
|
||||||
|
|
||||||
@@ -143,14 +143,14 @@ streams, or use different decoders for the same stream, the resulting audio may
|
|||||||
@usage Reads "data/example.dfpwm" in blocks of 16KiB (the speaker can accept a maximum of 128×1024 samples), decodes
|
@usage Reads "data/example.dfpwm" in blocks of 16KiB (the speaker can accept a maximum of 128×1024 samples), decodes
|
||||||
them and then plays them through the speaker.
|
them and then plays them through the speaker.
|
||||||
|
|
||||||
```lua
|
```lua {data-peripheral=speaker}
|
||||||
local dfpwm = require "cc.audio.dfpwm"
|
local dfpwm = require "cc.audio.dfpwm"
|
||||||
local speaker = peripheral.find("speaker")
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
local decoder = dfpwm.make_decoder()
|
local decoder = dfpwm.make_decoder()
|
||||||
for input in io.lines("data/example.dfpwm", 16 * 1024 * 2) do
|
for input in io.lines("data/example.dfpwm", 16 * 1024) do
|
||||||
local decoded = decoder(input)
|
local decoded = decoder(input)
|
||||||
while not speaker.playAudio(output) do
|
while not speaker.playAudio(decoded) do
|
||||||
os.pullEvent("speaker_audio_empty")
|
os.pullEvent("speaker_audio_empty")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -224,8 +224,8 @@ end
|
|||||||
|
|
||||||
--- Display a document on the terminal.
|
--- Display a document on the terminal.
|
||||||
--
|
--
|
||||||
-- @tparam Doc doc The document to render
|
-- @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)
|
local function write(doc, ribbon_frac)
|
||||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||||
expect(2, ribbon_frac, "number", "nil")
|
expect(2, ribbon_frac, "number", "nil")
|
||||||
@@ -286,8 +286,8 @@ end
|
|||||||
|
|
||||||
--- Display a document on the terminal with a trailing new line.
|
--- Display a document on the terminal with a trailing new line.
|
||||||
--
|
--
|
||||||
-- @tparam Doc doc The document to render.
|
-- @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)
|
local function print(doc, ribbon_frac)
|
||||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
||||||
expect(2, ribbon_frac, "number", "nil")
|
expect(2, ribbon_frac, "number", "nil")
|
||||||
@@ -297,10 +297,10 @@ end
|
|||||||
|
|
||||||
--- Render a document, converting it into a string.
|
--- Render a document, converting it into a string.
|
||||||
--
|
--
|
||||||
-- @tparam Doc doc The document to render.
|
-- @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
|
-- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to fit
|
||||||
-- fit this width - it is only used for finding the best layout.
|
-- 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=0.6] number ribbon_frac The maximum fraction of the width that we should write in.
|
||||||
-- @treturn string The rendered document as a string.
|
-- @treturn string The rendered document as a string.
|
||||||
local function render(doc, width, ribbon_frac)
|
local function render(doc, width, ribbon_frac)
|
||||||
if getmetatable(doc) ~= Doc then expect(1, doc, "document") end
|
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_args`: Show the arguments to a function if known (`false` by default).
|
||||||
- `function_source`: Show where the function was defined, instead of
|
- `function_source`: Show where the function was defined, instead of
|
||||||
`function: xxxxxxxx` (`false` by default).
|
`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.
|
@usage Display a table on the screen.
|
||||||
|
|
||||||
|
@@ -1,22 +1,24 @@
|
|||||||
--- This provides a pure Lua implementation of the builtin @{require} function
|
--[[- This provides a pure Lua implementation of the builtin @{require} function
|
||||||
-- and @{package} library.
|
and @{package} library.
|
||||||
--
|
|
||||||
-- Generally you do not need to use this module - it is injected into the
|
Generally you do not need to use this module - it is injected into the every
|
||||||
-- every program's environment. However, it may be useful when building a
|
program's environment. However, it may be useful when building a custom shell or
|
||||||
-- custom shell or when running programs yourself.
|
when running programs yourself.
|
||||||
--
|
|
||||||
-- @module cc.require
|
@module cc.require
|
||||||
-- @since 1.88.0
|
@since 1.88.0
|
||||||
-- @usage Construct the package and require function, and insert them into a
|
@see using_require For an introduction on how to use @{require}.
|
||||||
-- custom environment.
|
@usage Construct the package and require function, and insert them into a
|
||||||
--
|
custom environment.
|
||||||
-- local r = require "cc.require"
|
|
||||||
-- local env = setmetatable({}, { __index = _ENV })
|
local r = require "cc.require"
|
||||||
-- env.require, env.package = r.make(env, "/")
|
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"
|
-- Now we have our own require function, separate to the original.
|
||||||
-- print(r, r2)
|
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 = require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")
|
||||||
local expect = expect.expect
|
local expect = expect.expect
|
||||||
|
@@ -171,7 +171,7 @@ end
|
|||||||
local current_section = nil
|
local current_section = nil
|
||||||
local offset = 0
|
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.
|
-- This could potentially be a binary search, but right now it's not worth it.
|
||||||
local function find_section()
|
local function find_section()
|
||||||
|
Reference in New Issue
Block a user