1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-16 14:37:39 +00:00

Compare commits

...

22 Commits

Author SHA1 Message Date
SquidDev
abd06133fb More binary compatibility stubs for ILuaAPI 2017-12-07 09:24:44 +00:00
SquidDev
29a3a0c48f Bump version 2017-12-07 09:08:15 +00:00
SquidDev
2728c63512 Add back ILuaAPI
This ensures binary compatibility with all those evil mods (Plethora)
which CC internals.
2017-12-07 09:06:47 +00:00
SquidDev
f3b11bc1c2 Copy over CCTweaks's command system
This adds several commands which may be useful for server owners. It'd
be nice to integrate this into ComputerCraft itself, but the associated
command framework is quite large so we'd have to think about it.
2017-12-06 15:51:51 +00:00
SquidDev
04590befb3 Add support for viewing arbitrary computers in a GUI
Important terminal (such as terminal size) is packed into the the
coordinate fields.
2017-12-06 15:43:25 +00:00
SquidDev
4e9034f910 Make http.websocket call synchronous in bios.lua
I thought I'd done this already, but it's possible it got lost during a
rebase.
2017-12-06 09:28:38 +00:00
SquidDev
ba9cfa3764 Bump version
Also add CurseGradle support
2017-12-01 20:05:26 +00:00
SquidDev
341e3e2f89 Merge pull request #497 from SquidDev-CC/ComputerCraft/feature/pocket-map
Add map-like rendering for pocket computers
2017-12-01 19:41:07 +00:00
SquidDev
3f70ca5192 Merge pull request #492 from SquidDev-CC/ComputerCraft/feature/fun-turtle-rendering
Improve vertex transformation system
2017-12-01 19:39:33 +00:00
SquidDev
f11bfc53ee Use IComputer instead of ServerComputer on the client 2017-12-01 19:32:15 +00:00
SquidDev
61e3967b8e Merge pull request #494 from SquidDev-CC/ComputerCraft/hotfix/collision-aabb
Fix getCollisionBoundingBox not using all AABBs
2017-12-01 19:19:17 +00:00
SquidDev
add86ea100 Merge pull request #491 from SquidDev-CC/ComputerCraft/feature/api-api
Provide an API for registering custom APIs
2017-12-01 19:18:40 +00:00
SquidDev
dd51c89278 Add map-like rendering for pocket computers 2017-12-01 11:28:15 +00:00
SquidDev
788d783745 Fix getCollisionBoundingBox not using all AABBs
Closes #493
2017-11-22 10:52:28 +00:00
SquidDev
35da60543e Improve turtles by 200%
Every other mod has some fun feature, so should we. And yes, this was
worth the 400 lines it took to implement.
2017-11-21 00:34:35 +00:00
SquidDev
ce7923d248 Improve vertex transformation system
This migrates TurtleMultiModel's current vertex transformation system
into something more powerful and "correct". Namely, it has the following
improvements:

 - Handles all position formats (float, byte, etc...)
 - Correctly translates normals of quads
 - Reorders faces if the winding order is reversed
2017-11-21 00:18:03 +00:00
SquidDev
55847460c5 Provide an API for registering custom APIs
ILuaAPI has been moved to dan200.computercraft.api.lua. One creates
a new API by registering an instance of ILuaAPIFactory. This takes an
instance of IComputerSystem and returns such an API.

IComputerSystem is an extension of IComputerAccess, with methods to
access additional information about the the computer, such as its label
and filesystem.
2017-11-19 18:23:38 +00:00
SquidDev
893524b0a8 Mark computers as changed when changing on state
Previously they were not marked as such, meaning computer state was not
broadcast to the client until blinking state changed.
2017-11-19 15:23:12 +00:00
SquidDev
8fb3ae405f Ensure we don't strip any whitespace 2017-11-19 15:04:21 +00:00
SquidDev
aa447ec101 Fix term.getTextScale() not using the main monitor 2017-11-19 14:03:48 +00:00
SquidDev
56b1cb4521 Fixup README a little 2017-11-19 13:48:17 +00:00
SquidDev
90cc24614c Add a subjectively fancy logo 2017-11-15 18:20:22 +00:00
56 changed files with 2939 additions and 402 deletions

View File

@@ -5,7 +5,9 @@ indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
# Sadly too many files have whitespace errors, so we leave this as is for
# now and just make sure we don't introduce any more.
# trim_trailing_whitespace = true
insert_final_newline = true
[*.md]

View File

@@ -1,8 +1,8 @@
# CC: Tweaked
# ![CC: Tweaked](logo.png)
[![Build Status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked)
CC: Tweaked is a fork of ComputerCraft which aims to provide more earlier access to the more experimental and
in-development features of the mod. For a more stable experience, I recommend checking out the
CC: Tweaked is a fork of ComputerCraft which aims to provide earlier access to the more experimental and in-development
features of the mod. For a more stable experience, I recommend checking out the
[original mod](https://github.com/dan200/ComputerCraft).
## What?

View File

@@ -13,17 +13,16 @@ buildscript {
classpath 'org.ajoberstar:gradle-git:1.6.0'
}
}
plugins {
id 'com.matthewprenger.cursegradle' version '1.0.9'
}
apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'org.ajoberstar.grgit'
/*
// for people who want stable - not yet functional for MC 1.8.8 - we require the forgegradle 2.1 snapshot
plugins {
id "net.minecraftforge.gradle.forge" version "2.0.2"
}
*/
version = "1.80pr1.1"
version = "1.80pr1.3"
group = "org.squiddev"
archivesBaseName = "cc-tweaked"
@@ -115,6 +114,16 @@ processResources {
}
}
curseforge {
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
project {
id = '282001'
releaseType = 'beta'
changelog = ''
}
}
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint"

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -9,6 +9,7 @@ package dan200.computercraft;
import com.google.common.base.CaseFormat;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.media.IMediaProvider;
import dan200.computercraft.api.network.IPacketNetwork;
@@ -22,12 +23,16 @@ import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.filesystem.ComboMount;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.JarMount;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.command.CommandComputer;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
@@ -82,10 +87,7 @@ import java.io.*;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -110,6 +112,7 @@ public class ComputerCraft
// ComputerCraftEdu uses ID 104
public static final int printoutGUIID = 105;
public static final int pocketComputerGUIID = 106;
public static final int viewComputerGUIID = 110;
// Configuration options
private static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
@@ -251,6 +254,7 @@ public class ComputerCraft
private static List<IMediaProvider> mediaProviders = new ArrayList<>();
private static List<ITurtlePermissionProvider> permissionProviders = new ArrayList<>();
private static final Map<String, IPocketUpgrade> pocketUpgrades = new HashMap<>();
private static final Set<ILuaAPIFactory> apiFactories = new LinkedHashSet<>();
// Implementation
@Mod.Instance( value = ComputerCraft.MOD_ID )
@@ -424,6 +428,7 @@ public class ComputerCraft
public void onServerStarting( FMLServerStartingEvent event )
{
event.registerServerCommand( new CommandComputer() );
event.registerServerCommand( new CommandComputerCraft() );
}
@Mod.EventHandler
@@ -520,6 +525,24 @@ public class ComputerCraft
player.openGui( ComputerCraft.instance, ComputerCraft.pocketComputerGUIID, player.getEntityWorld(), hand.ordinal(), 0, 0 );
}
public static void openComputerGUI( EntityPlayer player, ServerComputer computer )
{
ComputerFamily family = computer.getFamily();
int width = 0, height = 0;
Terminal terminal = computer.getTerminal();
if( terminal != null )
{
width = terminal.getWidth();
height = terminal.getHeight();
}
// Pack useful terminal information into the various coordinate bits.
// These are extracted in ComputerCraftProxyCommon.getClientGuiElement
player.openGui( ComputerCraft.instance, ComputerCraft.viewComputerGUIID, player.getEntityWorld(),
computer.getInstanceID(), family.ordinal(), (width & 0xFFFF) << 16 | (height & 0xFFFF)
);
}
public static File getBaseDir()
{
return FMLCommonHandler.instance().getMinecraftServerInstance().getFile(".");
@@ -673,6 +696,14 @@ public class ComputerCraft
}
}
public static void registerAPIFactory( ILuaAPIFactory provider )
{
if( provider != null )
{
apiFactories.add( provider );
}
}
public static IPeripheral getPeripheralAt( World world, BlockPos pos, EnumFacing side )
{
// Try the handlers in order:
@@ -800,6 +831,11 @@ public class ComputerCraft
return WirelessNetwork.getUniversal();
}
public static Iterable<ILuaAPIFactory> getAPIFactories()
{
return apiFactories;
}
public static int createUniqueNumberedSaveDir( World world, String parentSubPath )
{
return IDAssigner.getNextIDFromDirectory(new File(getWorldDir(world), parentSubPath));

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.api;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.media.IMediaProvider;
import dan200.computercraft.api.network.IPacketNetwork;
@@ -311,6 +312,22 @@ public final class ComputerCraftAPI
return null;
}
public static void registerAPIFactory( @Nonnull ILuaAPIFactory upgrade )
{
findCC();
if( computerCraft_registerAPIFactory != null )
{
try
{
computerCraft_registerAPIFactory.invoke( null, upgrade );
}
catch( Exception e )
{
// It failed
}
}
}
// The functions below here are private, and are used to interface with the non-API ComputerCraft classes.
// Reflection is used here so you can develop your mod without decompiling ComputerCraft and including
// it in your solution, and so your mod won't crash if ComputerCraft is installed.
@@ -354,6 +371,9 @@ public final class ComputerCraftAPI
} );
computerCraft_getWirelessNetwork = findCCMethod( "getWirelessNetwork", new Class<?>[] {
} );
computerCraft_registerAPIFactory = findCCMethod( "registerAPIFactory", new Class<?>[] {
ILuaAPIFactory.class
} );
} catch( Exception e ) {
System.out.println( "ComputerCraftAPI: ComputerCraft not found." );
} finally {
@@ -390,4 +410,5 @@ public final class ComputerCraftAPI
private static Method computerCraft_registerPermissionProvider = null;
private static Method computerCraft_registerPocketUpgrade = null;
private static Method computerCraft_getWirelessNetwork = null;
private static Method computerCraft_registerAPIFactory = null;
}

View File

@@ -0,0 +1,38 @@
package dan200.computercraft.api.filesystem;
import java.io.IOException;
/**
* Provides a mount of the entire computer's file system.
*
* This exists for use by various APIs - one should not attempt to mount it.
*/
public interface IFileSystem extends IWritableMount
{
/**
* Combine two paths together, reducing them into a normalised form.
*
* @param path The main path.
* @param child The path to append.
* @return The combined, normalised path.
*/
String combine( String path, String child );
/**
* Copy files from one location to another.
*
* @param from The location to copy from.
* @param to The location to copy to. This should not exist.
* @throws IOException If the copy failed.
*/
void copy( String from, String to ) throws IOException;
/**
* Move files from one location to another.
*
* @param from The location to move from.
* @param to The location to move to. This should not exist.
* @throws IOException If the move failed.
*/
void move( String from, String to ) throws IOException;
}

View File

