1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-26 23:42:18 +00:00

Add button to view computer's folder

Implementation is a little awkward, as we can't send OPEN_FILE links
from the server, so we ensure the client runs a
/computercraft open-computer ID command instead. We then intercept this
on the client side and use that to open the folder.
This commit is contained in:
Jonathan Coates 2021-05-28 14:19:04 -07:00 committed by ToadDev
parent a4dd6c24e5
commit 1831e81dd4
13 changed files with 478 additions and 455 deletions

View File

@ -25,7 +25,6 @@ import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;

View File

@ -6,7 +6,7 @@
package dan200.computercraft.mixin;
import dan200.computercraft.shared.command.CommandCopy;
import dan200.computercraft.shared.command.ClientCommands;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -22,7 +22,7 @@ import net.fabricmc.api.Environment;
public class MixinScreen {
@Inject (method = "sendMessage(Ljava/lang/String;Z)V", at = @At ("HEAD"), cancellable = true)
public void sendClientCommand(String message, boolean add, CallbackInfo info) {
if (CommandCopy.onClientSendMessage(message)) {
if (ClientCommands.onClientSendMessage(message)) {
info.cancel();
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.command;
import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.util.Util;
import java.io.File;
/**
* Basic client-side commands.
*
* Simply hooks into client chat messages and intercepts matching strings.
*/
public final class ClientCommands
{
public static final String OPEN_COMPUTER = "/computercraft open-computer ";
private ClientCommands()
{
}
public static boolean onClientSendMessage( String message )
{
// Emulate the command on the client side
if( message.startsWith( OPEN_COMPUTER ) )
{
String idStr = message.substring( OPEN_COMPUTER.length() ).trim();
int id;
try
{
id = Integer.parseInt( idStr );
}
catch( NumberFormatException ignore )
{
return true;
}
File file = new File( IDAssigner.getDir(), "computer/" + id );
if( !file.isDirectory() ) return true;
Util.getOperatingSystem().open( file );
return true;
}
return false;
}
}

View File

@ -3,44 +3,8 @@
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.command;
import static dan200.computercraft.shared.command.CommandUtils.isPlayer;
import static dan200.computercraft.shared.command.Exceptions.NOT_TRACKING_EXCEPTION;
import static dan200.computercraft.shared.command.Exceptions.NO_TIMINGS_EXCEPTION;
import static dan200.computercraft.shared.command.Exceptions.TP_NOT_PLAYER;
import static dan200.computercraft.shared.command.Exceptions.TP_NOT_THERE;
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.getComputerArgument;
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.oneComputer;
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.getComputersArgument;
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.manyComputers;
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.unwrap;
import static dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType.trackingField;
import static dan200.computercraft.shared.command.builder.CommandBuilder.args;
import static dan200.computercraft.shared.command.builder.CommandBuilder.command;
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
import static dan200.computercraft.shared.command.text.ChatHelpers.bool;
import static dan200.computercraft.shared.command.text.ChatHelpers.header;
import static dan200.computercraft.shared.command.text.ChatHelpers.link;
import static dan200.computercraft.shared.command.text.ChatHelpers.position;
import static dan200.computercraft.shared.command.text.ChatHelpers.text;
import static dan200.computercraft.shared.command.text.ChatHelpers.translate;
import static net.minecraft.server.command.CommandManager.literal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nonnull;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
@ -57,10 +21,12 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import dan200.computercraft.shared.util.IDAssigner;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.screen.ScreenHandler;
@ -69,345 +35,388 @@ import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public final class CommandComputerCraft {
public static final UUID SYSTEM_UUID = new UUID(0, 0);
import javax.annotation.Nonnull;
import java.io.File;
import java.util.*;
import static dan200.computercraft.shared.command.CommandUtils.isPlayer;
import static dan200.computercraft.shared.command.Exceptions.*;
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.getComputerArgument;
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.oneComputer;
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.*;
import static dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType.trackingField;
import static dan200.computercraft.shared.command.builder.CommandBuilder.args;
import static dan200.computercraft.shared.command.builder.CommandBuilder.command;
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
import static dan200.computercraft.shared.command.text.ChatHelpers.*;
import static net.minecraft.server.command.CommandManager.literal;
public final class CommandComputerCraft
{
public static final UUID SYSTEM_UUID = new UUID( 0, 0 );
private static final int DUMP_LIST_ID = 5373952;
private static final int DUMP_SINGLE_ID = 1844510720;
private static final int TRACK_ID = 373882880;
private static final List<TrackingField> DEFAULT_FIELDS = Arrays.asList(TrackingField.TASKS,
TrackingField.TOTAL_TIME,
TrackingField.AVERAGE_TIME,
TrackingField.MAX_TIME);
public CommandComputerCraft() {
private CommandComputerCraft()
{
}
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, boolean bool) {
dispatcher.register(choice("computercraft").then(literal("dump").requires(UserLevel.OWNER_OP)
.executes(context -> {
TableBuilder table = new TableBuilder(DUMP_LIST_ID,
"Computer",
"On",
"Position");
public static void register( CommandDispatcher<ServerCommandSource> dispatcher, Boolean dedicated )
{
dispatcher.register( choice( "computercraft" )
.then( literal( "dump" )
.requires( UserLevel.OWNER_OP )
.executes( context -> {
TableBuilder table = new TableBuilder( DUMP_LIST_ID, "Computer", "On", "Position" );
ServerCommandSource source = context.getSource();
List<ServerComputer> computers =
new ArrayList<>(ComputerCraft.serverComputerRegistry.getComputers());
ServerCommandSource source = context.getSource();
List<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() );
// Unless we're on a server, limit the number of rows we can send.
World world = source.getWorld();
BlockPos pos = new BlockPos(source.getPosition());
// Unless we're on a server, limit the number of rows we can send.
World world = source.getWorld();
BlockPos pos = new BlockPos( source.getPosition() );
computers.sort((a, b) -> {
if (a.getWorld() == b.getWorld() && a.getWorld() == world) {
return Double.compare(a.getPosition()
.getSquaredDistance(pos),
b.getPosition()
.getSquaredDistance(pos));
} else if (a.getWorld() == world) {
return -1;
} else if (b.getWorld() == world) {
return 1;
} else {
return Integer.compare(a.getInstanceID(), b.getInstanceID());
}
});
computers.sort( ( a, b ) -> {
if( a.getWorld() == b.getWorld() && a.getWorld() == world )
{
return Double.compare( a.getPosition().getSquaredDistance( pos ), b.getPosition().getSquaredDistance( pos ) );
}
else if( a.getWorld() == world )
{
return -1;
}
else if( b.getWorld() == world )
{
return 1;
}
else
{
return Integer.compare( a.getInstanceID(), b.getInstanceID() );
}
} );
for (ServerComputer computer : computers) {
table.row(linkComputer(source, computer, computer.getID()),
bool(computer.isOn()),
linkPosition(source, computer));
}
for( ServerComputer computer : computers )
{
table.row(
linkComputer( source, computer, computer.getID() ),
bool( computer.isOn() ),
linkPosition( source, computer )
);
}
table.display(context.getSource());
return computers.size();
})
.then(args().arg("computer", oneComputer())
.executes(context -> {
ServerComputer computer = getComputerArgument(context, "computer");
table.display( context.getSource() );
return computers.size();
} )
.then( args()
.arg( "computer", oneComputer() )
.executes( context -> {
ServerComputer computer = getComputerArgument( context, "computer" );
TableBuilder table = new TableBuilder(DUMP_SINGLE_ID);
table.row(header("Instance"),
text(Integer.toString(computer.getInstanceID())));
table.row(header("Id"), text(Integer.toString(computer.getID())));
table.row(header("Label"), text(computer.getLabel()));
table.row(header("On"), bool(computer.isOn()));
table.row(header("Position"),
linkPosition(context.getSource(), computer));
table.row(header("Family"),
text(computer.getFamily()
.toString()));
TableBuilder table = new TableBuilder( DUMP_SINGLE_ID );
table.row( header( "Instance" ), text( Integer.toString( computer.getInstanceID() ) ) );
table.row( header( "Id" ), text( Integer.toString( computer.getID() ) ) );
table.row( header( "Label" ), text( computer.getLabel() ) );
table.row( header( "On" ), bool( computer.isOn() ) );
table.row( header( "Position" ), linkPosition( context.getSource(), computer ) );
table.row( header( "Family" ), text( computer.getFamily().toString() ) );
for (ComputerSide side : ComputerSide.values()) {
IPeripheral peripheral = computer.getPeripheral(side);
if (peripheral != null) {
table.row(header("Peripheral " + side.getName()),
text(peripheral.getType()));
}
}
for( ComputerSide side : ComputerSide.values() )
{
IPeripheral peripheral = computer.getPeripheral( side );
if( peripheral != null )
{
table.row( header( "Peripheral " + side.getName() ), text( peripheral.getType() ) );
}
}
table.display(context.getSource());
return 1;
})))
table.display( context.getSource() );
return 1;
} ) ) )
.then(command("shutdown").requires(UserLevel.OWNER_OP)
.argManyValue("computers",
manyComputers(),
s -> ComputerCraft.serverComputerRegistry.getComputers())
.executes((context, computers) -> {
int shutdown = 0;
for (ServerComputer computer : unwrap(context.getSource(), computers)) {
if (computer.isOn()) {
shutdown++;
}
computer.shutdown();
}
context.getSource()
.sendFeedback(translate("commands.computercraft.shutdown.done",
shutdown,
computers.size()), false);
return shutdown;
}))
.then( command( "shutdown" )
.requires( UserLevel.OWNER_OP )
.argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() )
.executes( ( context, computers ) -> {
int shutdown = 0;
for( ServerComputer computer : unwrap( context.getSource(), computers ) )
{
if( computer.isOn() ) shutdown++;
computer.shutdown();
}
context.getSource().sendFeedback( translate( "commands.computercraft.shutdown.done", shutdown, computers.size() ), false );
return shutdown;
} ) )
.then(command("turn-on").requires(UserLevel.OWNER_OP)
.argManyValue("computers",
manyComputers(),
s -> ComputerCraft.serverComputerRegistry.getComputers())
.executes((context, computers) -> {
int on = 0;
for (ServerComputer computer : unwrap(context.getSource(), computers)) {
if (!computer.isOn()) {
on++;
}
computer.turnOn();
}
context.getSource()
.sendFeedback(translate("commands.computercraft.turn_on.done",
on,
computers.size()), false);
return on;
}))
.then( command( "turn-on" )
.requires( UserLevel.OWNER_OP )
.argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() )
.executes( ( context, computers ) -> {
int on = 0;
for( ServerComputer computer : unwrap( context.getSource(), computers ) )
{
if( !computer.isOn() ) on++;
computer.turnOn();
}
context.getSource().sendFeedback( translate( "commands.computercraft.turn_on.done", on, computers.size() ), false );
return on;
} ) )
.then(command("tp").requires(UserLevel.OP)
.arg("computer", oneComputer())
.executes(context -> {
ServerComputer computer = getComputerArgument(context, "computer");
World world = computer.getWorld();
BlockPos pos = computer.getPosition();
.then( command( "tp" )
.requires( UserLevel.OP )
.arg( "computer", oneComputer() )
.executes( context -> {
ServerComputer computer = getComputerArgument( context, "computer" );
World world = computer.getWorld();
BlockPos pos = computer.getPosition();
if (world == null || pos == null) {
throw TP_NOT_THERE.create();
}
if( world == null || pos == null ) throw TP_NOT_THERE.create();
Entity entity = context.getSource()
.getEntityOrThrow();
if (!(entity instanceof ServerPlayerEntity)) {
throw TP_NOT_PLAYER.create();
}
Entity entity = context.getSource().getEntityOrThrow();
if( !(entity instanceof ServerPlayerEntity) ) throw TP_NOT_PLAYER.create();
ServerPlayerEntity player = (ServerPlayerEntity) entity;
if (player.getEntityWorld() == world) {
player.networkHandler.teleportRequest(pos.getX() + 0.5,
pos.getY(),
pos.getZ() + 0.5,
0,
0,
EnumSet.noneOf(
PlayerPositionLookS2CPacket.Flag.class));
} else {
player.teleport((ServerWorld) world,
pos.getX() + 0.5,
pos.getY(),
pos.getZ() + 0.5,
0,
0);
}
ServerPlayerEntity player = (ServerPlayerEntity) entity;
if( player.getEntityWorld() == world )
{
player.networkHandler.teleportRequest(
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0,
EnumSet.noneOf( PlayerPositionLookS2CPacket.Flag.class )
);
}
else
{
player.teleport( (ServerWorld) world,
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0
);
}
return 1;
}))
return 1;
} ) )
.then(command("queue").requires(UserLevel.ANYONE)
.arg("computer", manyComputers())
.argManyValue("args", StringArgumentType.string(), Collections.emptyList())
.executes((ctx, args) -> {
Collection<ServerComputer> computers = getComputersArgument(ctx, "computer");
Object[] rest = args.toArray();
.then( command( "queue" )
.requires( UserLevel.ANYONE )
.arg( "computer", manyComputers() )
.argManyValue( "args", StringArgumentType.string(), Collections.emptyList() )
.executes( ( ctx, args ) -> {
Collection<ServerComputer> computers = getComputersArgument( ctx, "computer" );
Object[] rest = args.toArray();
int queued = 0;
for (ServerComputer computer : computers) {
if (computer.getFamily() == ComputerFamily.COMMAND && computer.isOn()) {
computer.queueEvent("computer_command", rest);
queued++;
}
}
int queued = 0;
for( ServerComputer computer : computers )
{
if( computer.getFamily() == ComputerFamily.COMMAND && computer.isOn() )
{
computer.queueEvent( "computer_command", rest );
queued++;
}
}
return queued;
}))
return queued;
} ) )
.then(command("view").requires(UserLevel.OP)
.arg("computer", oneComputer())
.executes(context -> {
ServerPlayerEntity player = context.getSource()
.getPlayer();
ServerComputer computer = getComputerArgument(context, "computer");
new ViewComputerContainerData(computer).open(player,
new NamedScreenHandlerFactory() {
@Nonnull
@Override
public Text getDisplayName() {
return new TranslatableText(
"gui.computercraft.view_computer");
}
.then( command( "view" )
.requires( UserLevel.OP )
.arg( "computer", oneComputer() )
.executes( context -> {
ServerPlayerEntity player = context.getSource().getPlayer();
ServerComputer computer = getComputerArgument( context, "computer" );
ViewComputerContainerData container = new ViewComputerContainerData( computer );
container.open( player, new ExtendedScreenHandlerFactory()
{
@Override
public void writeScreenOpeningData( ServerPlayerEntity player, PacketByteBuf buf )
{
container.toBytes( buf );
}
@Nonnull
@Override
public ScreenHandler createMenu(int id, @Nonnull PlayerInventory player, @Nonnull PlayerEntity entity) {
return new ContainerViewComputer(
id,
computer);
}
});
return 1;
}))
@Nonnull
@Override
public MutableText getDisplayName()
{
return new TranslatableText( "gui.computercraft.view_computer" );
}
.then(choice("track").then(command("start").requires(UserLevel.OWNER_OP)
.executes(context -> {
getTimingContext(context.getSource()).start();
@Nonnull
@Override
public ScreenHandler createMenu( int id, @Nonnull PlayerInventory player, @Nonnull PlayerEntity entity )
{
return new ContainerViewComputer( id, computer );
}
} );
return 1;
} ) )
String stopCommand = "/computercraft track stop";
context.getSource()
.sendFeedback(translate(
"commands.computercraft.track.start.stop",
link(text(stopCommand),
stopCommand,
translate(
"commands.computercraft.track.stop.action"))),
false);
return 1;
}))
.then( choice( "track" )
.then( command( "start" )
.requires( UserLevel.OWNER_OP )
.executes( context -> {
getTimingContext( context.getSource() ).start();
.then(command("stop").requires(UserLevel.OWNER_OP)
.executes(context -> {
TrackingContext timings = getTimingContext(context.getSource());
if (!timings.stop()) {
throw NOT_TRACKING_EXCEPTION.create();
}
displayTimings(context.getSource(),
timings.getImmutableTimings(),
TrackingField.AVERAGE_TIME,
DEFAULT_FIELDS);
return 1;
}))
String stopCommand = "/computercraft track stop";
context.getSource().sendFeedback( translate( "commands.computercraft.track.start.stop",
link( text( stopCommand ), stopCommand, translate( "commands.computercraft.track.stop.action" ) ) ), false );
return 1;
} ) )
.then(command("dump").requires(UserLevel.OWNER_OP)
.argManyValue("fields", trackingField(), DEFAULT_FIELDS)
.executes((context, fields) -> {
TrackingField sort;
if (fields.size() == 1 && DEFAULT_FIELDS.contains(fields.get(
0))) {
sort = fields.get(0);
fields = DEFAULT_FIELDS;
} else {
sort = fields.get(0);
}
.then( command( "stop" )
.requires( UserLevel.OWNER_OP )
.executes( context -> {
TrackingContext timings = getTimingContext( context.getSource() );
if( !timings.stop() ) throw NOT_TRACKING_EXCEPTION.create();
displayTimings( context.getSource(), timings.getImmutableTimings(), TrackingField.AVERAGE_TIME, DEFAULT_FIELDS );
return 1;
} ) )
return displayTimings(context.getSource(), sort, fields);
}))));
.then( command( "dump" )
.requires( UserLevel.OWNER_OP )
.argManyValue( "fields", trackingField(), DEFAULT_FIELDS )
.executes( ( context, fields ) -> {
TrackingField sort;
if( fields.size() == 1 && DEFAULT_FIELDS.contains( fields.get( 0 ) ) )
{
sort = fields.get( 0 );
fields = DEFAULT_FIELDS;
}
else
{
sort = fields.get( 0 );
}
return displayTimings( context.getSource(), sort, fields );
} ) ) )
);
}
private static Text linkComputer(ServerCommandSource source, ServerComputer serverComputer, int computerId) {
MutableText out = new LiteralText("");
private static MutableText linkComputer( ServerCommandSource source, ServerComputer serverComputer, int computerId )
{
MutableText out = new LiteralText( "" );
// Append the computer instance
if (serverComputer == null) {
out.append(text("?"));
} else {
out.append(link(text(Integer.toString(serverComputer.getInstanceID())),
"/computercraft dump " + serverComputer.getInstanceID(),
translate("commands.computercraft.dump.action")));
if( serverComputer == null )
{
out.append( text( "?" ) );
}
else
{
out.append( link(
text( Integer.toString( serverComputer.getInstanceID() ) ),
"/computercraft dump " + serverComputer.getInstanceID(),
translate( "commands.computercraft.dump.action" )
) );
}
// And ID
out.append(" (id " + computerId + ")");
out.append( " (id " + computerId + ")" );
// And, if we're a player, some useful links
if (serverComputer != null && UserLevel.OP.test(source) && isPlayer(source)) {
out.append(" ")
.append(link(text("\u261b"), "/computercraft tp " + serverComputer.getInstanceID(), translate("commands.computercraft.tp.action")))
.append(" ")
.append(link(text("\u20e2"), "/computercraft view " + serverComputer.getInstanceID(), translate("commands.computercraft.view.action")));
if( serverComputer != null && UserLevel.OP.test( source ) && isPlayer( source ) )
{
out
.append( " " )
.append( link(
text( "\u261b" ),
"/computercraft tp " + serverComputer.getInstanceID(),
translate( "commands.computercraft.tp.action" )
) )
.append( " " )
.append( link(
text( "\u20e2" ),
"/computercraft view " + serverComputer.getInstanceID(),
translate( "commands.computercraft.view.action" )
) );
}
if( UserLevel.OWNER.test( source ) && isPlayer( source ) )
{
MutableText linkPath = linkStorage( computerId );
if( linkPath != null ) out.append( " " ).append( linkPath );
}
return out;
}
private static Text linkPosition(ServerCommandSource context, ServerComputer computer) {
if (UserLevel.OP.test(context)) {
return link(position(computer.getPosition()), "/computercraft tp " + computer.getInstanceID(), translate("commands.computercraft.tp.action"));
} else {
return position(computer.getPosition());
private static MutableText linkPosition( ServerCommandSource context, ServerComputer computer )
{
if( UserLevel.OP.test( context ) )
{
return link(
position( computer.getPosition() ),
"/computercraft tp " + computer.getInstanceID(),
translate( "commands.computercraft.tp.action" )
);
}
else
{
return position( computer.getPosition() );
}
}
private static MutableText linkStorage( int id )
{
File file = new File( IDAssigner.getDir(), "computer/" + id );
if( !file.isDirectory() ) return null;
return link(
text( "\u270E" ),
ClientCommands.OPEN_COMPUTER + id,
translate( "commands.computercraft.dump.open_path" )
);
}
@Nonnull
private static TrackingContext getTimingContext(ServerCommandSource source) {
private static TrackingContext getTimingContext( ServerCommandSource source )
{
Entity entity = source.getEntity();
return entity instanceof PlayerEntity ? Tracking.getContext(entity.getUuid()) : Tracking.getContext(SYSTEM_UUID);
return entity instanceof PlayerEntity ? Tracking.getContext( entity.getUuid() ) : Tracking.getContext( SYSTEM_UUID );
}
private static int displayTimings(ServerCommandSource source, @Nonnull List<ComputerTracker> timings, @Nonnull TrackingField sortField, @Nonnull List<TrackingField> fields) throws CommandSyntaxException {
if (timings.isEmpty()) {
throw NO_TIMINGS_EXCEPTION.create();
}
private static final List<TrackingField> DEFAULT_FIELDS = Arrays.asList( TrackingField.TASKS, TrackingField.TOTAL_TIME, TrackingField.AVERAGE_TIME, TrackingField.MAX_TIME );
private static int displayTimings( ServerCommandSource source, TrackingField sortField, List<TrackingField> fields ) throws CommandSyntaxException
{
return displayTimings( source, getTimingContext( source ).getTimings(), sortField, fields );
}
private static int displayTimings( ServerCommandSource source, @Nonnull List<ComputerTracker> timings, @Nonnull TrackingField sortField, @Nonnull List<TrackingField> fields ) throws CommandSyntaxException
{
if( timings.isEmpty() ) throw NO_TIMINGS_EXCEPTION.create();
Map<Computer, ServerComputer> lookup = new HashMap<>();
int maxId = 0, maxInstance = 0;
for (ServerComputer server : ComputerCraft.serverComputerRegistry.getComputers()) {
lookup.put(server.getComputer(), server);
for( ServerComputer server : ComputerCraft.serverComputerRegistry.getComputers() )
{
lookup.put( server.getComputer(), server );
if (server.getInstanceID() > maxInstance) {
maxInstance = server.getInstanceID();
}
if (server.getID() > maxId) {
maxId = server.getID();
}
if( server.getInstanceID() > maxInstance ) maxInstance = server.getInstanceID();
if( server.getID() > maxId ) maxId = server.getID();
}
timings.sort(Comparator.<ComputerTracker, Long>comparing(x -> x.get(sortField)).reversed());
timings.sort( Comparator.<ComputerTracker, Long>comparing( x -> x.get( sortField ) ).reversed() );
Text[] headers = new Text[1 + fields.size()];
headers[0] = translate("commands.computercraft.track.dump.computer");
for (int i = 0; i < fields.size(); i++) {
headers[i + 1] = translate(fields.get(i)
.translationKey());
}
TableBuilder table = new TableBuilder(TRACK_ID, headers);
MutableText[] headers = new MutableText[1 + fields.size()];
headers[0] = translate( "commands.computercraft.track.dump.computer" );
for( int i = 0; i < fields.size(); i++ ) headers[i + 1] = translate( fields.get( i ).translationKey() );
TableBuilder table = new TableBuilder( TRACK_ID, headers );
for (ComputerTracker entry : timings) {
for( ComputerTracker entry : timings )
{
Computer computer = entry.getComputer();
ServerComputer serverComputer = computer == null ? null : lookup.get(computer);
ServerComputer serverComputer = computer == null ? null : lookup.get( computer );
Text computerComponent = linkComputer(source, serverComputer, entry.getComputerId());
MutableText computerComponent = linkComputer( source, serverComputer, entry.getComputerId() );
Text[] row = new Text[1 + fields.size()];
MutableText[] row = new MutableText[1 + fields.size()];
row[0] = computerComponent;
for (int i = 0; i < fields.size(); i++) {
row[i + 1] = text(entry.getFormatted(fields.get(i)));
}
table.row(row);
for( int i = 0; i < fields.size(); i++ ) row[i + 1] = text( entry.getFormatted( fields.get( i ) ) );
table.row( row );
}
table.display(source);
table.display( source );
return timings.size();
}
private static int displayTimings(ServerCommandSource source, TrackingField sortField, List<TrackingField> fields) throws CommandSyntaxException {
return displayTimings(source, getTimingContext(source).getTimings(), sortField, fields);
}
}

View File

@ -1,57 +0,0 @@
/*
* 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.command;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import net.minecraft.client.MinecraftClient;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment (EnvType.CLIENT)
public final class CommandCopy {
private static final String PREFIX = "/computercraft copy ";
private CommandCopy() {
}
public static void register(CommandDispatcher<ServerCommandSource> registry) {
registry.register(literal("computercraft").then(literal("copy"))
.then(argument("message", StringArgumentType.greedyString()))
.executes(context -> {
MinecraftClient.getInstance().keyboard.setClipboard(context.getArgument("message", String.class));
return 1;
}));
}
public static boolean onClientSendMessage(String message) {
// Emulate the command on the client side
if (message.startsWith(PREFIX)) {
MinecraftClient.getInstance().keyboard.setClipboard(message.substring(PREFIX.length()));
return true;
}
return false;
}
public static Text createCopyText(String text) {
return new LiteralText(text).fillStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, PREFIX + text))
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new TranslatableText("gui.computercraft.tooltip.copy"))));
}
}

View File

@ -3,20 +3,20 @@
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.command;
import java.util.function.Predicate;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import java.util.function.Predicate;
/**
* The level a user must be at in order to execute a command.
*/
public enum UserLevel implements Predicate<ServerCommandSource> {
public enum UserLevel implements Predicate<ServerCommandSource>
{
/**
* Only can be used by the owner of the server: namely the server console or the player in SSP.
*/
@ -37,37 +37,37 @@ public enum UserLevel implements Predicate<ServerCommandSource> {
*/
ANYONE;
@Override
public boolean test(ServerCommandSource source) {
if (this == ANYONE) {
return true;
public int toLevel()
{
switch( this )
{
case OWNER:
return 4;
case OP:
case OWNER_OP:
return 2;
case ANYONE:
default:
return 0;
}
}
// We *always* allow level 0 stuff, even if the
MinecraftServer server = source.getMinecraftServer();
Entity sender = source.getEntity();
@Override
public boolean test( ServerCommandSource source )
{
if( this == ANYONE ) return true;
if (server.isSinglePlayer() && sender instanceof PlayerEntity && ((PlayerEntity) sender).getGameProfile()
.getName()
.equalsIgnoreCase(server.getServerModName())) {
if (this == OWNER || this == OWNER_OP) {
if( this == OWNER || this == OWNER_OP )
{
MinecraftServer server = source.getMinecraftServer();
Entity sender = source.getEntity();
if( server.isSinglePlayer() && sender instanceof PlayerEntity &&
((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
{
return true;
}
}
return source.hasPermissionLevel(this.toLevel());
}
public int toLevel() {
switch (this) {
case OWNER:
return 4;
case OP:
case OWNER_OP:
return 2;
case ANYONE:
default:
return 0;
}
return source.hasPermissionLevel( toLevel() );
}
}

View File

@ -3,81 +3,101 @@
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.command.text;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.text.*;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos;
/**
* Various helpers for building chat messages.
*/
public final class ChatHelpers {
public final class ChatHelpers
{
private static final Formatting HEADER = Formatting.LIGHT_PURPLE;
private ChatHelpers() {}
public static MutableText text(String text) {
return new LiteralText(text == null ? "" : text);
public static MutableText coloured( String text, Formatting colour )
{
MutableText component = new LiteralText( text == null ? "" : text );
component.setStyle( component.getStyle().withColor( colour ) );
return component;
}
public static MutableText list(Text... children) {
MutableText component = new LiteralText("");
for (Text child : children) {
component.append(child);
public static <T extends MutableText> T coloured( T component, Formatting colour )
{
component.setStyle( component.getStyle().withColor( colour ) );
return component;
}
public static MutableText text( String text )
{
return new LiteralText( text == null ? "" : text );
}
public static MutableText translate( String text )
{
return new TranslatableText( text == null ? "" : text );
}
public static MutableText translate( String text, Object... args )
{
return new TranslatableText( text == null ? "" : text, args );
}
public static MutableText list( MutableText... children )
{
MutableText component = new LiteralText( "" );
for( MutableText child : children )
{
component.append( child );
}
return component;
}
public static MutableText position(BlockPos pos) {
if (pos == null) {
return translate("commands.computercraft.generic.no_position");
}
return translate("commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ());
public static MutableText position( BlockPos pos )
{
if( pos == null ) return translate( "commands.computercraft.generic.no_position" );
return translate( "commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ() );
}
public static MutableText translate(String text) {
return new TranslatableText(text == null ? "" : text);
public static MutableText bool( boolean value )
{
return value
? coloured( translate( "commands.computercraft.generic.yes" ), Formatting.GREEN )
: coloured( translate( "commands.computercraft.generic.no" ), Formatting.RED );
}
public static MutableText translate(String text, Object... args) {
return new TranslatableText(text == null ? "" : text, args);
public static MutableText link( MutableText component, String command, MutableText toolTip )
{
return link( component, new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ), toolTip );
}
public static MutableText bool(boolean value) {
return value ? coloured(translate("commands.computercraft.generic.yes"),
Formatting.GREEN) : coloured(translate("commands.computercraft.generic.no"), Formatting.RED);
}
public static <T extends MutableText> T coloured(T component, Formatting colour) {
component.formatted(colour);
return component;
}
public static MutableText link(MutableText component, String command, Text toolTip) {
public static MutableText link( MutableText component, ClickEvent click, MutableText toolTip )
{
Style style = component.getStyle();
if (style.getColor() == null) {
style = style.withColor(Formatting.YELLOW);
}
style = style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command));
style = style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, toolTip));
if( style.getColor() == null ) style = style.withColor( Formatting.YELLOW );
style = style.withClickEvent( click );
style = style.withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) );
return component.setStyle(style);
component.setStyle(style);
return component;
}
public static MutableText header(String text) {
return coloured(text, HEADER);
public static MutableText header( String text )
{
return coloured( text, HEADER );
}
public static MutableText coloured(String text, Formatting colour) {
return new LiteralText(text == null ? "" : text).formatted(colour);
public static MutableText copy( String text )
{
LiteralText name = new LiteralText( text );
name.setStyle( name.getStyle()
.withClickEvent( new ClickEvent( ClickEvent.Action.COPY_TO_CLIPBOARD, text ) )
.withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslatableText( "gui.computercraft.tooltip.copy" ) ) ) );
return name;
}
}

View File

@ -37,7 +37,7 @@ public class ContainerComputerBase extends ScreenHandler implements IContainerCo
id,
x -> true,
getComputer(player, new ComputerContainerData(new PacketByteBuf(packetByteBuf.copy()))),
new ComputerContainerData(packetByteBuf).getFamily());
new ComputerContainerData(new PacketByteBuf(packetByteBuf.copy())).getFamily());
}
protected ContainerComputerBase(ScreenHandlerType<? extends ContainerComputerBase> type, int id, Predicate<PlayerEntity> canUse, IComputer computer,

View File

@ -29,6 +29,13 @@ public class ContainerViewComputer extends ContainerComputerBase implements ICon
this.width = this.height = 0;
}
public ContainerViewComputer(int id, PlayerInventory player, PacketByteBuf packetByteBuf) {
super(ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, packetByteBuf);
ViewComputerContainerData data = new ViewComputerContainerData(new PacketByteBuf(packetByteBuf.copy()));
this.width = data.getWidth();
this.height = data.getHeight();
}
private static boolean canInteractWith(@Nonnull ServerComputer computer, @Nonnull PlayerEntity player) {
// If this computer no longer exists then discard it.
if (ComputerCraft.serverComputerRegistry.get(computer.getInstanceID()) != computer) {
@ -39,13 +46,6 @@ public class ContainerViewComputer extends ContainerComputerBase implements ICon
return computer.getFamily() != ComputerFamily.COMMAND || TileCommandComputer.isUsable(player);
}
public ContainerViewComputer(int id, PlayerInventory player, PacketByteBuf packetByteBuf) {
super(ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, packetByteBuf);
ViewComputerContainerData data = new ViewComputerContainerData((PacketByteBuf) packetByteBuf.copy());
this.width = data.getWidth();
this.height = data.getHeight();
}
public int getWidth() {
return this.width;
}

View File

@ -37,8 +37,8 @@ public class ViewComputerContainerData extends ComputerContainerData {
}
public ViewComputerContainerData(PacketByteBuf packetByteBuf) {
super(packetByteBuf);
this.toBytes(packetByteBuf);
super(new PacketByteBuf(packetByteBuf.copy()));
this.fromBytes(packetByteBuf);
}
@Override

View File

@ -19,7 +19,7 @@ import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.ComputerCraftRegistry;
import dan200.computercraft.shared.command.CommandCopy;
import dan200.computercraft.shared.command.text.ChatHelpers;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.DirectionUtil;
@ -109,11 +109,12 @@ public class TileCable extends TileGeneric implements IPeripheralTile {
String newName = this.m_peripheral.getConnectedName();
if (!Objects.equal(newName, oldName)) {
if (oldName != null) {
player.sendMessage(new TranslatableText("chat.computercraft.wired_modem.peripheral_disconnected", CommandCopy.createCopyText(oldName)),
false);
player.sendMessage(new TranslatableText("chat.computercraft.wired_modem.peripheral_disconnected",
ChatHelpers.copy(oldName)), false);
}
if (newName != null) {
player.sendMessage(new TranslatableText("chat.computercraft.wired_modem.peripheral_connected", CommandCopy.createCopyText(newName)), false);
player.sendMessage(new TranslatableText("chat.computercraft.wired_modem.peripheral_connected",
ChatHelpers.copy(newName)), false);
}
}

View File

@ -27,7 +27,7 @@ import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.command.CommandCopy;
import dan200.computercraft.shared.command.text.ChatHelpers;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.DirectionUtil;
@ -260,7 +260,7 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile {
if (i > 0) {
base.append(", ");
}
base.append(CommandCopy.createCopyText(names.get(i)));
base.append(ChatHelpers.copy(names.get(i)));
}
player.sendMessage(new TranslatableText(kind, base), false);

View File

@ -49,6 +49,7 @@
"commands.computercraft.dump.synopsis": "Display the status of computers.",
"commands.computercraft.dump.desc": "Display the status of all computers or specific information about one computer. You can specify the computer's instance id (e.g. 123), computer id (e.g #123) or label (e.g. \"@My Computer\").",
"commands.computercraft.dump.action": "View more info about this computer",
"commands.computercraft.dump.open_path": "View this computer's files",
"commands.computercraft.shutdown.synopsis": "Shutdown computers remotely.",
"commands.computercraft.shutdown.desc": "Shutdown the listed computers or all if none are specified. You can specify the computer's instance id (e.g. 123), computer id (e.g #123) or label (e.g. \"@My Computer\").",
"commands.computercraft.shutdown.done": "Shutdown %s/%s computers",