1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-30 00:57:55 +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.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer; import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; 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.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter; import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;

View File

@@ -6,7 +6,7 @@
package dan200.computercraft.mixin; 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.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -22,7 +22,7 @@ import net.fabricmc.api.Environment;
public class MixinScreen { public class MixinScreen {
@Inject (method = "sendMessage(Ljava/lang/String;Z)V", at = @At ("HEAD"), cancellable = true) @Inject (method = "sendMessage(Ljava/lang/String;Z)V", at = @At ("HEAD"), cancellable = true)
public void sendClientCommand(String message, boolean add, CallbackInfo info) { public void sendClientCommand(String message, boolean add, CallbackInfo info) {
if (CommandCopy.onClientSendMessage(message)) { if (ClientCommands.onClientSendMessage(message)) {
info.cancel(); 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. * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.shared.command; 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.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException; 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.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.ViewComputerContainerData; 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.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.screen.NamedScreenHandlerFactory; import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandler;
@@ -69,86 +35,103 @@ import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
public final class CommandComputerCraft { 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 ); public static final UUID SYSTEM_UUID = new UUID( 0, 0 );
private static final int DUMP_LIST_ID = 5373952; private static final int DUMP_LIST_ID = 5373952;
private static final int DUMP_SINGLE_ID = 1844510720; private static final int DUMP_SINGLE_ID = 1844510720;
private static final int TRACK_ID = 373882880; 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) { public static void register( CommandDispatcher<ServerCommandSource> dispatcher, Boolean dedicated )
dispatcher.register(choice("computercraft").then(literal("dump").requires(UserLevel.OWNER_OP) {
dispatcher.register( choice( "computercraft" )
.then( literal( "dump" )
.requires( UserLevel.OWNER_OP )
.executes( context -> { .executes( context -> {
TableBuilder table = new TableBuilder(DUMP_LIST_ID, TableBuilder table = new TableBuilder( DUMP_LIST_ID, "Computer", "On", "Position" );
"Computer",
"On",
"Position");
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
List<ServerComputer> computers = List<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() );
new ArrayList<>(ComputerCraft.serverComputerRegistry.getComputers());
// Unless we're on a server, limit the number of rows we can send. // Unless we're on a server, limit the number of rows we can send.
World world = source.getWorld(); World world = source.getWorld();
BlockPos pos = new BlockPos( source.getPosition() ); BlockPos pos = new BlockPos( source.getPosition() );
computers.sort( ( a, b ) -> { computers.sort( ( a, b ) -> {
if (a.getWorld() == b.getWorld() && a.getWorld() == world) { if( a.getWorld() == b.getWorld() && a.getWorld() == world )
return Double.compare(a.getPosition() {
.getSquaredDistance(pos), return Double.compare( a.getPosition().getSquaredDistance( pos ), b.getPosition().getSquaredDistance( pos ) );
b.getPosition() }
.getSquaredDistance(pos)); else if( a.getWorld() == world )
} else if (a.getWorld() == world) { {
return -1; return -1;
} else if (b.getWorld() == world) { }
else if( b.getWorld() == world )
{
return 1; return 1;
} else { }
else
{
return Integer.compare( a.getInstanceID(), b.getInstanceID() ); return Integer.compare( a.getInstanceID(), b.getInstanceID() );
} }
} ); } );
for (ServerComputer computer : computers) { for( ServerComputer computer : computers )
table.row(linkComputer(source, computer, computer.getID()), {
table.row(
linkComputer( source, computer, computer.getID() ),
bool( computer.isOn() ), bool( computer.isOn() ),
linkPosition(source, computer)); linkPosition( source, computer )
);
} }
table.display( context.getSource() ); table.display( context.getSource() );
return computers.size(); return computers.size();
} ) } )
.then(args().arg("computer", oneComputer()) .then( args()
.arg( "computer", oneComputer() )
.executes( context -> { .executes( context -> {
ServerComputer computer = getComputerArgument( context, "computer" ); ServerComputer computer = getComputerArgument( context, "computer" );
TableBuilder table = new TableBuilder( DUMP_SINGLE_ID ); TableBuilder table = new TableBuilder( DUMP_SINGLE_ID );
table.row(header("Instance"), table.row( header( "Instance" ), text( Integer.toString( computer.getInstanceID() ) ) );
text(Integer.toString(computer.getInstanceID())));
table.row( header( "Id" ), text( Integer.toString( computer.getID() ) ) ); table.row( header( "Id" ), text( Integer.toString( computer.getID() ) ) );
table.row( header( "Label" ), text( computer.getLabel() ) ); table.row( header( "Label" ), text( computer.getLabel() ) );
table.row( header( "On" ), bool( computer.isOn() ) ); table.row( header( "On" ), bool( computer.isOn() ) );
table.row(header("Position"), table.row( header( "Position" ), linkPosition( context.getSource(), computer ) );
linkPosition(context.getSource(), computer)); table.row( header( "Family" ), text( computer.getFamily().toString() ) );
table.row(header("Family"),
text(computer.getFamily()
.toString()));
for (ComputerSide side : ComputerSide.values()) { for( ComputerSide side : ComputerSide.values() )
{
IPeripheral peripheral = computer.getPeripheral( side ); IPeripheral peripheral = computer.getPeripheral( side );
if (peripheral != null) { if( peripheral != null )
table.row(header("Peripheral " + side.getName()), {
text(peripheral.getType())); table.row( header( "Peripheral " + side.getName() ), text( peripheral.getType() ) );
} }
} }
@@ -156,83 +139,67 @@ public final class CommandComputerCraft {
return 1; return 1;
} ) ) ) } ) ) )
.then(command("shutdown").requires(UserLevel.OWNER_OP) .then( command( "shutdown" )
.argManyValue("computers", .requires( UserLevel.OWNER_OP )
manyComputers(), .argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() )
s -> ComputerCraft.serverComputerRegistry.getComputers())
.executes( ( context, computers ) -> { .executes( ( context, computers ) -> {
int shutdown = 0; int shutdown = 0;
for (ServerComputer computer : unwrap(context.getSource(), computers)) { for( ServerComputer computer : unwrap( context.getSource(), computers ) )
if (computer.isOn()) { {
shutdown++; if( computer.isOn() ) shutdown++;
}
computer.shutdown(); computer.shutdown();
} }
context.getSource() context.getSource().sendFeedback( translate( "commands.computercraft.shutdown.done", shutdown, computers.size() ), false );
.sendFeedback(translate("commands.computercraft.shutdown.done",
shutdown,
computers.size()), false);
return shutdown; return shutdown;
} ) ) } ) )
.then(command("turn-on").requires(UserLevel.OWNER_OP) .then( command( "turn-on" )
.argManyValue("computers", .requires( UserLevel.OWNER_OP )
manyComputers(), .argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() )
s -> ComputerCraft.serverComputerRegistry.getComputers())
.executes( ( context, computers ) -> { .executes( ( context, computers ) -> {
int on = 0; int on = 0;
for (ServerComputer computer : unwrap(context.getSource(), computers)) { for( ServerComputer computer : unwrap( context.getSource(), computers ) )
if (!computer.isOn()) { {
on++; if( !computer.isOn() ) on++;
}
computer.turnOn(); computer.turnOn();
} }
context.getSource() context.getSource().sendFeedback( translate( "commands.computercraft.turn_on.done", on, computers.size() ), false );
.sendFeedback(translate("commands.computercraft.turn_on.done",
on,
computers.size()), false);
return on; return on;
} ) ) } ) )
.then(command("tp").requires(UserLevel.OP) .then( command( "tp" )
.requires( UserLevel.OP )
.arg( "computer", oneComputer() ) .arg( "computer", oneComputer() )
.executes( context -> { .executes( context -> {
ServerComputer computer = getComputerArgument( context, "computer" ); ServerComputer computer = getComputerArgument( context, "computer" );
World world = computer.getWorld(); World world = computer.getWorld();
BlockPos pos = computer.getPosition(); BlockPos pos = computer.getPosition();
if (world == null || pos == null) { if( world == null || pos == null ) throw TP_NOT_THERE.create();
throw TP_NOT_THERE.create();
}
Entity entity = context.getSource() Entity entity = context.getSource().getEntityOrThrow();
.getEntityOrThrow(); if( !(entity instanceof ServerPlayerEntity) ) throw TP_NOT_PLAYER.create();
if (!(entity instanceof ServerPlayerEntity)) {
throw TP_NOT_PLAYER.create();
}
ServerPlayerEntity player = (ServerPlayerEntity) entity; ServerPlayerEntity player = (ServerPlayerEntity) entity;
if (player.getEntityWorld() == world) { if( player.getEntityWorld() == world )
player.networkHandler.teleportRequest(pos.getX() + 0.5, {
pos.getY(), player.networkHandler.teleportRequest(
pos.getZ() + 0.5, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0,
0, EnumSet.noneOf( PlayerPositionLookS2CPacket.Flag.class )
0, );
EnumSet.noneOf( }
PlayerPositionLookS2CPacket.Flag.class)); else
} else { {
player.teleport( (ServerWorld) world, player.teleport( (ServerWorld) world,
pos.getX() + 0.5, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0
pos.getY(), );
pos.getZ() + 0.5,
0,
0);
} }
return 1; return 1;
} ) ) } ) )
.then(command("queue").requires(UserLevel.ANYONE) .then( command( "queue" )
.requires( UserLevel.ANYONE )
.arg( "computer", manyComputers() ) .arg( "computer", manyComputers() )
.argManyValue( "args", StringArgumentType.string(), Collections.emptyList() ) .argManyValue( "args", StringArgumentType.string(), Collections.emptyList() )
.executes( ( ctx, args ) -> { .executes( ( ctx, args ) -> {
@@ -240,8 +207,10 @@ public final class CommandComputerCraft {
Object[] rest = args.toArray(); Object[] rest = args.toArray();
int queued = 0; int queued = 0;
for (ServerComputer computer : computers) { for( ServerComputer computer : computers )
if (computer.getFamily() == ComputerFamily.COMMAND && computer.isOn()) { {
if( computer.getFamily() == ComputerFamily.COMMAND && computer.isOn() )
{
computer.queueEvent( "computer_command", rest ); computer.queueEvent( "computer_command", rest );
queued++; queued++;
} }
@@ -250,164 +219,204 @@ public final class CommandComputerCraft {
return queued; return queued;
} ) ) } ) )
.then(command("view").requires(UserLevel.OP) .then( command( "view" )
.requires( UserLevel.OP )
.arg( "computer", oneComputer() ) .arg( "computer", oneComputer() )
.executes( context -> { .executes( context -> {
ServerPlayerEntity player = context.getSource() ServerPlayerEntity player = context.getSource().getPlayer();
.getPlayer();
ServerComputer computer = getComputerArgument( context, "computer" ); ServerComputer computer = getComputerArgument( context, "computer" );
new ViewComputerContainerData(computer).open(player, ViewComputerContainerData container = new ViewComputerContainerData( computer );
new NamedScreenHandlerFactory() { container.open( player, new ExtendedScreenHandlerFactory()
@Nonnull {
@Override @Override
public Text getDisplayName() { public void writeScreenOpeningData( ServerPlayerEntity player, PacketByteBuf buf )
return new TranslatableText( {
"gui.computercraft.view_computer"); container.toBytes( buf );
} }
@Nonnull @Nonnull
@Override @Override
public ScreenHandler createMenu(int id, @Nonnull PlayerInventory player, @Nonnull PlayerEntity entity) { public MutableText getDisplayName()
return new ContainerViewComputer( {
id, return new TranslatableText( "gui.computercraft.view_computer" );
computer); }
@Nonnull
@Override
public ScreenHandler createMenu( int id, @Nonnull PlayerInventory player, @Nonnull PlayerEntity entity )
{
return new ContainerViewComputer( id, computer );
} }
} ); } );
return 1; return 1;
} ) ) } ) )
.then(choice("track").then(command("start").requires(UserLevel.OWNER_OP) .then( choice( "track" )
.then( command( "start" )
.requires( UserLevel.OWNER_OP )
.executes( context -> { .executes( context -> {
getTimingContext( context.getSource() ).start(); getTimingContext( context.getSource() ).start();
String stopCommand = "/computercraft track stop"; String stopCommand = "/computercraft track stop";
context.getSource() context.getSource().sendFeedback( translate( "commands.computercraft.track.start.stop",
.sendFeedback(translate( link( text( stopCommand ), stopCommand, translate( "commands.computercraft.track.stop.action" ) ) ), false );
"commands.computercraft.track.start.stop",
link(text(stopCommand),
stopCommand,
translate(
"commands.computercraft.track.stop.action"))),
false);
return 1; return 1;
} ) ) } ) )
.then(command("stop").requires(UserLevel.OWNER_OP) .then( command( "stop" )
.requires( UserLevel.OWNER_OP )
.executes( context -> { .executes( context -> {
TrackingContext timings = getTimingContext( context.getSource() ); TrackingContext timings = getTimingContext( context.getSource() );
if (!timings.stop()) { if( !timings.stop() ) throw NOT_TRACKING_EXCEPTION.create();
throw NOT_TRACKING_EXCEPTION.create(); displayTimings( context.getSource(), timings.getImmutableTimings(), TrackingField.AVERAGE_TIME, DEFAULT_FIELDS );
}
displayTimings(context.getSource(),
timings.getImmutableTimings(),
TrackingField.AVERAGE_TIME,
DEFAULT_FIELDS);
return 1; return 1;
} ) ) } ) )
.then(command("dump").requires(UserLevel.OWNER_OP) .then( command( "dump" )
.requires( UserLevel.OWNER_OP )
.argManyValue( "fields", trackingField(), DEFAULT_FIELDS ) .argManyValue( "fields", trackingField(), DEFAULT_FIELDS )
.executes( ( context, fields ) -> { .executes( ( context, fields ) -> {
TrackingField sort; TrackingField sort;
if (fields.size() == 1 && DEFAULT_FIELDS.contains(fields.get( if( fields.size() == 1 && DEFAULT_FIELDS.contains( fields.get( 0 ) ) )
0))) { {
sort = fields.get( 0 ); sort = fields.get( 0 );
fields = DEFAULT_FIELDS; fields = DEFAULT_FIELDS;
} else { }
else
{
sort = fields.get( 0 ); sort = fields.get( 0 );
} }
return displayTimings( context.getSource(), sort, fields ); return displayTimings( context.getSource(), sort, fields );
})))); } ) ) )
);
} }
private static Text linkComputer(ServerCommandSource source, ServerComputer serverComputer, int computerId) { private static MutableText linkComputer( ServerCommandSource source, ServerComputer serverComputer, int computerId )
{
MutableText out = new LiteralText( "" ); MutableText out = new LiteralText( "" );
// Append the computer instance // Append the computer instance
if (serverComputer == null) { if( serverComputer == null )
{
out.append( text( "?" ) ); out.append( text( "?" ) );
} else { }
out.append(link(text(Integer.toString(serverComputer.getInstanceID())), else
{
out.append( link(
text( Integer.toString( serverComputer.getInstanceID() ) ),
"/computercraft dump " + serverComputer.getInstanceID(), "/computercraft dump " + serverComputer.getInstanceID(),
translate("commands.computercraft.dump.action"))); translate( "commands.computercraft.dump.action" )
) );
} }
// And ID // And ID
out.append( " (id " + computerId + ")" ); out.append( " (id " + computerId + ")" );
// And, if we're a player, some useful links // And, if we're a player, some useful links
if (serverComputer != null && UserLevel.OP.test(source) && isPlayer(source)) { if( serverComputer != null && UserLevel.OP.test( source ) && isPlayer( source ) )
out.append(" ") {
.append(link(text("\u261b"), "/computercraft tp " + serverComputer.getInstanceID(), translate("commands.computercraft.tp.action"))) out
.append( " " ) .append( " " )
.append(link(text("\u20e2"), "/computercraft view " + serverComputer.getInstanceID(), translate("commands.computercraft.view.action"))); .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; return out;
} }
private static Text linkPosition(ServerCommandSource context, ServerComputer computer) { 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")); if( UserLevel.OP.test( context ) )
} else { {
return link(
position( computer.getPosition() ),
"/computercraft tp " + computer.getInstanceID(),
translate( "commands.computercraft.tp.action" )
);
}
else
{
return position( computer.getPosition() ); 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 @Nonnull
private static TrackingContext getTimingContext(ServerCommandSource source) { private static TrackingContext getTimingContext( ServerCommandSource source )
{
Entity entity = source.getEntity(); 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 { private static final List<TrackingField> DEFAULT_FIELDS = Arrays.asList( TrackingField.TASKS, TrackingField.TOTAL_TIME, TrackingField.AVERAGE_TIME, TrackingField.MAX_TIME );
if (timings.isEmpty()) {
throw NO_TIMINGS_EXCEPTION.create(); 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<>(); Map<Computer, ServerComputer> lookup = new HashMap<>();
int maxId = 0, maxInstance = 0; int maxId = 0, maxInstance = 0;
for (ServerComputer server : ComputerCraft.serverComputerRegistry.getComputers()) { for( ServerComputer server : ComputerCraft.serverComputerRegistry.getComputers() )
{
lookup.put( server.getComputer(), server ); lookup.put( server.getComputer(), server );
if (server.getInstanceID() > maxInstance) { if( server.getInstanceID() > maxInstance ) maxInstance = server.getInstanceID();
maxInstance = server.getInstanceID(); if( server.getID() > maxId ) maxId = server.getID();
}
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()]; MutableText[] headers = new MutableText[1 + fields.size()];
headers[0] = translate( "commands.computercraft.track.dump.computer" ); headers[0] = translate( "commands.computercraft.track.dump.computer" );
for (int i = 0; i < fields.size(); i++) { for( int i = 0; i < fields.size(); i++ ) headers[i + 1] = translate( fields.get( i ).translationKey() );
headers[i + 1] = translate(fields.get(i)
.translationKey());
}
TableBuilder table = new TableBuilder( TRACK_ID, headers ); TableBuilder table = new TableBuilder( TRACK_ID, headers );
for (ComputerTracker entry : timings) { for( ComputerTracker entry : timings )
{
Computer computer = entry.getComputer(); 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; row[0] = computerComponent;
for (int i = 0; i < fields.size(); i++) { for( int i = 0; i < fields.size(); i++ ) row[i + 1] = text( entry.getFormatted( fields.get( i ) ) );
row[i + 1] = text(entry.getFormatted(fields.get(i)));
}
table.row( row ); table.row( row );
} }
table.display( source ); table.display( source );
return timings.size(); 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. * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.shared.command; package dan200.computercraft.shared.command;
import java.util.function.Predicate;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import java.util.function.Predicate;
/** /**
* The level a user must be at in order to execute a command. * 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. * Only can be used by the owner of the server: namely the server console or the player in SSP.
*/ */
@@ -37,29 +37,10 @@ public enum UserLevel implements Predicate<ServerCommandSource> {
*/ */
ANYONE; ANYONE;
@Override public int toLevel()
public boolean test(ServerCommandSource source) { {
if (this == ANYONE) { switch( this )
return true; {
}
// We *always* allow level 0 stuff, even if the
MinecraftServer server = source.getMinecraftServer();
Entity sender = source.getEntity();
if (server.isSinglePlayer() && sender instanceof PlayerEntity && ((PlayerEntity) sender).getGameProfile()
.getName()
.equalsIgnoreCase(server.getServerModName())) {
if (this == OWNER || this == OWNER_OP) {
return true;
}
}
return source.hasPermissionLevel(this.toLevel());
}
public int toLevel() {
switch (this) {
case OWNER: case OWNER:
return 4; return 4;
case OP: case OP:
@@ -70,4 +51,23 @@ public enum UserLevel implements Predicate<ServerCommandSource> {
return 0; return 0;
} }
} }
@Override
public boolean test( ServerCommandSource source )
{
if( this == ANYONE ) return true;
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( toLevel() );
}
} }

View File

@@ -3,81 +3,101 @@
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.shared.command.text; package dan200.computercraft.shared.command.text;
import net.minecraft.text.ClickEvent; import net.minecraft.text.*;
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.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
/** /**
* Various helpers for building chat messages. * Various helpers for building chat messages.
*/ */
public final class ChatHelpers { public final class ChatHelpers
{
private static final Formatting HEADER = Formatting.LIGHT_PURPLE; private static final Formatting HEADER = Formatting.LIGHT_PURPLE;
private ChatHelpers() {} private ChatHelpers() {}
public static MutableText text(String 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 <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 ); return new LiteralText( text == null ? "" : text );
} }
public static MutableText list(Text... children) { 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( "" ); MutableText component = new LiteralText( "" );
for (Text child : children) { for( MutableText child : children )
{
component.append( child ); component.append( child );
} }
return component; return component;
} }
public static MutableText position(BlockPos pos) { public static MutableText position( BlockPos pos )
if (pos == null) { {
return translate("commands.computercraft.generic.no_position"); if( pos == null ) return translate( "commands.computercraft.generic.no_position" );
}
return translate( "commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ() ); return translate( "commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ() );
} }
public static MutableText translate(String text) { public static MutableText bool( boolean value )
return new TranslatableText(text == null ? "" : text); {
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) { public static MutableText link( MutableText component, String command, MutableText toolTip )
return new TranslatableText(text == null ? "" : text, args); {
return link( component, new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ), toolTip );
} }
public static MutableText bool(boolean value) { public static MutableText link( MutableText component, ClickEvent click, MutableText toolTip )
return value ? coloured(translate("commands.computercraft.generic.yes"), {
Formatting.GREEN) : coloured(translate("commands.computercraft.generic.no"), Formatting.RED); Style style = component.getStyle();
}
if( style.getColor() == null ) style = style.withColor( Formatting.YELLOW );
style = style.withClickEvent( click );
style = style.withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) );
component.setStyle(style);
public static <T extends MutableText> T coloured(T component, Formatting colour) {
component.formatted(colour);
return component; return component;
} }
public static MutableText link(MutableText component, String command, Text toolTip) { public static MutableText header( String text )
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));
return component.setStyle(style);
}
public static MutableText header(String text) {
return coloured( text, HEADER ); return coloured( text, HEADER );
} }
public static MutableText coloured(String text, Formatting colour) { public static MutableText copy( String text )
return new LiteralText(text == null ? "" : text).formatted(colour); {
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, id,
x -> true, x -> true,
getComputer(player, new ComputerContainerData(new PacketByteBuf(packetByteBuf.copy()))), 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, 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; 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) { private static boolean canInteractWith(@Nonnull ServerComputer computer, @Nonnull PlayerEntity player) {
// If this computer no longer exists then discard it. // If this computer no longer exists then discard it.
if (ComputerCraft.serverComputerRegistry.get(computer.getInstanceID()) != computer) { 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); 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() { public int getWidth() {
return this.width; return this.width;
} }

View File

@@ -37,8 +37,8 @@ public class ViewComputerContainerData extends ComputerContainerData {
} }
public ViewComputerContainerData(PacketByteBuf packetByteBuf) { public ViewComputerContainerData(PacketByteBuf packetByteBuf) {
super(packetByteBuf); super(new PacketByteBuf(packetByteBuf.copy()));
this.toBytes(packetByteBuf); this.fromBytes(packetByteBuf);
} }
@Override @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.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile; import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.ComputerCraftRegistry; 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.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
@@ -109,11 +109,12 @@ public class TileCable extends TileGeneric implements IPeripheralTile {
String newName = this.m_peripheral.getConnectedName(); String newName = this.m_peripheral.getConnectedName();
if (!Objects.equal(newName, oldName)) { if (!Objects.equal(newName, oldName)) {
if (oldName != null) { if (oldName != null) {
player.sendMessage(new TranslatableText("chat.computercraft.wired_modem.peripheral_disconnected", CommandCopy.createCopyText(oldName)), player.sendMessage(new TranslatableText("chat.computercraft.wired_modem.peripheral_disconnected",
false); ChatHelpers.copy(oldName)), false);
} }
if (newName != null) { 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.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralTile; 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.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
@@ -260,7 +260,7 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile {
if (i > 0) { if (i > 0) {
base.append(", "); base.append(", ");
} }
base.append(CommandCopy.createCopyText(names.get(i))); base.append(ChatHelpers.copy(names.get(i)));
} }
player.sendMessage(new TranslatableText(kind, base), false); 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.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.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.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.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.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", "commands.computercraft.shutdown.done": "Shutdown %s/%s computers",