@@ -0,0 +1,29 @@
package dan200.computercraft.api.lua;
import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.peripheral.IComputerAccess;
import javax.annotation.Nullable;
/**
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
* about a computer.
*/
public interface IComputerSystem extends IComputerAccess
{
/**
* Get the file system for this computer.
*
* @return The computer's file system, or {@code null} if it is not initialised.
*/
@Nullable
IFileSystem getFileSystem();
/**
* Get the label for this computer
*
* @return This computer's label, or {@code null} if it is not set.
*/
@Nullable
String getLabel();
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2016. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.api.lua;
import dan200.computercraft.api.ComputerCraftAPI;
/**
* Represents a {@link ILuaObject} which is stored as a global variable on computer startup.
*
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
* to use peripherals to provide functionality to users.
*
* @see ILuaAPIFactory
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/
public interface ILuaAPI extends ILuaObject
{
/**
* Get the globals this API will be assigned to. This will override any other global, so you should
*
* @return A list of globals this API will be assigned to.
*/
String[] getNames();
/**
* Called when the computer is turned on.
*
* One should only interact with the file system.
*/
default void startup() { }
/**
* Called every time the computer is ticked. This can be used to process various.
*/
default void update() { }
/**
* Called when the computer is turned off or unloaded.
*
* This should reset the state of the object, disposing any remaining file handles, or other resources.
*/
default void shutdown() { }
}

View File

@@ -0,0 +1,24 @@
package dan200.computercraft.api.lua;
import dan200.computercraft.api.ComputerCraftAPI;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Construct an {@link ILuaAPI} for a specific computer.
*
* @see ILuaAPI
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/
public interface ILuaAPIFactory
{
/**
* Create a new API instance for a given computer.
*
* @param computer The computer this API is for.
* @return The created API, or {@code null} if one should not be injected.
*/
@Nullable
ILuaAPI create( @Nonnull IComputerSystem computer );
}

View File

@@ -41,7 +41,8 @@ public interface IPacketNetwork
* to all receivers within range (or any interdimensional ones).
*
* @param packet The packet to send.
* @see #transmitInterdimensional(Packet)
* @param range The maximum distance this packet will be sent.
* @see #transmitInterdimensional(Packet)
* @see IPacketReceiver#receiveSameDimension(Packet, double)
*/
void transmitSameDimension( @Nonnull Packet packet, double range );
@@ -51,7 +52,7 @@ public interface IPacketNetwork
* to all receivers across all dimensions.
*
* @param packet The packet to send.
* @see #transmitSameDimension(Packet, double)
* @see #transmitSameDimension(Packet, double)
* @see IPacketReceiver#receiveDifferentDimension(Packet)
*/
void transmitInterdimensional( @Nonnull Packet packet );

View File

@@ -33,7 +33,7 @@ public class GuiComputer extends GuiContainer
private final int m_termHeight;
private WidgetTerminal m_terminal;
protected GuiComputer( Container container, ComputerFamily family, IComputer computer, int termWidth, int termHeight )
public GuiComputer( Container container, ComputerFamily family, IComputer computer, int termWidth, int termHeight )
{
super( container );
m_family = family;

View File

@@ -8,13 +8,16 @@ package dan200.computercraft.client.proxy;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.ItemPocketRenderer;
import dan200.computercraft.client.render.RenderOverlayCable;
import dan200.computercraft.client.render.TileEntityCableRenderer;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.shared.command.ContainerViewComputer;
import dan200.computercraft.shared.computer.blocks.ComputerState;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.items.ItemComputer;
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
@@ -363,6 +366,13 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
return null;
}
@Override
public Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family )
{
ContainerViewComputer container = new ContainerViewComputer( computer );
return new GuiComputer( container, family, computer, width, height );
}
@Override
public File getWorldDir( World world )
{
@@ -454,6 +464,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
ForgeHandlers handlers = new ForgeHandlers();
MinecraftForge.EVENT_BUS.register( handlers );
MinecraftForge.EVENT_BUS.register( new RenderOverlayCable() );
MinecraftForge.EVENT_BUS.register( new ItemPocketRenderer() );
}
public class ForgeHandlers

View File

@@ -0,0 +1,264 @@
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.RenderItem;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
/**
* Emulates map rendering for pocket computers
*/
@SideOnly(Side.CLIENT)
public class ItemPocketRenderer
{
@SubscribeEvent
public void renderItem( RenderSpecificHandEvent event )
{
ItemStack stack = event.getItemStack();
if( !(stack.getItem() instanceof ItemPocketComputer) ) return;
event.setCanceled( true );
EntityPlayer player = Minecraft.getMinecraft().player;
GlStateManager.pushMatrix();
if( event.getHand() == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
{
renderItemFirstCentre(
event.getInterpolatedPitch(),
event.getEquipProgress(),
event.getSwingProgress(),
stack
);
}
else
{
renderItemFirstPersonSide(
event.getHand() == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
event.getEquipProgress(),
event.getSwingProgress(),
stack
);
}
GlStateManager.popMatrix();
}
/**
* The main rendering method for pocket computers and their associated terminal
*
* @param stack The stack to render
* @see ItemRenderer#renderMapFirstPerson(ItemStack)
*/
private void renderPocketComputerItem( ItemStack stack )
{
// Setup various transformations. Note that these are partially adapated from the corresponding method
// in ItemRenderer
GlStateManager.disableLighting();
GlStateManager.rotate( 180f, 0f, 1f, 0f );
GlStateManager.rotate( 180f, 0f, 0f, 1f );
GlStateManager.scale( 0.5, 0.5, 0.5 );
ItemPocketComputer pocketComputer = ComputerCraft.Items.pocketComputer;
ClientComputer computer = pocketComputer.createClientComputer( stack );
{
// First render the background item. We use the item's model rather than a direct texture as this ensures
// we display the pocket light and other such decorations.
GlStateManager.pushMatrix();
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
Minecraft minecraft = Minecraft.getMinecraft();
TextureManager textureManager = minecraft.getTextureManager();
RenderItem renderItem = minecraft.getRenderItem();
// Copy of RenderItem#renderItemModelIntoGUI but without the translation or scaling
textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
textureManager.getTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ).setBlurMipmap( false, false );
GlStateManager.enableRescaleNormal();
GlStateManager.enableAlpha();
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
GlStateManager.enableBlend();
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
GlStateManager.color( 1.0F, 1.0F, 1.0F, 1.0F );
IBakedModel bakedmodel = renderItem.getItemModelWithOverrides( stack, null, null );
bakedmodel = ForgeHooksClient.handleCameraTransforms( bakedmodel, ItemCameraTransforms.TransformType.GUI, false );
renderItem.renderItem( stack, bakedmodel );
GlStateManager.disableAlpha();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
}
// If we've a computer and terminal then attempt to render it.
if( computer != null )
{
Terminal terminal = computer.getTerminal();
if( terminal != null )
{
synchronized( terminal )
{
GlStateManager.pushMatrix();
GlStateManager.disableDepth();
// Reset the position to be at the top left corner of the pocket computer
// Note we translate towards the screen slightly too.
GlStateManager.translate( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 );
// Translate to the top left of the screen.
GlStateManager.translate( 4 / 16.0, 3 / 16.0, 0 );
// Work out the scaling required to resize the terminal in order to fit on the computer
final int margin = 2;
int tw = terminal.getWidth();
int th = terminal.getHeight();
int width = tw * FONT_WIDTH + margin * 2;
int height = th * FONT_HEIGHT + margin * 2;
int max = Math.max( height, width );
// The grid is 8 * 8 wide, so we start with a base of 1/2 (8 / 16).
double scale = 1.0 / 2.0 / max;
GlStateManager.scale( scale, scale, scale );
// The margin/start positions are determined in order for the terminal to be centred.
int startX = (max - width) / 2 + margin;
int startY = (max - height) / 2 + margin;
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
boolean greyscale = !computer.isColour();
Palette palette = terminal.getPalette();
// Render the actual text
for( int line = 0; line < th; ++line )
{
TextBuffer text = terminal.getLine( line );
TextBuffer colour = terminal.getTextColourLine( line );
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
fontRenderer.drawString(
text, startX, startY + line * FONT_HEIGHT,
colour, backgroundColour, margin, margin, greyscale, palette
);
}
// And render the cursor;
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
if( terminal.getCursorBlink() && ComputerCraft.getGlobalCursorBlink() &&
tx >= 0 && ty >= 0 && tx < tw && ty < th )
{
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
cursorColour, null, 0, 0, greyscale, palette
);
}
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
}
GlStateManager.enableLighting();
}
/**
* Renders a pocket computer to one side of the player.
*
* @param side The side to render on
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see ItemRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack)
*/
private void renderItemFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack )
{
Minecraft minecraft = Minecraft.getMinecraft();
float offset = side == EnumHandSide.RIGHT ? 1f : -1f;
GlStateManager.translate( offset * 0.125f, -0.125f, 0f );
// If the player is not invisible then render a single arm
if( !minecraft.player.isInvisible() )
{
GlStateManager.pushMatrix();
GlStateManager.rotate( offset * 10f, 0f, 0f, 1f );
minecraft.getItemRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
GlStateManager.popMatrix();
}
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
GlStateManager.pushMatrix();
GlStateManager.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
float f1 = MathHelper.sqrt( swingProgress );
float f2 = MathHelper.sin( f1 * (float) Math.PI );
float f3 = -0.5f * f2;
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
GlStateManager.translate( offset * f3, f4 - 0.3f * f2, f5 );
GlStateManager.rotate( f2 * -45f, 1f, 0f, 0f );
GlStateManager.rotate( offset * f2 * -30f, 0f, 1f, 0f );
renderPocketComputerItem( stack );
GlStateManager.popMatrix();
}
/**
* Render an item in the middle of the screen
*
* @param pitch The pitch of the player
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see ItemRenderer#renderMapFirstPerson(float, float, float)
*/
private void renderItemFirstCentre( float pitch, float equipProgress, float swingProgress, ItemStack stack )
{
ItemRenderer itemRenderer = Minecraft.getMinecraft().getItemRenderer();
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
float swingRt = MathHelper.sqrt( swingProgress );
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
GlStateManager.translate( 0f, -tX / 2f, tZ );
float pitchAngle = itemRenderer.getMapAngleFromPitch( pitch );
GlStateManager.translate( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
GlStateManager.rotate( pitchAngle * -85f, 1f, 0f, 0f );
itemRenderer.renderArms();
float rX = MathHelper.sin( swingRt * (float) Math.PI );
GlStateManager.rotate( rX * 20f, 1f, 0f, 0f );
GlStateManager.scale( 2f, 2f, 2f );
renderPocketComputerItem( stack );
}
}

View File

@@ -0,0 +1,264 @@
package dan200.computercraft.client.render;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.LightUtil;
import net.minecraftforge.client.model.pipeline.VertexTransformer;
import net.minecraftforge.common.model.TRSRTransformation;
import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;
import java.util.List;
/**
* Transforms vertices of a model, remaining aware of winding order, and rearranging
* vertices if needed.
*/
public final class ModelTransformer
{
private static final Matrix4f identity;
static
{
identity = new Matrix4f();
identity.setIdentity();
}
private ModelTransformer()
{
}
public static void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform )
{
if( transform == null || transform.equals( identity ) )
{
output.addAll( input );
}
else
{
Matrix4f normalMatrix = new Matrix4f( transform );
normalMatrix.invert();
normalMatrix.transpose();
for( BakedQuad quad : input ) output.add( doTransformQuad( quad, transform, normalMatrix ) );
}
}
public static BakedQuad transformQuad( BakedQuad input, Matrix4f transform )
{
if( transform == null || transform.equals( identity ) ) return input;
Matrix4f normalMatrix = new Matrix4f( transform );
normalMatrix.invert();
normalMatrix.transpose();
return doTransformQuad( input, transform, normalMatrix );
}
private static BakedQuad doTransformQuad( BakedQuad input, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
BakedQuadBuilder builder = new BakedQuadBuilder( input.getFormat() );
NormalAwareTransformer transformer = new NormalAwareTransformer( builder, positionMatrix, normalMatrix );
input.pipe( transformer );
if( transformer.areNormalsInverted() )
{
builder.swap( 1, 3 );
transformer.areNormalsInverted();
}
return builder.build();
}
/**
* A vertex transformer that tracks whether the normals have been inverted and so the vertices
* should be reordered so backface culling works as expected.
*/
private static class NormalAwareTransformer extends VertexTransformer
{
private final Matrix4f positionMatrix;
private final Matrix4f normalMatrix;
private int vertexIndex = 0, elementIndex = 0;
private final Point3f[] before = new Point3f[ 4 ];
private final Point3f[] after = new Point3f[ 4 ];
public NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
super( parent );
this.positionMatrix = positionMatrix;
this.normalMatrix = normalMatrix;
}
@Override
public void setQuadOrientation( EnumFacing orientation )
{
super.setQuadOrientation( orientation == null ? orientation : TRSRTransformation.rotate( positionMatrix, orientation ) );
}
@Override
public void put( int element, @Nonnull float... data )
{
switch( getVertexFormat().getElement( element ).getUsage() )
{
case POSITION:
{
Point3f vec = new Point3f( data );
Point3f newVec = new Point3f();
positionMatrix.transform( vec, newVec );
float[] newData = new float[ 4 ];
newVec.get( newData );
super.put( element, newData );
before[ vertexIndex ] = vec;
after[ vertexIndex ] = newVec;
break;
}
case NORMAL:
{
Vector3f vec = new Vector3f( data );
normalMatrix.transform( vec );
float[] newData = new float[ 4 ];
vec.get( newData );
super.put( element, newData );
break;
}
default:
super.put( element, data );
break;
}
elementIndex++;
if( elementIndex == getVertexFormat().getElementCount() )
{
vertexIndex++;
elementIndex = 0;
}
}
public boolean areNormalsInverted()
{
Vector3f temp1 = new Vector3f(), temp2 = new Vector3f();
Vector3f crossBefore = new Vector3f(), crossAfter = new Vector3f();
// Determine what cross product we expect to have
temp1.sub( before[ 1 ], before[ 0 ] );
temp2.sub( before[ 1 ], before[ 2 ] );
crossBefore.cross( temp1, temp2 );
normalMatrix.transform( crossBefore );
// And determine what cross product we actually have
temp1.sub( after[ 1 ], after[ 0 ] );
temp2.sub( after[ 1 ], after[ 2 ] );
crossAfter.cross( temp1, temp2 );
// If the angle between expected and actual cross product is greater than
// pi/2 radians then we will need to reorder our quads.
return Math.abs( crossBefore.angle( crossAfter ) ) >= Math.PI / 2;
}
}
/**
* A vertex consumer which is capable of building {@link BakedQuad}s.
*
* Equivalent to {@link net.minecraftforge.client.model.pipeline.UnpackedBakedQuad.Builder} but more memory
* efficient.
*
* This also provides the ability to swap vertices through {@link #swap(int, int)} to allow reordering.
*/
private static class BakedQuadBuilder implements IVertexConsumer
{
private final VertexFormat format;
private final int[] vertexData;
private int vertexIndex = 0, elementIndex = 0;
private EnumFacing orientation;
private int quadTint;
private boolean diffuse;
private TextureAtlasSprite texture;
private BakedQuadBuilder( VertexFormat format )
{
this.format = format;
this.vertexData = new int[ format.getNextOffset() ];
}
@Nonnull
@Override
public VertexFormat getVertexFormat()
{
return format;
}
@Override
public void setQuadTint( int tint )
{
this.quadTint = tint;
}
@Override
public void setQuadOrientation( @Nonnull EnumFacing orientation )
{
this.orientation = orientation;
}
@Override
public void setApplyDiffuseLighting( boolean diffuse )
{
this.diffuse = diffuse;
}
@Override
public void setTexture( @Nonnull TextureAtlasSprite texture )
{
this.texture = texture;
}
@Override
public void put( int element, @Nonnull float... data )
{
LightUtil.pack( data, vertexData, format, vertexIndex, element );
elementIndex++;
if( elementIndex == getVertexFormat().getElementCount() )
{
vertexIndex++;
elementIndex = 0;
}
}
public void swap( int a, int b )
{
int length = vertexData.length / 4;
for( int i = 0; i < length; i++ )
{
int temp = vertexData[ a * length + i ];
vertexData[ a * length + i ] = vertexData[ b * length + i ];
vertexData[ b * length + i ] = temp;
}
}
public BakedQuad build()
{
if( elementIndex != 0 || vertexIndex != 4 )
{
throw new IllegalStateException( "Got an unexpected number of elements/vertices" );
}
if( texture == null )
{
throw new IllegalStateException( "Texture has not been set" );
}
return new BakedQuad( vertexData, quadTint, orientation, texture, diffuse, format );
}
}
}

