mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-24 00:54:48 +00:00
Merge branch 'mc-1.16.x' into mc-1.17.x
This commit is contained in:
@@ -19,10 +19,10 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* This works with energy storage blocks, as well as generators and machines which consume energy.
|
||||
*
|
||||
* <blockquote>
|
||||
* <strong>Note:</strong> Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. RF
|
||||
* :::note
|
||||
* Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. RF
|
||||
* used/generated per tick).
|
||||
* </blockquote>
|
||||
* :::
|
||||
*
|
||||
* @cc.module energy_storage
|
||||
*/
|
||||
|
||||
@@ -21,9 +21,60 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The modem peripheral allows you to send messages between computers.
|
||||
* Modems allow you to send messages between computers over long distances.
|
||||
*
|
||||
* :::tip
|
||||
* Modems provide a fairly basic set of methods, which makes them very flexible but often hard to work with. The
|
||||
* {@literal @}{rednet} API is built on top of modems, and provides a more user-friendly interface.
|
||||
* :::
|
||||
*
|
||||
* ## Sending and receiving messages
|
||||
* Modems operate on a series of channels, a bit like frequencies on a radio. Any modem can send a message on a
|
||||
* particular channel, but only those which have {@link #open opened} the channel and are "listening in" can receive
|
||||
* messages.
|
||||
*
|
||||
* Channels are represented as an integer between 0 and 65535 inclusive. These channels don't have any defined meaning,
|
||||
* though some APIs or programs will assign a meaning to them. For instance, the @{gps} module sends all its messages on
|
||||
* channel 65534 (@{gps.CHANNEL_GPS}), while @{rednet} uses channels equal to the computer's ID.
|
||||
*
|
||||
* - Sending messages is done with the {@link #transmit(int, int, Object)} message.
|
||||
* - Receiving messages is done by listening to the @{modem_message} event.
|
||||
*
|
||||
* ## Types of modem
|
||||
* CC: Tweaked comes with three kinds of modem, with different capabilities.
|
||||
*
|
||||
* <ul>
|
||||
* <li><strong>Wireless modems:</strong> Wireless modems can send messages to any other wireless modem. They can be placed next to a
|
||||
* computer, or equipped as a pocket computer or turtle upgrade.
|
||||
*
|
||||
* Wireless modems have a limited range, only sending messages to modems within 64 blocks. This range increases
|
||||
* linearly once the modem is above y=96, to a maximum of 384 at world height.</li>
|
||||
* <li><strong>Ender modems:</strong> These are upgraded versions of normal wireless modems. They do not have a distance
|
||||
* limit, and can send messages between dimensions.</li>
|
||||
* <li><strong>Wired modems:</strong> These send messages to other any other wired modems connected to the same network
|
||||
* (using <em>Networking Cable</em>). They also can be used to attach additional peripherals to a computer.</li></ul>
|
||||
*
|
||||
* @cc.module modem
|
||||
* @cc.see modem_message Queued when a modem receives a message on an {@link #open(int) open channel}.
|
||||
* @cc.see rednet A networking API built on top of the modem peripheral.
|
||||
* @cc.usage Wrap a modem and a message on channel 15, requesting a response on channel 43. Then wait for a message to
|
||||
* arrive on channel 43 and print it.
|
||||
*
|
||||
* <pre>{@code
|
||||
* local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||
* modem.open(43) -- Open 43 so we can receive replies
|
||||
*
|
||||
* -- Send our message
|
||||
* modem.transmit(15, 43, "Hello, world!")
|
||||
*
|
||||
* -- And wait for a reply
|
||||
* local event, side, channel, replyChannel, message, distance
|
||||
* repeat
|
||||
* event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
||||
* until channel == 43
|
||||
*
|
||||
* print("Received a reply: " .. tostring(message))
|
||||
* }</pre>
|
||||
*/
|
||||
public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPacketReceiver
|
||||
{
|
||||
@@ -157,12 +208,23 @@ public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPa
|
||||
* Sends a modem message on a certain channel. Modems listening on the channel will queue a {@code modem_message}
|
||||
* event on adjacent computers.
|
||||
*
|
||||
* <blockquote><strong>Note:</strong> The channel does not need be open to send a message.</blockquote>
|
||||
* :::note
|
||||
* The channel does not need be open to send a message.
|
||||
* :::
|
||||
*
|
||||
* @param channel The channel to send messages on.
|
||||
* @param replyChannel The channel that responses to this message should be sent on.
|
||||
* @param payload The object to send. This can be a boolean, string, number, or table.
|
||||
* @param replyChannel The channel that responses to this message should be sent on. This can be the same as
|
||||
* {@code channel} or entirely different. The channel must have been {@link #open opened} on
|
||||
* the sending computer in order to receive the replies.
|
||||
* @param payload The object to send. This can be any primitive type (boolean, number, string) as well as
|
||||
* tables. Other types (like functions), as well as metatables, will not be transmitted.
|
||||
* @throws LuaException If the channel is out of range.
|
||||
* @cc.usage Wrap a modem and a message on channel 15, requesting a response on channel 43.
|
||||
*
|
||||
* <pre>{@code
|
||||
* local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||
* modem.transmit(15, 43, "Hello, world!")
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void transmit( int channel, int replyChannel, Object payload ) throws LuaException
|
||||
|
||||
@@ -80,8 +80,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
* If this computer is attached to the network, it _will not_ be included in
|
||||
* this list.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @return Remote peripheral names on the network.
|
||||
@@ -95,8 +96,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Determine if a peripheral is available on this wired network.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@@ -112,8 +114,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Get the type of a peripheral is available on this wired network.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@@ -132,8 +135,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Check a peripheral is of a particular type.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@@ -153,8 +157,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Get all available methods for the remote peripheral with the given name.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param name The peripheral's name.
|
||||
@@ -174,8 +179,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
/**
|
||||
* Call a method on a peripheral on this wired network.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @param computer The calling computer.
|
||||
* @param context The Lua context we're executing in.
|
||||
@@ -204,8 +210,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
* may be used by other computers on the network to wrap this computer as a
|
||||
* peripheral.
|
||||
*
|
||||
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
|
||||
* returns false before calling it.</blockquote>
|
||||
* :::note
|
||||
* This function only appears on wired modems. Check {@link #isWireless} returns false before calling it.
|
||||
* :::
|
||||
*
|
||||
* @return The current computer's name.
|
||||
* @cc.treturn string|nil The current computer's name on the wired network.
|
||||
|
||||
@@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaTable;
|
||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -27,7 +28,7 @@ class DfpwmState
|
||||
* The minimum size of the client's audio buffer. Once we have less than this on the client, we should send another
|
||||
* batch of audio.
|
||||
*/
|
||||
private static final long CLIENT_BUFFER = (long) (SECOND * 1.5);
|
||||
private static final long CLIENT_BUFFER = (long) (SECOND * 0.5);
|
||||
|
||||
private static final int PREC = 10;
|
||||
|
||||
@@ -36,7 +37,7 @@ class DfpwmState
|
||||
private boolean previousBit = false;
|
||||
|
||||
private boolean unplayed = true;
|
||||
private long clientEndTime = System.nanoTime();
|
||||
private long clientEndTime = PauseAwareTimer.getTime();
|
||||
private float pendingVolume = 1.0f;
|
||||
private ByteBuffer pendingAudio;
|
||||
|
||||
@@ -107,7 +108,7 @@ class DfpwmState
|
||||
|
||||
boolean isPlaying()
|
||||
{
|
||||
return unplayed || clientEndTime >= System.nanoTime();
|
||||
return unplayed || clientEndTime >= PauseAwareTimer.getTime();
|
||||
}
|
||||
|
||||
float getVolume()
|
||||
|
||||
@@ -17,6 +17,7 @@ import dan200.computercraft.shared.network.client.SpeakerAudioClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||
import net.minecraft.ResourceLocationException;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.protocol.game.ClientboundCustomSoundPacket;
|
||||
@@ -119,7 +120,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
return;
|
||||
}
|
||||
|
||||
long now = System.nanoTime();
|
||||
long now = PauseAwareTimer.getTime();
|
||||
if( sound != null )
|
||||
{
|
||||
lastPlayTime = clock;
|
||||
@@ -342,7 +343,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
// TODO: Use ArgumentHelpers instead?
|
||||
int length = audio.length();
|
||||
if( length <= 0 ) throw new LuaException( "Cannot play empty audio" );
|
||||
if( length > 1024 * 16 * 8 ) throw new LuaException( "Audio data is too large" );
|
||||
if( length > 128 * 1024 ) throw new LuaException( "Audio data is too large" );
|
||||
|
||||
DfpwmState state;
|
||||
synchronized( lock )
|
||||
@@ -387,17 +388,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
computers.remove( computer );
|
||||
}
|
||||
|
||||
private static final class PendingSound
|
||||
private record PendingSound(ResourceLocation location, float volume, float pitch)
|
||||
{
|
||||
final ResourceLocation location;
|
||||
final float volume;
|
||||
final float pitch;
|
||||
|
||||
private PendingSound( ResourceLocation location, float volume, float pitch )
|
||||
{
|
||||
this.location = location;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
||||
@@ -34,6 +33,7 @@ import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
@@ -80,53 +80,65 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
PocketUpgrades.getVanillaUpgrades().map( x -> create( -1, null, -1, x ) ).forEach( stacks::add );
|
||||
}
|
||||
|
||||
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
|
||||
public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entity entity, int slotNum, boolean selected )
|
||||
{
|
||||
if( !world.isClientSide )
|
||||
{
|
||||
// Server side
|
||||
Container inventory = entity instanceof Player ? ((Player) entity).getInventory() : null;
|
||||
PocketServerComputer computer = createServerComputer( world, inventory, entity, stack );
|
||||
if( computer != null )
|
||||
{
|
||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||
Container inventory = entity instanceof Player player ? player.getInventory() : null;
|
||||
PocketServerComputer computer = createServerComputer( world, entity, inventory, stack );
|
||||
computer.keepAlive();
|
||||
|
||||
// Ping computer
|
||||
computer.keepAlive();
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
boolean changed = tick( stack, world, entity, computer );
|
||||
if( changed && inventory != null ) inventory.setChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client side
|
||||
createClientComputer( stack );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEntityItemUpdate( ItemStack stack, ItemEntity entity )
|
||||
{
|
||||
if( entity.level.isClientSide ) return false;
|
||||
|
||||
PocketServerComputer computer = getServerComputer( stack );
|
||||
if( computer != null && tick( stack, entity.level, entity, computer ) ) entity.setItem( stack.copy() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use( Level world, Player player, @Nonnull InteractionHand hand )
|
||||
@@ -134,22 +146,18 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
ItemStack stack = player.getItemInHand( hand );
|
||||
if( !world.isClientSide )
|
||||
{
|
||||
PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack );
|
||||
PocketServerComputer computer = createServerComputer( world, player, player.getInventory(), stack );
|
||||
computer.turnOn();
|
||||
|
||||
boolean stop = false;
|
||||
if( computer != null )
|
||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||
if( upgrade != null )
|
||||
{
|
||||
computer.turnOn();
|
||||
|
||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||
if( upgrade != null )
|
||||
{
|
||||
computer.updateValues( player, stack, upgrade );
|
||||
stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||
}
|
||||
computer.updateValues( player, stack, upgrade );
|
||||
stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) );
|
||||
}
|
||||
|
||||
if( !stop && computer != null )
|
||||
if( !stop )
|
||||
{
|
||||
boolean isTypingOnly = hand == InteractionHand.OFF_HAND;
|
||||
new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) );
|
||||
@@ -207,17 +215,17 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
return super.getCreatorModId( stack );
|
||||
}
|
||||
|
||||
public PocketServerComputer createServerComputer( final Level world, Container inventory, Entity entity, @Nonnull ItemStack stack )
|
||||
@Nonnull
|
||||
public PocketServerComputer createServerComputer( Level world, Entity entity, @Nullable Container inventory, @Nonnull ItemStack stack )
|
||||
{
|
||||
if( world.isClientSide ) return null;
|
||||
if( world.isClientSide ) throw new IllegalStateException( "Cannot call createServerComputer on the client" );
|
||||
|
||||
PocketServerComputer computer;
|
||||
int instanceID = getInstanceID( stack );
|
||||
int sessionID = getSessionID( stack );
|
||||
int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID();
|
||||
|
||||
if( instanceID >= 0 && sessionID == correctSessionID &&
|
||||
ComputerCraft.serverComputerRegistry.contains( instanceID ) )
|
||||
if( instanceID >= 0 && sessionID == correctSessionID && ComputerCraft.serverComputerRegistry.contains( instanceID ) )
|
||||
{
|
||||
computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID );
|
||||
}
|
||||
@@ -235,13 +243,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" );
|
||||
setComputerID( stack, computerID );
|
||||
}
|
||||
computer = new PocketServerComputer(
|
||||
world,
|
||||
computerID,
|
||||
getLabel( stack ),
|
||||
instanceID,
|
||||
getFamily()
|
||||
);
|
||||
computer = new PocketServerComputer( world, computerID, getLabel( stack ), instanceID, getFamily() );
|
||||
computer.updateValues( entity, stack, getUpgrade( stack ) );
|
||||
computer.addAPI( new PocketAPI( computer ) );
|
||||
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
|
||||
@@ -251,15 +253,17 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
return computer;
|
||||
}
|
||||
|
||||
public static ServerComputer getServerComputer( @Nonnull ItemStack stack )
|
||||
@Nullable
|
||||
public static PocketServerComputer getServerComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int session = getSessionID( stack );
|
||||
if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null;
|
||||
|
||||
int instanceID = getInstanceID( stack );
|
||||
return instanceID >= 0 ? ComputerCraft.serverComputerRegistry.get( instanceID ) : null;
|
||||
return instanceID >= 0 ? (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID ) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ClientComputer createClientComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int instanceID = getInstanceID( stack );
|
||||
@@ -274,6 +278,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClientComputer getClientComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int instanceID = getInstanceID( stack );
|
||||
|
||||
@@ -22,8 +22,50 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The turtle API allows you to control your turtle.
|
||||
* Turtles are a robotic device, which can break and place blocks, attack mobs, and move about the world. They have
|
||||
* an internal inventory of 16 slots, allowing them to store blocks they have broken or would like to place.
|
||||
*
|
||||
* ## Movement
|
||||
* Turtles are capable of moving throug the world. As turtles are blocks themselves, they are confined to Minecraft's
|
||||
* grid, moving a single block at a time.
|
||||
*
|
||||
* {@literal @}{turtle.forward} and @{turtle.back} move the turtle in the direction it is facing, while @{turtle.up} and
|
||||
* {@literal @}{turtle.down} move it up and down (as one might expect!). In order to move left or right, you first need
|
||||
* to turn the turtle using @{turtle.turnLeft}/@{turtle.turnRight} and then move forward or backwards.
|
||||
*
|
||||
* :::info
|
||||
* The name "turtle" comes from [Turtle graphics], which originated from the Logo programming language. Here you'd move
|
||||
* a turtle with various commands like "move 10" and "turn left", much like ComputerCraft's turtles!
|
||||
* :::
|
||||
*
|
||||
* Moving a turtle (though not turning it) consumes *fuel*. If a turtle does not have any @{turtle.refuel|fuel}, it
|
||||
* won't move, and the movement functions will return @{false}. If your turtle isn't going anywhere, the first thing to
|
||||
* check is if you've fuelled your turtle.
|
||||
*
|
||||
* :::tip Handling errors
|
||||
* Many turtle functions can fail in various ways. For instance, a turtle cannot move forward if there's already a block
|
||||
* there. Instead of erroring, functions which can fail either return @{true} if they succeed, or @{false} and some
|
||||
* error message if they fail.
|
||||
*
|
||||
* Unexpected failures can often lead to strange behaviour. It's often a good idea to check the return values of these
|
||||
* functions, or wrap them in @{assert} (for instance, use `assert(turtle.forward())` rather than `turtle.forward()`),
|
||||
* so the program doesn't misbehave.
|
||||
* :::
|
||||
*
|
||||
* ## Turtle upgrades
|
||||
* While a normal turtle can move about the world and place blocks, its functionality is limited. Thankfully, turtles
|
||||
* can be upgraded with *tools* and @{peripheral|peripherals}. Turtles have two upgrade slots, one on the left and right
|
||||
* sides. Upgrades can be equipped by crafting a turtle with the upgrade, or calling the @{turtle.equipLeft}/@{turtle.equipRight}
|
||||
* functions.
|
||||
*
|
||||
* Turtle tools allow you to break blocks (@{turtle.dig}) and attack entities (@{turtle.attack}). Some tools are more
|
||||
* suitable to a task than others. For instance, a diamond pickaxe can break every block, while a sword does more
|
||||
* damage. Other tools have more niche use-cases, for instance hoes can til dirt.
|
||||
*
|
||||
* Peripherals (such as the @{modem|wireless modem} or @{speaker}) can also be equipped as upgrades. These are then
|
||||
* accessible by accessing the `"left"` or `"right"` peripheral.
|
||||
*
|
||||
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
|
||||
* @cc.module turtle
|
||||
* @cc.since 1.3
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,6 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntitySelector;
|
||||
import net.minecraft.world.entity.MoverType;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -39,6 +38,7 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
@@ -48,6 +48,7 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static dan200.computercraft.shared.common.IColouredItem.NBT_COLOUR;
|
||||
import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED;
|
||||
@@ -65,6 +66,8 @@ public class TurtleBrain implements ITurtleAccess
|
||||
|
||||
private static final int ANIM_DURATION = 8;
|
||||
|
||||
public static final Predicate<Entity> PUSHABLE_ENTITY = entity -> !entity.isSpectator() && entity.getPistonPushReaction() != PushReaction.IGNORE;
|
||||
|
||||
private TileTurtle owner;
|
||||
private ComputerProxy proxy;
|
||||
private GameProfile owningPlayer;
|
||||
@@ -886,7 +889,7 @@ public class TurtleBrain implements ITurtleAccess
|
||||
}
|
||||
|
||||
AABB aabb = new AABB( minX, minY, minZ, maxX, maxY, maxZ );
|
||||
List<Entity> list = world.getEntitiesOfClass( Entity.class, aabb, EntitySelector.NO_SPECTATORS );
|
||||
List<Entity> list = world.getEntitiesOfClass( Entity.class, aabb, PUSHABLE_ENTITY );
|
||||
if( !list.isEmpty() )
|
||||
{
|
||||
double pushStep = 1.0f / ANIM_DURATION;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
/**
|
||||
* A monotonically increasing clock which accounts for the game being paused.
|
||||
*/
|
||||
@Mod.EventBusSubscriber( Dist.CLIENT )
|
||||
public final class PauseAwareTimer
|
||||
{
|
||||
private static boolean paused;
|
||||
private static long pauseTime;
|
||||
private static long pauseOffset;
|
||||
|
||||
private PauseAwareTimer()
|
||||
{
|
||||
}
|
||||
|
||||
public static long getTime()
|
||||
{
|
||||
return (paused ? pauseTime : Util.getNanos()) - pauseOffset;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void tick( TickEvent.RenderTickEvent event )
|
||||
{
|
||||
if( event.phase != TickEvent.Phase.START ) return;
|
||||
|
||||
boolean isPaused = Minecraft.getInstance().isPaused();
|
||||
if( isPaused == paused ) return;
|
||||
|
||||
if( isPaused )
|
||||
{
|
||||
pauseTime = Util.getNanos();
|
||||
paused = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pauseOffset += Util.getNanos() - pauseTime;
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user