View File

@@ -9,7 +9,6 @@ package dan200.computercraft.client.render;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
import dan200.computercraft.shared.util.Holiday;
@@ -121,18 +120,22 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
GlStateManager.translate( posX + offset.x, posY + offset.y, posZ + offset.z );
// Render the label
IComputer computer = turtle.getComputer();
String label = (computer != null) ? computer.getLabel() : null;
String label = turtle.createProxy().getLabel();
if( label != null )
{
renderLabel( turtle.getAccess().getPosition(), label );
}
// Render the turtle
GlStateManager.translate( 0.5f, 0.0f, 0.5f );
GlStateManager.translate( 0.5f, 0.5f, 0.5f );
GlStateManager.rotate( 180.0f - yaw, 0.0f, 1.0f, 0.0f );
GlStateManager.translate( -0.5f, 0.0f, -0.5f );
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
{
// Flip the model and swap the cull face as winding order will have changed.
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
GlStateManager.cullFace( GlStateManager.CullFace.FRONT );
}
GlStateManager.translate( -0.5f, -0.5f, -0.5f );
// Render the turtle
int colour;
ComputerFamily family;
@@ -172,6 +175,7 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
finally
{
GlStateManager.popMatrix();
GlStateManager.cullFace( GlStateManager.CullFace.BACK );
}
}

View File

@@ -6,13 +6,10 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.util.EnumFacing;
import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -20,16 +17,17 @@ import java.util.Map;
public class TurtleMultiModel implements IBakedModel
{
private IBakedModel m_baseModel;
private IBakedModel m_overlayModel;
private IBakedModel m_leftUpgradeModel;
private Matrix4f m_leftUpgradeTransform;
private IBakedModel m_rightUpgradeModel;
private Matrix4f m_rightUpgradeTransform;
private final IBakedModel m_baseModel;
private final IBakedModel m_overlayModel;
private final Matrix4f m_generalTransform;
private final IBakedModel m_leftUpgradeModel;
private final Matrix4f m_leftUpgradeTransform;
private final IBakedModel m_rightUpgradeModel;
private final Matrix4f m_rightUpgradeTransform;
private List<BakedQuad> m_generalQuads;
private Map<EnumFacing, List<BakedQuad>> m_faceQuads;
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, Matrix4f generalTransform, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
{
// Get the models
m_baseModel = baseModel;
@@ -38,6 +36,7 @@ public class TurtleMultiModel implements IBakedModel
m_leftUpgradeTransform = leftUpgradeTransform;
m_rightUpgradeModel = rightUpgradeModel;
m_rightUpgradeTransform = rightUpgradeTransform;
m_generalTransform = generalTransform;
m_generalQuads = null;
m_faceQuads = new HashMap<>();
}
@@ -48,51 +47,52 @@ public class TurtleMultiModel implements IBakedModel
{
if( side != null )
{
if( !m_faceQuads.containsKey( side ) )
{
ArrayList<BakedQuad> quads = new ArrayList<>();
if( m_overlayModel != null )
{
quads.addAll( m_overlayModel.getQuads( state, side, rand ) );
}
if( m_leftUpgradeModel != null )
{
quads.addAll( transformQuads( m_leftUpgradeModel.getQuads( state, side, rand ), m_leftUpgradeTransform ) );
}
if( m_rightUpgradeModel != null )
{
quads.addAll( transformQuads( m_rightUpgradeModel.getQuads( state, side, rand ), m_rightUpgradeTransform ) );
}
quads.trimToSize();
m_faceQuads.put( side, quads );
}
if( !m_faceQuads.containsKey( side ) ) m_faceQuads.put( side, buildQuads( state, side, rand ) );
return m_faceQuads.get( side );
}
else
{
if( m_generalQuads == null )
{
ArrayList<BakedQuad> quads = new ArrayList<>();
quads.addAll( m_baseModel.getQuads( state, side, rand ) );
if( m_overlayModel != null )
{
quads.addAll( m_overlayModel.getQuads( state, side, rand ) );
}
if( m_leftUpgradeModel != null )
{
quads.addAll( transformQuads( m_leftUpgradeModel.getQuads( state, side, rand ), m_leftUpgradeTransform ) );
}
if( m_rightUpgradeModel != null )
{
quads.addAll( transformQuads( m_rightUpgradeModel.getQuads( state, side, rand ), m_rightUpgradeTransform ) );
}
quads.trimToSize();
m_generalQuads = quads;
}
if( m_generalQuads == null ) m_generalQuads = buildQuads( state, side, rand );
return m_generalQuads;
}
}
private List<BakedQuad> buildQuads( IBlockState state, EnumFacing side, long rand )
{
ArrayList<BakedQuad> quads = new ArrayList<>();
ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand ), m_generalTransform );
if( m_overlayModel != null )
{
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
}
if( m_overlayModel != null )
{
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
}
if( m_leftUpgradeModel != null )
{
Matrix4f upgradeTransform = m_generalTransform;
if( m_leftUpgradeTransform != null )
{
upgradeTransform = new Matrix4f( m_generalTransform );
upgradeTransform.mul( m_leftUpgradeTransform );
}
ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getQuads( state, side, rand ), upgradeTransform );
}
if( m_rightUpgradeModel != null )
{
Matrix4f upgradeTransform = m_generalTransform;
if( m_rightUpgradeTransform != null )
{
upgradeTransform = new Matrix4f( m_generalTransform );
upgradeTransform.mul( m_rightUpgradeTransform );
}
ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getQuads( state, side, rand ), upgradeTransform );
}
quads.trimToSize();
return quads;
}
@Override
public boolean isAmbientOcclusion()
{
@@ -132,63 +132,4 @@ public class TurtleMultiModel implements IBakedModel
{
return ItemOverrideList.NONE;
}
private List<BakedQuad> transformQuads( List<BakedQuad> input, Matrix4f transform )
{
if( transform == null || input.size() == 0 )
{
return input;
}
else
{
List<BakedQuad> output = new ArrayList<>( input.size() );
for( BakedQuad quad : input )
{
output.add( transformQuad( quad, transform ) );
}
return output;
}
}
private BakedQuad transformQuad( BakedQuad quad, Matrix4f transform )
{
int[] vertexData = quad.getVertexData().clone();
int offset = 0;
BakedQuad copy = new BakedQuad( vertexData, -1, quad.getFace(), quad.getSprite(), quad.shouldApplyDiffuseLighting(), quad.getFormat() );
VertexFormat format = copy.getFormat();
for( int i=0; i<format.getElementCount(); ++i ) // For each vertex element
{
VertexFormatElement element = format.getElement( i );
if( element.isPositionElement() &&
element.getType() == VertexFormatElement.EnumType.FLOAT &&
element.getElementCount() == 3 ) // When we find a position element
{
for( int j=0; j<4; ++j ) // For each corner of the quad
{
int start = offset + j * format.getNextOffset();
if( (start % 4) == 0 )
{
start = start / 4;
// Extract the position
Point3f pos = new Point3f(
Float.intBitsToFloat( vertexData[ start ] ),
Float.intBitsToFloat( vertexData[ start + 1 ] ),
Float.intBitsToFloat( vertexData[ start + 2 ] )
);
// Transform the position
transform.transform( pos );
// Insert the position
vertexData[ start ] = Float.floatToRawIntBits( pos.x );
vertexData[ start + 1 ] = Float.floatToRawIntBits( pos.y );
vertexData[ start + 2 ] = Float.floatToRawIntBits( pos.z );
}
}
}
offset += element.getSize();
}
return copy;
}
}

View File

@@ -35,6 +35,19 @@ import java.util.List;
public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReloadListener
{
private static final Matrix4f s_identity, s_flip;
static
{
s_identity = new Matrix4f();
s_identity.setIdentity();
s_flip = new Matrix4f();
s_flip.setIdentity();
s_flip.m11 = -1; // Flip on the y axis
s_flip.m13 = 1; // Models go from (0,0,0) to (1,1,1), so push back up.
}
private static class TurtleModelCombination
{
public final ComputerFamily m_family;
@@ -43,8 +56,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
public final ITurtleUpgrade m_rightUpgrade;
public final ResourceLocation m_overlay;
public final boolean m_christmas;
public final boolean m_flip;
public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas )
public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas, boolean flip )
{
m_family = family;
m_colour = colour;
@@ -52,22 +66,26 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
m_rightUpgrade = rightUpgrade;
m_overlay = overlay;
m_christmas = christmas;
m_flip = flip;
}
@Override
public boolean equals( Object other )
{
if( other == this ) {
if( other == this )
{
return true;
}
if( other instanceof TurtleModelCombination ) {
TurtleModelCombination otherCombo = (TurtleModelCombination)other;
if( other instanceof TurtleModelCombination )
{
TurtleModelCombination otherCombo = (TurtleModelCombination) other;
if( otherCombo.m_family == m_family &&
otherCombo.m_colour == m_colour &&
otherCombo.m_leftUpgrade == m_leftUpgrade &&
otherCombo.m_rightUpgrade == m_rightUpgrade &&
Objects.equal( otherCombo.m_overlay, m_overlay ) &&
otherCombo.m_christmas == m_christmas )
otherCombo.m_christmas == m_christmas &&
otherCombo.m_flip == m_flip )
{
return true;
}
@@ -86,10 +104,11 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
result = prime * result + (m_rightUpgrade != null ? m_rightUpgrade.hashCode() : 0);
result = prime * result + (m_overlay != null ? m_overlay.hashCode() : 0);
result = prime * result + (m_christmas ? 1 : 0);
result = prime * result + (m_flip ? 1 : 0);
return result;
}
}
private HashMap<TurtleModelCombination, IBakedModel> m_cachedModels;
private ItemOverrideList m_overrides;
private final TurtleModelCombination m_defaultCombination;
@@ -97,12 +116,12 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
public TurtleSmartItemModel()
{
m_cachedModels = new HashMap<>();
m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false );
m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false, false );
m_overrides = new ItemOverrideList( new ArrayList<>() )
{
@Nonnull
@Override
public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity )
{
ItemTurtleBase turtle = (ItemTurtleBase) stack.getItem();
ComputerFamily family = turtle.getFamily( stack );
@@ -111,7 +130,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.Right );
ResourceLocation overlay = turtle.getOverlay( stack );
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.Christmas;
TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas );
String label = turtle.getLabel( stack );
boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" ));
TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip );
if( m_cachedModels.containsKey( combo ) )
{
return m_cachedModels.get( combo );
@@ -147,27 +168,24 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
ModelResourceLocation overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.m_family, combo.m_overlay, combo.m_christmas );
IBakedModel baseModel = modelManager.getModel( baseModelLocation );
IBakedModel overlayModel = (overlayModelLocation != null) ? modelManager.getModel( baseModelLocation ) : null;
Matrix4f transform = combo.m_flip ? s_flip : s_identity;
Pair<IBakedModel, Matrix4f> leftModel = (combo.m_leftUpgrade != null) ? combo.m_leftUpgrade.getModel( null, TurtleSide.Left ) : null;
Pair<IBakedModel, Matrix4f> rightModel = (combo.m_rightUpgrade != null) ? combo.m_rightUpgrade.getModel( null, TurtleSide.Right ) : null;
if( leftModel != null && rightModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
}
else if( leftModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, leftModel.getLeft(), leftModel.getRight(), null, null );
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), null, null );
}
else if( rightModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, null, null, rightModel.getLeft(), rightModel.getRight() );
}
else if( overlayModel != null )
{
return new TurtleMultiModel( baseModel, overlayModel, null, null, null, null );
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, rightModel.getLeft(), rightModel.getRight() );
}
else
{
return baseModel;
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, null, null );
}
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
@@ -100,21 +101,6 @@ public class BufferAPI implements ILuaAPI
};
}
@Override
public void startup()
{
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown()
{
}
@Nonnull
@Override
public String[] getMethodNames()

View File

@@ -0,0 +1,155 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Set;
public abstract class ComputerAccess implements IComputerAccess
{
private final IAPIEnvironment m_environment;
private final Set<String> m_mounts = new HashSet<>();
protected ComputerAccess( IAPIEnvironment m_environment )
{
this.m_environment = m_environment;
}
public void unmountAll()
{
FileSystem fileSystem = m_environment.getFileSystem();
for( String m_mount : m_mounts )
{
fileSystem.unmount( m_mount );
}
m_mounts.clear();
}
@Override
public String mount( @Nonnull String desiredLoc, @Nonnull IMount mount )
{
return mount( desiredLoc, mount, getAttachmentName() );
}
@Override
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
{
// Mount the location
String location;
FileSystem fileSystem = m_environment.getFileSystem();
if( fileSystem == null )
{
throw new IllegalStateException( "File system has not been created" );
}
synchronized( fileSystem )
{
location = findFreeLocation( desiredLoc );
if( location != null )
{
try
{
fileSystem.mount( driveName, location, mount );
}
catch( FileSystemException ignored )
{
}
}
}
if( location != null )
{
m_mounts.add( location );
}
return location;
}
@Override
public String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount )
{
return mountWritable( desiredLoc, mount, getAttachmentName() );
}
@Override
public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName )
{
// Mount the location
String location;
FileSystem fileSystem = m_environment.getFileSystem();
if( fileSystem == null )
{
throw new IllegalStateException( "File system has not been created" );
}
synchronized( fileSystem )
{
location = findFreeLocation( desiredLoc );
if( location != null )
{
try
{
fileSystem.mountWritable( driveName, location, mount );
}
catch( FileSystemException ignored )
{
}
}
}
if( location != null )
{
m_mounts.add( location );
}
return location;
}
@Override
public void unmount( String location )
{
if( location != null )
{
if( !m_mounts.contains( location ) )
{
throw new RuntimeException( "You didn't mount this location" );
}
m_environment.getFileSystem().unmount( location );
m_mounts.remove( location );
}
}
@Override
public int getID()
{
return m_environment.getComputerID();
}
@Override
public void queueEvent( @Nonnull final String event, final Object[] arguments )
{
m_environment.queueEvent( event, arguments );
}
private String findFreeLocation( String desiredLoc )
{
try
{
FileSystem fileSystem = m_environment.getFileSystem();
if( !fileSystem.exists( desiredLoc ) )
{
return desiredLoc;
}
// We used to check foo2,foo3,foo4,etc here
// but the disk drive does this itself now
return null;
}
catch( FileSystemException e )
{
return null;
}
}
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
@@ -48,11 +49,6 @@ public class FSAPI implements ILuaAPI
m_fileSystem = m_env.getFileSystem();
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{

View File

@@ -7,6 +7,7 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.http.*;
@@ -43,12 +44,7 @@ public class HTTPAPI implements ILuaAPI
}
@Override
public void startup( )
{
}
@Override
public void advance( double _dt )
public void update()
{
// Wait for all of our http requests
synchronized( m_httpTasks )

View File

@@ -1,17 +1,16 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaObject;
public interface ILuaAPI extends ILuaObject
/**
* This exists purely to ensure binary compatibility.
*
* @see dan200.computercraft.api.lua.ILuaAPI
*/
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI
{
String[] getNames();
void advance( double v );
void startup(); // LT
void advance( double _dt ); // MT
void shutdown(); // LT
default void update()
{
advance( 0.05 );
}
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.shared.util.StringUtil;
@@ -102,7 +103,7 @@ public class OSAPI implements ILuaAPI
}
@Override
public void advance( double dt )
public void update()
{
synchronized( m_timers )
{

View File

@@ -8,24 +8,25 @@ package dan200.computercraft.core.apis;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ComputerThread;
import dan200.computercraft.core.computer.ITask;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener
{
private class PeripheralWrapper implements IComputerAccess
private class PeripheralWrapper extends ComputerAccess
{
private final String m_side;
private final IPeripheral m_peripheral;
@@ -35,10 +36,9 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private Map<String, Integer> m_methodMap;
private boolean m_attached;
private Set<String> m_mounts;
public PeripheralWrapper( IPeripheral peripheral, String side )
{
super(m_environment);
m_side = side;
m_peripheral = peripheral;
m_attached = false;
@@ -54,8 +54,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
m_methodMap.put( m_methods[i], i );
}
}
m_mounts = new HashSet<>();
}
public IPeripheral getPeripheral()
@@ -91,14 +89,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
synchronized( this )
{
m_attached = false;
// Unmount everything the detach function forgot to do
for( String m_mount : m_mounts )
{
m_fileSystem.unmount( m_mount );
}
m_mounts.clear();
unmountAll();
}
m_attached = false;
}
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
@@ -122,13 +117,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
// IComputerAccess implementation
@Override
public String mount( @Nonnull String desiredLoc, @Nonnull IMount mount )
{
return mount( desiredLoc, mount, m_side );
}
@Override
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
{
@@ -137,31 +125,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
throw new RuntimeException( "You are not attached to this Computer" );
}
// Mount the location
String location;
synchronized( m_fileSystem )
{
location = findFreeLocation( desiredLoc );
if( location != null )
{
try {
m_fileSystem.mount( driveName, location, mount );
} catch( FileSystemException e ) {
// fail and return null
}
}
}
if( location != null )
{
m_mounts.add( location );
}
return location;
}
@Override
public String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount )
{
return mountWritable( desiredLoc, mount, m_side );
return super.mount( desiredLoc, mount, driveName );
}
@Override
@@ -172,68 +136,46 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
throw new RuntimeException( "You are not attached to this Computer" );
}
// Mount the location
String location;
synchronized( m_fileSystem )
{
location = findFreeLocation( desiredLoc );
if( location != null )
{
try {
m_fileSystem.mountWritable( driveName, location, mount );
} catch( FileSystemException e ) {
// fail and return null
}
}
}
if( location != null )
{
m_mounts.add( location );
}
return location;
return super.mountWritable( desiredLoc, mount, driveName );
}
@Override
public synchronized void unmount( String location )
{
if( !m_attached ) {
if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
if( location != null )
{
if( !m_mounts.contains( location ) ) {
throw new RuntimeException( "You didn't mount this location" );
}
m_fileSystem.unmount( location );
m_mounts.remove( location );
}
super.unmount( location );
}
@Override
public int getID()
{
if( !m_attached ) {
if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
return m_environment.getComputerID();
return super.getID();
}
@Override
public void queueEvent( @Nonnull final String event, final Object[] arguments )
{
if( !m_attached ) {
if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
m_environment.queueEvent( event, arguments );
super.queueEvent( event, arguments );
}
@Nonnull
@Override
public String getAttachmentName()
{
if( !m_attached ) {
if( !m_attached )
{
throw new RuntimeException( "You are not attached to this Computer" );
}
return m_side;
@@ -356,11 +298,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{
@@ -504,25 +441,4 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
return -1;
}
private String findFreeLocation( String desiredLoc )
{
try
{
synchronized( m_fileSystem )
{
if( !m_fileSystem.exists( desiredLoc ) )
{
return desiredLoc;
}
// We used to check foo2,foo3,foo4,etc here
// but the disk drive does this itself now
return null;
}
}
catch( FileSystemException e )
{
return null;
}
}
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.computer.Computer;
@@ -33,21 +34,6 @@ public class RedstoneAPI implements ILuaAPI
};
}
@Override
public void startup( )
{
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{
}
@Nonnull
@Override
public String[] getMethodNames()

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.computer.IComputerEnvironment;
@@ -36,21 +37,6 @@ public class TermAPI implements ILuaAPI
};
}
@Override
public void startup( )
{
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{
}
@Nonnull
@Override
public String[] getMethodNames()

View File

@@ -8,16 +8,21 @@ package dan200.computercraft.core.computer;
import com.google.common.base.Objects;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.*;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.*;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.lua.ILuaMachine;
import dan200.computercraft.core.lua.CobaltLuaMachine;
import dan200.computercraft.core.lua.ILuaMachine;
import dan200.computercraft.core.terminal.Terminal;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -173,7 +178,91 @@ public class Computer
}
}
}
private static class ComputerSystem extends ComputerAccess implements IComputerSystem
{
private final IAPIEnvironment m_environment;
private ComputerSystem( IAPIEnvironment m_environment )
{
super( m_environment );
this.m_environment = m_environment;
}
@Nonnull
@Override
public String getAttachmentName()
{
return "computer";
}
@Nullable
@Override
public IFileSystem getFileSystem()
{
FileSystem fs = m_environment.getFileSystem();
return fs == null ? null : fs.getMountWrapper();
}
@Nullable
@Override
public String getLabel()
{
return m_environment.getLabel();
}
}
private static class APIWrapper implements ILuaAPI
{
private final ILuaAPI delegate;
private final ComputerSystem system;
private APIWrapper( ILuaAPI delegate, ComputerSystem system )
{
this.delegate = delegate;
this.system = system;
}
@Override
public String[] getNames()
{
return delegate.getNames();
}
@Override
public void startup()
{
delegate.startup();
}
@Override
public void update()
{
delegate.update();
}
@Override
public void shutdown()
{
delegate.shutdown();
system.unmountAll();
}
@Nonnull
@Override
public String[] getMethodNames()
{
return delegate.getMethodNames();
}
@Nullable
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{
return delegate.callMethod( context, method, arguments );
}
}
private static IMount s_romMount = null;
private int m_id;
@@ -371,7 +460,7 @@ public class Computer
{
for(ILuaAPI api : m_apis)
{
api.advance( _dt );
api.update();
}
}
}
@@ -420,14 +509,13 @@ public class Computer
}
}
public boolean pollChanged()
public boolean pollAndResetChanged()
{
return m_externalOutputChanged;
}
public void clearChanged()
{
m_externalOutputChanged = false;
synchronized(this) {
boolean changed = m_externalOutputChanged;
m_externalOutputChanged = false;
return changed;
}
}
public boolean isBlinking()
@@ -578,6 +666,11 @@ public class Computer
{
m_apis.add( api );
}
public void addAPI( dan200.computercraft.core.apis.ILuaAPI api )
{
addAPI( (ILuaAPI) api );
}
public void setPeripheral( int side, IPeripheral peripheral )
{
@@ -616,6 +709,16 @@ public class Computer
{
m_apis.add( new HTTPAPI( m_apiEnvironment ) );
}
for( ILuaAPIFactory factory : ComputerCraft.getAPIFactories() )
{
ComputerSystem system = new ComputerSystem( m_apiEnvironment );
ILuaAPI api = factory.create( system );
if( api != null )
{
m_apis.add( api );
}
}
}
private void initLua()
@@ -687,6 +790,7 @@ public class Computer
return;
}
m_state = State.Starting;
m_externalOutputChanged = true;
m_ticksSinceStart = 0;
}
@@ -746,6 +850,7 @@ public class Computer
// Start a new state
m_state = State.Running;
m_externalOutputChanged = true;
synchronized( m_machine )
{
m_machine.handleEvent( null, null );
@@ -764,6 +869,7 @@ public class Computer
return;
}
m_state = State.Stopping;
m_externalOutputChanged = true;
}
// Turn the computercraft off
@@ -788,7 +894,7 @@ public class Computer
// Shutdown our APIs
synchronized( m_apis )
{
for(ILuaAPI api : m_apis)
for( ILuaAPI api : m_apis )
{
api.shutdown();
}
@@ -827,6 +933,7 @@ public class Computer
}
m_state = State.Off;
m_externalOutputChanged = true;
if( reboot )
{
m_startRequested = true;

View File

@@ -7,6 +7,7 @@
package dan200.computercraft.core.filesystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
@@ -290,6 +291,7 @@ public class FileSystem
}
}
private final FileSystemMount m_wrapper = new FileSystemMount( this );
private final Map<String, MountWrapper> m_mounts = new HashMap<>();
private final Set<Closeable> m_openFiles = Collections.newSetFromMap( new WeakHashMap<Closeable, Boolean>() );
@@ -734,6 +736,11 @@ public class FileSystem
return match;
}
public IFileSystem getMountWrapper()
{
return m_wrapper;
}
private static String sanitizePath( String path )
{
return sanitizePath( path, false );

View File

@@ -0,0 +1,185 @@
package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.IFileSystem;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
public class FileSystemMount implements IFileSystem
{
private final FileSystem m_filesystem;
public FileSystemMount( FileSystem m_filesystem )
{
this.m_filesystem = m_filesystem;
}
@Override
public void makeDirectory( @Nonnull String path ) throws IOException
{
try
{
m_filesystem.makeDir( path );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public void delete( @Nonnull String path ) throws IOException
{
try
{
m_filesystem.delete( path );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Nonnull
@Override
public OutputStream openForWrite( @Nonnull String path ) throws IOException
{
try
{
return m_filesystem.openForWrite( path, false );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Nonnull
@Override
public OutputStream openForAppend( @Nonnull String path ) throws IOException
{
try
{
return m_filesystem.openForWrite( path, true );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public long getRemainingSpace() throws IOException
{
try
{
return m_filesystem.getFreeSpace( "/" );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public boolean exists( @Nonnull String path ) throws IOException
{
try
{
return m_filesystem.exists( path );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public boolean isDirectory( @Nonnull String path ) throws IOException
{
try
{
return m_filesystem.exists( path );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
{
try
{
Collections.addAll( contents, m_filesystem.list( path ) );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public long getSize( @Nonnull String path ) throws IOException
{
try
{
return m_filesystem.getSize( path );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Nonnull
@Override
public InputStream openForRead( @Nonnull String path ) throws IOException
{
try
{
return m_filesystem.openForRead( path );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public String combine( String path, String child )
{
return m_filesystem.combine( path, child );
}
@Override
public void copy( String from, String to ) throws IOException
{
try
{
m_filesystem.copy( from, to );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
@Override
public void move( String from, String to ) throws IOException
{
try
{
m_filesystem.move( from, to );
}
catch( FileSystemException e )
{
throw new IOException( e.getMessage() );
}
}
}

View File

@@ -11,7 +11,7 @@ import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ITask;
import dan200.computercraft.core.computer.MainThread;

View File

@@ -5,7 +5,7 @@
*/
package dan200.computercraft.core.lua;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.api.lua.ILuaAPI;
import java.io.InputStream;
import java.io.OutputStream;

View File

@@ -8,6 +8,8 @@ package dan200.computercraft.server.proxy;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.network.ComputerCraftPacket;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
@@ -97,6 +99,12 @@ public class ComputerCraftProxyServer extends ComputerCraftProxyCommon
return null;
}
@Override
public Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family )
{
return null;
}
@Override
public File getWorldDir( World world )
{

View File

@@ -0,0 +1,256 @@
package dan200.computercraft.shared.command;
import com.google.common.collect.Lists;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.shared.command.framework.*;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import static dan200.computercraft.shared.command.framework.ChatHelpers.*;
public final class CommandComputerCraft extends CommandDelegate
{
public CommandComputerCraft()
{
super( create() );
}
private static ISubCommand create()
{
CommandRoot root = new CommandRoot(
"computercraft", "Various commands for controlling computers.",
"The /computercraft command provides various debugging and administrator tools for controlling and " +
"interacting with computers."
);
root.register( new SubCommandBase(
"dump", "[id]", "Display the status of computers.", UserLevel.OWNER_OP,
"Display the status of all computers or specific information about one computer. You can either specify the computer's instance " +
"id (e.g. 123) or computer id (e.g #123)."
)
{
@Override
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
{
if( arguments.size() == 0 )
{
TextTable table = new TextTable( "Instance", "Id", "On", "Position" );
int max = 50;
for( ServerComputer computer : ComputerCraft.serverComputerRegistry.getComputers() )
{
table.addRow(
linkComputer( computer ),
text( Integer.toString( computer.getID() ) ),
bool( computer.isOn() ),
linkPosition( context, computer )
);
if( max-- < 0 ) break;
}
table.displayTo( context.getSender() );
}
else if( arguments.size() == 1 )
{
ServerComputer computer = ComputerSelector.getComputer( arguments.get( 0 ) );
TextTable table = new TextTable();
table.addRow( header( "Instance" ), text( Integer.toString( computer.getInstanceID() ) ) );
table.addRow( header( "Id" ), text( Integer.toString( computer.getID() ) ) );
table.addRow( header( "Label" ), text( computer.getLabel() ) );
table.addRow( header( "On" ), bool( computer.isOn() ) );
table.addRow( header( "Position" ), linkPosition( context, computer ) );
table.addRow( header( "Family" ), text( computer.getFamily().toString() ) );
for( int i = 0; i < 6; i++ )
{
IPeripheral peripheral = computer.getPeripheral( i );
if( peripheral != null )
{
table.addRow( header( "Peripheral " + Computer.s_sideNames[ i ] ), text( peripheral.getType() ) );
}
}
table.displayTo( context.getSender() );
}
else
{
throw new CommandException( context.getFullUsage() );
}
}
@Nonnull
@Override
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
{
return arguments.size() == 1
? ComputerSelector.completeComputer( arguments.get( 0 ) )
: Collections.<String>emptyList();
}
} );
root.register( new SubCommandBase(
"shutdown", "[ids...]", "Shutdown computers remotely.", UserLevel.OWNER_OP,
"Shutdown the listed computers or all if none are specified. You can either specify the computer's instance " +
"id (e.g. 123) or computer id (e.g #123)."
)
{
@Override
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
{
List<ServerComputer> computers = Lists.newArrayList();
if( arguments.size() > 0 )
{
for( String arg : arguments )
{
computers.add( ComputerSelector.getComputer( arg ) );
}
}
else
{
computers.addAll( ComputerCraft.serverComputerRegistry.getComputers() );
}
int shutdown = 0;
for( ServerComputer computer : computers )
{
if( computer.isOn() ) shutdown++;
computer.unload();
}
context.getSender().sendMessage( text( "Shutdown " + shutdown + " / " + computers.size() + " computers" ) );
}
@Nonnull
@Override
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
{
return arguments.size() == 0
? Collections.<String>emptyList()
: ComputerSelector.completeComputer( arguments.get( arguments.size() - 1 ) );
}
} );
root.register( new SubCommandBase(
"tp", "<id>", "Teleport to a specific computer.", UserLevel.OP,
"Teleport to the location of a computer. You can either specify the computer's instance " +
"id (e.g. 123) or computer id (e.g #123)."
)
{
@Override
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
{
if( arguments.size() != 1 ) throw new CommandException( context.getFullUsage() );
ServerComputer computer = ComputerSelector.getComputer( arguments.get( 0 ) );
World world = computer.getWorld();
BlockPos pos = computer.getPosition();
if( world == null || pos == null ) throw new CommandException( "Cannot locate computer in world" );
ICommandSender sender = context.getSender();
if( !(sender instanceof Entity) ) throw new CommandException( "Sender is not an entity" );
if( sender instanceof EntityPlayerMP )
{
EntityPlayerMP entity = (EntityPlayerMP) sender;
if( entity.getEntityWorld() != world )
{
context.getServer().getPlayerList().changePlayerDimension( entity, world.provider.getDimension() );
}
entity.setPositionAndUpdate( pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5 );
}
else
{
Entity entity = (Entity) sender;
if( entity.getEntityWorld() != world )
{
entity.changeDimension( world.provider.getDimension() );
}
entity.setLocationAndAngles(
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5,
entity.rotationYaw, entity.rotationPitch
);
}
}
@Nonnull
@Override
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
{
return arguments.size() == 1
? ComputerSelector.completeComputer( arguments.get( 0 ) )
: Collections.<String>emptyList();
}
} );
root.register(new SubCommandBase(
"view", "<id>", "View the terminal of a computer.", UserLevel.OP,
"Open the terminal of a computer, allowing remote control of a computer. This does not provide access to " +
"turtle's inventories. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123)."
) {
@Override
public void execute(@Nonnull CommandContext context, @Nonnull List<String> arguments) throws CommandException {
if (arguments.size() != 1) throw new CommandException(context.getFullUsage());
ICommandSender sender = context.getSender();
if (!(sender instanceof EntityPlayerMP)) {
throw new CommandException("Cannot open terminal for non-player");
}
ServerComputer computer = ComputerSelector.getComputer(arguments.get(0));
ComputerCraft.openComputerGUI( (EntityPlayerMP) sender, computer );
}
@Nonnull
@Override
public List<String> getCompletion(@Nonnull CommandContext context, @Nonnull List<String> arguments) {
return arguments.size() == 1
? ComputerSelector.completeComputer( arguments.get( 0 ) )
: Collections.emptyList();
}
});
return root;
}
private static ITextComponent linkComputer( ServerComputer computer )
{
return link(
text( Integer.toString( computer.getInstanceID() ) ),
"/computercraft dump " + computer.getInstanceID(),
"View more info about this computer"
);
}
private static ITextComponent linkPosition( CommandContext context, ServerComputer computer )
{
if( UserLevel.OP.canExecute( context ) )
{
return link(
position( computer.getPosition() ),
"/computercraft tp " + computer.getInstanceID(),
"Teleport to this computer"
);
}
else
{
return position( computer.getPosition() );
}
}
}

View File

@@ -0,0 +1,126 @@
package dan200.computercraft.shared.command;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.command.CommandException;
import java.util.List;
import java.util.Set;
public final class ComputerSelector
{
public static ServerComputer getComputer( String selector ) throws CommandException
{
if( selector.length() > 0 && selector.charAt( 0 ) == '#' )
{
selector = selector.substring( 1 );
int id;
try
{
id = Integer.parseInt( selector );
}
catch( NumberFormatException e )
{
throw new CommandException( "'" + selector + "' is not a valid number" );
}
// We copy it to prevent concurrent modifications.
List<ServerComputer> computers = Lists.newArrayList( ComputerCraft.serverComputerRegistry.getComputers() );
List<ServerComputer> candidates = Lists.newArrayList();
for( ServerComputer searchComputer : computers )
{
if( searchComputer.getID() == id )
{
candidates.add( searchComputer );
}
}
if( candidates.size() == 0 )
{
throw new CommandException( "No such computer for id " + id );
}
else if( candidates.size() == 1 )
{
return candidates.get( 0 );
}
else
{
StringBuilder builder = new StringBuilder( "Multiple computers with id " )
.append( id ).append( " (instances " );
boolean first = true;
for( ServerComputer computer : candidates )
{
if( first )
{
first = false;
}
else
{
builder.append( ", " );
}
builder.append( computer.getInstanceID() );
}
builder.append( ")" );
throw new CommandException( builder.toString() );
}
}
else
{
int instance;
try
{
instance = Integer.parseInt( selector );
}
catch( NumberFormatException e )
{
throw new CommandException( "'" + selector + "' is not a valid number" );
}
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance );
if( computer == null )
{
throw new CommandException( "No such computer for instance id " + instance );
}
else
{
return computer;
}
}
}
public static List<String> completeComputer( String selector )
{
Set<String> options = Sets.newHashSet();
// We copy it to prevent concurrent modifications.
List<ServerComputer> computers = Lists.newArrayList( ComputerCraft.serverComputerRegistry.getComputers() );
if( selector.length() > 0 && selector.charAt( 0 ) == '#' )
{
selector = selector.substring( 1 );
for( ServerComputer computer : computers )
{
String id = Integer.toString( computer.getID() );
if( id.startsWith( selector ) ) options.add( "#" + id );
}
}
else
{
for( ServerComputer computer : computers )
{
String id = Integer.toString( computer.getInstanceID() );
if( id.startsWith( selector ) ) options.add( id );
}
}
return Lists.newArrayList( options );
}
}

View File

@@ -0,0 +1,64 @@
package dan200.computercraft.shared.command;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.text.TextComponentTranslation;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ContainerViewComputer extends Container implements IContainerComputer
{
private final IComputer computer;
public ContainerViewComputer( IComputer computer )
{
this.computer = computer;
}
@Nullable
@Override
public IComputer getComputer()
{
return computer;
}
@Override
public boolean canInteractWith( @Nonnull EntityPlayer player )
{
if( computer instanceof ServerComputer )
{
ServerComputer serverComputer = (ServerComputer) computer;
// If this computer no longer exists then discard it.
if( ComputerCraft.serverComputerRegistry.get( serverComputer.getInstanceID() ) != serverComputer )
{
return false;
}
// If we're a command computer then ensure we're in creative
if( serverComputer.getFamily() == ComputerFamily.Command )
{
MinecraftServer server = player.getServer();
if( server == null || !server.isCommandBlockEnabled() )
{
player.sendMessage( new TextComponentTranslation( "advMode.notEnabled" ) );
return false;
}
else if( !ComputerCraft.canPlayerUseCommands( player ) || !player.capabilities.isCreativeMode )
{
player.sendMessage( new TextComponentTranslation( "advMode.notAllowed" ) );
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,117 @@
package dan200.computercraft.shared.command.framework;
import com.google.common.base.Strings;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.event.ClickEvent;
import net.minecraft.util.text.event.HoverEvent;
/**
* Various helpers for building chat messages
*/
public final class ChatHelpers
{
private static final TextFormatting HEADER = TextFormatting.LIGHT_PURPLE;
private static final TextFormatting SYNOPSIS = TextFormatting.AQUA;
private static final TextFormatting NAME = TextFormatting.GREEN;
public static ITextComponent coloured( String text, TextFormatting colour )
{
ITextComponent component = new TextComponentString( text == null ? "" : text );
component.getStyle().setColor( colour );
return component;
}
public static ITextComponent text( String text )
{
return new TextComponentString( text == null ? "" : text );
}
public static ITextComponent list( ITextComponent... children )
{
ITextComponent component = new TextComponentString( "" );
for( ITextComponent child : children )
{
component.appendSibling( child );
}
return component;
}
public static ITextComponent getHelp( CommandContext context, ISubCommand command, String prefix )
{
ITextComponent output = new TextComponentString( "" )
.appendSibling( coloured( "/" + prefix + " " + command.getUsage( context ), HEADER ) )
.appendText( " " )
.appendSibling( coloured( command.getSynopsis(), SYNOPSIS ) );
String desc = command.getDescription();
if( !Strings.isNullOrEmpty( desc ) ) output.appendText( "\n" + desc );
if( command instanceof CommandRoot )
{
for( ISubCommand subCommand : ((CommandRoot) command).getSubCommands().values() )
{
if( !subCommand.checkPermission( context ) ) continue;
output.appendText( "\n" );
ITextComponent component = coloured( subCommand.getName(), NAME );
component.getStyle().setClickEvent( new ClickEvent(
ClickEvent.Action.SUGGEST_COMMAND,
"/" + prefix + " " + subCommand.getName()
) );
output.appendSibling( component );
output.appendText( " - " + subCommand.getSynopsis() );
}
}
return output;
}
public static ITextComponent position( BlockPos pos )
{
if( pos == null ) return text( "<no pos>" );
return formatted( "%d, %d, %d", pos.getX(), pos.getY(), pos.getZ() );
}
public static ITextComponent bool( boolean value )
{
if( value )
{
ITextComponent component = new TextComponentString( "Y" );
component.getStyle().setColor( TextFormatting.GREEN );
return component;
}
else
{
ITextComponent component = new TextComponentString( "N" );
component.getStyle().setColor( TextFormatting.RED );
return component;
}
}
public static ITextComponent formatted( String format, Object... args )
{
return new TextComponentString( String.format( format, args ) );
}
public static ITextComponent link( ITextComponent component, String command, String toolTip )
{
Style style = component.getStyle();
if( style.getColor() == null ) style.setColor( TextFormatting.YELLOW );
style.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ) );
style.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TextComponentString( toolTip ) ) );
return component;
}
public static ITextComponent header( String text )
{
return coloured( text, HEADER );
}
}

View File

@@ -0,0 +1,93 @@
package dan200.computercraft.shared.command.framework;
import com.google.common.collect.Lists;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import java.util.Collections;
import java.util.List;
/**
* Represents the way a command was invoked, including the command sender, the current server and
* the "path" to this command.
*/
public final class CommandContext
{
private final MinecraftServer server;
private final ICommandSender sender;
private final List<ISubCommand> path;
public CommandContext( MinecraftServer server, ICommandSender sender, ISubCommand initial )
{
this.server = server;
this.sender = sender;
this.path = Collections.singletonList( initial );
}
private CommandContext( MinecraftServer server, ICommandSender sender, List<ISubCommand> path )
{
this.server = server;
this.sender = sender;
this.path = path;
}
public CommandContext enter( ISubCommand child )
{
List<ISubCommand> newPath = Lists.newArrayListWithExpectedSize( path.size() + 1 );
newPath.addAll( path );
newPath.add( child );
return new CommandContext( server, sender, newPath );
}
public CommandContext parent()
{
if( path.size() == 1 ) throw new IllegalStateException( "No parent command" );
return new CommandContext( server, sender, path.subList( 0, path.size() - 1 ) );
}
public String getFullPath()
{
StringBuilder out = new StringBuilder();
boolean first = true;
for( ISubCommand command : path )
{
if( first )
{
first = false;
}
else
{
out.append( ' ' );
}
out.append( command.getName() );
}
return out.toString();
}
public String getFullUsage()
{
return "/" + getFullPath() + " " + path.get( path.size() - 1 ).getUsage( this );
}
public List<ISubCommand> getPath()
{
return Collections.unmodifiableList( path );
}
public String getRootCommand()
{
return path.get( 0 ).getName();
}
public MinecraftServer getServer()
{
return server;
}
public ICommandSender getSender()
{
return sender;
}
}

View File

@@ -0,0 +1,91 @@
package dan200.computercraft.shared.command.framework;
import dan200.computercraft.ComputerCraft;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* {@link net.minecraft.command.ICommand} which delegates to a {@link ISubCommand}.
*/
public class CommandDelegate implements ICommand
{
private final ISubCommand command;
public CommandDelegate( ISubCommand command )
{
this.command = command;
}
@Nonnull
@Override
public String getName()
{
return command.getName();
}
@Nonnull
@Override
public String getUsage( @Nonnull ICommandSender sender )
{
return "/" + command.getName() + " " + command.getUsage( new CommandContext( sender.getServer(), sender, command ) );
}
@Nonnull
@Override
public List<String> getAliases()
{
return Collections.emptyList();
}
@Override
public void execute( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args ) throws CommandException
{
try
{
command.execute( new CommandContext( server, sender, command ), Arrays.asList( args ) );
}
catch( CommandException e )
{
throw e;
}
catch( Throwable e )
{
ComputerCraft.log.error( "Unhandled exception in command", e );
throw new CommandException( "Unhandled exception: " + e.toString() );
}
}
@Nonnull
@Override
public List<String> getTabCompletions( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args, @Nullable BlockPos pos )
{
return command.getCompletion( new CommandContext( server, sender, command ), Arrays.asList( args ) );
}
@Override
public boolean checkPermission( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender )
{
return command.checkPermission( new CommandContext( server, sender, command ) );
}
@Override
public boolean isUsernameIndex( @Nonnull String[] args, int index )
{
return false;
}
@Override
public int compareTo( @Nonnull ICommand o )
{
return getName().compareTo( o.getName() );
}
}

View File

@@ -0,0 +1,149 @@
package dan200.computercraft.shared.command.framework;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* A command which delegates to a series of sub commands
*/
public class CommandRoot implements ISubCommand
{
private final String name;
private final String synopsis;
private final String description;
private final Map<String, ISubCommand> subCommands = Maps.newHashMap();
public CommandRoot( String name, String synopsis, String description )
{
this.name = name;
this.synopsis = synopsis;
this.description = description;
register( new SubCommandHelp( this ) );
}
public void register( ISubCommand command )
{
subCommands.put( command.getName(), command );
}
@Nonnull
@Override
public String getName()
{
return name;
}
@Nonnull
@Override
public String getUsage( CommandContext context )
{
StringBuilder out = new StringBuilder( "<" );
boolean first = true;
for( ISubCommand command : subCommands.values() )
{
if( command.checkPermission( context ) )
{
if( first )
{
first = false;
}
else
{
out.append( "|" );
}
out.append( command.getName() );
}
}
return out.append( ">" ).toString();
}
@Nonnull
@Override
public String getSynopsis()
{
return synopsis;
}
@Nonnull
@Override
public String getDescription()
{
return description;
}
@Override
public boolean checkPermission( @Nonnull CommandContext context )
{
for( ISubCommand command : subCommands.values() )
{
if( command.checkPermission( context ) ) return true;
}
return false;
}
public Map<String, ISubCommand> getSubCommands()
{
return Collections.unmodifiableMap( subCommands );
}
@Override
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
{
if( arguments.size() == 0 )
{
context.getSender().sendMessage( ChatHelpers.getHelp( context, this, context.getFullPath() ) );
}
else
{
ISubCommand command = subCommands.get( arguments.get( 0 ) );
if( command == null || !command.checkPermission( context ) )
{
throw new CommandException( getName() + " " + getUsage( context ) );
}
command.execute( context.enter( command ), arguments.subList( 1, arguments.size() ) );
}
}
@Nonnull
@Override
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
{
if( arguments.size() == 0 )
{
return Lists.newArrayList( subCommands.keySet() );
}
else if( arguments.size() == 1 )
{
List<String> list = Lists.newArrayList();
String match = arguments.get( 0 );
for( ISubCommand command : subCommands.values() )
{
if( CommandBase.doesStringStartWith( match, command.getName() ) && command.checkPermission( context ) )
{
list.add( command.getName() );
}
}
return list;
}
else
{
ISubCommand command = subCommands.get( arguments.get( 0 ) );
if( command == null || !command.checkPermission( context ) ) return Collections.emptyList();
return command.getCompletion( context, arguments.subList( 1, arguments.size() ) );
}
}
}

View File

@@ -0,0 +1,80 @@
package dan200.computercraft.shared.command.framework;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import javax.annotation.Nonnull;
import java.util.List;
/**
* A slightly different implementation of {@link ICommand} which is delegated to.
*/
public interface ISubCommand
{
/**
* Get the name of this command
*
* @return The name of this command
* @see ICommand#getName()
*/
@Nonnull
String getName();
/**
* Get the usage of this command
*
* @param context The context this command is executed in
* @return The usage of this command
* @see ICommand#getUsage(ICommandSender)
*/
@Nonnull
String getUsage( CommandContext context );
/**
* Get a short description of this command, including its usage.
*
* @return The command's synopsis
*/
@Nonnull
String getSynopsis();
/**
* Get the lengthy description of this command. This synopsis is prepended to this.
*
* @return The command's description
*/
@Nonnull
String getDescription();
/**
* Determine whether a given command sender has permission to execute this command.
*
* @param context The current command context.
* @return Whether this command can be executed.
* @see ICommand#checkPermission(MinecraftServer, ICommandSender)
*/
boolean checkPermission( @Nonnull CommandContext context );
/**
* Execute this command
*
* @param context The current command context.
* @param arguments The arguments passed @throws CommandException When an error occurs
* @see ICommand#execute(MinecraftServer, ICommandSender, String[])
*/
void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException;
/**
* Get a list of possible completions
*
* @param context The current command context.
* @param arguments The arguments passed. You should complete the last one.
* @return List of possible completions
* @see ICommand#getTabCompletions(MinecraftServer, ICommandSender, String[], BlockPos)
*/
@Nonnull
List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments );
}

View File

@@ -0,0 +1,69 @@
package dan200.computercraft.shared.command.framework;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
public abstract class SubCommandBase implements ISubCommand
{
private final String name;
private final String usage;
private final String synopsis;
private final String description;
private final UserLevel level;
public SubCommandBase( String name, String usage, String synopsis, UserLevel level, String description )
{
this.name = name;
this.usage = usage;
this.synopsis = synopsis;
this.description = description;
this.level = level;
}
public SubCommandBase( String name, String synopsis, UserLevel level, String description )
{
this( name, "", synopsis, level, description );
}
@Nonnull
@Override
public String getName()
{
return name;
}
@Nonnull
@Override
public String getUsage( CommandContext context )
{
return usage;
}
@Nonnull
@Override
public String getSynopsis()
{
return synopsis;
}
@Nonnull
@Override
public String getDescription()
{
return description;
}
@Override
public boolean checkPermission( @Nonnull CommandContext context )
{
return level.canExecute( context );
}
@Nonnull
@Override
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
{
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,127 @@
package dan200.computercraft.shared.command.framework;
import com.google.common.collect.Lists;
import joptsimple.internal.Strings;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
public class SubCommandHelp implements ISubCommand
{
private final CommandRoot branchCommand;
public SubCommandHelp( CommandRoot branchCommand )
{
this.branchCommand = branchCommand;
}
@Nonnull
@Override
public String getName()
{
return "help";
}
@Nonnull
@Override
public String getUsage( CommandContext context )
{
return "[command]";
}
@Nonnull
@Override
public String getSynopsis()
{
return "Provide help for a specific command";
}
@Nonnull
@Override
public String getDescription()
{
return "";
}
@Override
public boolean checkPermission( @Nonnull CommandContext context )
{
return true;
}
@Override
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
{
ISubCommand command = branchCommand;
for( int i = 0; i < arguments.size(); i++ )
{
String commandName = arguments.get( i );
if( command instanceof CommandRoot )
{
command = ((CommandRoot) command).getSubCommands().get( commandName );
}
else
{
throw new CommandException( Strings.join( arguments.subList( 0, i ), " " ) + " has no sub-commands" );
}
if( command == null )
{
throw new CommandException( "No such command " + Strings.join( arguments.subList( 0, i + 1 ), " " ) );
}
}
StringBuilder prefix = new StringBuilder( context.parent().getFullPath() );
for( String argument : arguments )
{
prefix.append( ' ' ).append( argument );
}
context.getSender().sendMessage( ChatHelpers.getHelp( context, command, prefix.toString() ) );
}
@Nonnull
@Override
public List<String> getCompletion( @Nonnull CommandContext context, @Nonnull List<String> arguments )
{
CommandRoot command = branchCommand;
for( int i = 0; i < arguments.size() - 1; i++ )
{
String commandName = arguments.get( i );
ISubCommand subCommand = command.getSubCommands().get( commandName );
if( subCommand instanceof CommandRoot )
{
command = (CommandRoot) subCommand;
}
else
{
return Collections.emptyList();
}
}
if( arguments.size() == 0 )
{
return Lists.newArrayList( command.getSubCommands().keySet() );
}
else
{
List<String> list = Lists.newArrayList();
String match = arguments.get( arguments.size() - 1 );
for( String entry : command.getSubCommands().keySet() )
{
if( CommandBase.doesStringStartWith( match, entry ) )
{
list.add( entry );
}
}
return list;
}
}
}

View File

@@ -0,0 +1,250 @@
package dan200.computercraft.shared.command.framework;
import com.google.common.collect.Lists;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.common.util.FakePlayer;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nonnull;
import java.util.List;
import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured;
import static dan200.computercraft.shared.command.framework.ChatHelpers.text;
public class TextTable
{
private static final String CHARACTERS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
private static final int[] CHAR_WIDTHS = new int[] {
6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6, 4,
4, 6, 7, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1,
1, 2, 5, 6, 6, 6, 6, 3, 5, 5, 5, 6, 2, 6, 2, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 6,
7, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 4, 6, 6,
3, 6, 6, 6, 6, 6, 5, 6, 6, 2, 6, 5, 3, 6, 6, 6,
6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 5, 2, 5, 7, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 3, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6,
6, 3, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 2, 6, 6,
8, 9, 9, 6, 6, 6, 8, 8, 6, 8, 8, 8, 8, 8, 6, 6,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 9, 9, 9, 5, 9, 9,
8, 7, 7, 8, 7, 8, 8, 8, 7, 8, 8, 7, 9, 9, 6, 7,
7, 7, 7, 7, 9, 6, 7, 8, 7, 6, 6, 9, 7, 6, 7, 1
};
private static final ITextComponent SEPARATOR = coloured( " | ", TextFormatting.GRAY );
private static final ITextComponent LINE = text( "\n" );
private static int getWidth( char character, ICommandSender sender )
{
if( sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer) )
{
// Use font widths here.
if( character == 167 )
{
return -1;
}
else if( character == 32 )
{
return 4;
}
else if( CHARACTERS.indexOf( character ) != -1 )
{
return CHAR_WIDTHS[ character ];
}
else
{
// Eh, close enough.
return 6;
}
}
else
{
return 1;
}
}
private static int getWidth( ITextComponent text, ICommandSender sender )
{
int sum = 0;
String chars = text.getUnformattedText();
for( int i = 0; i < chars.length(); i++ )
{
sum += getWidth( chars.charAt( i ), sender );
}
return sum;
}
private static boolean isPlayer( ICommandSender sender )
{
return sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer);
}
private static int getMaxWidth( ICommandSender sender )
{
return isPlayer( sender ) ? 320 : 80;
}
private int columns = -1;
private final ITextComponent[] header;
private final List<ITextComponent[]> rows = Lists.newArrayList();
public TextTable( @Nonnull ITextComponent... header )
{
this.header = header;
this.columns = header.length;
}
public TextTable()
{
header = null;
}
public TextTable( @Nonnull String... header )
{
this.header = new ITextComponent[ header.length ];
for( int i = 0; i < header.length; i++ )
{
this.header[ i ] = ChatHelpers.header( header[ i ] );
}
this.columns = header.length;
}
public void addRow( @Nonnull ITextComponent... row )
{
if( columns == -1 )
{
columns = row.length;
}
else if( row.length != columns )
{
throw new IllegalArgumentException( "Row is the incorrect length" );
}
rows.add( row );
}
public void displayTo( ICommandSender sender )
{
if( columns <= 0 ) return;
final int maxWidth = getMaxWidth( sender );
int[] minWidths = new int[ columns ];
int[] maxWidths = new int[ columns ];
int[] rowWidths = new int[ columns ];
if( header != null )
{
for( int i = 0; i < columns; i++ )
{
maxWidths[ i ] = minWidths[ i ] = getWidth( header[ i ], sender );
}
}
for( ITextComponent[] row : rows )
{
for( int i = 0; i < row.length; i++ )
{
int width = getWidth( row[ i ], sender );
rowWidths[ i ] += width;
if( width > maxWidths[ i ] )
{
maxWidths[ i ] = width;
}
}
}
// Calculate the average width
for( int i = 0; i < columns; i++ )
{
rowWidths[ i ] = Math.max( rowWidths[ i ], rows.size() );
}
int totalWidth = (columns - 1) * getWidth( SEPARATOR, sender );
for( int x : maxWidths ) totalWidth += x;
// TODO: Limit the widths of some entries if totalWidth > maxWidth
ITextComponent out = new TextComponentString( "" );
if( header != null )
{
for( int i = 0; i < columns; i++ )
{
if( i != 0 ) out.appendSibling( SEPARATOR );
appendFixed( out, sender, header[ i ], maxWidths[ i ] );
}
out.appendSibling( LINE );
// Round the width up rather than down
int rowCharWidth = getWidth( '=', sender );
int rowWidth = totalWidth / rowCharWidth + (totalWidth % rowCharWidth == 0 ? 0 : 1);
out.appendSibling( coloured( StringUtils.repeat( '=', rowWidth ), TextFormatting.GRAY ) );
out.appendSibling( LINE );
}
for( int i = 0; i < rows.size(); i++ )
{
ITextComponent[] row = rows.get( i );
if( i != 0 ) out.appendSibling( LINE );
for( int j = 0; j < columns; j++ )
{
if( j != 0 ) out.appendSibling( SEPARATOR );
appendFixed( out, sender, row[ j ], maxWidths[ j ] );
}
}
sender.sendMessage( out );
}
private static void appendFixed( ITextComponent out, ICommandSender sender, ITextComponent entry, int maxWidth )
{
int length = getWidth( entry, sender );
int delta = length - maxWidth;
if( delta < 0 )
{
// Convert to overflow;
delta = -delta;
// We have to remove some padding as there is a padding added between formatted and unformatted text
if( !entry.getStyle().isEmpty() && isPlayer( sender ) ) delta -= 1;
out.appendSibling( entry );
int spaceWidth = getWidth( ' ', sender );
int spaces = delta / spaceWidth;
int missing = delta % spaceWidth;
spaces -= missing;
ITextComponent component = new TextComponentString( StringUtils.repeat( ' ', spaces < 0 ? 0 : spaces ) );
if( missing > 0 )
{
ITextComponent bold = new TextComponentString( StringUtils.repeat( ' ', missing ) );
bold.getStyle().setBold( true );
component.appendSibling( bold );
}
out.appendSibling( component );
}
else if( delta > 0 )
{
out.appendSibling( entry );
}
else
{
out.appendSibling( entry );
// We have to add some padding as we expect a padding between formatted and unformatted text
// and there won't be.
if( entry.getStyle().isEmpty() && isPlayer( sender ) ) out.appendText( " " );
}
}
}

View File

@@ -0,0 +1,63 @@
package dan200.computercraft.shared.command.framework;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
/**
* The level a user must be at in order to execute a command.
*/
public enum UserLevel
{
/**
* Only can be used by the owner of the server: namely the server console or the player in SSP.
*/
OWNER,
/**
* Can only be used by ops.
*/
OP,
/**
* Can be used by any op, or the player in SSP.
*/
OWNER_OP,
/**
* Can be used by anyone.
*/
ANYONE;
public int toLevel()
{
switch( this )
{
case OWNER:
return 4;
case OP:
case OWNER_OP:
return 2;
case ANYONE:
default:
return 0;
}
}
public boolean canExecute( CommandContext context )
{
if( this == ANYONE ) return true;
// We *always* allow level 0 stuff, even if the
MinecraftServer server = context.getServer();
ICommandSender sender = context.getSender();
if( server.isSinglePlayer() && sender instanceof EntityPlayerMP &&
((EntityPlayerMP) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerOwner() ) )
{
if( this == OWNER || this == OWNER_OP ) return true;
}
return sender.canUseCommand( toLevel(), context.getRootCommand() );
}
}

View File

@@ -241,10 +241,9 @@ public abstract class BlockGeneric extends Block implements
if( collision.size() > 0 )
{
AxisAlignedBB aabb = collision.get( 0 );
for (int i=1; i<collision.size(); ++i )
for( int i = 1; i < collision.size(); i++ )
{
AxisAlignedBB other = collision.get( 1 );
aabb = aabb.union( other );
aabb = aabb.union( collision.get( i ) );
}
return aabb;
}

View File

@@ -8,9 +8,9 @@ package dan200.computercraft.shared.computer.apis;
import com.google.common.collect.ImmutableMap;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
@@ -49,21 +49,6 @@ public class CommandAPI implements ILuaAPI
};
}
@Override
public void startup()
{
}
@Override
public void advance( double dt )
{
}
@Override
public void shutdown()
{
}
@Nonnull
@Override
public String[] getMethodNames()

View File

@@ -1,9 +1,9 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.IComputer;
/**
* A proxy object for computer objects, delegating to {@link ServerComputer} or {@link TileComputer} where appropriate.
* A proxy object for computer objects, delegating to {@link IComputer} or {@link TileComputer} where appropriate.
*/
public abstract class ComputerProxy
{
@@ -12,7 +12,7 @@ public abstract class ComputerProxy
public void turnOn()
{
TileComputerBase tile = getTile();
ServerComputer computer = tile.getServerComputer();
IComputer computer = tile.getComputer();
if( computer == null )
{
tile.m_startOn = true;
@@ -26,7 +26,7 @@ public abstract class ComputerProxy
public void shutdown()
{
TileComputerBase tile = getTile();
ServerComputer computer = tile.getServerComputer();
IComputer computer = tile.getComputer();
if( computer == null )
{
tile.m_startOn = false;
@@ -40,7 +40,7 @@ public abstract class ComputerProxy
public void reboot()
{
TileComputerBase tile = getTile();
ServerComputer computer = tile.getServerComputer();
IComputer computer = tile.getComputer();
if( computer == null )
{
tile.m_startOn = true;
@@ -54,7 +54,7 @@ public abstract class ComputerProxy
public int assignID()
{
TileComputerBase tile = getTile();
ServerComputer computer = tile.getServerComputer();
IComputer computer = tile.getComputer();
if( computer == null )
{
return tile.m_computerID;
@@ -67,14 +67,14 @@ public abstract class ComputerProxy
public boolean isOn()
{
ServerComputer computer = getTile().getServerComputer();
IComputer computer = getTile().getComputer();
return computer != null && computer.isOn();
}
public String getLabel()
{
TileComputerBase tile = getTile();
ServerComputer computer = tile.getServerComputer();
IComputer computer = tile.getComputer();
if( computer == null )
{
return tile.m_label;

View File

@@ -10,9 +10,9 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.IComputerEnvironment;
import dan200.computercraft.shared.common.ServerTerminal;
@@ -100,8 +100,7 @@ public class ServerComputer extends ServerTerminal
super.update();
m_computer.advance( 0.05 );
m_changedLastFrame = m_changed || m_computer.pollChanged();
m_computer.clearChanged();
m_changedLastFrame = m_computer.pollAndResetChanged() || m_changed;
m_changed = false;
m_ticksSincePing++;
@@ -314,6 +313,11 @@ public class ServerComputer extends ServerTerminal
m_computer.addAPI( api );
}
public void addAPI( dan200.computercraft.core.apis.ILuaAPI api )
{
m_computer.addAPI( api );
}
public void setPeripheral( int side, IPeripheral peripheral )
{
m_computer.setPeripheral( side, peripheral );

View File

@@ -290,7 +290,8 @@ public class TileMonitor extends TilePeripheralBase
public double getTextScale()
{
return m_textScale * 0.5;
TileMonitor origin = getOrigin();
return (origin == null ? m_textScale : origin.m_textScale) * 0.5;
}
private void rebuildTerminal()

View File

@@ -7,10 +7,10 @@
package dan200.computercraft.shared.pocket.apis;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
@@ -39,21 +39,6 @@ public class PocketAPI implements ILuaAPI
};
}
@Override
public void startup()
{
}
@Override
public void advance( double dt )
{
}
@Override
public void shutdown()
{
}
@Nonnull
@Override
public String[] getMethodNames()

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.shared.command.ContainerViewComputer;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.common.TileGeneric;
@@ -17,10 +18,7 @@ import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.*;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
import dan200.computercraft.shared.computer.items.ItemComputer;
@@ -184,6 +182,9 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
@Override
public abstract Object getPocketComputerGUI( EntityPlayer player, EnumHand hand );
@Override
public abstract Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family );
@Override
public abstract File getWorldDir( World world );
@@ -553,6 +554,11 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
{
return new ContainerPocketComputer( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
}
case ComputerCraft.viewComputerGUIID:
{
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( x );
return computer == null ? null : new ContainerViewComputer( computer );
}
}
return null;
}
@@ -611,6 +617,27 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
{
return getPocketComputerGUI( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
}
case ComputerCraft.viewComputerGUIID:
{
ClientComputer computer = ComputerCraft.clientComputerRegistry.get( x );
// We extract some terminal information from the various coordinate flags.
// See ComputerCraft.openComputerGUI for how they are packed.
ComputerFamily family = ComputerFamily.values()[ y ];
int width = (z >> 16) & 0xFFFF, height = z & 0xFF;
if( computer == null )
{
computer = new ClientComputer( x );
ComputerCraft.clientComputerRegistry.add( x, computer );
}
else if( computer.getTerminal() != null )
{
width = computer.getTerminal().getWidth();
height = computer.getTerminal().getHeight();
}
return getComputerGUI( computer, width, height, family );
}
}
return null;
}

View File

@@ -7,6 +7,8 @@
package dan200.computercraft.shared.proxy;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.network.ComputerCraftPacket;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
@@ -42,6 +44,7 @@ public interface IComputerCraftProxy
Object getTurtleGUI( InventoryPlayer inventory, TileTurtle turtle );
Object getPrintoutGUI( EntityPlayer player, EnumHand hand );
Object getPocketComputerGUI( EntityPlayer player, EnumHand hand );
Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family );
File getWorldDir( World world );
void handlePacket( ComputerCraftPacket packet, EntityPlayer player );

View File

@@ -6,13 +6,13 @@
package dan200.computercraft.shared.turtle.apis;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.shared.turtle.core.*;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -44,21 +44,6 @@ public class TurtleAPI implements ILuaAPI
"turtle"
};
}
@Override
public void startup( )
{
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{
}
@Nonnull
@Override

View File

@@ -1,3 +1,7 @@
# RecordMedia (and related methods)
public net.minecraft.item.ItemRecord field_185076_b # sound
public net.minecraft.item.ItemRecord field_185077_c # displayName
# ItemPocketRenderer
public net.minecraft.client.renderer.ItemRenderer func_187466_c()V # renderArms
public net.minecraft.client.renderer.ItemRenderer func_178100_c(F)F # getMapAngleFromPitch
public net.minecraft.client.renderer.ItemRenderer func_187456_a(FFLnet/minecraft/util/EnumHandSide;)V # renderArmFirstPerson

View File

@@ -737,6 +737,28 @@ if http then
if url == _url then return ok, err end
end
end
local nativeWebsocket = http.websocket
http.websocketAsync = nativeWebsocket
http.websocket = function( _url, _headers )
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
end
local ok, err = nativeWebsocket( _url, _headers )
if not ok then return ok, err end
while true do
local event, url, param = os.pullEvent( )
if event == "websocket_success" and url == _url then
return param
elseif event == "websocket_failure" and url == _url then
return false, param
end
end
end
end
-- Install the lua part of the FS api