mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-11-03 23:22:59 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			mc-1.14.x
			...
			v1.15.2-1.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3d7a81696d | ||
| 
						 | 
					33260a7747 | ||
| 
						 | 
					af40f5ae5c | ||
| 
						 | 
					759d02a249 | ||
| 
						 | 
					d7729337ac | ||
| 
						 | 
					f106733d71 | ||
| 
						 | 
					2360a6e951 | ||
| 
						 | 
					f4f71185ae | ||
| 
						 | 
					649acbae1c | ||
| 
						 | 
					05eada427b | ||
| 
						 | 
					f3a330e330 | ||
| 
						 | 
					044d2b2b06 | ||
| 
						 | 
					fb440b0d2e | 
							
								
								
									
										14
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								build.gradle
									
									
									
									
									
								
							@@ -9,7 +9,7 @@ buildscript {
 | 
			
		||||
    }
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath 'com.google.code.gson:gson:2.8.1'
 | 
			
		||||
        classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.154'
 | 
			
		||||
        classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.169'
 | 
			
		||||
        classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
 | 
			
		||||
        classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
 | 
			
		||||
    }
 | 
			
		||||
@@ -47,7 +47,7 @@ minecraft {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        server {
 | 
			
		||||
            workingDirectory project.file('run')
 | 
			
		||||
            workingDirectory project.file("run/server-${mc_version}")
 | 
			
		||||
            property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
 | 
			
		||||
            property 'forge.logging.console.level', 'debug'
 | 
			
		||||
 | 
			
		||||
@@ -99,10 +99,10 @@ dependencies {
 | 
			
		||||
 | 
			
		||||
    minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
 | 
			
		||||
 | 
			
		||||
    compileOnly fg.deobf("mezz.jei:jei-1.14.4:6.0.0.27:api")
 | 
			
		||||
    compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.14.4:5.0.1.162")
 | 
			
		||||
    compileOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3:api")
 | 
			
		||||
    compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.15.2:6.0.0.9")
 | 
			
		||||
 | 
			
		||||
    runtimeOnly fg.deobf("mezz.jei:jei-1.14.4:6.0.0.27")
 | 
			
		||||
    runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3")
 | 
			
		||||
 | 
			
		||||
    shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT'
 | 
			
		||||
 | 
			
		||||
@@ -373,7 +373,7 @@ curseforge {
 | 
			
		||||
    apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
 | 
			
		||||
    project {
 | 
			
		||||
        id = '282001'
 | 
			
		||||
        releaseType = 'release'
 | 
			
		||||
        releaseType = 'alpha'
 | 
			
		||||
        changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
 | 
			
		||||
 | 
			
		||||
        relations {
 | 
			
		||||
@@ -451,7 +451,7 @@ githubRelease {
 | 
			
		||||
            .takeWhile { it != 'Type "help changelog" to see the full version history.' }
 | 
			
		||||
            .join("\n").trim()
 | 
			
		||||
    }
 | 
			
		||||
    prerelease false
 | 
			
		||||
    prerelease true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"]
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,10 @@
 | 
			
		||||
        <property name="file" value="config/checkstyle/suppressions.xml" />
 | 
			
		||||
    </module>
 | 
			
		||||
 | 
			
		||||
    <module name="BeforeExecutionExclusionFileFilter">
 | 
			
		||||
        <property name="fileNamePattern" value="render_old"/>
 | 
			
		||||
    </module>
 | 
			
		||||
 | 
			
		||||
    <module name="TreeWalker">
 | 
			
		||||
        <!-- Annotations -->
 | 
			
		||||
        <module name="AnnotationLocation" />
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,6 @@
 | 
			
		||||
mod_version=1.87.0
 | 
			
		||||
 | 
			
		||||
# Minecraft properties (update mods.toml when changing)
 | 
			
		||||
mc_version=1.14.4
 | 
			
		||||
forge_version=28.1.71
 | 
			
		||||
mappings_version=20191123-1.14.3
 | 
			
		||||
mc_version=1.15.2
 | 
			
		||||
forge_version=31.1.41
 | 
			
		||||
mappings_version=20200410-1.15.1
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
package dan200.computercraft;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.turtle.event.TurtleAction;
 | 
			
		||||
import dan200.computercraft.core.apis.AddressPredicate;
 | 
			
		||||
import dan200.computercraft.core.apis.http.AddressRule;
 | 
			
		||||
import dan200.computercraft.shared.Config;
 | 
			
		||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
 | 
			
		||||
@@ -39,8 +39,13 @@ import org.apache.logging.log4j.Logger;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.EnumSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
@Mod( ComputerCraft.MOD_ID )
 | 
			
		||||
public final class ComputerCraft
 | 
			
		||||
@@ -50,8 +55,8 @@ public final class ComputerCraft
 | 
			
		||||
    public static final int DATAFIXER_VERSION = 0;
 | 
			
		||||
 | 
			
		||||
    // Configuration options
 | 
			
		||||
    public static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
 | 
			
		||||
    public static final String[] DEFAULT_HTTP_BLACKLIST = new String[] {
 | 
			
		||||
    public static final String[] DEFAULT_HTTP_ALLOW = new String[] { "*" };
 | 
			
		||||
    public static final String[] DEFAULT_HTTP_DENY = new String[] {
 | 
			
		||||
        "127.0.0.0/8",
 | 
			
		||||
        "10.0.0.0/8",
 | 
			
		||||
        "172.16.0.0/12",
 | 
			
		||||
@@ -71,10 +76,12 @@ public final class ComputerCraft
 | 
			
		||||
    public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( 10 );
 | 
			
		||||
    public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( 5 );
 | 
			
		||||
 | 
			
		||||
    public static boolean http_enable = true;
 | 
			
		||||
    public static boolean http_websocket_enable = true;
 | 
			
		||||
    public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
 | 
			
		||||
    public static AddressPredicate http_blacklist = new AddressPredicate( DEFAULT_HTTP_BLACKLIST );
 | 
			
		||||
    public static boolean httpEnabled = true;
 | 
			
		||||
    public static boolean httpWebsocketEnabled = true;
 | 
			
		||||
    public static List<AddressRule> httpRules = Collections.unmodifiableList( Stream.concat(
 | 
			
		||||
        Stream.of( DEFAULT_HTTP_DENY ).map( x -> AddressRule.parse( x, AddressRule.Action.DENY ) ).filter( Objects::nonNull ),
 | 
			
		||||
        Stream.of( DEFAULT_HTTP_ALLOW ).map( x -> AddressRule.parse( x, AddressRule.Action.ALLOW ) ).filter( Objects::nonNull )
 | 
			
		||||
    ).collect( Collectors.toList() ) );
 | 
			
		||||
 | 
			
		||||
    public static int httpTimeout = 30000;
 | 
			
		||||
    public static int httpMaxRequests = 16;
 | 
			
		||||
 
 | 
			
		||||
@@ -112,31 +112,6 @@ public final class ComputerCraftAPI
 | 
			
		||||
        return getInstance().createResourceMount( domain, subPath );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a file system mount to a resource folder, and returns it.
 | 
			
		||||
     *
 | 
			
		||||
     * Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a
 | 
			
		||||
     * resource folder onto a computer's file system.
 | 
			
		||||
     *
 | 
			
		||||
     * The files in this mount will be a combination of files in all mod jar, and data packs that contain
 | 
			
		||||
     * resources with the same domain and path.
 | 
			
		||||
     *
 | 
			
		||||
     * @param klass   The mod class to which the files belong.
 | 
			
		||||
     * @param domain  The domain under which to look for resources. eg: "mymod".
 | 
			
		||||
     * @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
 | 
			
		||||
     * @return The mount, or {@code null} if it could be created for some reason.
 | 
			
		||||
     * @see IComputerAccess#mount(String, IMount)
 | 
			
		||||
     * @see IComputerAccess#mountWritable(String, IWritableMount)
 | 
			
		||||
     * @see IMount
 | 
			
		||||
     * @deprecated Use {@link #createResourceMount(String, String)} instead.
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public static IMount createResourceMount( Class<?> klass, @Nonnull String domain, @Nonnull String subPath )
 | 
			
		||||
    {
 | 
			
		||||
        return getInstance().createResourceMount( domain, subPath );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of the public ComputerCraft API - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
 | 
			
		||||
 * For help using the API, and posting your mods, visit the forums at computercraft.info.
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.api.client;
 | 
			
		||||
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.TransformationMatrix;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelManager;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A model to render, combined with a transformation matrix to apply.
 | 
			
		||||
 */
 | 
			
		||||
public final class TransformedModel
 | 
			
		||||
{
 | 
			
		||||
    private final IBakedModel model;
 | 
			
		||||
    private final TransformationMatrix matrix;
 | 
			
		||||
 | 
			
		||||
    public TransformedModel( @Nonnull IBakedModel model, @Nonnull TransformationMatrix matrix )
 | 
			
		||||
    {
 | 
			
		||||
        this.model = Objects.requireNonNull( model );
 | 
			
		||||
        this.matrix = Objects.requireNonNull( matrix );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TransformedModel( @Nonnull IBakedModel model )
 | 
			
		||||
    {
 | 
			
		||||
        this.model = Objects.requireNonNull( model );
 | 
			
		||||
        this.matrix = TransformationMatrix.identity();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static TransformedModel of( @Nonnull ModelResourceLocation location )
 | 
			
		||||
    {
 | 
			
		||||
        ModelManager modelManager = Minecraft.getInstance().getModelManager();
 | 
			
		||||
        return new TransformedModel( modelManager.getModel( location ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static TransformedModel of( @Nonnull ItemStack item, @Nonnull TransformationMatrix transform )
 | 
			
		||||
    {
 | 
			
		||||
        IBakedModel model = Minecraft.getInstance().getItemRenderer().getItemModelMesher().getItemModel( item );
 | 
			
		||||
        return new TransformedModel( model, transform );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public IBakedModel getModel()
 | 
			
		||||
    {
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public TransformationMatrix getMatrix()
 | 
			
		||||
    {
 | 
			
		||||
        return matrix;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,8 +11,6 @@ import net.minecraft.world.World;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.nio.channels.Channels;
 | 
			
		||||
import java.nio.channels.ReadableByteChannel;
 | 
			
		||||
import java.nio.file.attribute.BasicFileAttributes;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -23,10 +21,10 @@ import java.util.List;
 | 
			
		||||
 *
 | 
			
		||||
 * Ready made implementations of this interface can be created using
 | 
			
		||||
 * {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or
 | 
			
		||||
 * {@link ComputerCraftAPI#createResourceMount(Class, String, String)}, or you're free to implement it yourselves!
 | 
			
		||||
 * {@link ComputerCraftAPI#createResourceMount(String, String)}, or you're free to implement it yourselves!
 | 
			
		||||
 *
 | 
			
		||||
 * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
 | 
			
		||||
 * @see ComputerCraftAPI#createResourceMount(Class, String, String)
 | 
			
		||||
 * @see ComputerCraftAPI#createResourceMount(String, String)
 | 
			
		||||
 * @see IComputerAccess#mount(String, IMount)
 | 
			
		||||
 * @see IWritableMount
 | 
			
		||||
 */
 | 
			
		||||
@@ -68,18 +66,6 @@ public interface IMount
 | 
			
		||||
     */
 | 
			
		||||
    long getSize( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a file with a given path, and returns an {@link InputStream} representing its contents.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
 | 
			
		||||
     * @return A stream representing the contents of the file.
 | 
			
		||||
     * @throws IOException If the file does not exist, or could not be opened.
 | 
			
		||||
     * @deprecated Use {@link #openChannelForRead(String)} instead
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    InputStream openForRead( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents.
 | 
			
		||||
     *
 | 
			
		||||
@@ -90,10 +76,7 @@ public interface IMount
 | 
			
		||||
     * @throws IOException If the file does not exist, or could not be opened.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newChannel( openForRead( path ) );
 | 
			
		||||
    }
 | 
			
		||||
    ReadableByteChannel openForRead( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get attributes about the given file.
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ import net.minecraft.world.World;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.nio.channels.Channels;
 | 
			
		||||
import java.nio.channels.WritableByteChannel;
 | 
			
		||||
import java.util.OptionalLong;
 | 
			
		||||
 | 
			
		||||
@@ -46,18 +45,6 @@ public interface IWritableMount extends IMount
 | 
			
		||||
     */
 | 
			
		||||
    void delete( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
 | 
			
		||||
     * @return A stream for writing to
 | 
			
		||||
     * @throws IOException If the file could not be opened for writing.
 | 
			
		||||
     * @deprecated Use {@link #openChannelForWrite(String)} instead.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    OutputStream openForWrite( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
 | 
			
		||||
     *
 | 
			
		||||
@@ -67,22 +54,7 @@ public interface IWritableMount extends IMount
 | 
			
		||||
     * @throws IOException If the file could not be opened for writing.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    default WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newChannel( openForWrite( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
 | 
			
		||||
     * @return A stream for writing to.
 | 
			
		||||
     * @throws IOException If the file could not be opened for writing.
 | 
			
		||||
     * @deprecated Use {@link #openChannelForAppend(String)} instead.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    OutputStream openForAppend( @Nonnull String path ) throws IOException;
 | 
			
		||||
    WritableByteChannel openForWrite( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
 | 
			
		||||
@@ -93,10 +65,7 @@ public interface IWritableMount extends IMount
 | 
			
		||||
     * @throws IOException If the file could not be opened for writing.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newChannel( openForAppend( path ) );
 | 
			
		||||
    }
 | 
			
		||||
    WritableByteChannel openForAppend( @Nonnull String path ) throws IOException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,7 @@ public interface IMedia
 | 
			
		||||
     * @see IMount
 | 
			
		||||
     * @see dan200.computercraft.api.filesystem.IWritableMount
 | 
			
		||||
     * @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(World, String, long)
 | 
			
		||||
     * @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(Class, String, String)
 | 
			
		||||
     * @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String)
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ import net.minecraft.world.World;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -31,9 +30,9 @@ public interface IComputerAccess
 | 
			
		||||
     * @param mount           The mount object to mount on the computer.
 | 
			
		||||
     * @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a
 | 
			
		||||
     * file in the desired location. Store this value if you wish to unmount the mount later.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(Class, String, String)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(String, String)
 | 
			
		||||
     * @see #mount(String, IMount, String)
 | 
			
		||||
     * @see #mountWritable(String, IWritableMount)
 | 
			
		||||
     * @see #unmount(String)
 | 
			
		||||
@@ -53,9 +52,9 @@ public interface IComputerAccess
 | 
			
		||||
     * @param driveName       A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
 | 
			
		||||
     * @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a
 | 
			
		||||
     * file in the desired location. Store this value if you wish to unmount the mount later.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(Class, String, String)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(String, String)
 | 
			
		||||
     * @see #mount(String, IMount)
 | 
			
		||||
     * @see #mountWritable(String, IWritableMount)
 | 
			
		||||
     * @see #unmount(String)
 | 
			
		||||
@@ -71,9 +70,9 @@ public interface IComputerAccess
 | 
			
		||||
     * @param mount           The mount object to mount on the computer.
 | 
			
		||||
     * @return The location on the computer's file system where you the mount mounted, or null if there was already a
 | 
			
		||||
     * file in the desired location. Store this value if you wish to unmount the mount later.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(Class, String, String)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(String, String)
 | 
			
		||||
     * @see #mount(String, IMount)
 | 
			
		||||
     * @see #unmount(String)
 | 
			
		||||
     * @see IMount
 | 
			
		||||
@@ -92,9 +91,9 @@ public interface IComputerAccess
 | 
			
		||||
     * @param driveName       A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
 | 
			
		||||
     * @return The location on the computer's file system where you the mount mounted, or null if there was already a
 | 
			
		||||
     * file in the desired location. Store this value if you wish to unmount the mount later.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(Class, String, String)
 | 
			
		||||
     * @see ComputerCraftAPI#createResourceMount(String, String)
 | 
			
		||||
     * @see #mount(String, IMount)
 | 
			
		||||
     * @see #unmount(String)
 | 
			
		||||
     * @see IMount
 | 
			
		||||
@@ -114,8 +113,8 @@ public interface IComputerAccess
 | 
			
		||||
     * @param location The desired location in the computers file system of the directory to unmount.
 | 
			
		||||
     *                 This must be the location of a directory previously mounted by {@link #mount(String, IMount)} or
 | 
			
		||||
     *                 {@link #mountWritable(String, IWritableMount)}, as indicated by their return value.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws RuntimeException If the mount does not exist, or was mounted by another peripheral.
 | 
			
		||||
     * @throws NotAttachedException  If the peripheral has been detached.
 | 
			
		||||
     * @throws IllegalStateException If the mount does not exist, or was mounted by another peripheral.
 | 
			
		||||
     * @see #mount(String, IMount)
 | 
			
		||||
     * @see #mountWritable(String, IWritableMount)
 | 
			
		||||
     */
 | 
			
		||||
@@ -146,7 +145,7 @@ public interface IComputerAccess
 | 
			
		||||
     *                  to lua data types in the same fashion as the return values of IPeripheral.callMethod().
 | 
			
		||||
     *
 | 
			
		||||
     *                  You may supply {@code null} to indicate that no arguments are to be supplied.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     * @see IPeripheral#callMethod
 | 
			
		||||
     */
 | 
			
		||||
    void queueEvent( @Nonnull String event, @Nullable Object[] arguments );
 | 
			
		||||
@@ -159,7 +158,7 @@ public interface IComputerAccess
 | 
			
		||||
     * which peripheral the event came.
 | 
			
		||||
     *
 | 
			
		||||
     * @return A string unique to the computer, but not globally.
 | 
			
		||||
     * @throws RuntimeException If the peripheral has been detached.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    String getAttachmentName();
 | 
			
		||||
@@ -170,14 +169,12 @@ public interface IComputerAccess
 | 
			
		||||
     * This may include other peripherals on the wired network or peripherals on other sides of the computer.
 | 
			
		||||
     *
 | 
			
		||||
     * @return All reachable peripherals
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     * @see #getAttachmentName()
 | 
			
		||||
     * @see #getAvailablePeripheral(String)
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    default Map<String, IPeripheral> getAvailablePeripherals()
 | 
			
		||||
    {
 | 
			
		||||
        return Collections.emptyMap();
 | 
			
		||||
    }
 | 
			
		||||
    Map<String, IPeripheral> getAvailablePeripherals();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a reachable peripheral with the given attachment name. This is a equivalent to
 | 
			
		||||
@@ -188,10 +185,7 @@ public interface IComputerAccess
 | 
			
		||||
     * @see #getAvailablePeripherals()
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    default IPeripheral getAvailablePeripheral( @Nonnull String name )
 | 
			
		||||
    {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    IPeripheral getAvailablePeripheral( @Nonnull String name );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread.
 | 
			
		||||
@@ -205,10 +199,8 @@ public interface IComputerAccess
 | 
			
		||||
     * thread.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The work monitor for the main thread, or {@code null} if this computer does not have one.
 | 
			
		||||
     * @throws NotAttachedException If the peripheral has been detached.
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    default IWorkMonitor getMainThreadMonitor()
 | 
			
		||||
    {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    IWorkMonitor getMainThreadMonitor();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of the public ComputerCraft API - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
 | 
			
		||||
 * For help using the API, and posting your mods, visit the forums at computercraft.info.
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.api.peripheral;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Thrown when performing operations on {@link IComputerAccess} when the current peripheral is no longer attached to
 | 
			
		||||
 * the computer.
 | 
			
		||||
 */
 | 
			
		||||
public class NotAttachedException extends IllegalStateException
 | 
			
		||||
{
 | 
			
		||||
    private static final long serialVersionUID = 1221244785535553536L;
 | 
			
		||||
 | 
			
		||||
    public NotAttachedException()
 | 
			
		||||
    {
 | 
			
		||||
        super( "You are not attached to this Computer" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NotAttachedException( String s )
 | 
			
		||||
    {
 | 
			
		||||
        super( s );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,10 +3,8 @@
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
 | 
			
		||||
 * For help using the API, and posting your mods, visit the forums at computercraft.info.
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.api;
 | 
			
		||||
package dan200.computercraft.api.turtle;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
			
		||||
import dan200.computercraft.api.turtle.TurtleUpgradeType;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
import net.minecraft.util.IItemProvider;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
@@ -6,10 +6,10 @@
 | 
			
		||||
package dan200.computercraft.api.turtle;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.client.TransformedModel;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
 | 
			
		||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
@@ -18,11 +18,9 @@ import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.api.distmarker.OnlyIn;
 | 
			
		||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
 | 
			
		||||
import net.minecraftforge.event.world.BlockEvent;
 | 
			
		||||
import org.apache.commons.lang3.tuple.Pair;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.vecmath.Matrix4f;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The primary interface for defining an update for Turtles. A turtle update
 | 
			
		||||
@@ -126,12 +124,11 @@ public interface ITurtleUpgrade
 | 
			
		||||
     *
 | 
			
		||||
     * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
 | 
			
		||||
     * @param side   Which side of the turtle (left or right) the upgrade resides on.
 | 
			
		||||
     * @return The model that you wish to be used to render your upgrade, and a transformation to apply to it. Returning
 | 
			
		||||
     * a transformation of {@code null} has the same effect as the identify matrix.
 | 
			
		||||
     * @return The model that you wish to be used to render your upgrade.
 | 
			
		||||
     */
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @OnlyIn( Dist.CLIENT )
 | 
			
		||||
    Pair<IBakedModel, Matrix4f> getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
 | 
			
		||||
    TransformedModel getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called once per tick for each turtle which has the upgrade equipped.
 | 
			
		||||
 
 | 
			
		||||
@@ -17,70 +17,70 @@ public enum TurtleAnimation
 | 
			
		||||
    /**
 | 
			
		||||
     * An animation which does nothing. This takes no time to complete.
 | 
			
		||||
     *
 | 
			
		||||
     * @see #Wait
 | 
			
		||||
     * @see #ShortWait
 | 
			
		||||
     * @see #WAIT
 | 
			
		||||
     * @see #SHORT_WAIT
 | 
			
		||||
     */
 | 
			
		||||
    None,
 | 
			
		||||
    NONE,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and
 | 
			
		||||
     * moves into this one.
 | 
			
		||||
     */
 | 
			
		||||
    MoveForward,
 | 
			
		||||
    MOVE_FORWARD,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and
 | 
			
		||||
     * moves into this one.
 | 
			
		||||
     */
 | 
			
		||||
    MoveBack,
 | 
			
		||||
    MOVE_BACK,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and
 | 
			
		||||
     * moves into this one.
 | 
			
		||||
     */
 | 
			
		||||
    MoveUp,
 | 
			
		||||
    MOVE_UP,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and
 | 
			
		||||
     * moves into this one.
 | 
			
		||||
     */
 | 
			
		||||
    MoveDown,
 | 
			
		||||
    MOVE_DOWN,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and
 | 
			
		||||
     * the turtle turns to face in the current direction.
 | 
			
		||||
     */
 | 
			
		||||
    TurnLeft,
 | 
			
		||||
    TURN_LEFT,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and
 | 
			
		||||
     * the turtle turns to face in the current direction.
 | 
			
		||||
     */
 | 
			
		||||
    TurnRight,
 | 
			
		||||
    TURN_RIGHT,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Swing the tool on the left.
 | 
			
		||||
     */
 | 
			
		||||
    SwingLeftTool,
 | 
			
		||||
    SWING_LEFT_TOOL,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Swing the tool on the right.
 | 
			
		||||
     */
 | 
			
		||||
    SwingRightTool,
 | 
			
		||||
    SWING_RIGHT_TOOL,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Wait until the animation has finished, performing no movement.
 | 
			
		||||
     *
 | 
			
		||||
     * @see #ShortWait
 | 
			
		||||
     * @see #None
 | 
			
		||||
     * @see #SHORT_WAIT
 | 
			
		||||
     * @see #NONE
 | 
			
		||||
     */
 | 
			
		||||
    Wait,
 | 
			
		||||
    WAIT,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Wait until the animation has finished, performing no movement. This takes 4 ticks to complete.
 | 
			
		||||
     *
 | 
			
		||||
     * @see #Wait
 | 
			
		||||
     * @see #None
 | 
			
		||||
     * @see #WAIT
 | 
			
		||||
     * @see #NONE
 | 
			
		||||
     */
 | 
			
		||||
    ShortWait,
 | 
			
		||||
    SHORT_WAIT,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,10 @@ public enum TurtleSide
 | 
			
		||||
    /**
 | 
			
		||||
     * The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle).
 | 
			
		||||
     */
 | 
			
		||||
    Left,
 | 
			
		||||
    LEFT,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The turtle's right side (where the modem usually is on a Wireless Mining Turtle).
 | 
			
		||||
     */
 | 
			
		||||
    Right,
 | 
			
		||||
    RIGHT,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,28 +16,28 @@ public enum TurtleUpgradeType
 | 
			
		||||
     * A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()}
 | 
			
		||||
     * and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles).
 | 
			
		||||
     */
 | 
			
		||||
    Tool,
 | 
			
		||||
    TOOL,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A peripheral adds a special peripheral which is attached to the side of the turtle,
 | 
			
		||||
     * and can be interacted with the {@code peripheral} API (Such as the modem on Wireless Turtles).
 | 
			
		||||
     */
 | 
			
		||||
    Peripheral,
 | 
			
		||||
    PERIPHERAL,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An upgrade which provides both a tool and a peripheral. This can be used when you wish
 | 
			
		||||
     * your upgrade to also provide methods. For example, a pickaxe could provide methods
 | 
			
		||||
     * determining whether it can break the given block or not.
 | 
			
		||||
     */
 | 
			
		||||
    Both;
 | 
			
		||||
    BOTH;
 | 
			
		||||
 | 
			
		||||
    public boolean isTool()
 | 
			
		||||
    {
 | 
			
		||||
        return this == Tool || this == Both;
 | 
			
		||||
        return this == TOOL || this == BOTH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isPeripheral()
 | 
			
		||||
    {
 | 
			
		||||
        return this == Peripheral || this == Both;
 | 
			
		||||
        return this == PERIPHERAL || this == BOTH;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,10 +19,10 @@ public enum TurtleVerb
 | 
			
		||||
    /**
 | 
			
		||||
     * The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}.
 | 
			
		||||
     */
 | 
			
		||||
    Dig,
 | 
			
		||||
    DIG,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The turtle called {@code turtle.attack()}, {@code turtle.attackUp()} or {@code turtle.attackDown()}.
 | 
			
		||||
     */
 | 
			
		||||
    Attack,
 | 
			
		||||
    ATTACK,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,20 +11,19 @@ import dan200.computercraft.shared.common.IColouredItem;
 | 
			
		||||
import dan200.computercraft.shared.media.items.ItemDisk;
 | 
			
		||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
 | 
			
		||||
import dan200.computercraft.shared.util.Colour;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.IUnbakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.inventory.container.PlayerContainer;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.client.event.ColorHandlerEvent;
 | 
			
		||||
import net.minecraftforge.client.event.ModelBakeEvent;
 | 
			
		||||
import net.minecraftforge.client.event.ModelRegistryEvent;
 | 
			
		||||
import net.minecraftforge.client.event.TextureStitchEvent;
 | 
			
		||||
import net.minecraftforge.client.model.BasicState;
 | 
			
		||||
import net.minecraftforge.client.model.ModelLoader;
 | 
			
		||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
 | 
			
		||||
import net.minecraftforge.client.model.SimpleModelTransform;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
 | 
			
		||||
@@ -71,13 +70,13 @@ public final class ClientRegistry
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void registerModels( ModelRegistryEvent event )
 | 
			
		||||
    {
 | 
			
		||||
        ModelLoaderRegistry.registerLoader( TurtleModelLoader.INSTANCE );
 | 
			
		||||
        ModelLoaderRegistry.registerLoader( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ), TurtleModelLoader.INSTANCE );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void onTextureStitchEvent( TextureStitchEvent.Pre event )
 | 
			
		||||
    {
 | 
			
		||||
        if( event.getMap() != Minecraft.getInstance().getTextureMap() ) return;
 | 
			
		||||
        if( !event.getMap().getTextureLocation().equals( PlayerContainer.LOCATION_BLOCKS_TEXTURE ) ) return;
 | 
			
		||||
 | 
			
		||||
        for( String extra : EXTRA_TEXTURES )
 | 
			
		||||
        {
 | 
			
		||||
@@ -92,29 +91,18 @@ public final class ClientRegistry
 | 
			
		||||
        ModelLoader loader = event.getModelLoader();
 | 
			
		||||
        Map<ResourceLocation, IBakedModel> registry = event.getModelRegistry();
 | 
			
		||||
 | 
			
		||||
        for( String model : EXTRA_MODELS )
 | 
			
		||||
        for( String modelName : EXTRA_MODELS )
 | 
			
		||||
        {
 | 
			
		||||
            IBakedModel bakedModel = bake( loader, loader.getUnbakedModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/" + model ) ) );
 | 
			
		||||
            ResourceLocation location = new ResourceLocation( ComputerCraft.MOD_ID, "item/" + modelName );
 | 
			
		||||
            IUnbakedModel model = loader.getUnbakedModel( location );
 | 
			
		||||
            model.getTextures( loader::getUnbakedModel, new HashSet<>() );
 | 
			
		||||
 | 
			
		||||
            if( bakedModel != null )
 | 
			
		||||
            IBakedModel baked = model.bakeModel( loader, ModelLoader.defaultTextureGetter(), SimpleModelTransform.IDENTITY, location );
 | 
			
		||||
            if( baked != null )
 | 
			
		||||
            {
 | 
			
		||||
                registry.put(
 | 
			
		||||
                    new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ),
 | 
			
		||||
                    bakedModel
 | 
			
		||||
                );
 | 
			
		||||
                registry.put( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, modelName ), "inventory" ), baked );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // And load the custom turtle models in too.
 | 
			
		||||
        registry.put(
 | 
			
		||||
            new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_normal" ), "inventory" ),
 | 
			
		||||
            bake( loader, TurtleModelLoader.INSTANCE.loadModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/turtle_normal" ) ) )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        registry.put(
 | 
			
		||||
            new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_advanced" ), "inventory" ),
 | 
			
		||||
            bake( loader, TurtleModelLoader.INSTANCE.loadModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/turtle_advanced" ) ) )
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
@@ -142,7 +130,7 @@ public final class ClientRegistry
 | 
			
		||||
                case 2: // Light colour
 | 
			
		||||
                {
 | 
			
		||||
                    int light = ItemPocketComputer.getLightState( stack );
 | 
			
		||||
                    return light == -1 ? Colour.Black.getHex() : light;
 | 
			
		||||
                    return light == -1 ? Colour.BLACK.getHex() : light;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }, ComputerCraft.Items.pocketComputerNormal, ComputerCraft.Items.pocketComputerAdvanced );
 | 
			
		||||
@@ -153,15 +141,4 @@ public final class ClientRegistry
 | 
			
		||||
            ComputerCraft.Blocks.turtleNormal, ComputerCraft.Blocks.turtleAdvanced
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static IBakedModel bake( ModelLoader loader, IUnbakedModel model )
 | 
			
		||||
    {
 | 
			
		||||
        model.getTextures( loader::getUnbakedModel, new HashSet<>() );
 | 
			
		||||
 | 
			
		||||
        return model.bake(
 | 
			
		||||
            loader,
 | 
			
		||||
            ModelLoader.defaultTextureGetter(),
 | 
			
		||||
            new BasicState( model.getDefaultState(), false ), DefaultVertexFormats.BLOCK
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,15 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.client.FrameInfo;
 | 
			
		||||
import dan200.computercraft.core.terminal.Terminal;
 | 
			
		||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
			
		||||
import dan200.computercraft.shared.util.Colour;
 | 
			
		||||
import dan200.computercraft.shared.util.Palette;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.renderer.*;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.VertexFormat;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
@@ -24,21 +24,10 @@ import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
public final class FixedWidthFontRenderer
 | 
			
		||||
{
 | 
			
		||||
    private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
 | 
			
		||||
 | 
			
		||||
    private static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Like {@link DefaultVertexFormats#POSITION_TEX_COLOR}, but flipped. This is backported from 1.15, hence the
 | 
			
		||||
     * custom format.
 | 
			
		||||
     */
 | 
			
		||||
    public static final VertexFormat POSITION_COLOR_TEX = new VertexFormat();
 | 
			
		||||
 | 
			
		||||
    static
 | 
			
		||||
    {
 | 
			
		||||
        POSITION_COLOR_TEX.addElement( DefaultVertexFormats.POSITION_3F );
 | 
			
		||||
        POSITION_COLOR_TEX.addElement( DefaultVertexFormats.COLOR_4UB );
 | 
			
		||||
        POSITION_COLOR_TEX.addElement( DefaultVertexFormats.TEX_2F );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static final int FONT_HEIGHT = 9;
 | 
			
		||||
    public static final int FONT_WIDTH = 6;
 | 
			
		||||
    public static final float WIDTH = 256.0f;
 | 
			
		||||
@@ -46,6 +35,8 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
    public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
 | 
			
		||||
    public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
 | 
			
		||||
 | 
			
		||||
    public static final RenderType TYPE = Type.MAIN;
 | 
			
		||||
 | 
			
		||||
    private FixedWidthFontRenderer()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
@@ -61,7 +52,7 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
        return i < 0 ? 0 : 15 - i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawChar( BufferBuilder buffer, float x, float y, int index, float r, float g, float b )
 | 
			
		||||
    private static void drawChar( Matrix4f transform, IVertexBuilder buffer, float x, float y, int index, float r, float g, float b )
 | 
			
		||||
    {
 | 
			
		||||
        // Short circuit to avoid the common case - the texture should be blank here after all.
 | 
			
		||||
        if( index == '\0' || index == ' ' ) return;
 | 
			
		||||
@@ -72,25 +63,25 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
        int xStart = 1 + column * (FONT_WIDTH + 2);
 | 
			
		||||
        int yStart = 1 + row * (FONT_HEIGHT + 2);
 | 
			
		||||
 | 
			
		||||
        buffer.pos( x, y, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, yStart / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x, y, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, yStart / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawQuad( BufferBuilder buffer, float x, float y, float width, float height, float r, float g, float b )
 | 
			
		||||
    private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, float r, float g, float b )
 | 
			
		||||
    {
 | 
			
		||||
        buffer.pos( x, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_START ).endVertex();
 | 
			
		||||
        buffer.pos( x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex();
 | 
			
		||||
        buffer.pos( x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_END ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_START ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex();
 | 
			
		||||
        buffer.pos( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_END ).endVertex();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawQuad( BufferBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
 | 
			
		||||
    private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
 | 
			
		||||
    {
 | 
			
		||||
        double[] colour = palette.getColour( getColour( colourIndex ) );
 | 
			
		||||
        float r, g, b;
 | 
			
		||||
@@ -105,23 +96,23 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
            b = (float) colour[2];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        drawQuad( buffer, x, y, width, height, r, g, b );
 | 
			
		||||
        drawQuad( transform, buffer, x, y, width, height, r, g, b );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawBackground(
 | 
			
		||||
        @Nonnull BufferBuilder renderer, float x, float y,
 | 
			
		||||
        @Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
 | 
			
		||||
        @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
 | 
			
		||||
        float leftMarginSize, float rightMarginSize, float height
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        if( leftMarginSize > 0 )
 | 
			
		||||
        {
 | 
			
		||||
            drawQuad( renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
 | 
			
		||||
            drawQuad( transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if( rightMarginSize > 0 )
 | 
			
		||||
        {
 | 
			
		||||
            drawQuad( renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
 | 
			
		||||
            drawQuad( transform, renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Batch together runs of identical background cells.
 | 
			
		||||
@@ -134,7 +125,7 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
 | 
			
		||||
            if( blockColour != '\0' )
 | 
			
		||||
            {
 | 
			
		||||
                drawQuad( renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
 | 
			
		||||
                drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            blockColour = colourIndex;
 | 
			
		||||
@@ -143,24 +134,24 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
 | 
			
		||||
        if( blockColour != '\0' )
 | 
			
		||||
        {
 | 
			
		||||
            drawQuad( renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
 | 
			
		||||
            drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawString(
 | 
			
		||||
        @Nonnull BufferBuilder renderer, float x, float y,
 | 
			
		||||
        @Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
 | 
			
		||||
        @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
 | 
			
		||||
        @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        if( backgroundColour != null )
 | 
			
		||||
        {
 | 
			
		||||
            drawBackground( renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
 | 
			
		||||
            drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for( int i = 0; i < text.length(); i++ )
 | 
			
		||||
        {
 | 
			
		||||
            double[] colour = palette.getColour( getColour( textColour.charAt( i ) ) );
 | 
			
		||||
            double[] colour = palette.getColour( 15 - "0123456789abcdef".indexOf( textColour.charAt( i ) ) );
 | 
			
		||||
            float r, g, b;
 | 
			
		||||
            if( greyscale )
 | 
			
		||||
            {
 | 
			
		||||
@@ -176,7 +167,7 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
            // Draw char
 | 
			
		||||
            int index = text.charAt( i );
 | 
			
		||||
            if( index > 255 ) index = '?';
 | 
			
		||||
            drawChar( renderer, x + i * FONT_WIDTH, y, index, r, g, b );
 | 
			
		||||
            drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@@ -188,15 +179,13 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
    {
 | 
			
		||||
        bindFont();
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        begin( buffer );
 | 
			
		||||
        drawString( buffer, x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize );
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
        IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
 | 
			
		||||
        drawString( IDENTITY, ((IRenderTypeBuffer) renderer).getBuffer( TYPE ), x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize );
 | 
			
		||||
        renderer.finish();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawTerminalWithoutCursor(
 | 
			
		||||
        @Nonnull BufferBuilder buffer, float x, float y,
 | 
			
		||||
        @Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
 | 
			
		||||
        @Nonnull Terminal terminal, boolean greyscale,
 | 
			
		||||
        float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
 | 
			
		||||
    )
 | 
			
		||||
@@ -206,13 +195,13 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
 | 
			
		||||
        // Top and bottom margins
 | 
			
		||||
        drawBackground(
 | 
			
		||||
            buffer, x, y - topMarginSize,
 | 
			
		||||
            transform, buffer, x, y - topMarginSize,
 | 
			
		||||
            terminal.getBackgroundColourLine( 0 ), palette, greyscale,
 | 
			
		||||
            leftMarginSize, rightMarginSize, topMarginSize
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        drawBackground(
 | 
			
		||||
            buffer, x, y + height * FONT_HEIGHT,
 | 
			
		||||
            transform, buffer, x, y + height * FONT_HEIGHT,
 | 
			
		||||
            terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
 | 
			
		||||
            leftMarginSize, rightMarginSize, bottomMarginSize
 | 
			
		||||
        );
 | 
			
		||||
@@ -221,7 +210,7 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
        for( int i = 0; i < height; i++ )
 | 
			
		||||
        {
 | 
			
		||||
            drawString(
 | 
			
		||||
                buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
 | 
			
		||||
                transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
 | 
			
		||||
                terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ),
 | 
			
		||||
                palette, greyscale, leftMarginSize, rightMarginSize
 | 
			
		||||
            );
 | 
			
		||||
@@ -229,7 +218,7 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawCursor(
 | 
			
		||||
        @Nonnull BufferBuilder buffer, float x, float y,
 | 
			
		||||
        @Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
 | 
			
		||||
        @Nonnull Terminal terminal, boolean greyscale
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
@@ -254,75 +243,104 @@ public final class FixedWidthFontRenderer
 | 
			
		||||
                b = (float) colour[2];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            drawChar( buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b );
 | 
			
		||||
            drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawTerminal(
 | 
			
		||||
        @Nonnull BufferBuilder buffer, float x, float y,
 | 
			
		||||
        @Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
 | 
			
		||||
        @Nonnull Terminal terminal, boolean greyscale,
 | 
			
		||||
        float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        drawTerminalWithoutCursor( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
 | 
			
		||||
        drawCursor( buffer, x, y, terminal, greyscale );
 | 
			
		||||
        drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
 | 
			
		||||
        drawCursor( transform, buffer, x, y, terminal, greyscale );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawTerminal(
 | 
			
		||||
        @Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
 | 
			
		||||
        float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        bindFont();
 | 
			
		||||
 | 
			
		||||
        IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
 | 
			
		||||
        IVertexBuilder buffer = renderer.getBuffer( TYPE );
 | 
			
		||||
        drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
 | 
			
		||||
        renderer.finish( TYPE );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawTerminal(
 | 
			
		||||
        float x, float y, @Nonnull Terminal terminal, boolean greyscale,
 | 
			
		||||
        float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        drawTerminal( IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
 | 
			
		||||
    {
 | 
			
		||||
        Colour colour = Colour.BLACK;
 | 
			
		||||
        drawQuad( transform, renderer.getBuffer( TYPE ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height )
 | 
			
		||||
    {
 | 
			
		||||
        bindFont();
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        begin( buffer );
 | 
			
		||||
        drawTerminal( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
        IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
 | 
			
		||||
        drawEmptyTerminal( transform, renderer, x, y, width, height );
 | 
			
		||||
        renderer.finish();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawEmptyTerminal( float x, float y, float width, float height )
 | 
			
		||||
    {
 | 
			
		||||
        bindFont();
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        begin( buffer );
 | 
			
		||||
 | 
			
		||||
        Colour colour = Colour.Black;
 | 
			
		||||
        drawQuad( buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
 | 
			
		||||
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
        drawEmptyTerminal( IDENTITY, x, y, width, height );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawBlocker( @Nonnull BufferBuilder buffer, float x, float y, float width, float height )
 | 
			
		||||
    public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
 | 
			
		||||
    {
 | 
			
		||||
        Colour colour = Colour.Black;
 | 
			
		||||
        drawQuad( buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
 | 
			
		||||
        Colour colour = Colour.BLACK;
 | 
			
		||||
        drawQuad( transform, renderer.getBuffer( Type.BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawBlocker( float x, float y, float width, float height )
 | 
			
		||||
    {
 | 
			
		||||
        bindFont();
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        begin( buffer );
 | 
			
		||||
        drawBlocker( buffer, x, y, width, height );
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void bindFont()
 | 
			
		||||
    private static void bindFont()
 | 
			
		||||
    {
 | 
			
		||||
        Minecraft.getInstance().getTextureManager().bindTexture( FONT );
 | 
			
		||||
        GlStateManager.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableTexture();
 | 
			
		||||
        RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void begin( BufferBuilder buffer )
 | 
			
		||||
    private static final class Type extends RenderState
 | 
			
		||||
    {
 | 
			
		||||
        buffer.begin( GL11.GL_TRIANGLES, POSITION_COLOR_TEX );
 | 
			
		||||
        private static final int GL_MODE = GL11.GL_TRIANGLES;
 | 
			
		||||
 | 
			
		||||
        private static final VertexFormat FORMAT = DefaultVertexFormats.POSITION_COLOR_TEX;
 | 
			
		||||
 | 
			
		||||
        static final RenderType MAIN = RenderType.makeType(
 | 
			
		||||
            "terminal_font", FORMAT, GL_MODE, 1024,
 | 
			
		||||
            false, false, // useDelegate, needsSorting
 | 
			
		||||
            RenderType.State.getBuilder()
 | 
			
		||||
                .texture( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
 | 
			
		||||
                .alpha( DEFAULT_ALPHA )
 | 
			
		||||
                .lightmap( LIGHTMAP_DISABLED )
 | 
			
		||||
                .writeMask( COLOR_WRITE )
 | 
			
		||||
                .build( false )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        static final RenderType BLOCKER = RenderType.makeType(
 | 
			
		||||
            "terminal_blocker", FORMAT, GL_MODE, 256,
 | 
			
		||||
            false, false, // useDelegate, needsSorting
 | 
			
		||||
            RenderType.State.getBuilder()
 | 
			
		||||
                .texture( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
 | 
			
		||||
                .alpha( DEFAULT_ALPHA )
 | 
			
		||||
                .writeMask( DEPTH_WRITE )
 | 
			
		||||
                .lightmap( LIGHTMAP_DISABLED )
 | 
			
		||||
                .build( false )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        private Type( String name, Runnable setup, Runnable destroy )
 | 
			
		||||
        {
 | 
			
		||||
            super( name, setup, destroy );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
 | 
			
		||||
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
 | 
			
		||||
@@ -134,17 +134,17 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
 | 
			
		||||
        terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
 | 
			
		||||
 | 
			
		||||
        // Draw a border around the terminal
 | 
			
		||||
        GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
 | 
			
		||||
        RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
 | 
			
		||||
        switch( m_family )
 | 
			
		||||
        {
 | 
			
		||||
            case Normal:
 | 
			
		||||
            case NORMAL:
 | 
			
		||||
            default:
 | 
			
		||||
                minecraft.getTextureManager().bindTexture( BACKGROUND_NORMAL );
 | 
			
		||||
                break;
 | 
			
		||||
            case Advanced:
 | 
			
		||||
            case ADVANCED:
 | 
			
		||||
                minecraft.getTextureManager().bindTexture( BACKGROUND_ADVANCED );
 | 
			
		||||
                break;
 | 
			
		||||
            case Command:
 | 
			
		||||
            case COMMAND:
 | 
			
		||||
                minecraft.getTextureManager().bindTexture( BACKGROUND_COMMAND );
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
 | 
			
		||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
 | 
			
		||||
import net.minecraft.entity.player.PlayerInventory;
 | 
			
		||||
@@ -32,7 +32,7 @@ public class GuiDiskDrive extends ContainerScreen<ContainerDiskDrive>
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
        RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
        minecraft.getTextureManager().bindTexture( BACKGROUND );
 | 
			
		||||
        blit( guiLeft, guiTop, 0, 0, xSize, ySize );
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
 | 
			
		||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
 | 
			
		||||
import net.minecraft.client.resources.I18n;
 | 
			
		||||
@@ -33,7 +33,7 @@ public class GuiPrinter extends ContainerScreen<ContainerPrinter>
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
        RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
        minecraft.getTextureManager().bindTexture( BACKGROUND );
 | 
			
		||||
        blit( guiLeft, guiTop, 0, 0, xSize, ySize );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,15 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
			
		||||
import dan200.computercraft.shared.common.ContainerHeldItem;
 | 
			
		||||
import dan200.computercraft.shared.media.items.ItemPrintout;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
 | 
			
		||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
 | 
			
		||||
import net.minecraft.client.renderer.Matrix4f;
 | 
			
		||||
import net.minecraft.client.renderer.TransformationMatrix;
 | 
			
		||||
import net.minecraft.entity.player.PlayerInventory;
 | 
			
		||||
import net.minecraft.util.text.ITextComponent;
 | 
			
		||||
import org.lwjgl.glfw.GLFW;
 | 
			
		||||
@@ -18,6 +22,8 @@ import static dan200.computercraft.client.render.PrintoutRenderer.*;
 | 
			
		||||
 | 
			
		||||
public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
 | 
			
		||||
{
 | 
			
		||||
    private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
 | 
			
		||||
 | 
			
		||||
    private final boolean m_book;
 | 
			
		||||
    private final int m_pages;
 | 
			
		||||
    private final TextBuffer[] m_text;
 | 
			
		||||
@@ -88,20 +94,22 @@ public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
 | 
			
		||||
    public void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
 | 
			
		||||
    {
 | 
			
		||||
        // Draw the printout
 | 
			
		||||
        GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
 | 
			
		||||
        GlStateManager.enableDepthTest();
 | 
			
		||||
        RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
 | 
			
		||||
        RenderSystem.enableDepthTest();
 | 
			
		||||
 | 
			
		||||
        drawBorder( guiLeft, guiTop, blitOffset, m_page, m_pages, m_book );
 | 
			
		||||
        drawText( guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
 | 
			
		||||
        IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
 | 
			
		||||
        drawBorder( IDENTITY, renderer, guiLeft, guiTop, getBlitOffset(), m_page, m_pages, m_book );
 | 
			
		||||
        drawText( IDENTITY, renderer, guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
 | 
			
		||||
        renderer.finish();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render( int mouseX, int mouseY, float partialTicks )
 | 
			
		||||
    {
 | 
			
		||||
        // We must take the background further back in order to not overlap with our printed pages.
 | 
			
		||||
        blitOffset--;
 | 
			
		||||
        setBlitOffset( getBlitOffset() - 1 );
 | 
			
		||||
        renderBackground();
 | 
			
		||||
        blitOffset++;
 | 
			
		||||
        setBlitOffset( getBlitOffset() + 1 );
 | 
			
		||||
 | 
			
		||||
        super.render( mouseX, mouseY, partialTicks );
 | 
			
		||||
        renderHoveredToolTip( mouseX, mouseY );
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
 | 
			
		||||
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
 | 
			
		||||
@@ -98,7 +98,7 @@ public class GuiTurtle extends ContainerScreen<ContainerTurtle>
 | 
			
		||||
        int slot = m_container.getSelectedSlot();
 | 
			
		||||
        if( slot >= 0 )
 | 
			
		||||
        {
 | 
			
		||||
            GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
            RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
            int slotX = slot % 4;
 | 
			
		||||
            int slotY = slot / 4;
 | 
			
		||||
            minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
 | 
			
		||||
@@ -110,11 +110,11 @@ public class GuiTurtle extends ContainerScreen<ContainerTurtle>
 | 
			
		||||
    protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
 | 
			
		||||
    {
 | 
			
		||||
        // Draw term
 | 
			
		||||
        boolean advanced = m_family == ComputerFamily.Advanced;
 | 
			
		||||
        boolean advanced = m_family == ComputerFamily.ADVANCED;
 | 
			
		||||
        terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
 | 
			
		||||
 | 
			
		||||
        // Draw border/inventory
 | 
			
		||||
        GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
        RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
 | 
			
		||||
        minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
 | 
			
		||||
        blit( guiLeft, guiTop, 0, 0, xSize, ySize );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,9 @@ import org.lwjgl.glfw.GLFW;
 | 
			
		||||
import java.util.BitSet;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
 | 
			
		||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
 | 
			
		||||
 | 
			
		||||
public class WidgetTerminal implements IGuiEventListener
 | 
			
		||||
{
 | 
			
		||||
    private static final float TERMINATE_TIME = 0.5f;
 | 
			
		||||
@@ -173,8 +176,8 @@ public class WidgetTerminal implements IGuiEventListener
 | 
			
		||||
        Terminal term = computer.getTerminal();
 | 
			
		||||
        if( term != null )
 | 
			
		||||
        {
 | 
			
		||||
            int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
 | 
			
		||||
            int charX = (int) (mouseX / FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FONT_HEIGHT);
 | 
			
		||||
            charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
 | 
			
		||||
            charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
 | 
			
		||||
 | 
			
		||||
@@ -197,8 +200,8 @@ public class WidgetTerminal implements IGuiEventListener
 | 
			
		||||
        Terminal term = computer.getTerminal();
 | 
			
		||||
        if( term != null )
 | 
			
		||||
        {
 | 
			
		||||
            int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
 | 
			
		||||
            int charX = (int) (mouseX / FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FONT_HEIGHT);
 | 
			
		||||
            charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
 | 
			
		||||
            charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
 | 
			
		||||
 | 
			
		||||
@@ -224,8 +227,8 @@ public class WidgetTerminal implements IGuiEventListener
 | 
			
		||||
        Terminal term = computer.getTerminal();
 | 
			
		||||
        if( term != null )
 | 
			
		||||
        {
 | 
			
		||||
            int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
 | 
			
		||||
            int charX = (int) (mouseX / FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FONT_HEIGHT);
 | 
			
		||||
            charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
 | 
			
		||||
            charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
 | 
			
		||||
 | 
			
		||||
@@ -249,8 +252,8 @@ public class WidgetTerminal implements IGuiEventListener
 | 
			
		||||
        Terminal term = computer.getTerminal();
 | 
			
		||||
        if( term != null )
 | 
			
		||||
        {
 | 
			
		||||
            int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
 | 
			
		||||
            int charX = (int) (mouseX / FONT_WIDTH);
 | 
			
		||||
            int charY = (int) (mouseY / FONT_HEIGHT);
 | 
			
		||||
            charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
 | 
			
		||||
            charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
 | 
			
		||||
 | 
			
		||||
@@ -318,19 +321,15 @@ public class WidgetTerminal implements IGuiEventListener
 | 
			
		||||
            Terminal terminal = computer != null ? computer.getTerminal() : null;
 | 
			
		||||
            if( terminal != null )
 | 
			
		||||
            {
 | 
			
		||||
                FixedWidthFontRenderer.drawTerminal(
 | 
			
		||||
                    originX, originY,
 | 
			
		||||
                    terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin
 | 
			
		||||
                );
 | 
			
		||||
                FixedWidthFontRenderer.drawTerminal( originX, originY, terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin );
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                int x = originX - leftMargin;
 | 
			
		||||
                int y = originY - rightMargin;
 | 
			
		||||
                int width = termWidth * FixedWidthFontRenderer.FONT_WIDTH + leftMargin + rightMargin;
 | 
			
		||||
                int height = termHeight * FixedWidthFontRenderer.FONT_HEIGHT + topMargin + bottomMargin;
 | 
			
		||||
 | 
			
		||||
                FixedWidthFontRenderer.drawEmptyTerminal( x, y, width, height );
 | 
			
		||||
                FixedWidthFontRenderer.drawEmptyTerminal(
 | 
			
		||||
                    originX - leftMargin, originY - rightMargin,
 | 
			
		||||
                    termWidth * FONT_WIDTH + leftMargin + rightMargin,
 | 
			
		||||
                    termHeight * FONT_HEIGHT + topMargin + bottomMargin
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ package dan200.computercraft.client.proxy;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.client.gui.*;
 | 
			
		||||
import dan200.computercraft.client.render.TileEntityCableRenderer;
 | 
			
		||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
 | 
			
		||||
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
 | 
			
		||||
import dan200.computercraft.client.render.TurtlePlayerRenderer;
 | 
			
		||||
@@ -15,7 +14,6 @@ import dan200.computercraft.shared.common.ContainerHeldItem;
 | 
			
		||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
 | 
			
		||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
 | 
			
		||||
@@ -24,6 +22,8 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
 | 
			
		||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
 | 
			
		||||
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
 | 
			
		||||
import net.minecraft.client.gui.ScreenManager;
 | 
			
		||||
import net.minecraft.client.renderer.RenderType;
 | 
			
		||||
import net.minecraft.client.renderer.RenderTypeLookup;
 | 
			
		||||
import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.event.world.WorldEvent;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
@@ -40,12 +40,22 @@ public final class ComputerCraftProxyClient
 | 
			
		||||
    {
 | 
			
		||||
        registerContainers();
 | 
			
		||||
 | 
			
		||||
        // Setup TESRs
 | 
			
		||||
        ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() );
 | 
			
		||||
        ClientRegistry.bindTileEntitySpecialRenderer( TileCable.class, new TileEntityCableRenderer() );
 | 
			
		||||
        ClientRegistry.bindTileEntitySpecialRenderer( TileTurtle.class, new TileEntityTurtleRenderer() );
 | 
			
		||||
        // While turtles themselves are not transparent, their upgrades may be.
 | 
			
		||||
        RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.turtleNormal, RenderType.getTranslucent() );
 | 
			
		||||
        RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.turtleAdvanced, RenderType.getTranslucent() );
 | 
			
		||||
 | 
			
		||||
        RenderingRegistry.registerEntityRenderingHandler( TurtlePlayer.class, TurtlePlayerRenderer::new );
 | 
			
		||||
        // Monitors' textures have transparent fronts and so count as cutouts.
 | 
			
		||||
        RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.monitorNormal, RenderType.getCutout() );
 | 
			
		||||
        RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.monitorAdvanced, RenderType.getCutout() );
 | 
			
		||||
 | 
			
		||||
        // Setup TESRs
 | 
			
		||||
        ClientRegistry.bindTileEntityRenderer( TileMonitor.FACTORY_NORMAL, TileEntityMonitorRenderer::new );
 | 
			
		||||
        ClientRegistry.bindTileEntityRenderer( TileMonitor.FACTORY_ADVANCED, TileEntityMonitorRenderer::new );
 | 
			
		||||
        ClientRegistry.bindTileEntityRenderer( TileTurtle.FACTORY_NORMAL, TileEntityTurtleRenderer::new );
 | 
			
		||||
        ClientRegistry.bindTileEntityRenderer( TileTurtle.FACTORY_ADVANCED, TileEntityTurtleRenderer::new );
 | 
			
		||||
        // TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
 | 
			
		||||
 | 
			
		||||
        RenderingRegistry.registerEntityRenderingHandler( TurtlePlayer.TYPE, TurtlePlayerRenderer::new );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void registerContainers()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,26 +5,27 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
 | 
			
		||||
import dan200.computercraft.shared.util.WorldUtil;
 | 
			
		||||
import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.ActiveRenderInfo;
 | 
			
		||||
import net.minecraft.client.renderer.Matrix4f;
 | 
			
		||||
import net.minecraft.client.renderer.RenderType;
 | 
			
		||||
import net.minecraft.client.renderer.WorldRenderer;
 | 
			
		||||
import net.minecraft.entity.Entity;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.RayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.Vec3d;
 | 
			
		||||
import net.minecraft.util.math.shapes.VoxelShape;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
 | 
			
		||||
import net.minecraftforge.client.event.DrawHighlightEvent;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
 | 
			
		||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
 | 
			
		||||
public final class CableHighlightRenderer
 | 
			
		||||
@@ -37,14 +38,12 @@ public final class CableHighlightRenderer
 | 
			
		||||
     * Draw an outline for a specific part of a cable "Multipart".
 | 
			
		||||
     *
 | 
			
		||||
     * @param event The event to observe
 | 
			
		||||
     * @see WorldRenderer#drawSelectionBox(ActiveRenderInfo, RayTraceResult, int)
 | 
			
		||||
     * @see WorldRenderer#drawSelectionBox(MatrixStack, IVertexBuilder, Entity, double, double, double, BlockPos, BlockState)
 | 
			
		||||
     */
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void drawHighlight( DrawBlockHighlightEvent event )
 | 
			
		||||
    public static void drawHighlight( DrawHighlightEvent.HighlightBlock event )
 | 
			
		||||
    {
 | 
			
		||||
        if( event.getTarget().getType() != RayTraceResult.Type.BLOCK ) return;
 | 
			
		||||
 | 
			
		||||
        BlockRayTraceResult hit = (BlockRayTraceResult) event.getTarget();
 | 
			
		||||
        BlockRayTraceResult hit = event.getTarget();
 | 
			
		||||
        BlockPos pos = hit.getPos();
 | 
			
		||||
        World world = event.getInfo().getRenderViewEntity().getEntityWorld();
 | 
			
		||||
        ActiveRenderInfo info = event.getInfo();
 | 
			
		||||
@@ -59,31 +58,22 @@ public final class CableHighlightRenderer
 | 
			
		||||
 | 
			
		||||
        event.setCanceled( true );
 | 
			
		||||
 | 
			
		||||
        Minecraft mc = Minecraft.getInstance();
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
 | 
			
		||||
        GlStateManager.lineWidth( Math.max( 2.5F, mc.mainWindow.getFramebufferWidth() / 1920.0F * 2.5F ) );
 | 
			
		||||
        GlStateManager.disableTexture();
 | 
			
		||||
        GlStateManager.depthMask( false );
 | 
			
		||||
        GlStateManager.matrixMode( GL11.GL_PROJECTION );
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
        GlStateManager.scalef( 1.0F, 1.0F, 0.999F );
 | 
			
		||||
 | 
			
		||||
        VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getHitVec().subtract( pos.getX(), pos.getY(), pos.getZ() ) )
 | 
			
		||||
            ? CableShapes.getModemShape( state )
 | 
			
		||||
            : CableShapes.getCableShape( state );
 | 
			
		||||
 | 
			
		||||
        Vec3d cameraPos = info.getProjectedView();
 | 
			
		||||
        WorldRenderer.drawShape(
 | 
			
		||||
            shape, pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ(),
 | 
			
		||||
            0.0F, 0.0F, 0.0F, 0.4F
 | 
			
		||||
        );
 | 
			
		||||
        double xOffset = pos.getX() - cameraPos.getX();
 | 
			
		||||
        double yOffset = pos.getY() - cameraPos.getY();
 | 
			
		||||
        double zOffset = pos.getZ() - cameraPos.getZ();
 | 
			
		||||
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
        GlStateManager.matrixMode( GL11.GL_MODELVIEW );
 | 
			
		||||
        GlStateManager.depthMask( true );
 | 
			
		||||
        GlStateManager.enableTexture();
 | 
			
		||||
        GlStateManager.disableBlend();
 | 
			
		||||
        IVertexBuilder buffer = event.getBuffers().getBuffer( RenderType.getLines() );
 | 
			
		||||
        Matrix4f matrix4f = event.getMatrix().getLast().getMatrix();
 | 
			
		||||
        shape.forEachEdge( ( x1, y1, z1, x2, y2, z2 ) -> {
 | 
			
		||||
            buffer.pos( matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset) )
 | 
			
		||||
                .color( 0, 0, 0, 0.4f ).endVertex();
 | 
			
		||||
            buffer.pos( matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset) )
 | 
			
		||||
                .color( 0, 0, 0, 0.4f ).endVertex();
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,12 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.entity.player.AbstractClientPlayerEntity;
 | 
			
		||||
import net.minecraft.client.renderer.FirstPersonRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
 | 
			
		||||
import net.minecraft.client.renderer.Vector3f;
 | 
			
		||||
import net.minecraft.entity.player.PlayerEntity;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
@@ -19,99 +22,118 @@ public abstract class ItemMapLikeRenderer
 | 
			
		||||
    /**
 | 
			
		||||
     * The main rendering method for the item.
 | 
			
		||||
     *
 | 
			
		||||
     * @param stack The stack to render
 | 
			
		||||
     * @see FirstPersonRenderer#renderMapFirstPerson(ItemStack)
 | 
			
		||||
     * @param transform The matrix transformation stack
 | 
			
		||||
     * @param render    The buffer to render to
 | 
			
		||||
     * @param stack     The stack to render
 | 
			
		||||
     * @see FirstPersonRenderer#renderItemInFirstPerson(AbstractClientPlayerEntity, float, float, Hand, float, ItemStack, float, MatrixStack, IRenderTypeBuffer, int)
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract void renderItem( ItemStack stack );
 | 
			
		||||
    protected abstract void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack );
 | 
			
		||||
 | 
			
		||||
    protected void renderItemFirstPerson( Hand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack )
 | 
			
		||||
    protected void renderItemFirstPerson( MatrixStack transform, IRenderTypeBuffer render, int lightTexture, Hand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        PlayerEntity player = Minecraft.getInstance().player;
 | 
			
		||||
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
        transform.push();
 | 
			
		||||
        if( hand == Hand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
 | 
			
		||||
        {
 | 
			
		||||
            renderItemFirstPersonCenter( pitch, equipProgress, swingProgress, stack );
 | 
			
		||||
            renderItemFirstPersonCenter( transform, render, lightTexture, pitch, equipProgress, swingProgress, stack );
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            renderItemFirstPersonSide(
 | 
			
		||||
                transform, render, lightTexture,
 | 
			
		||||
                hand == Hand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
 | 
			
		||||
                equipProgress, swingProgress, stack
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
        transform.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Renders the item to one side of the player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param transform     The matrix transformation stack
 | 
			
		||||
     * @param render        The buffer to render to
 | 
			
		||||
     * @param combinedLight The current light level
 | 
			
		||||
     * @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 FirstPersonRenderer#renderMapFirstPersonSide(float, HandSide, float, ItemStack)
 | 
			
		||||
     * @see FirstPersonRenderer#renderMapFirstPersonSide(MatrixStack, IRenderTypeBuffer, int, float, HandSide, float, ItemStack)
 | 
			
		||||
     */
 | 
			
		||||
    private void renderItemFirstPersonSide( HandSide side, float equipProgress, float swingProgress, ItemStack stack )
 | 
			
		||||
    private void renderItemFirstPersonSide( MatrixStack transform, IRenderTypeBuffer render, int combinedLight, HandSide side, float equipProgress, float swingProgress, ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        Minecraft minecraft = Minecraft.getInstance();
 | 
			
		||||
        float offset = side == HandSide.RIGHT ? 1f : -1f;
 | 
			
		||||
        GlStateManager.translatef( offset * 0.125f, -0.125f, 0f );
 | 
			
		||||
        transform.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.rotatef( offset * 10f, 0f, 0f, 1f );
 | 
			
		||||
            minecraft.getFirstPersonRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
 | 
			
		||||
            GlStateManager.popMatrix();
 | 
			
		||||
            transform.push();
 | 
			
		||||
            transform.rotate( Vector3f.ZP.rotationDegrees( offset * 10f ) );
 | 
			
		||||
            minecraft.getFirstPersonRenderer().renderArmFirstPerson( transform, render, combinedLight, equipProgress, swingProgress, side );
 | 
			
		||||
            transform.pop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Setup the appropriate transformations. This is just copied from the
 | 
			
		||||
        // corresponding method in ItemRenderer.
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
        GlStateManager.translatef( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
 | 
			
		||||
        transform.push();
 | 
			
		||||
        transform.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.translatef( offset * f3, f4 - 0.3f * f2, f5 );
 | 
			
		||||
        GlStateManager.rotatef( f2 * -45f, 1f, 0f, 0f );
 | 
			
		||||
        GlStateManager.rotatef( offset * f2 * -30f, 0f, 1f, 0f );
 | 
			
		||||
        transform.translate( offset * f3, f4 - 0.3f * f2, f5 );
 | 
			
		||||
        transform.rotate( Vector3f.XP.rotationDegrees( f2 * -45f ) );
 | 
			
		||||
        transform.rotate( Vector3f.YP.rotationDegrees( offset * f2 * -30f ) );
 | 
			
		||||
 | 
			
		||||
        renderItem( stack );
 | 
			
		||||
        renderItem( transform, render, stack );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
        transform.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Render an item in the middle of the screen.
 | 
			
		||||
     *
 | 
			
		||||
     * @param transform     The matrix transformation stack
 | 
			
		||||
     * @param render        The buffer to render to
 | 
			
		||||
     * @param combinedLight The current light level
 | 
			
		||||
     * @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 FirstPersonRenderer#renderMapFirstPerson(float, float, float)
 | 
			
		||||
     * @see FirstPersonRenderer#renderMapFirstPerson(MatrixStack, IRenderTypeBuffer, int, float, float, float)
 | 
			
		||||
     */
 | 
			
		||||
    private void renderItemFirstPersonCenter( float pitch, float equipProgress, float swingProgress, ItemStack stack )
 | 
			
		||||
    private void renderItemFirstPersonCenter( MatrixStack transform, IRenderTypeBuffer render, int combinedLight, float pitch, float equipProgress, float swingProgress, ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        FirstPersonRenderer renderer = Minecraft.getInstance().getFirstPersonRenderer();
 | 
			
		||||
        Minecraft minecraft = Minecraft.getInstance();
 | 
			
		||||
        FirstPersonRenderer renderer = minecraft.getFirstPersonRenderer();
 | 
			
		||||
 | 
			
		||||
        // 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.translatef( 0f, -tX / 2f, tZ );
 | 
			
		||||
        float pitchAngle = renderer.getMapAngleFromPitch( pitch );
 | 
			
		||||
        GlStateManager.translatef( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
 | 
			
		||||
        GlStateManager.rotatef( pitchAngle * -85f, 1f, 0f, 0f );
 | 
			
		||||
        renderer.renderArms();
 | 
			
		||||
        float rX = MathHelper.sin( swingRt * (float) Math.PI );
 | 
			
		||||
        GlStateManager.rotatef( rX * 20f, 1f, 0f, 0f );
 | 
			
		||||
        GlStateManager.scalef( 2f, 2f, 2f );
 | 
			
		||||
        transform.translate( 0, -tX / 2, tZ );
 | 
			
		||||
 | 
			
		||||
        renderItem( stack );
 | 
			
		||||
        float pitchAngle = renderer.getMapAngleFromPitch( pitch );
 | 
			
		||||
        transform.translate( 0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
 | 
			
		||||
        transform.rotate( Vector3f.XP.rotationDegrees( pitchAngle * -85.0f ) );
 | 
			
		||||
        if( !minecraft.player.isInvisible() )
 | 
			
		||||
        {
 | 
			
		||||
            transform.push();
 | 
			
		||||
            transform.rotate( Vector3f.YP.rotationDegrees( 90.0F ) );
 | 
			
		||||
            renderer.renderArm( transform, render, combinedLight, HandSide.RIGHT );
 | 
			
		||||
            renderer.renderArm( transform, render, combinedLight, HandSide.LEFT );
 | 
			
		||||
            transform.pop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float rX = MathHelper.sin( swingRt * (float) Math.PI );
 | 
			
		||||
        transform.rotate( Vector3f.XP.rotationDegrees( rX * 20.0F ) );
 | 
			
		||||
        transform.scale( 2.0F, 2.0F, 2.0F );
 | 
			
		||||
 | 
			
		||||
        renderItem( transform, render, stack );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,9 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
 | 
			
		||||
import dan200.computercraft.core.terminal.Terminal;
 | 
			
		||||
@@ -14,12 +16,11 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
 | 
			
		||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
 | 
			
		||||
import dan200.computercraft.shared.util.Colour;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.renderer.*;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
 | 
			
		||||
import net.minecraftforge.client.event.RenderHandEvent;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
@@ -45,17 +46,20 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void renderItem( RenderSpecificHandEvent event )
 | 
			
		||||
    public static void onRenderInHand( RenderHandEvent event )
 | 
			
		||||
    {
 | 
			
		||||
        ItemStack stack = event.getItemStack();
 | 
			
		||||
        if( !(stack.getItem() instanceof ItemPocketComputer) ) return;
 | 
			
		||||
 | 
			
		||||
        event.setCanceled( true );
 | 
			
		||||
        INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() );
 | 
			
		||||
        INSTANCE.renderItemFirstPerson(
 | 
			
		||||
            event.getMatrixStack(), event.getBuffers(), event.getLight(),
 | 
			
		||||
            event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void renderItem( ItemStack stack )
 | 
			
		||||
    protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
 | 
			
		||||
        Terminal terminal = computer == null ? null : computer.getTerminal();
 | 
			
		||||
@@ -77,50 +81,45 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
 | 
			
		||||
 | 
			
		||||
        // Setup various transformations. Note that these are partially adapted from the corresponding method
 | 
			
		||||
        // in ItemRenderer
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
        transform.push();
 | 
			
		||||
        transform.rotate( Vector3f.YP.rotationDegrees( 180f ) );
 | 
			
		||||
        transform.rotate( Vector3f.ZP.rotationDegrees( 180f ) );
 | 
			
		||||
        transform.scale( 0.5f, 0.5f, 0.5f );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.disableLighting();
 | 
			
		||||
        GlStateManager.disableDepthTest();
 | 
			
		||||
 | 
			
		||||
        GlStateManager.rotatef( 180f, 0f, 1f, 0f );
 | 
			
		||||
        GlStateManager.rotatef( 180f, 0f, 0f, 1f );
 | 
			
		||||
        GlStateManager.scalef( 0.5f, 0.5f, 0.5f );
 | 
			
		||||
 | 
			
		||||
        double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
 | 
			
		||||
        GlStateManager.scaled( scale, scale, 0 );
 | 
			
		||||
        GlStateManager.translated( -0.5 * width, -0.5 * height, 0 );
 | 
			
		||||
        float scale = 0.75f / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
 | 
			
		||||
        transform.scale( scale, scale, 0 );
 | 
			
		||||
        transform.translate( -0.5 * width, -0.5 * height, 0 );
 | 
			
		||||
 | 
			
		||||
        // Render the main frame
 | 
			
		||||
        ItemPocketComputer item = (ItemPocketComputer) stack.getItem();
 | 
			
		||||
        ComputerFamily family = item.getFamily();
 | 
			
		||||
        int frameColour = item.getColour( stack );
 | 
			
		||||
        renderFrame( family, frameColour, width, height );
 | 
			
		||||
 | 
			
		||||
        Matrix4f matrix = transform.getLast().getMatrix();
 | 
			
		||||
        renderFrame( matrix, family, frameColour, width, height );
 | 
			
		||||
 | 
			
		||||
        // Render the light
 | 
			
		||||
        int lightColour = ItemPocketComputer.getLightState( stack );
 | 
			
		||||
        if( lightColour == -1 ) lightColour = Colour.Black.getHex();
 | 
			
		||||
        renderLight( lightColour, width, height );
 | 
			
		||||
        if( lightColour == -1 ) lightColour = Colour.BLACK.getHex();
 | 
			
		||||
        renderLight( matrix, lightColour, width, height );
 | 
			
		||||
 | 
			
		||||
        if( computer != null && terminal != null )
 | 
			
		||||
        {
 | 
			
		||||
            FixedWidthFontRenderer.drawTerminal( MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
 | 
			
		||||
            FixedWidthFontRenderer.drawTerminal( matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            FixedWidthFontRenderer.drawEmptyTerminal( 0, 0, width, height );
 | 
			
		||||
            FixedWidthFontRenderer.drawEmptyTerminal( matrix, 0, 0, width, height );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableDepthTest();
 | 
			
		||||
        GlStateManager.enableLighting();
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
        transform.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderFrame( ComputerFamily family, int colour, int width, int height )
 | 
			
		||||
    private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height )
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        Minecraft.getInstance().getTextureManager().bindTexture( colour != -1
 | 
			
		||||
            ? BACKGROUND_COLOUR
 | 
			
		||||
            : family == ComputerFamily.Normal ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
 | 
			
		||||
            : family == ComputerFamily.NORMAL ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        float r = ((colour >>> 16) & 0xFF) / 255.0f;
 | 
			
		||||
@@ -129,38 +128,38 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR );
 | 
			
		||||
        buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX );
 | 
			
		||||
 | 
			
		||||
        // Top left, middle, right
 | 
			
		||||
        renderTexture( buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
 | 
			
		||||
        renderTexture( buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
 | 
			
		||||
        renderTexture( buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
 | 
			
		||||
 | 
			
		||||
        // Left and bright border
 | 
			
		||||
        renderTexture( buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
 | 
			
		||||
        renderTexture( buffer, width, 0, 36, 28, FRAME, height, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, width, 0, 36, 28, FRAME, height, r, g, b );
 | 
			
		||||
 | 
			
		||||
        // Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
 | 
			
		||||
        // lights, and then the bottom outer corners.
 | 
			
		||||
        renderTexture( buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
 | 
			
		||||
        renderTexture( buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
 | 
			
		||||
        renderTexture( buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
 | 
			
		||||
        renderTexture( buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
 | 
			
		||||
 | 
			
		||||
        renderTexture( buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
 | 
			
		||||
        renderTexture( transform, buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
 | 
			
		||||
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderLight( int colour, int width, int height )
 | 
			
		||||
    private static void renderLight( Matrix4f transform, int colour, int width, int height )
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.disableTexture();
 | 
			
		||||
        RenderSystem.enableBlend();
 | 
			
		||||
        RenderSystem.disableTexture();
 | 
			
		||||
 | 
			
		||||
        float r = ((colour >>> 16) & 0xFF) / 255.0f;
 | 
			
		||||
        float g = ((colour >>> 8) & 0xFF) / 255.0f;
 | 
			
		||||
@@ -169,26 +168,26 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
 | 
			
		||||
        buffer.pos( width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( width, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( transform, width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( transform, width, height + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
        GlStateManager.enableTexture();
 | 
			
		||||
        RenderSystem.enableTexture();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
 | 
			
		||||
    private static void renderTexture( Matrix4f transform, IVertexBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
 | 
			
		||||
    {
 | 
			
		||||
        renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
 | 
			
		||||
        renderTexture( transform, builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
 | 
			
		||||
    private static void renderTexture( Matrix4f transform, IVertexBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
 | 
			
		||||
    {
 | 
			
		||||
        float scale = 1 / 255.0f;
 | 
			
		||||
        builder.pos( x, y + height, 0 ).tex( textureX * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        builder.pos( x + width, y + height, 0 ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        builder.pos( x + width, y, 0 ).tex( (textureX + textureWidth) * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        builder.pos( x, y, 0 ).tex( textureX * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
 | 
			
		||||
        builder.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( textureX * scale, (textureY + textureHeight) * scale ).endVertex();
 | 
			
		||||
        builder.pos( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).endVertex();
 | 
			
		||||
        builder.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( (textureX + textureWidth) * scale, textureY * scale ).endVertex();
 | 
			
		||||
        builder.pos( transform, x, y, 0 ).color( r, g, b, 1.0f ).tex( textureX * scale, textureY * scale ).endVertex();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,16 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.shared.media.items.ItemPrintout;
 | 
			
		||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
 | 
			
		||||
import net.minecraft.client.renderer.Matrix4f;
 | 
			
		||||
import net.minecraft.client.renderer.Vector3f;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.client.event.RenderHandEvent;
 | 
			
		||||
import net.minecraftforge.client.event.RenderItemInFrameEvent;
 | 
			
		||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
 | 
			
		||||
@@ -34,30 +37,26 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void onRenderInHand( RenderSpecificHandEvent event )
 | 
			
		||||
    public static void onRenderInHand( RenderHandEvent event )
 | 
			
		||||
    {
 | 
			
		||||
        ItemStack stack = event.getItemStack();
 | 
			
		||||
        if( !(stack.getItem() instanceof ItemPrintout) ) return;
 | 
			
		||||
 | 
			
		||||
        event.setCanceled( true );
 | 
			
		||||
        INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() );
 | 
			
		||||
        INSTANCE.renderItemFirstPerson(
 | 
			
		||||
            event.getMatrixStack(), event.getBuffers(), event.getLight(),
 | 
			
		||||
            event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void renderItem( ItemStack stack )
 | 
			
		||||
    protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        // Setup various transformations. Note that these are partially adapated from the corresponding method
 | 
			
		||||
        // in FirstPersonRenderer.renderFirstPersonMap
 | 
			
		||||
        GlStateManager.disableLighting();
 | 
			
		||||
        transform.rotate( Vector3f.XP.rotationDegrees( 180f ) );
 | 
			
		||||
        transform.scale( 0.42f, 0.42f, -0.42f );
 | 
			
		||||
        transform.translate( -0.5f, -0.48f, 0.0f );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.rotatef( 180f, 0f, 1f, 0f );
 | 
			
		||||
        GlStateManager.rotatef( 180f, 0f, 0f, 1f );
 | 
			
		||||
        GlStateManager.scalef( 0.42f, 0.42f, -0.42f );
 | 
			
		||||
        GlStateManager.translatef( -0.5f, -0.48f, 0.0f );
 | 
			
		||||
 | 
			
		||||
        drawPrintout( stack );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableLighting();
 | 
			
		||||
        drawPrintout( transform, render, stack );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
@@ -65,24 +64,20 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
 | 
			
		||||
    {
 | 
			
		||||
        ItemStack stack = event.getItem();
 | 
			
		||||
        if( !(stack.getItem() instanceof ItemPrintout) ) return;
 | 
			
		||||
 | 
			
		||||
        event.setCanceled( true );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.disableLighting();
 | 
			
		||||
        MatrixStack transform = event.getMatrix();
 | 
			
		||||
 | 
			
		||||
        // Move a little bit forward to ensure we're not clipping with the frame
 | 
			
		||||
        GlStateManager.translatef( 0.0f, 0.0f, -0.001f );
 | 
			
		||||
        GlStateManager.rotatef( 180f, 0f, 0f, 1f );
 | 
			
		||||
        GlStateManager.scalef( 0.95f, 0.95f, -0.95f );
 | 
			
		||||
        GlStateManager.translatef( -0.5f, -0.5f, 0.0f );
 | 
			
		||||
        transform.translate( 0.0f, 0.0f, -0.001f );
 | 
			
		||||
        transform.rotate( Vector3f.ZP.rotationDegrees( 180f ) );
 | 
			
		||||
        transform.scale( 0.95f, 0.95f, -0.95f );
 | 
			
		||||
        transform.translate( -0.5f, -0.5f, 0.0f );
 | 
			
		||||
 | 
			
		||||
        drawPrintout( stack );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableLighting();
 | 
			
		||||
        GlStateManager.disableBlend();
 | 
			
		||||
        drawPrintout( transform, event.getBuffers(), stack );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawPrintout( ItemStack stack )
 | 
			
		||||
    private static void drawPrintout( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        int pages = ItemPrintout.getPageCount( stack );
 | 
			
		||||
        boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK;
 | 
			
		||||
@@ -105,11 +100,14 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
 | 
			
		||||
        double max = Math.max( visualHeight, visualWidth );
 | 
			
		||||
 | 
			
		||||
        // Scale the printout to fit correctly.
 | 
			
		||||
        double scale = 1.0 / max;
 | 
			
		||||
        GlStateManager.scaled( scale, scale, scale );
 | 
			
		||||
        GlStateManager.translated( (max - width) / 2.0, (max - height) / 2.0, 0.0 );
 | 
			
		||||
        float scale = (float) (1.0 / max);
 | 
			
		||||
        transform.scale( scale, scale, scale );
 | 
			
		||||
        transform.translate( (max - width) / 2.0, (max - height) / 2.0, 0.0 );
 | 
			
		||||
 | 
			
		||||
        drawBorder( 0, 0, -0.01, 0, pages, book );
 | 
			
		||||
        drawText( X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) );
 | 
			
		||||
        Matrix4f matrix = transform.getLast().getMatrix();
 | 
			
		||||
        drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book );
 | 
			
		||||
        drawText( matrix, render,
 | 
			
		||||
            X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack )
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,269 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of ComputerCraft - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
 | 
			
		||||
 * Send enquiries to dratcliffe@gmail.com
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import net.minecraft.client.renderer.model.BakedQuad;
 | 
			
		||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.VertexFormat;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
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];
 | 
			
		||||
 | 
			
		||||
        NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
 | 
			
		||||
        {
 | 
			
		||||
            super( parent );
 | 
			
		||||
            this.positionMatrix = positionMatrix;
 | 
			
		||||
            this.normalMatrix = normalMatrix;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void setQuadOrientation( @Nonnull Direction 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 final class BakedQuadBuilder implements IVertexConsumer
 | 
			
		||||
    {
 | 
			
		||||
        private final VertexFormat format;
 | 
			
		||||
 | 
			
		||||
        private final int[] vertexData;
 | 
			
		||||
        private int vertexIndex = 0, elementIndex = 0;
 | 
			
		||||
 | 
			
		||||
        private Direction orientation;
 | 
			
		||||
        private int quadTint;
 | 
			
		||||
        private boolean diffuse;
 | 
			
		||||
        private TextureAtlasSprite texture;
 | 
			
		||||
 | 
			
		||||
        private BakedQuadBuilder( VertexFormat format )
 | 
			
		||||
        {
 | 
			
		||||
            this.format = format;
 | 
			
		||||
            vertexData = new int[format.getSize()];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        @Override
 | 
			
		||||
        public VertexFormat getVertexFormat()
 | 
			
		||||
        {
 | 
			
		||||
            return format;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void setQuadTint( int tint )
 | 
			
		||||
        {
 | 
			
		||||
            quadTint = tint;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void setQuadOrientation( @Nonnull Direction 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 );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,49 +5,45 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.client.renderer.Matrix4f;
 | 
			
		||||
import net.minecraft.client.renderer.RenderType;
 | 
			
		||||
import net.minecraft.tileentity.TileEntity;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.RayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.Vec3d;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
import net.minecraftforge.api.distmarker.Dist;
 | 
			
		||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
 | 
			
		||||
import net.minecraftforge.client.event.DrawHighlightEvent;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
 | 
			
		||||
import java.util.EnumSet;
 | 
			
		||||
 | 
			
		||||
import static net.minecraft.util.Direction.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current
 | 
			
		||||
 * block. This means you do not get an intrusive outline on top of the screen.
 | 
			
		||||
 */
 | 
			
		||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
 | 
			
		||||
public final class MonitorHighlightRenderer
 | 
			
		||||
{
 | 
			
		||||
    private static final float EXPAND = 0.002f;
 | 
			
		||||
 | 
			
		||||
    private MonitorHighlightRenderer()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void drawHighlight( DrawBlockHighlightEvent event )
 | 
			
		||||
    public static void drawHighlight( DrawHighlightEvent.HighlightBlock event )
 | 
			
		||||
    {
 | 
			
		||||
        if( event.getTarget().getType() != RayTraceResult.Type.BLOCK || event.getInfo().getRenderViewEntity().isSneaking() )
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // Preserve normal behaviour when crouching.
 | 
			
		||||
        if( event.getInfo().getRenderViewEntity().isCrouching() ) return;
 | 
			
		||||
 | 
			
		||||
        World world = event.getInfo().getRenderViewEntity().getEntityWorld();
 | 
			
		||||
        BlockPos pos = ((BlockRayTraceResult) event.getTarget()).getPos();
 | 
			
		||||
        BlockPos pos = event.getTarget().getPos();
 | 
			
		||||
 | 
			
		||||
        TileEntity tile = world.getTileEntity( pos );
 | 
			
		||||
        if( !(tile instanceof TileMonitor) ) return;
 | 
			
		||||
@@ -64,53 +60,37 @@ public final class MonitorHighlightRenderer
 | 
			
		||||
        if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() );
 | 
			
		||||
        if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
 | 
			
		||||
        GlStateManager.lineWidth( Math.max( 2.5F, (float) Minecraft.getInstance().mainWindow.getFramebufferWidth() / 1920.0F * 2.5F ) );
 | 
			
		||||
        GlStateManager.disableTexture();
 | 
			
		||||
        GlStateManager.depthMask( false );
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
 | 
			
		||||
        MatrixStack transformStack = event.getMatrix();
 | 
			
		||||
        Vec3d cameraPos = event.getInfo().getProjectedView();
 | 
			
		||||
        GlStateManager.translated( pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ() );
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR );
 | 
			
		||||
        transformStack.push();
 | 
			
		||||
        transformStack.translate( pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ() );
 | 
			
		||||
 | 
			
		||||
        // I wish I could think of a better way to do this
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 0, UP );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 1, UP );
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 0, UP );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 1, UP );
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, EAST );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 1, EAST );
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, EAST );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 1, EAST );
 | 
			
		||||
        if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, SOUTH );
 | 
			
		||||
        if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, 1, 0, 0, SOUTH );
 | 
			
		||||
        if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, SOUTH );
 | 
			
		||||
        if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, 1, 1, 0, SOUTH );
 | 
			
		||||
        IVertexBuilder buffer = event.getBuffers().getBuffer( RenderType.getLines() );
 | 
			
		||||
        Matrix4f transform = transformStack.getLast().getMatrix();
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, transform, 0, 0, 0, UP );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, transform, 0, 0, 1, UP );
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, transform, 1, 0, 0, UP );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, transform, 1, 0, 1, UP );
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, transform, 0, 0, 0, EAST );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, transform, 0, 0, 1, EAST );
 | 
			
		||||
        if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, transform, 0, 1, 0, EAST );
 | 
			
		||||
        if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, transform, 0, 1, 1, EAST );
 | 
			
		||||
        if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, transform, 0, 0, 0, SOUTH );
 | 
			
		||||
        if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, transform, 1, 0, 0, SOUTH );
 | 
			
		||||
        if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, transform, 0, 1, 0, SOUTH );
 | 
			
		||||
        if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, transform, 1, 1, 0, SOUTH );
 | 
			
		||||
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
        GlStateManager.depthMask( true );
 | 
			
		||||
        GlStateManager.enableTexture();
 | 
			
		||||
        GlStateManager.disableBlend();
 | 
			
		||||
        transformStack.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void line( BufferBuilder buffer, int x, int y, int z, Direction direction )
 | 
			
		||||
    private static void line( IVertexBuilder buffer, Matrix4f transform, float x, float y, float z, Direction direction )
 | 
			
		||||
    {
 | 
			
		||||
        double minX = x == 0 ? -EXPAND : 1 + EXPAND;
 | 
			
		||||
        double minY = y == 0 ? -EXPAND : 1 + EXPAND;
 | 
			
		||||
        double minZ = z == 0 ? -EXPAND : 1 + EXPAND;
 | 
			
		||||
 | 
			
		||||
        buffer.pos( minX, minY, minZ ).color( 0, 0, 0, 0.4f ).endVertex();
 | 
			
		||||
        buffer.pos(
 | 
			
		||||
            minX + direction.getXOffset() * (1 + EXPAND * 2),
 | 
			
		||||
            minY + direction.getYOffset() * (1 + EXPAND * 2),
 | 
			
		||||
            minZ + direction.getZOffset() * (1 + EXPAND * 2)
 | 
			
		||||
        buffer.pos( transform, x, y, z ).color( 0, 0, 0, 0.4f ).endVertex();
 | 
			
		||||
        buffer.pos( transform,
 | 
			
		||||
            x + direction.getXOffset(),
 | 
			
		||||
            y + direction.getYOffset(),
 | 
			
		||||
            z + direction.getZOffset()
 | 
			
		||||
        ).color( 0, 0, 0, 0.4f ).endVertex();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,14 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager.DestFactor;
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager.SourceFactor;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
 | 
			
		||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
			
		||||
import dan200.computercraft.shared.util.Palette;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
 | 
			
		||||
import net.minecraft.client.renderer.Matrix4f;
 | 
			
		||||
import net.minecraft.client.renderer.RenderState;
 | 
			
		||||
import net.minecraft.client.renderer.RenderType;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
@@ -24,7 +23,7 @@ import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAG
 | 
			
		||||
public final class PrintoutRenderer
 | 
			
		||||
{
 | 
			
		||||
    private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
 | 
			
		||||
    private static final double BG_SIZE = 256.0;
 | 
			
		||||
    private static final float BG_SIZE = 256.0f;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Width of a page.
 | 
			
		||||
@@ -61,27 +60,24 @@ public final class PrintoutRenderer
 | 
			
		||||
 | 
			
		||||
    private PrintoutRenderer() {}
 | 
			
		||||
 | 
			
		||||
    public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
 | 
			
		||||
    public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
 | 
			
		||||
    {
 | 
			
		||||
        IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
 | 
			
		||||
        for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
 | 
			
		||||
        {
 | 
			
		||||
            FixedWidthFontRenderer.drawString(
 | 
			
		||||
            FixedWidthFontRenderer.drawString( transform, buffer,
 | 
			
		||||
                x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT,
 | 
			
		||||
                false, 0, 0
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawText( int x, int y, int start, String[] text, String[] colours )
 | 
			
		||||
    public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, String[] text, String[] colours )
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.enableTexture();
 | 
			
		||||
        GlStateManager.blendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO );
 | 
			
		||||
 | 
			
		||||
        IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
 | 
			
		||||
        for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
 | 
			
		||||
        {
 | 
			
		||||
            FixedWidthFontRenderer.drawString(
 | 
			
		||||
            FixedWidthFontRenderer.drawString( transform, buffer,
 | 
			
		||||
                x, y + line * FONT_HEIGHT,
 | 
			
		||||
                new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ),
 | 
			
		||||
                null, Palette.DEFAULT, false, 0, 0
 | 
			
		||||
@@ -89,55 +85,46 @@ public final class PrintoutRenderer
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook )
 | 
			
		||||
    public static void drawBorder( Matrix4f transform, IRenderTypeBuffer renderer, float x, float y, float z, int page, int pages, boolean isBook )
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.enableTexture();
 | 
			
		||||
        GlStateManager.blendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO );
 | 
			
		||||
 | 
			
		||||
        Minecraft.getInstance().getTextureManager().bindTexture( BG );
 | 
			
		||||
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
 | 
			
		||||
 | 
			
		||||
        int leftPages = page;
 | 
			
		||||
        int rightPages = pages - page - 1;
 | 
			
		||||
 | 
			
		||||
        IVertexBuilder buffer = renderer.getBuffer( Type.TYPE );
 | 
			
		||||
 | 
			
		||||
        if( isBook )
 | 
			
		||||
        {
 | 
			
		||||
            // Border
 | 
			
		||||
            double offset = offsetAt( pages );
 | 
			
		||||
            final double left = x - 4 - offset;
 | 
			
		||||
            final double right = x + X_SIZE + offset - 4;
 | 
			
		||||
            float offset = offsetAt( pages );
 | 
			
		||||
            float left = x - 4 - offset;
 | 
			
		||||
            float right = x + X_SIZE + offset - 4;
 | 
			
		||||
 | 
			
		||||
            // Left and right border
 | 
			
		||||
            drawTexture( buffer, left - 4, y - 8, z - 0.02, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
 | 
			
		||||
            drawTexture( buffer, right, y - 8, z - 0.02, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
 | 
			
		||||
            drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
 | 
			
		||||
            drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
 | 
			
		||||
 | 
			
		||||
            // Draw centre panel (just stretched texture, sorry).
 | 
			
		||||
            drawTexture( buffer,
 | 
			
		||||
                x - offset, y, z - 0.02, X_SIZE + offset * 2, Y_SIZE,
 | 
			
		||||
            drawTexture( transform, buffer,
 | 
			
		||||
                x - offset, y, z - 0.02f, X_SIZE + offset * 2, Y_SIZE,
 | 
			
		||||
                COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            double borderX = left;
 | 
			
		||||
            float borderX = left;
 | 
			
		||||
            while( borderX < right )
 | 
			
		||||
            {
 | 
			
		||||
                double thisWidth = Math.min( right - borderX, X_SIZE );
 | 
			
		||||
                drawTexture( buffer, borderX, y - 8, z - 0.02, 0, COVER_Y, thisWidth, COVER_SIZE );
 | 
			
		||||
                drawTexture( buffer, borderX, y + Y_SIZE - 4, z - 0.02, 0, COVER_Y + COVER_SIZE, thisWidth, COVER_SIZE );
 | 
			
		||||
                drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE );
 | 
			
		||||
                drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE );
 | 
			
		||||
                borderX += thisWidth;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Left half
 | 
			
		||||
        drawTexture( buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE );
 | 
			
		||||
        drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE );
 | 
			
		||||
        for( int n = 0; n <= leftPages; n++ )
 | 
			
		||||
        {
 | 
			
		||||
            drawTexture( buffer,
 | 
			
		||||
                x - offsetAt( n ), y, z - 1e-3 * n,
 | 
			
		||||
            drawTexture( transform, buffer,
 | 
			
		||||
                x - offsetAt( n ), y, z - 1e-3f * n,
 | 
			
		||||
                // Use the left "bold" fold for the outermost page
 | 
			
		||||
                n == leftPages ? 0 : X_FOLD_SIZE, 0,
 | 
			
		||||
                X_FOLD_SIZE, Y_SIZE
 | 
			
		||||
@@ -145,38 +132,54 @@ public final class PrintoutRenderer
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Right half
 | 
			
		||||
        drawTexture( buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE );
 | 
			
		||||
        drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE );
 | 
			
		||||
        for( int n = 0; n <= rightPages; n++ )
 | 
			
		||||
        {
 | 
			
		||||
            drawTexture( buffer,
 | 
			
		||||
                x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3 * n,
 | 
			
		||||
            drawTexture( transform, buffer,
 | 
			
		||||
                x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3f * n,
 | 
			
		||||
                // Two folds, then the main page. Use the right "bold" fold for the outermost page.
 | 
			
		||||
                X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0,
 | 
			
		||||
                X_FOLD_SIZE, Y_SIZE
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double u, double v, double width, double height )
 | 
			
		||||
    private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float u, float v, float width, float height )
 | 
			
		||||
    {
 | 
			
		||||
        buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double width, double height, double u, double v, double tWidth, double tHeight )
 | 
			
		||||
    private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float width, float height, float u, float v, float tWidth, float tHeight )
 | 
			
		||||
    {
 | 
			
		||||
        buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
        buffer.pos( matrix, x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static double offsetAt( int page )
 | 
			
		||||
    public static float offsetAt( int page )
 | 
			
		||||
    {
 | 
			
		||||
        return 32 * (1 - Math.pow( 1.2, -page ));
 | 
			
		||||
        return (float) (32 * (1 - Math.pow( 1.2, -page )));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class Type extends RenderState
 | 
			
		||||
    {
 | 
			
		||||
        static final RenderType TYPE = RenderType.makeType(
 | 
			
		||||
            "printout_background", DefaultVertexFormats.POSITION_TEX, GL11.GL_QUADS, 1024,
 | 
			
		||||
            false, false, // useDelegate, needsSorting
 | 
			
		||||
            RenderType.State.getBuilder()
 | 
			
		||||
                .texture( new RenderState.TextureState( BG, false, false ) ) // blur, minimap
 | 
			
		||||
                .alpha( DEFAULT_ALPHA )
 | 
			
		||||
                .lightmap( LIGHTMAP_DISABLED )
 | 
			
		||||
                .build( false )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        private Type( String name, Runnable setup, Runnable destroy )
 | 
			
		||||
        {
 | 
			
		||||
            super( name, setup, destroy );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,140 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of ComputerCraft - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
 | 
			
		||||
 * Send enquiries to dratcliffe@gmail.com
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
 | 
			
		||||
import dan200.computercraft.shared.util.WorldUtil;
 | 
			
		||||
import net.minecraft.block.Block;
 | 
			
		||||
import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.renderer.WorldRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
 | 
			
		||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.util.BlockRenderLayer;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.RayTraceResult;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
import net.minecraftforge.client.ForgeHooksClient;
 | 
			
		||||
import net.minecraftforge.client.model.data.EmptyModelData;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Render breaking animation only over part of a {@link TileCable}.
 | 
			
		||||
 */
 | 
			
		||||
public class TileEntityCableRenderer extends TileEntityRenderer<TileCable>
 | 
			
		||||
{
 | 
			
		||||
    private static final ResourceLocation[] DESTROY_STAGES = new ResourceLocation[10];
 | 
			
		||||
    private static final Random random = new Random();
 | 
			
		||||
 | 
			
		||||
    static
 | 
			
		||||
    {
 | 
			
		||||
        for( int i = 0; i < DESTROY_STAGES.length; i++ )
 | 
			
		||||
        {
 | 
			
		||||
            DESTROY_STAGES[i] = new ResourceLocation( "block/destroy_stage_" + i );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render( @Nonnull TileCable te, double x, double y, double z, float partialTicks, int destroyStage )
 | 
			
		||||
    {
 | 
			
		||||
        if( destroyStage < 0 ) return;
 | 
			
		||||
 | 
			
		||||
        BlockPos pos = te.getPos();
 | 
			
		||||
 | 
			
		||||
        Minecraft mc = Minecraft.getInstance();
 | 
			
		||||
 | 
			
		||||
        RayTraceResult hit = mc.objectMouseOver;
 | 
			
		||||
        if( hit == null || hit.getType() != RayTraceResult.Type.BLOCK || !((BlockRayTraceResult) hit).getPos().equals( pos ) )
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        World world = te.getWorld();
 | 
			
		||||
        BlockState state = world.getBlockState( pos );
 | 
			
		||||
        Block block = state.getBlock();
 | 
			
		||||
        if( block != ComputerCraft.Blocks.cable ) return;
 | 
			
		||||
 | 
			
		||||
        state = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getHitVec().subtract( pos.getX(), pos.getY(), pos.getZ() ) )
 | 
			
		||||
            ? block.getDefaultState().with( BlockCable.MODEM, state.get( BlockCable.MODEM ) )
 | 
			
		||||
            : state.with( BlockCable.MODEM, CableModemVariant.None );
 | 
			
		||||
 | 
			
		||||
        IBakedModel model = mc.getBlockRendererDispatcher().getModelForState( state );
 | 
			
		||||
 | 
			
		||||
        preRenderDamagedBlocks();
 | 
			
		||||
 | 
			
		||||
        ForgeHooksClient.setRenderLayer( block.getRenderLayer() );
 | 
			
		||||
 | 
			
		||||
        // See BlockRendererDispatcher#renderBlockDamage
 | 
			
		||||
        TextureAtlasSprite breakingTexture = mc.getTextureMap().getSprite( DESTROY_STAGES[destroyStage] );
 | 
			
		||||
 | 
			
		||||
        BufferBuilder buffer = Tessellator.getInstance().getBuffer();
 | 
			
		||||
        buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.BLOCK );
 | 
			
		||||
        buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
 | 
			
		||||
        buffer.noColor();
 | 
			
		||||
 | 
			
		||||
        mc.getBlockRendererDispatcher().getBlockModelRenderer().renderModel(
 | 
			
		||||
            world,
 | 
			
		||||
            ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos, 0 ),
 | 
			
		||||
            state, pos, buffer, true, random, state.getPositionRandom( pos ), EmptyModelData.INSTANCE
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID );
 | 
			
		||||
 | 
			
		||||
        buffer.setTranslation( 0, 0, 0 );
 | 
			
		||||
        Tessellator.getInstance().draw();
 | 
			
		||||
 | 
			
		||||
        postRenderDamagedBlocks();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set up the state for rendering block-breaking progress.
 | 
			
		||||
     *
 | 
			
		||||
     * @see WorldRenderer#preRenderDamagedBlocks()
 | 
			
		||||
     */
 | 
			
		||||
    private void preRenderDamagedBlocks()
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.disableLighting();
 | 
			
		||||
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
 | 
			
		||||
        GlStateManager.enableBlend();
 | 
			
		||||
        GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 0.5F );
 | 
			
		||||
        GlStateManager.polygonOffset( -3.0F, -3.0F );
 | 
			
		||||
        GlStateManager.enablePolygonOffset();
 | 
			
		||||
        GlStateManager.alphaFunc( 516, 0.1F );
 | 
			
		||||
        GlStateManager.enableAlphaTest();
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tear down the state for rendering block-breaking progress.
 | 
			
		||||
     *
 | 
			
		||||
     * @see WorldRenderer#postRenderDamagedBlocks()
 | 
			
		||||
     */
 | 
			
		||||
    private void postRenderDamagedBlocks()
 | 
			
		||||
    {
 | 
			
		||||
        GlStateManager.disableAlphaTest();
 | 
			
		||||
        GlStateManager.polygonOffset( 0.0F, 0.0F );
 | 
			
		||||
        GlStateManager.disablePolygonOffset();
 | 
			
		||||
        GlStateManager.disablePolygonOffset();
 | 
			
		||||
        GlStateManager.depthMask( true );
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,8 +5,8 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GLX;
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.client.FrameInfo;
 | 
			
		||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
 | 
			
		||||
import dan200.computercraft.core.terminal.Terminal;
 | 
			
		||||
@@ -14,25 +14,32 @@ import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
 | 
			
		||||
import dan200.computercraft.shared.util.DirectionUtil;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.renderer.*;
 | 
			
		||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.shared.peripheral.monitor.TileMonitor.RENDER_MARGIN;
 | 
			
		||||
 | 
			
		||||
public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between
 | 
			
		||||
     * the monitor frame and contents.
 | 
			
		||||
     */
 | 
			
		||||
    private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
 | 
			
		||||
 | 
			
		||||
    private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
 | 
			
		||||
 | 
			
		||||
    public TileEntityMonitorRenderer( TileEntityRendererDispatcher rendererDispatcher )
 | 
			
		||||
    {
 | 
			
		||||
        super( rendererDispatcher );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render( @Nonnull TileMonitor monitor, double posX, double posY, double posZ, float f, int i )
 | 
			
		||||
    public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull MatrixStack transform, @Nonnull IRenderTypeBuffer renderer, int lightmapCoord, int overlayLight )
 | 
			
		||||
    {
 | 
			
		||||
        // Render from the origin monitor
 | 
			
		||||
        ClientMonitor originTerminal = monitor.getClientMonitor();
 | 
			
		||||
@@ -54,9 +61,6 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
 | 
			
		||||
        originTerminal.lastRenderPos = monitorPos;
 | 
			
		||||
 | 
			
		||||
        BlockPos originPos = origin.getPos();
 | 
			
		||||
        posX += originPos.getX() - monitorPos.getX();
 | 
			
		||||
        posY += originPos.getY() - monitorPos.getY();
 | 
			
		||||
        posZ += originPos.getZ() - monitorPos.getZ();
 | 
			
		||||
 | 
			
		||||
        // Determine orientation
 | 
			
		||||
        Direction dir = origin.getDirection();
 | 
			
		||||
@@ -64,130 +68,94 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
 | 
			
		||||
        float yaw = dir.getHorizontalAngle();
 | 
			
		||||
        float pitch = DirectionUtil.toPitchAngle( front );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
 | 
			
		||||
        // Setup initial transform
 | 
			
		||||
        GlStateManager.translated( posX + 0.5, posY + 0.5, posZ + 0.5 );
 | 
			
		||||
        GlStateManager.rotatef( -yaw, 0.0f, 1.0f, 0.0f );
 | 
			
		||||
        GlStateManager.rotatef( pitch, 1.0f, 0.0f, 0.0f );
 | 
			
		||||
        GlStateManager.translated(
 | 
			
		||||
            -0.5 + TileMonitor.RENDER_BORDER + RENDER_MARGIN,
 | 
			
		||||
            origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + RENDER_MARGIN) + 0,
 | 
			
		||||
        transform.push();
 | 
			
		||||
        transform.translate(
 | 
			
		||||
            originPos.getX() - monitorPos.getX() + 0.5,
 | 
			
		||||
            originPos.getY() - monitorPos.getY() + 0.5,
 | 
			
		||||
            originPos.getZ() - monitorPos.getZ() + 0.5
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        transform.rotate( Vector3f.YN.rotationDegrees( yaw ) );
 | 
			
		||||
        transform.rotate( Vector3f.XP.rotationDegrees( pitch ) );
 | 
			
		||||
        transform.translate(
 | 
			
		||||
            -0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
 | 
			
		||||
            origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0,
 | 
			
		||||
            0.5
 | 
			
		||||
        );
 | 
			
		||||
        double xSize = origin.getWidth() - 2.0 * (RENDER_MARGIN + TileMonitor.RENDER_BORDER);
 | 
			
		||||
        double ySize = origin.getHeight() - 2.0 * (RENDER_MARGIN + TileMonitor.RENDER_BORDER);
 | 
			
		||||
 | 
			
		||||
        // Get renderers
 | 
			
		||||
        Minecraft mc = Minecraft.getInstance();
 | 
			
		||||
 | 
			
		||||
        // Set up render state for monitors. We disable writing to the depth buffer (we draw a "blocker" later),
 | 
			
		||||
        // and setup lighting so that we render with a glow.
 | 
			
		||||
        GlStateManager.depthMask( false );
 | 
			
		||||
        GLX.glMultiTexCoord2f( GLX.GL_TEXTURE1, 0xFFFF, 0xFFFF );
 | 
			
		||||
        GlStateManager.disableLighting();
 | 
			
		||||
        mc.gameRenderer.disableLightmap();
 | 
			
		||||
        double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
 | 
			
		||||
        double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
 | 
			
		||||
 | 
			
		||||
        // Draw the contents
 | 
			
		||||
        Terminal terminal = originTerminal.getTerminal();
 | 
			
		||||
        if( terminal != null )
 | 
			
		||||
        {
 | 
			
		||||
            boolean redraw = originTerminal.pollTerminalChanged();
 | 
			
		||||
            if( originTerminal.buffer == null )
 | 
			
		||||
            {
 | 
			
		||||
                originTerminal.createBuffer( MonitorRenderer.VBO );
 | 
			
		||||
                redraw = true;
 | 
			
		||||
            }
 | 
			
		||||
            VertexBuffer vbo = originTerminal.buffer;
 | 
			
		||||
 | 
			
		||||
            // Draw a terminal
 | 
			
		||||
            double xScale = xSize / (terminal.getWidth() * FixedWidthFontRenderer.FONT_WIDTH);
 | 
			
		||||
            double yScale = ySize / (terminal.getHeight() * FixedWidthFontRenderer.FONT_HEIGHT);
 | 
			
		||||
            transform.push();
 | 
			
		||||
            transform.scale( (float) xScale, (float) -yScale, 1.0f );
 | 
			
		||||
 | 
			
		||||
            GlStateManager.pushMatrix();
 | 
			
		||||
            GlStateManager.scaled( (float) xScale, (float) -yScale, 1.0f );
 | 
			
		||||
            float xMargin = (float) (MARGIN / xScale);
 | 
			
		||||
            float yMargin = (float) (MARGIN / yScale);
 | 
			
		||||
 | 
			
		||||
            renderTerminal( originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
 | 
			
		||||
            Matrix4f matrix = transform.getLast().getMatrix();
 | 
			
		||||
 | 
			
		||||
            GlStateManager.popMatrix();
 | 
			
		||||
            if( redraw )
 | 
			
		||||
            {
 | 
			
		||||
                Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
                BufferBuilder builder = tessellator.getBuffer();
 | 
			
		||||
                builder.begin( FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat() );
 | 
			
		||||
                FixedWidthFontRenderer.drawTerminalWithoutCursor(
 | 
			
		||||
                    IDENTITY, builder, 0, 0,
 | 
			
		||||
                    terminal, !originTerminal.isColour(), yMargin, yMargin, xMargin, xMargin
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                builder.finishDrawing();
 | 
			
		||||
                vbo.upload( builder );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
 | 
			
		||||
            // render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
 | 
			
		||||
            // for now.
 | 
			
		||||
            IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
 | 
			
		||||
            FixedWidthFontRenderer.TYPE.setupRenderState();
 | 
			
		||||
 | 
			
		||||
            vbo.bindBuffer();
 | 
			
		||||
            FixedWidthFontRenderer.TYPE.getVertexFormat().setupBufferState( 0L );
 | 
			
		||||
            vbo.draw( matrix, FixedWidthFontRenderer.TYPE.getDrawMode() );
 | 
			
		||||
            VertexBuffer.unbindBuffer();
 | 
			
		||||
            FixedWidthFontRenderer.TYPE.getVertexFormat().clearBufferState();
 | 
			
		||||
 | 
			
		||||
            // We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is
 | 
			
		||||
            // reasonable.
 | 
			
		||||
            FixedWidthFontRenderer.drawCursor( matrix, buffer, 0, 0, terminal, !originTerminal.isColour() );
 | 
			
		||||
 | 
			
		||||
            transform.pop();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            FixedWidthFontRenderer.drawEmptyTerminal(
 | 
			
		||||
                transform.getLast().getMatrix(), renderer,
 | 
			
		||||
                -MARGIN, MARGIN,
 | 
			
		||||
                (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Tear down render state for monitors.
 | 
			
		||||
        GlStateManager.depthMask( true );
 | 
			
		||||
        mc.gameRenderer.enableLightmap();
 | 
			
		||||
        GlStateManager.enableLighting();
 | 
			
		||||
 | 
			
		||||
        // Draw the depth blocker
 | 
			
		||||
        GlStateManager.colorMask( false, false, false, false );
 | 
			
		||||
        FixedWidthFontRenderer.drawBlocker(
 | 
			
		||||
            transform.getLast().getMatrix(), renderer,
 | 
			
		||||
            (float) -TileMonitor.RENDER_MARGIN, (float) TileMonitor.RENDER_MARGIN,
 | 
			
		||||
            (float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2)
 | 
			
		||||
        );
 | 
			
		||||
        GlStateManager.colorMask( true, true, true, true );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.popMatrix();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderTerminal( ClientMonitor monitor, float xMargin, float yMargin )
 | 
			
		||||
    {
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
 | 
			
		||||
        boolean redraw = monitor.pollTerminalChanged();
 | 
			
		||||
 | 
			
		||||
        // Setup the buffers if needed. We get the renderer here, to avoid the (unlikely) race condition between
 | 
			
		||||
        // creating the buffers and rendering.
 | 
			
		||||
        MonitorRenderer renderer = MonitorRenderer.current();
 | 
			
		||||
        if( monitor.createBuffer( renderer ) ) redraw = true;
 | 
			
		||||
 | 
			
		||||
        FixedWidthFontRenderer.bindFont();
 | 
			
		||||
 | 
			
		||||
        switch( renderer )
 | 
			
		||||
        {
 | 
			
		||||
            case VBO:
 | 
			
		||||
            {
 | 
			
		||||
                VertexBuffer vbo = monitor.buffer;
 | 
			
		||||
                if( redraw )
 | 
			
		||||
                {
 | 
			
		||||
                    renderTerminalTo( monitor, buffer, xMargin, yMargin );
 | 
			
		||||
                    buffer.finishDrawing();
 | 
			
		||||
                    buffer.reset();
 | 
			
		||||
                    vbo.bufferData( buffer.getByteBuffer() );
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                vbo.bindBuffer();
 | 
			
		||||
                setupBufferFormat();
 | 
			
		||||
                vbo.drawArrays( GL11.GL_TRIANGLES );
 | 
			
		||||
                VertexBuffer.unbindBuffer();
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We don't draw the cursor with a buffer, as it's dynamic and so we'll end up refreshing far more than is
 | 
			
		||||
        // reasonable.
 | 
			
		||||
        FixedWidthFontRenderer.begin( buffer );
 | 
			
		||||
        FixedWidthFontRenderer.drawCursor( buffer, 0, 0, monitor.getTerminal(), !monitor.isColour() );
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderTerminalTo( ClientMonitor monitor, BufferBuilder buffer, float xMargin, float yMargin )
 | 
			
		||||
    {
 | 
			
		||||
        FixedWidthFontRenderer.begin( buffer );
 | 
			
		||||
        FixedWidthFontRenderer.drawTerminalWithoutCursor(
 | 
			
		||||
            buffer, 0, 0,
 | 
			
		||||
            monitor.getTerminal(), !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void setupBufferFormat()
 | 
			
		||||
    {
 | 
			
		||||
        int stride = FixedWidthFontRenderer.POSITION_COLOR_TEX.getSize();
 | 
			
		||||
        GlStateManager.vertexPointer( 3, GL11.GL_FLOAT, stride, 0 );
 | 
			
		||||
        GlStateManager.enableClientState( GL11.GL_VERTEX_ARRAY );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.colorPointer( 4, GL11.GL_UNSIGNED_BYTE, stride, 12 );
 | 
			
		||||
        GlStateManager.enableClientState( GL11.GL_COLOR_ARRAY );
 | 
			
		||||
 | 
			
		||||
        GlStateManager.texCoordPointer( 2, GL11.GL_FLOAT, stride, 16 );
 | 
			
		||||
        GlStateManager.enableClientState( GL11.GL_TEXTURE_COORD_ARRAY );
 | 
			
		||||
        transform.pop();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,9 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GlStateManager;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
 | 
			
		||||
import dan200.computercraft.api.client.TransformedModel;
 | 
			
		||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
			
		||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
 | 
			
		||||
@@ -13,31 +15,26 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
 | 
			
		||||
import dan200.computercraft.shared.util.DirectionUtil;
 | 
			
		||||
import dan200.computercraft.shared.util.Holiday;
 | 
			
		||||
import dan200.computercraft.shared.util.HolidayUtil;
 | 
			
		||||
import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.BufferBuilder;
 | 
			
		||||
import net.minecraft.client.renderer.GameRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.Tessellator;
 | 
			
		||||
import net.minecraft.client.gui.FontRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.Atlases;
 | 
			
		||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
 | 
			
		||||
import net.minecraft.client.renderer.Matrix4f;
 | 
			
		||||
import net.minecraft.client.renderer.Vector3f;
 | 
			
		||||
import net.minecraft.client.renderer.model.BakedQuad;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelManager;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
 | 
			
		||||
import net.minecraft.client.renderer.texture.AtlasTexture;
 | 
			
		||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.VertexFormat;
 | 
			
		||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.RayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.Vec3d;
 | 
			
		||||
import net.minecraftforge.client.ForgeHooksClient;
 | 
			
		||||
import net.minecraftforge.client.model.data.EmptyModelData;
 | 
			
		||||
import net.minecraftforge.client.model.pipeline.LightUtil;
 | 
			
		||||
import org.apache.commons.lang3.tuple.Pair;
 | 
			
		||||
import org.lwjgl.opengl.GL11;
 | 
			
		||||
 | 
			
		||||
import javax.vecmath.Matrix4f;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
@@ -48,189 +45,147 @@ public class TileEntityTurtleRenderer extends TileEntityRenderer<TileTurtle>
 | 
			
		||||
    private static final ModelResourceLocation COLOUR_TURTLE_MODEL = new ModelResourceLocation( "computercraft:turtle_colour", "inventory" );
 | 
			
		||||
    private static final ModelResourceLocation ELF_OVERLAY_MODEL = new ModelResourceLocation( "computercraft:turtle_elf_overlay", "inventory" );
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render( TileTurtle tileEntity, double posX, double posY, double posZ, float partialTicks, int breaking )
 | 
			
		||||
    private final Random random = new Random( 0 );
 | 
			
		||||
 | 
			
		||||
    public TileEntityTurtleRenderer( TileEntityRendererDispatcher renderDispatcher )
 | 
			
		||||
    {
 | 
			
		||||
        if( tileEntity != null ) renderTurtleAt( tileEntity, posX, posY, posZ, partialTicks );
 | 
			
		||||
        super( renderDispatcher );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ModelResourceLocation getTurtleModel( ComputerFamily family, boolean coloured )
 | 
			
		||||
    {
 | 
			
		||||
        switch( family )
 | 
			
		||||
        {
 | 
			
		||||
            case Normal:
 | 
			
		||||
            case NORMAL:
 | 
			
		||||
            default:
 | 
			
		||||
                return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
 | 
			
		||||
            case Advanced:
 | 
			
		||||
            case ADVANCED:
 | 
			
		||||
                return coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ModelResourceLocation getTurtleOverlayModel( ResourceLocation overlay, boolean christmas )
 | 
			
		||||
    {
 | 
			
		||||
        if( overlay != null )
 | 
			
		||||
        {
 | 
			
		||||
            return new ModelResourceLocation( overlay, "inventory" );
 | 
			
		||||
        }
 | 
			
		||||
        else if( christmas )
 | 
			
		||||
        {
 | 
			
		||||
            return ELF_OVERLAY_MODEL;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        if( overlay != null ) return new ModelResourceLocation( overlay, "inventory" );
 | 
			
		||||
        if( christmas ) return ELF_OVERLAY_MODEL;
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void renderTurtleAt( TileTurtle turtle, double posX, double posY, double posZ, float partialTicks )
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull IRenderTypeBuffer renderer, int lightmapCoord, int overlayLight )
 | 
			
		||||
    {
 | 
			
		||||
        // Render the label
 | 
			
		||||
        String label = turtle.createProxy().getLabel();
 | 
			
		||||
        RayTraceResult hit = rendererDispatcher.cameraHitResult;
 | 
			
		||||
        RayTraceResult hit = renderDispatcher.cameraHitResult;
 | 
			
		||||
        if( label != null && hit.getType() == RayTraceResult.Type.BLOCK && turtle.getPos().equals( ((BlockRayTraceResult) hit).getPos() ) )
 | 
			
		||||
        {
 | 
			
		||||
            setLightmapDisabled( true );
 | 
			
		||||
            GameRenderer.drawNameplate(
 | 
			
		||||
                getFontRenderer(), label,
 | 
			
		||||
                (float) posX + 0.5F, (float) posY + 1.2F, (float) posZ + 0.5F, 0,
 | 
			
		||||
                rendererDispatcher.renderInfo.getYaw(), rendererDispatcher.renderInfo.getPitch(), false
 | 
			
		||||
            );
 | 
			
		||||
            setLightmapDisabled( false );
 | 
			
		||||
            Minecraft mc = Minecraft.getInstance();
 | 
			
		||||
            FontRenderer font = renderDispatcher.fontRenderer;
 | 
			
		||||
 | 
			
		||||
            transform.push();
 | 
			
		||||
            transform.translate( 0.5, 1.2, 0.5 );
 | 
			
		||||
            transform.rotate( mc.getRenderManager().getCameraOrientation() );
 | 
			
		||||
            transform.scale( -0.025f, -0.025f, 0.025f );
 | 
			
		||||
 | 
			
		||||
            Matrix4f matrix = transform.getLast().getMatrix();
 | 
			
		||||
            int opacity = (int) (mc.gameSettings.getTextBackgroundOpacity( 0.25f ) * 255) << 24;
 | 
			
		||||
            float width = -font.getStringWidth( label ) / 2.0f;
 | 
			
		||||
            font.renderString( label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord );
 | 
			
		||||
            font.renderString( label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord );
 | 
			
		||||
 | 
			
		||||
            transform.pop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GlStateManager.pushMatrix();
 | 
			
		||||
        try
 | 
			
		||||
        transform.push();
 | 
			
		||||
 | 
			
		||||
        // Setup the transform.
 | 
			
		||||
        Vec3d offset = turtle.getRenderOffset( partialTicks );
 | 
			
		||||
        float yaw = turtle.getRenderYaw( partialTicks );
 | 
			
		||||
        transform.translate( offset.x, offset.y, offset.z );
 | 
			
		||||
 | 
			
		||||
        transform.translate( 0.5f, 0.5f, 0.5f );
 | 
			
		||||
        transform.rotate( Vector3f.YP.rotationDegrees( 180.0f - yaw ) );
 | 
			
		||||
        if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
 | 
			
		||||
        {
 | 
			
		||||
            BlockState state = turtle.getBlockState();
 | 
			
		||||
            // Setup the transform
 | 
			
		||||
            Vec3d offset = turtle.getRenderOffset( partialTicks );
 | 
			
		||||
            float yaw = turtle.getRenderYaw( partialTicks );
 | 
			
		||||
            GlStateManager.translated( posX + offset.x, posY + offset.y, posZ + offset.z );
 | 
			
		||||
            // Render the turtle
 | 
			
		||||
            GlStateManager.translatef( 0.5f, 0.5f, 0.5f );
 | 
			
		||||
            GlStateManager.rotatef( 180.0f - yaw, 0.0f, 1.0f, 0.0f );
 | 
			
		||||
            if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
 | 
			
		||||
            {
 | 
			
		||||
                // Flip the model and swap the cull face as winding order will have changed.
 | 
			
		||||
                GlStateManager.scalef( 1.0f, -1.0f, 1.0f );
 | 
			
		||||
                GlStateManager.cullFace( GlStateManager.CullFace.FRONT );
 | 
			
		||||
            }
 | 
			
		||||
            GlStateManager.translatef( -0.5f, -0.5f, -0.5f );
 | 
			
		||||
            // Render the turtle
 | 
			
		||||
            int colour = turtle.getColour();
 | 
			
		||||
            ComputerFamily family = turtle.getFamily();
 | 
			
		||||
            ResourceLocation overlay = turtle.getOverlay();
 | 
			
		||||
 | 
			
		||||
            renderModel( state, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
 | 
			
		||||
 | 
			
		||||
            // Render the overlay
 | 
			
		||||
            ModelResourceLocation overlayModel = getTurtleOverlayModel(
 | 
			
		||||
                overlay,
 | 
			
		||||
                HolidayUtil.getCurrentHoliday() == Holiday.Christmas
 | 
			
		||||
            );
 | 
			
		||||
            if( overlayModel != null )
 | 
			
		||||
            {
 | 
			
		||||
                GlStateManager.disableCull();
 | 
			
		||||
                GlStateManager.enableBlend();
 | 
			
		||||
                GlStateManager.blendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    renderModel( state, overlayModel, null );
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    GlStateManager.disableBlend();
 | 
			
		||||
                    GlStateManager.enableCull();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Render the upgrades
 | 
			
		||||
            renderUpgrade( state, turtle, TurtleSide.Left, partialTicks );
 | 
			
		||||
            renderUpgrade( state, turtle, TurtleSide.Right, partialTicks );
 | 
			
		||||
            // Flip the model
 | 
			
		||||
            transform.scale( 1.0f, -1.0f, 1.0f );
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        transform.translate( -0.5f, -0.5f, -0.5f );
 | 
			
		||||
 | 
			
		||||
        // Render the turtle
 | 
			
		||||
        int colour = turtle.getColour();
 | 
			
		||||
        ComputerFamily family = turtle.getFamily();
 | 
			
		||||
        ResourceLocation overlay = turtle.getOverlay();
 | 
			
		||||
 | 
			
		||||
        IVertexBuilder buffer = renderer.getBuffer( Atlases.getTranslucentCullBlockType() );
 | 
			
		||||
        renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
 | 
			
		||||
 | 
			
		||||
        // Render the overlay
 | 
			
		||||
        ModelResourceLocation overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS );
 | 
			
		||||
        if( overlayModel != null )
 | 
			
		||||
        {
 | 
			
		||||
            GlStateManager.popMatrix();
 | 
			
		||||
            GlStateManager.cullFace( GlStateManager.CullFace.BACK );
 | 
			
		||||
            renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Render the upgrades
 | 
			
		||||
        renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks );
 | 
			
		||||
        renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks );
 | 
			
		||||
 | 
			
		||||
        transform.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void renderUpgrade( BlockState state, TileTurtle turtle, TurtleSide side, float f )
 | 
			
		||||
    private void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, TurtleSide side, float f )
 | 
			
		||||
    {
 | 
			
		||||
        ITurtleUpgrade upgrade = turtle.getUpgrade( side );
 | 
			
		||||
        if( upgrade != null )
 | 
			
		||||
        {
 | 
			
		||||
            GlStateManager.pushMatrix();
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                float toolAngle = turtle.getToolRenderAngle( side, f );
 | 
			
		||||
                GlStateManager.translatef( 0.0f, 0.5f, 0.5f );
 | 
			
		||||
                GlStateManager.rotatef( -toolAngle, 1.0f, 0.0f, 0.0f );
 | 
			
		||||
                GlStateManager.translatef( 0.0f, -0.5f, -0.5f );
 | 
			
		||||
        if( upgrade == null ) return;
 | 
			
		||||
        transform.push();
 | 
			
		||||
 | 
			
		||||
                Pair<IBakedModel, Matrix4f> pair = upgrade.getModel( turtle.getAccess(), side );
 | 
			
		||||
                if( pair != null )
 | 
			
		||||
                {
 | 
			
		||||
                    if( pair.getRight() != null )
 | 
			
		||||
                    {
 | 
			
		||||
                        ForgeHooksClient.multiplyCurrentGlMatrix( pair.getRight() );
 | 
			
		||||
                    }
 | 
			
		||||
                    if( pair.getLeft() != null )
 | 
			
		||||
                    {
 | 
			
		||||
                        renderModel( state, pair.getLeft(), null );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                GlStateManager.popMatrix();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        float toolAngle = turtle.getToolRenderAngle( side, f );
 | 
			
		||||
        transform.translate( 0.0f, 0.5f, 0.5f );
 | 
			
		||||
        transform.rotate( Vector3f.XN.rotationDegrees( toolAngle ) );
 | 
			
		||||
        transform.translate( 0.0f, -0.5f, -0.5f );
 | 
			
		||||
 | 
			
		||||
        TransformedModel model = upgrade.getModel( turtle.getAccess(), side );
 | 
			
		||||
        model.getMatrix().push( transform );
 | 
			
		||||
        renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null );
 | 
			
		||||
        transform.pop();
 | 
			
		||||
 | 
			
		||||
        transform.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void renderModel( BlockState state, ModelResourceLocation modelLocation, int[] tints )
 | 
			
		||||
    private void renderModel( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder renderer, int lightmapCoord, int overlayLight, ModelResourceLocation modelLocation, int[] tints )
 | 
			
		||||
    {
 | 
			
		||||
        Minecraft mc = Minecraft.getInstance();
 | 
			
		||||
        ModelManager modelManager = mc.getItemRenderer().getItemModelMesher().getModelManager();
 | 
			
		||||
        renderModel( state, modelManager.getModel( modelLocation ), tints );
 | 
			
		||||
        ModelManager modelManager = Minecraft.getInstance().getItemRenderer().getItemModelMesher().getModelManager();
 | 
			
		||||
        renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void renderModel( BlockState state, IBakedModel model, int[] tints )
 | 
			
		||||
    private void renderModel( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder renderer, int lightmapCoord, int overlayLight, IBakedModel model, int[] tints )
 | 
			
		||||
    {
 | 
			
		||||
        Random random = new Random( 0 );
 | 
			
		||||
        Tessellator tessellator = Tessellator.getInstance();
 | 
			
		||||
        rendererDispatcher.textureManager.bindTexture( AtlasTexture.LOCATION_BLOCKS_TEXTURE );
 | 
			
		||||
        renderQuads( tessellator, model.getQuads( state, null, random, EmptyModelData.INSTANCE ), tints );
 | 
			
		||||
        random.setSeed( 0 );
 | 
			
		||||
        renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random, EmptyModelData.INSTANCE ), tints );
 | 
			
		||||
        for( Direction facing : DirectionUtil.FACINGS )
 | 
			
		||||
        {
 | 
			
		||||
            renderQuads( tessellator, model.getQuads( state, facing, random, EmptyModelData.INSTANCE ), tints );
 | 
			
		||||
            renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random, EmptyModelData.INSTANCE ), tints );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void renderQuads( Tessellator tessellator, List<BakedQuad> quads, int[] tints )
 | 
			
		||||
    private static void renderQuads( @Nonnull MatrixStack transform, @Nonnull IVertexBuilder buffer, int lightmapCoord, int overlayLight, List<BakedQuad> quads, int[] tints )
 | 
			
		||||
    {
 | 
			
		||||
        BufferBuilder buffer = tessellator.getBuffer();
 | 
			
		||||
        VertexFormat format = DefaultVertexFormats.ITEM;
 | 
			
		||||
        buffer.begin( GL11.GL_QUADS, format );
 | 
			
		||||
        for( BakedQuad quad : quads )
 | 
			
		||||
        MatrixStack.Entry matrix = transform.getLast();
 | 
			
		||||
 | 
			
		||||
        for( BakedQuad bakedquad : quads )
 | 
			
		||||
        {
 | 
			
		||||
            VertexFormat quadFormat = quad.getFormat();
 | 
			
		||||
            if( quadFormat != format )
 | 
			
		||||
            int tint = -1;
 | 
			
		||||
            if( tints != null && bakedquad.hasTintIndex() )
 | 
			
		||||
            {
 | 
			
		||||
                tessellator.draw();
 | 
			
		||||
                format = quadFormat;
 | 
			
		||||
                buffer.begin( GL11.GL_QUADS, format );
 | 
			
		||||
                int idx = bakedquad.getTintIndex();
 | 
			
		||||
                if( idx >= 0 && idx < tints.length ) tint = tints[bakedquad.getTintIndex()];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int colour = 0xFFFFFFFF;
 | 
			
		||||
            if( quad.hasTintIndex() && tints != null )
 | 
			
		||||
            {
 | 
			
		||||
                int index = quad.getTintIndex();
 | 
			
		||||
                if( index >= 0 && index < tints.length ) colour = tints[index] | 0xFF000000;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            LightUtil.renderQuadColor( buffer, quad, colour );
 | 
			
		||||
            float f = (float) (tint >> 16 & 255) / 255.0F;
 | 
			
		||||
            float f1 = (float) (tint >> 8 & 255) / 255.0F;
 | 
			
		||||
            float f2 = (float) (tint & 255) / 255.0F;
 | 
			
		||||
            buffer.addVertexData( matrix, bakedquad, f, f1, f2, lightmapCoord, overlayLight, true );
 | 
			
		||||
        }
 | 
			
		||||
        tessellator.draw();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,28 +5,27 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.JsonDeserializationContext;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.mojang.datafixers.util.Pair;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.IUnbakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.ModelBakery;
 | 
			
		||||
import net.minecraft.client.renderer.texture.ISprite;
 | 
			
		||||
import net.minecraft.client.renderer.model.*;
 | 
			
		||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
 | 
			
		||||
import net.minecraft.client.renderer.vertex.VertexFormat;
 | 
			
		||||
import net.minecraft.resources.IResourceManager;
 | 
			
		||||
import net.minecraft.util.JSONUtils;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraftforge.client.model.ICustomModelLoader;
 | 
			
		||||
import net.minecraftforge.client.model.IModelConfiguration;
 | 
			
		||||
import net.minecraftforge.client.model.IModelLoader;
 | 
			
		||||
import net.minecraftforge.client.model.geometry.IModelGeometry;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
public final class TurtleModelLoader implements ICustomModelLoader
 | 
			
		||||
public final class TurtleModelLoader implements IModelLoader<TurtleModelLoader.TurtleModel>
 | 
			
		||||
{
 | 
			
		||||
    private static final ResourceLocation NORMAL_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_normal" );
 | 
			
		||||
    private static final ResourceLocation ADVANCED_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_advanced" );
 | 
			
		||||
    private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_colour" );
 | 
			
		||||
 | 
			
		||||
    public static final TurtleModelLoader INSTANCE = new TurtleModelLoader();
 | 
			
		||||
@@ -40,32 +39,15 @@ public final class TurtleModelLoader implements ICustomModelLoader
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean accepts( @Nonnull ResourceLocation name )
 | 
			
		||||
    {
 | 
			
		||||
        return name.getNamespace().equals( ComputerCraft.MOD_ID )
 | 
			
		||||
            && (name.getPath().equals( "item/turtle_normal" ) || name.getPath().equals( "item/turtle_advanced" ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public IUnbakedModel loadModel( @Nonnull ResourceLocation name )
 | 
			
		||||
    public TurtleModel read( @Nonnull JsonDeserializationContext deserializationContext, @Nonnull JsonObject modelContents )
 | 
			
		||||
    {
 | 
			
		||||
        if( name.getNamespace().equals( ComputerCraft.MOD_ID ) )
 | 
			
		||||
        {
 | 
			
		||||
            switch( name.getPath() )
 | 
			
		||||
            {
 | 
			
		||||
                case "item/turtle_normal":
 | 
			
		||||
                    return new TurtleModel( NORMAL_TURTLE_MODEL );
 | 
			
		||||
                case "item/turtle_advanced":
 | 
			
		||||
                    return new TurtleModel( ADVANCED_TURTLE_MODEL );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw new IllegalStateException( "Loader does not accept " + name );
 | 
			
		||||
        ResourceLocation model = new ResourceLocation( JSONUtils.getString( modelContents, "model" ) );
 | 
			
		||||
        return new TurtleModel( model );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class TurtleModel implements IUnbakedModel
 | 
			
		||||
    public static final class TurtleModel implements IModelGeometry<TurtleModel>
 | 
			
		||||
    {
 | 
			
		||||
        private final ResourceLocation family;
 | 
			
		||||
 | 
			
		||||
@@ -74,29 +56,21 @@ public final class TurtleModelLoader implements ICustomModelLoader
 | 
			
		||||
            this.family = family;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        @Override
 | 
			
		||||
        public Collection<ResourceLocation> getDependencies()
 | 
			
		||||
        public Collection<Material> getTextures( IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors )
 | 
			
		||||
        {
 | 
			
		||||
            return Arrays.asList( family, COLOUR_TURTLE_MODEL );
 | 
			
		||||
            Set<Material> materials = new HashSet<>();
 | 
			
		||||
            materials.addAll( modelGetter.apply( family ).getTextures( modelGetter, missingTextureErrors ) );
 | 
			
		||||
            materials.addAll( modelGetter.apply( COLOUR_TURTLE_MODEL ).getTextures( modelGetter, missingTextureErrors ) );
 | 
			
		||||
            return materials;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        @Override
 | 
			
		||||
        public Collection<ResourceLocation> getTextures( @Nonnull Function<ResourceLocation, IUnbakedModel> modelGetter, @Nonnull Set<String> missingTextureErrors )
 | 
			
		||||
        {
 | 
			
		||||
            return getDependencies().stream()
 | 
			
		||||
                .flatMap( x -> modelGetter.apply( x ).getTextures( modelGetter, missingTextureErrors ).stream() )
 | 
			
		||||
                .collect( Collectors.toSet() );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        @Override
 | 
			
		||||
        public IBakedModel bake( @Nonnull ModelBakery bakery, @Nonnull Function<ResourceLocation, TextureAtlasSprite> spriteGetter, @Nonnull ISprite sprite, @Nonnull VertexFormat format )
 | 
			
		||||
        public IBakedModel bake( IModelConfiguration owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, IModelTransform transform, ItemOverrideList overrides, ResourceLocation modelLocation )
 | 
			
		||||
        {
 | 
			
		||||
            return new TurtleSmartItemModel(
 | 
			
		||||
                bakery.getBakedModel( family, sprite, spriteGetter, format ),
 | 
			
		||||
                bakery.getBakedModel( COLOUR_TURTLE_MODEL, sprite, spriteGetter, format )
 | 
			
		||||
                bakery.getBakedModel( family, transform, spriteGetter ),
 | 
			
		||||
                bakery.getBakedModel( COLOUR_TURTLE_MODEL, transform, spriteGetter )
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,9 @@
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.client.TransformedModel;
 | 
			
		||||
import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.client.renderer.TransformationMatrix;
 | 
			
		||||
import net.minecraft.client.renderer.model.BakedQuad;
 | 
			
		||||
import net.minecraft.client.renderer.model.IBakedModel;
 | 
			
		||||
import net.minecraft.client.renderer.model.ItemOverrideList;
 | 
			
		||||
@@ -13,32 +15,29 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraftforge.client.model.data.EmptyModelData;
 | 
			
		||||
import net.minecraftforge.client.model.data.IModelData;
 | 
			
		||||
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
 | 
			
		||||
import net.minecraftforge.client.model.pipeline.TRSRTransformer;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.vecmath.Matrix4f;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public class TurtleMultiModel implements IBakedModel
 | 
			
		||||
{
 | 
			
		||||
    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 final TransformationMatrix m_generalTransform;
 | 
			
		||||
    private final TransformedModel m_leftUpgradeModel;
 | 
			
		||||
    private final TransformedModel m_rightUpgradeModel;
 | 
			
		||||
    private List<BakedQuad> m_generalQuads = null;
 | 
			
		||||
    private Map<Direction, List<BakedQuad>> m_faceQuads = new EnumMap<>( Direction.class );
 | 
			
		||||
 | 
			
		||||
    public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, Matrix4f generalTransform, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
 | 
			
		||||
    public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, TransformationMatrix generalTransform, TransformedModel leftUpgradeModel, TransformedModel rightUpgradeModel )
 | 
			
		||||
    {
 | 
			
		||||
        // Get the models
 | 
			
		||||
        m_baseModel = baseModel;
 | 
			
		||||
        m_overlayModel = overlayModel;
 | 
			
		||||
        m_leftUpgradeModel = leftUpgradeModel;
 | 
			
		||||
        m_leftUpgradeTransform = leftUpgradeTransform;
 | 
			
		||||
        m_rightUpgradeModel = rightUpgradeModel;
 | 
			
		||||
        m_rightUpgradeTransform = rightUpgradeTransform;
 | 
			
		||||
        m_generalTransform = generalTransform;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,30 +68,22 @@ public class TurtleMultiModel implements IBakedModel
 | 
			
		||||
    private List<BakedQuad> buildQuads( BlockState state, Direction side, Random rand )
 | 
			
		||||
    {
 | 
			
		||||
        ArrayList<BakedQuad> quads = new ArrayList<>();
 | 
			
		||||
        ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
 | 
			
		||||
        if( m_overlayModel != null )
 | 
			
		||||
        {
 | 
			
		||||
            ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), m_generalTransform );
 | 
			
		||||
            transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand, EmptyModelData.INSTANCE ), 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, EmptyModelData.INSTANCE ), upgradeTransform );
 | 
			
		||||
            TransformationMatrix upgradeTransform = m_generalTransform.compose( m_leftUpgradeModel.getMatrix() );
 | 
			
		||||
            transformQuadsTo( quads, m_leftUpgradeModel.getModel().getQuads( state, side, rand, EmptyModelData.INSTANCE ), 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, EmptyModelData.INSTANCE ), upgradeTransform );
 | 
			
		||||
            TransformationMatrix upgradeTransform = m_generalTransform.compose( m_rightUpgradeModel.getMatrix() );
 | 
			
		||||
            transformQuadsTo( quads, m_rightUpgradeModel.getModel().getQuads( state, side, rand, EmptyModelData.INSTANCE ), upgradeTransform );
 | 
			
		||||
        }
 | 
			
		||||
        quads.trimToSize();
 | 
			
		||||
        return quads;
 | 
			
		||||
@@ -116,6 +107,12 @@ public class TurtleMultiModel implements IBakedModel
 | 
			
		||||
        return m_baseModel.isBuiltInRenderer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean func_230044_c_()
 | 
			
		||||
    {
 | 
			
		||||
        return m_baseModel.func_230044_c_();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
@@ -138,4 +135,15 @@ public class TurtleMultiModel implements IBakedModel
 | 
			
		||||
    {
 | 
			
		||||
        return ItemOverrideList.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> quads, TransformationMatrix transform )
 | 
			
		||||
    {
 | 
			
		||||
        for( BakedQuad quad : quads )
 | 
			
		||||
        {
 | 
			
		||||
            BakedQuadBuilder builder = new BakedQuadBuilder();
 | 
			
		||||
            TRSRTransformer transformer = new TRSRTransformer( builder, transform );
 | 
			
		||||
            quad.pipe( transformer );
 | 
			
		||||
            output.add( builder.build() );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,15 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import dan200.computercraft.client.gui.GuiComputer;
 | 
			
		||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
 | 
			
		||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
 | 
			
		||||
import net.minecraft.client.renderer.entity.EntityRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.entity.EntityRendererManager;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer>
 | 
			
		||||
{
 | 
			
		||||
@@ -22,16 +23,15 @@ public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer>
 | 
			
		||||
        super( renderManager );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public void doRender( @Nonnull TurtlePlayer entity, double x, double y, double z, float entityYaw, float partialTicks )
 | 
			
		||||
    public ResourceLocation getEntityTexture( @Nonnull TurtlePlayer entity )
 | 
			
		||||
    {
 | 
			
		||||
        ComputerCraft.log.error( "Rendering TurtlePlayer on the client side, at {}", entity.getPosition() );
 | 
			
		||||
        return GuiComputer.BACKGROUND_NORMAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    protected ResourceLocation getEntityTexture( @Nonnull TurtlePlayer entity )
 | 
			
		||||
    public void render( @Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull MatrixStack transform, @Nonnull IRenderTypeBuffer buffer, int packedLightIn )
 | 
			
		||||
    {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Objects;
 | 
			
		||||
import com.mojang.blaze3d.matrix.MatrixStack;
 | 
			
		||||
import dan200.computercraft.api.client.TransformedModel;
 | 
			
		||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
			
		||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
			
		||||
import dan200.computercraft.shared.turtle.items.ItemTurtle;
 | 
			
		||||
@@ -13,6 +15,7 @@ import dan200.computercraft.shared.util.Holiday;
 | 
			
		||||
import dan200.computercraft.shared.util.HolidayUtil;
 | 
			
		||||
import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.renderer.TransformationMatrix;
 | 
			
		||||
import net.minecraft.client.renderer.model.*;
 | 
			
		||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
 | 
			
		||||
import net.minecraft.entity.LivingEntity;
 | 
			
		||||
@@ -21,28 +24,25 @@ import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
import net.minecraftforge.client.model.data.IModelData;
 | 
			
		||||
import org.apache.commons.lang3.tuple.Pair;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.vecmath.Matrix4f;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
public class TurtleSmartItemModel implements IBakedModel
 | 
			
		||||
{
 | 
			
		||||
    private static final Matrix4f s_identity, s_flip;
 | 
			
		||||
    private static final TransformationMatrix identity, flip;
 | 
			
		||||
 | 
			
		||||
    static
 | 
			
		||||
    {
 | 
			
		||||
        s_identity = new Matrix4f();
 | 
			
		||||
        s_identity.setIdentity();
 | 
			
		||||
        MatrixStack stack = new MatrixStack();
 | 
			
		||||
        stack.scale( 0, -1, 0 );
 | 
			
		||||
        stack.translate( 0, 0, 1 );
 | 
			
		||||
 | 
			
		||||
        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.
 | 
			
		||||
        identity = TransformationMatrix.identity();
 | 
			
		||||
        flip = new TransformationMatrix( stack.getLast().getMatrix() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class TurtleModelCombination
 | 
			
		||||
@@ -97,15 +97,14 @@ public class TurtleSmartItemModel implements IBakedModel
 | 
			
		||||
    private final IBakedModel familyModel;
 | 
			
		||||
    private final IBakedModel colourModel;
 | 
			
		||||
 | 
			
		||||
    private HashMap<TurtleModelCombination, IBakedModel> m_cachedModels;
 | 
			
		||||
    private ItemOverrideList m_overrides;
 | 
			
		||||
    private final HashMap<TurtleModelCombination, IBakedModel> m_cachedModels = new HashMap<>();
 | 
			
		||||
    private final ItemOverrideList m_overrides;
 | 
			
		||||
 | 
			
		||||
    public TurtleSmartItemModel( IBakedModel familyModel, IBakedModel colourModel )
 | 
			
		||||
    {
 | 
			
		||||
        this.familyModel = familyModel;
 | 
			
		||||
        this.colourModel = colourModel;
 | 
			
		||||
 | 
			
		||||
        m_cachedModels = new HashMap<>();
 | 
			
		||||
        m_overrides = new ItemOverrideList()
 | 
			
		||||
        {
 | 
			
		||||
            @Nonnull
 | 
			
		||||
@@ -114,10 +113,10 @@ public class TurtleSmartItemModel implements IBakedModel
 | 
			
		||||
            {
 | 
			
		||||
                ItemTurtle turtle = (ItemTurtle) stack.getItem();
 | 
			
		||||
                int colour = turtle.getColour( stack );
 | 
			
		||||
                ITurtleUpgrade leftUpgrade = turtle.getUpgrade( stack, TurtleSide.Left );
 | 
			
		||||
                ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.Right );
 | 
			
		||||
                ITurtleUpgrade leftUpgrade = turtle.getUpgrade( stack, TurtleSide.LEFT );
 | 
			
		||||
                ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.RIGHT );
 | 
			
		||||
                ResourceLocation overlay = turtle.getOverlay( stack );
 | 
			
		||||
                boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.Christmas;
 | 
			
		||||
                boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS;
 | 
			
		||||
                String label = turtle.getLabel( stack );
 | 
			
		||||
                boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" ));
 | 
			
		||||
                TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip );
 | 
			
		||||
@@ -144,25 +143,10 @@ public class TurtleSmartItemModel implements IBakedModel
 | 
			
		||||
 | 
			
		||||
        IBakedModel baseModel = combo.m_colour ? colourModel : familyModel;
 | 
			
		||||
        IBakedModel overlayModel = overlayModelLocation != null ? modelManager.getModel( overlayModelLocation ) : 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, transform, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
 | 
			
		||||
        }
 | 
			
		||||
        else if( leftModel != null )
 | 
			
		||||
        {
 | 
			
		||||
            return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), null, null );
 | 
			
		||||
        }
 | 
			
		||||
        else if( rightModel != null )
 | 
			
		||||
        {
 | 
			
		||||
            return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, rightModel.getLeft(), rightModel.getRight() );
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, null, null );
 | 
			
		||||
        }
 | 
			
		||||
        TransformationMatrix transform = combo.m_flip ? flip : identity;
 | 
			
		||||
        TransformedModel leftModel = combo.m_leftUpgrade != null ? combo.m_leftUpgrade.getModel( null, TurtleSide.LEFT ) : null;
 | 
			
		||||
        TransformedModel rightModel = combo.m_rightUpgrade != null ? combo.m_rightUpgrade.getModel( null, TurtleSide.RIGHT ) : null;
 | 
			
		||||
        return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel, rightModel );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
@@ -199,6 +183,12 @@ public class TurtleSmartItemModel implements IBakedModel
 | 
			
		||||
        return familyModel.isBuiltInRenderer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean func_230044_c_()
 | 
			
		||||
    {
 | 
			
		||||
        return familyModel.func_230044_c_();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
 
 | 
			
		||||
@@ -1,181 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of ComputerCraft - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
 | 
			
		||||
 * Send enquiries to dratcliffe@gmail.com
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.core.apis;
 | 
			
		||||
 | 
			
		||||
import com.google.common.net.InetAddresses;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
 | 
			
		||||
import java.net.Inet6Address;
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Used to determine whether a domain or IP address matches a series of patterns.
 | 
			
		||||
 */
 | 
			
		||||
public class AddressPredicate
 | 
			
		||||
{
 | 
			
		||||
    private static final class HostRange
 | 
			
		||||
    {
 | 
			
		||||
        private final byte[] min;
 | 
			
		||||
        private final byte[] max;
 | 
			
		||||
 | 
			
		||||
        private HostRange( byte[] min, byte[] max )
 | 
			
		||||
        {
 | 
			
		||||
            this.min = min;
 | 
			
		||||
            this.max = max;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public boolean contains( InetAddress address )
 | 
			
		||||
        {
 | 
			
		||||
            byte[] entry = address.getAddress();
 | 
			
		||||
            if( entry.length != min.length ) return false;
 | 
			
		||||
 | 
			
		||||
            for( int i = 0; i < entry.length; i++ )
 | 
			
		||||
            {
 | 
			
		||||
                int value = 0xFF & entry[i];
 | 
			
		||||
                if( value < (0xFF & min[i]) || value > (0xFF & max[i]) ) return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final List<Pattern> wildcards;
 | 
			
		||||
    private final List<HostRange> ranges;
 | 
			
		||||
 | 
			
		||||
    public AddressPredicate( String... filters )
 | 
			
		||||
    {
 | 
			
		||||
        this( Arrays.asList( filters ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AddressPredicate( Iterable<? extends String> filters )
 | 
			
		||||
    {
 | 
			
		||||
        List<Pattern> wildcards = this.wildcards = new ArrayList<>();
 | 
			
		||||
        List<HostRange> ranges = this.ranges = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        for( String filter : filters )
 | 
			
		||||
        {
 | 
			
		||||
            int cidr = filter.indexOf( '/' );
 | 
			
		||||
            if( cidr >= 0 )
 | 
			
		||||
            {
 | 
			
		||||
                String addressStr = filter.substring( 0, cidr );
 | 
			
		||||
                String prefixSizeStr = filter.substring( cidr + 1 );
 | 
			
		||||
 | 
			
		||||
                int prefixSize;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    prefixSize = Integer.parseInt( prefixSizeStr );
 | 
			
		||||
                }
 | 
			
		||||
                catch( NumberFormatException e )
 | 
			
		||||
                {
 | 
			
		||||
                    ComputerCraft.log.error(
 | 
			
		||||
                        "Malformed http whitelist/blacklist entry '{}': Cannot extract size of CIDR mask from '{}'.",
 | 
			
		||||
                        filter, prefixSizeStr
 | 
			
		||||
                    );
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                InetAddress address;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    address = InetAddresses.forString( addressStr );
 | 
			
		||||
                }
 | 
			
		||||
                catch( IllegalArgumentException e )
 | 
			
		||||
                {
 | 
			
		||||
                    ComputerCraft.log.error(
 | 
			
		||||
                        "Malformed http whitelist/blacklist entry '{}': Cannot extract IP address from '{}'.",
 | 
			
		||||
                        filter, prefixSizeStr
 | 
			
		||||
                    );
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Mask the bytes of the IP address.
 | 
			
		||||
                byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
 | 
			
		||||
                int size = prefixSize;
 | 
			
		||||
                for( int i = 0; i < minBytes.length; i++ )
 | 
			
		||||
                {
 | 
			
		||||
                    if( size <= 0 )
 | 
			
		||||
                    {
 | 
			
		||||
                        minBytes[i] &= 0;
 | 
			
		||||
                        maxBytes[i] |= 0xFF;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if( size < 8 )
 | 
			
		||||
                    {
 | 
			
		||||
                        minBytes[i] &= 0xFF << (8 - size);
 | 
			
		||||
                        maxBytes[i] |= ~(0xFF << (8 - size));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    size -= 8;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ranges.add( new HostRange( minBytes, maxBytes ) );
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                wildcards.add( Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" ) );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine whether a host name matches a series of patterns.
 | 
			
		||||
     *
 | 
			
		||||
     * This is intended to allow early exiting, before one has to look up the IP address. You should use
 | 
			
		||||
     * {@link #matches(InetAddress)} instead of/in addition to this one.
 | 
			
		||||
     *
 | 
			
		||||
     * @param domain The domain to match.
 | 
			
		||||
     * @return Whether the patterns were matched.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean matches( String domain )
 | 
			
		||||
    {
 | 
			
		||||
        for( Pattern domainPattern : wildcards )
 | 
			
		||||
        {
 | 
			
		||||
            if( domainPattern.matcher( domain ).matches() ) return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean matchesAddress( InetAddress address )
 | 
			
		||||
    {
 | 
			
		||||
        String addressString = address.getHostAddress();
 | 
			
		||||
        for( Pattern domainPattern : wildcards )
 | 
			
		||||
        {
 | 
			
		||||
            if( domainPattern.matcher( addressString ).matches() ) return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for( HostRange range : ranges )
 | 
			
		||||
        {
 | 
			
		||||
            if( range.contains( address ) ) return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine whether the given address matches a series of patterns.
 | 
			
		||||
     *
 | 
			
		||||
     * @param address The address to check.
 | 
			
		||||
     * @return Whether it matches any of these patterns.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean matches( InetAddress address )
 | 
			
		||||
    {
 | 
			
		||||
        // Match the host name
 | 
			
		||||
        String host = address.getHostName();
 | 
			
		||||
        if( host != null && matches( host ) ) return true;
 | 
			
		||||
 | 
			
		||||
        // Match the normal address
 | 
			
		||||
        if( matchesAddress( address ) ) return true;
 | 
			
		||||
 | 
			
		||||
        // If we're an IPv4 address in disguise then let's check that.
 | 
			
		||||
        return address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
 | 
			
		||||
            && matchesAddress( InetAddresses.get6to4IPv4Address( (Inet6Address) address ) );
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,117 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of ComputerCraft - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
 | 
			
		||||
 * Send enquiries to dratcliffe@gmail.com
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.core.apis;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A stub for any mods which depended on this version of the argument helper.
 | 
			
		||||
 *
 | 
			
		||||
 * @deprecated Use {@link dan200.computercraft.api.lua.ArgumentHelper}.
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated
 | 
			
		||||
public final class ArgumentHelper
 | 
			
		||||
{
 | 
			
		||||
    private ArgumentHelper()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public static String getType( @Nullable Object type )
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getType( type );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public static LuaException badArgument( int index, @Nonnull String expected, @Nullable Object actual )
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.badArgumentOf( index, expected, actual );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public static LuaException badArgument( int index, @Nonnull String expected, @Nonnull String actual )
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.badArgument( index, expected, actual );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static double getNumber( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getDouble( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int getInt( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getInt( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long getLong( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getLong( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static double getReal( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getFiniteDouble( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean getBoolean( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getBoolean( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public static String getString( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.getString( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @SuppressWarnings( "unchecked" )
 | 
			
		||||
    public static Map<Object, Object> getTable( @Nonnull Object[] args, int index ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return (Map<Object, Object>) dan200.computercraft.api.lua.ArgumentHelper.getTable( args, index );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static double optNumber( @Nonnull Object[] args, int index, double def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.optDouble( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int optInt( @Nonnull Object[] args, int index, int def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.optInt( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long optLong( @Nonnull Object[] args, int index, long def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.optLong( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static double optReal( @Nonnull Object[] args, int index, double def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.optFiniteDouble( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean optBoolean( @Nonnull Object[] args, int index, boolean def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.optBoolean( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String optString( @Nonnull Object[] args, int index, String def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return dan200.computercraft.api.lua.ArgumentHelper.optString( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings( "unchecked" )
 | 
			
		||||
    public static Map<Object, Object> optTable( @Nonnull Object[] args, int index, Map<Object, Object> def ) throws LuaException
 | 
			
		||||
    {
 | 
			
		||||
        return (Map<Object, Object>) dan200.computercraft.api.lua.ArgumentHelper.optTable( args, index, def );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,7 +13,6 @@ import dan200.computercraft.core.filesystem.FileSystem;
 | 
			
		||||
import dan200.computercraft.core.filesystem.FileSystemException;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
@@ -123,7 +122,7 @@ public abstract class ComputerAccess implements IComputerAccess
 | 
			
		||||
        m_environment.queueEvent( event, arguments );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public IWorkMonitor getMainThreadMonitor()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -173,7 +173,7 @@ public class HTTPAPI implements ILuaAPI
 | 
			
		||||
                String address = getString( args, 0 );
 | 
			
		||||
                Map<?, ?> headerTbl = optTable( args, 1, Collections.emptyMap() );
 | 
			
		||||
 | 
			
		||||
                if( !ComputerCraft.http_websocket_enable )
 | 
			
		||||
                if( !ComputerCraft.httpWebsocketEnabled )
 | 
			
		||||
                {
 | 
			
		||||
                    throw new LuaException( "Websocket connections are disabled" );
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of ComputerCraft - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
 | 
			
		||||
 * Send enquiries to dratcliffe@gmail.com
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.core.apis;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This exists purely to ensure binary compatibility.
 | 
			
		||||
 *
 | 
			
		||||
 * @see dan200.computercraft.api.lua.ILuaAPI
 | 
			
		||||
 * @deprecated Use the version in the public API. Only exists for compatibility with CCEmuX.
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated
 | 
			
		||||
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI
 | 
			
		||||
{
 | 
			
		||||
    void advance( double v );
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    default void update()
 | 
			
		||||
    {
 | 
			
		||||
        advance( 0.05 );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -103,7 +103,7 @@ public class OSAPI implements ILuaAPI
 | 
			
		||||
                    double t = alarm.m_day * 24.0 + alarm.m_time;
 | 
			
		||||
                    if( now >= t )
 | 
			
		||||
                    {
 | 
			
		||||
                        queueLuaEvent( "alarm", new Object[] { entry.getKey() } );
 | 
			
		||||
                        queueLuaEvent( "alarm", new Object[] { entry.getIntKey() } );
 | 
			
		||||
                        it.remove();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@ import dan200.computercraft.api.lua.ILuaAPI;
 | 
			
		||||
import dan200.computercraft.api.lua.ILuaContext;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
 | 
			
		||||
import dan200.computercraft.api.peripheral.NotAttachedException;
 | 
			
		||||
import dan200.computercraft.core.computer.ComputerSide;
 | 
			
		||||
import dan200.computercraft.core.tracking.TrackingField;
 | 
			
		||||
 | 
			
		||||
@@ -122,53 +124,35 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
        @Override
 | 
			
		||||
        public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            return super.mount( desiredLoc, mount, driveName );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName )
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            return super.mountWritable( desiredLoc, mount, driveName );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public synchronized void unmount( String location )
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            super.unmount( location );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getID()
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            return super.getID();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void queueEvent( @Nonnull final String event, final Object[] arguments )
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            super.queueEvent( event, arguments );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -176,10 +160,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
        @Override
 | 
			
		||||
        public String getAttachmentName()
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            return m_side;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -187,10 +168,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
        @Override
 | 
			
		||||
        public Map<String, IPeripheral> getAvailablePeripherals()
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
 | 
			
		||||
            Map<String, IPeripheral> peripherals = new HashMap<>();
 | 
			
		||||
            for( PeripheralWrapper wrapper : m_peripherals )
 | 
			
		||||
@@ -208,10 +186,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
        @Override
 | 
			
		||||
        public IPeripheral getAvailablePeripheral( @Nonnull String name )
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached )
 | 
			
		||||
            {
 | 
			
		||||
                throw new RuntimeException( "You are not attached to this Computer" );
 | 
			
		||||
            }
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
 | 
			
		||||
            for( PeripheralWrapper wrapper : m_peripherals )
 | 
			
		||||
            {
 | 
			
		||||
@@ -222,6 +197,14 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        @Override
 | 
			
		||||
        public IWorkMonitor getMainThreadMonitor()
 | 
			
		||||
        {
 | 
			
		||||
            if( !m_attached ) throw new NotAttachedException();
 | 
			
		||||
            return super.getMainThreadMonitor();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final IAPIEnvironment m_environment;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,167 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of ComputerCraft - http://www.computercraft.info
 | 
			
		||||
 * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
 | 
			
		||||
 * Send enquiries to dratcliffe@gmail.com
 | 
			
		||||
 */
 | 
			
		||||
package dan200.computercraft.core.apis.http;
 | 
			
		||||
 | 
			
		||||
import com.google.common.net.InetAddresses;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.net.Inet6Address;
 | 
			
		||||
import java.net.InetAddress;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A pattern which matches an address, and controls whether it is accessible or not.
 | 
			
		||||
 */
 | 
			
		||||
public final class AddressRule
 | 
			
		||||
{
 | 
			
		||||
    private static final class HostRange
 | 
			
		||||
    {
 | 
			
		||||
        private final byte[] min;
 | 
			
		||||
        private final byte[] max;
 | 
			
		||||
 | 
			
		||||
        private HostRange( byte[] min, byte[] max )
 | 
			
		||||
        {
 | 
			
		||||
            this.min = min;
 | 
			
		||||
            this.max = max;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public boolean contains( InetAddress address )
 | 
			
		||||
        {
 | 
			
		||||
            byte[] entry = address.getAddress();
 | 
			
		||||
            if( entry.length != min.length ) return false;
 | 
			
		||||
 | 
			
		||||
            for( int i = 0; i < entry.length; i++ )
 | 
			
		||||
            {
 | 
			
		||||
                int value = 0xFF & entry[i];
 | 
			
		||||
                if( value < (0xFF & min[i]) || value > (0xFF & max[i]) ) return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum Action
 | 
			
		||||
    {
 | 
			
		||||
        ALLOW,
 | 
			
		||||
        DENY,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final HostRange ip;
 | 
			
		||||
    private final Pattern domainPattern;
 | 
			
		||||
    private final Action action;
 | 
			
		||||
 | 
			
		||||
    private AddressRule( HostRange ip, Pattern domainPattern, Action action )
 | 
			
		||||
    {
 | 
			
		||||
        this.ip = ip;
 | 
			
		||||
        this.domainPattern = domainPattern;
 | 
			
		||||
        this.action = action;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public static AddressRule parse( String filter, Action action )
 | 
			
		||||
    {
 | 
			
		||||
        int cidr = filter.indexOf( '/' );
 | 
			
		||||
        if( cidr >= 0 )
 | 
			
		||||
        {
 | 
			
		||||
            String addressStr = filter.substring( 0, cidr );
 | 
			
		||||
            String prefixSizeStr = filter.substring( cidr + 1 );
 | 
			
		||||
 | 
			
		||||
            int prefixSize;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                prefixSize = Integer.parseInt( prefixSizeStr );
 | 
			
		||||
            }
 | 
			
		||||
            catch( NumberFormatException e )
 | 
			
		||||
            {
 | 
			
		||||
                ComputerCraft.log.error(
 | 
			
		||||
                    "Malformed http whitelist/blacklist entry '{}': Cannot extract size of CIDR mask from '{}'.",
 | 
			
		||||
                    filter, prefixSizeStr
 | 
			
		||||
                );
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            InetAddress address;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                address = InetAddresses.forString( addressStr );
 | 
			
		||||
            }
 | 
			
		||||
            catch( IllegalArgumentException e )
 | 
			
		||||
            {
 | 
			
		||||
                ComputerCraft.log.error(
 | 
			
		||||
                    "Malformed http whitelist/blacklist entry '{}': Cannot extract IP address from '{}'.",
 | 
			
		||||
                    filter, prefixSizeStr
 | 
			
		||||
                );
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Mask the bytes of the IP address.
 | 
			
		||||
            byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
 | 
			
		||||
            int size = prefixSize;
 | 
			
		||||
            for( int i = 0; i < minBytes.length; i++ )
 | 
			
		||||
            {
 | 
			
		||||
                if( size <= 0 )
 | 
			
		||||
                {
 | 
			
		||||
                    minBytes[i] &= 0;
 | 
			
		||||
                    maxBytes[i] |= 0xFF;
 | 
			
		||||
                }
 | 
			
		||||
                else if( size < 8 )
 | 
			
		||||
                {
 | 
			
		||||
                    minBytes[i] &= 0xFF << (8 - size);
 | 
			
		||||
                    maxBytes[i] |= ~(0xFF << (8 - size));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                size -= 8;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AddressRule( new HostRange( minBytes, maxBytes ), null, action );
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            Pattern pattern = Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" );
 | 
			
		||||
            return new AddressRule( null, pattern, action );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine whether the given address matches a series of patterns.
 | 
			
		||||
     *
 | 
			
		||||
     * @param domain  The domain to match
 | 
			
		||||
     * @param address The address to check.
 | 
			
		||||
     * @return Whether it matches any of these patterns.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean matches( String domain, InetAddress address )
 | 
			
		||||
    {
 | 
			
		||||
        if( domainPattern != null )
 | 
			
		||||
        {
 | 
			
		||||
            if( domainPattern.matcher( domain ).matches() ) return true;
 | 
			
		||||
            if( domainPattern.matcher( address.getHostName() ).matches() ) return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Match the normal address
 | 
			
		||||
        if( matchesAddress( address ) ) return true;
 | 
			
		||||
 | 
			
		||||
        // If we're an IPv4 address in disguise then let's check that.
 | 
			
		||||
        return address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
 | 
			
		||||
            && matchesAddress( InetAddresses.get6to4IPv4Address( (Inet6Address) address ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean matchesAddress( InetAddress address )
 | 
			
		||||
    {
 | 
			
		||||
        if( domainPattern != null && domainPattern.matcher( address.getHostAddress() ).matches() ) return true;
 | 
			
		||||
        return ip != null && ip.contains( address );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Action apply( Iterable<? extends AddressRule> rules, String domain, InetAddress address )
 | 
			
		||||
    {
 | 
			
		||||
        for( AddressRule rule : rules )
 | 
			
		||||
        {
 | 
			
		||||
            if( rule.matches( domain, address ) ) return rule.action;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Action.DENY;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -97,20 +97,6 @@ public final class NetworkUtils
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks a host is allowed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param host The domain to check against
 | 
			
		||||
     * @throws HTTPRequestException If the host is not permitted.
 | 
			
		||||
     */
 | 
			
		||||
    public static void checkHost( String host ) throws HTTPRequestException
 | 
			
		||||
    {
 | 
			
		||||
        if( !ComputerCraft.http_whitelist.matches( host ) || ComputerCraft.http_blacklist.matches( host ) )
 | 
			
		||||
        {
 | 
			
		||||
            throw new HTTPRequestException( "Domain not permitted" );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a {@link InetSocketAddress} from the resolved {@code host} and port.
 | 
			
		||||
     *
 | 
			
		||||
@@ -130,7 +116,7 @@ public final class NetworkUtils
 | 
			
		||||
        if( socketAddress.isUnresolved() ) throw new HTTPRequestException( "Unknown host" );
 | 
			
		||||
 | 
			
		||||
        InetAddress address = socketAddress.getAddress();
 | 
			
		||||
        if( !ComputerCraft.http_whitelist.matches( address ) || ComputerCraft.http_blacklist.matches( address ) )
 | 
			
		||||
        if( AddressRule.apply( ComputerCraft.httpRules, host, address ) == AddressRule.Action.DENY )
 | 
			
		||||
        {
 | 
			
		||||
            throw new HTTPRequestException( "Domain not permitted" );
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -119,8 +119,6 @@ public class HttpRequest extends Resource<HttpRequest>
 | 
			
		||||
        {
 | 
			
		||||
            throw new HTTPRequestException( "Invalid protocol '" + scheme + "'" );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        NetworkUtils.checkHost( url.getHost() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void request( URI uri, HttpMethod method )
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,6 @@ public class Websocket extends Resource<Websocket>
 | 
			
		||||
            throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        NetworkUtils.checkHost( uri.getHost() );
 | 
			
		||||
        return uri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -171,7 +171,7 @@ final class ComputerExecutor
 | 
			
		||||
        apis.add( new FSAPI( environment ) );
 | 
			
		||||
        apis.add( new PeripheralAPI( environment ) );
 | 
			
		||||
        apis.add( new OSAPI( environment ) );
 | 
			
		||||
        if( ComputerCraft.http_enable ) apis.add( new HTTPAPI( environment ) );
 | 
			
		||||
        if( ComputerCraft.httpEnabled ) apis.add( new HTTPAPI( environment ) );
 | 
			
		||||
 | 
			
		||||
        // Load in the externally registered APIs.
 | 
			
		||||
        for( ILuaAPIFactory factory : ApiFactories.getAll() )
 | 
			
		||||
@@ -611,7 +611,7 @@ final class ComputerExecutor
 | 
			
		||||
        terminal.reset();
 | 
			
		||||
 | 
			
		||||
        // Display our primary error message
 | 
			
		||||
        if( colour ) terminal.setTextColour( 15 - Colour.Red.ordinal() );
 | 
			
		||||
        if( colour ) terminal.setTextColour( 15 - Colour.RED.ordinal() );
 | 
			
		||||
        terminal.write( message );
 | 
			
		||||
 | 
			
		||||
        if( extra != null )
 | 
			
		||||
@@ -624,7 +624,7 @@ final class ComputerExecutor
 | 
			
		||||
 | 
			
		||||
        // And display our generic "CC may be installed incorrectly" message.
 | 
			
		||||
        terminal.setCursorPos( 0, terminal.getCursorY() + 1 );
 | 
			
		||||
        if( colour ) terminal.setTextColour( 15 - Colour.White.ordinal() );
 | 
			
		||||
        if( colour ) terminal.setTextColour( 15 - Colour.WHITE.ordinal() );
 | 
			
		||||
        terminal.write( "ComputerCraft may be installed incorrectly" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,15 @@ import dan200.computercraft.api.filesystem.IFileSystem;
 | 
			
		||||
import dan200.computercraft.api.lua.IComputerSystem;
 | 
			
		||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
import dan200.computercraft.core.apis.ComputerAccess;
 | 
			
		||||
import dan200.computercraft.core.apis.IAPIEnvironment;
 | 
			
		||||
import dan200.computercraft.core.filesystem.FileSystem;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs.
 | 
			
		||||
@@ -54,4 +57,19 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem
 | 
			
		||||
    {
 | 
			
		||||
        return environment.getLabel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public Map<String, IPeripheral> getAvailablePeripherals()
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: Should this return peripherals on the current computer?
 | 
			
		||||
        return Collections.emptyMap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public IPeripheral getAvailablePeripheral( @Nonnull String name )
 | 
			
		||||
    {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import dan200.computercraft.api.filesystem.IMount;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.nio.channels.ReadableByteChannel;
 | 
			
		||||
import java.nio.file.attribute.BasicFileAttributes;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@@ -116,8 +115,7 @@ public class ComboMount implements IMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        for( int i = m_parts.length - 1; i >= 0; --i )
 | 
			
		||||
        {
 | 
			
		||||
@@ -130,21 +128,6 @@ public class ComboMount implements IMount
 | 
			
		||||
        throw new FileOperationException( path, "No such file" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        for( int i = m_parts.length - 1; i >= 0; --i )
 | 
			
		||||
        {
 | 
			
		||||
            IMount part = m_parts[i];
 | 
			
		||||
            if( part.exists( path ) && !part.isDirectory( path ) )
 | 
			
		||||
            {
 | 
			
		||||
                return part.openChannelForRead( path );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new FileOperationException( path, "No such file" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import dan200.computercraft.api.filesystem.IMount;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.nio.channels.ReadableByteChannel;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -41,16 +40,7 @@ public class EmptyMount implements IMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        throw new FileOperationException( path, "No such file" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        throw new FileOperationException( path, "No such file" );
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,8 @@ import dan200.computercraft.api.filesystem.FileOperationException;
 | 
			
		||||
import dan200.computercraft.api.filesystem.IWritableMount;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.nio.channels.*;
 | 
			
		||||
import java.nio.file.*;
 | 
			
		||||
@@ -200,21 +201,7 @@ public class FileMount implements IWritableMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        if( created() )
 | 
			
		||||
        {
 | 
			
		||||
            File file = getRealPath( path );
 | 
			
		||||
            if( file.exists() && !file.isDirectory() ) return new FileInputStream( file );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw new FileOperationException( path, "No such file" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        if( created() )
 | 
			
		||||
        {
 | 
			
		||||
@@ -313,23 +300,7 @@ public class FileMount implements IWritableMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public OutputStream openForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newOutputStream( openChannelForWrite( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public OutputStream openForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newOutputStream( openChannelForAppend( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    public WritableByteChannel openForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        create();
 | 
			
		||||
        File file = getRealPath( path );
 | 
			
		||||
@@ -350,7 +321,7 @@ public class FileMount implements IWritableMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    public WritableByteChannel openForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        if( !created() )
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,6 @@ import dan200.computercraft.api.filesystem.IFileSystem;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.nio.channels.Channels;
 | 
			
		||||
import java.nio.channels.ReadableByteChannel;
 | 
			
		||||
import java.nio.channels.WritableByteChannel;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
@@ -55,7 +52,7 @@ public class FileSystemWrapperMount implements IFileSystem
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
@@ -70,7 +67,7 @@ public class FileSystemWrapperMount implements IFileSystem
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    public WritableByteChannel openForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
@@ -84,7 +81,7 @@ public class FileSystemWrapperMount implements IFileSystem
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    public WritableByteChannel openForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
@@ -96,30 +93,6 @@ public class FileSystemWrapperMount implements IFileSystem
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newInputStream( openChannelForRead( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public OutputStream openForWrite( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newOutputStream( openChannelForWrite( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public OutputStream openForAppend( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newOutputStream( openChannelForAppend( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long getRemainingSpace() throws IOException
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -184,15 +184,7 @@ public class JarMount implements IMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newInputStream( openChannelForRead( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        FileEntry file = get( path );
 | 
			
		||||
        if( file != null && !file.isDirectory() )
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,11 @@ import java.util.OptionalLong;
 | 
			
		||||
 | 
			
		||||
class MountWrapper
 | 
			
		||||
{
 | 
			
		||||
    private String label;
 | 
			
		||||
    private String location;
 | 
			
		||||
    private final String label;
 | 
			
		||||
    private final String location;
 | 
			
		||||
 | 
			
		||||
    private IMount mount;
 | 
			
		||||
    private IWritableMount writableMount;
 | 
			
		||||
    private final IMount mount;
 | 
			
		||||
    private final IWritableMount writableMount;
 | 
			
		||||
 | 
			
		||||
    MountWrapper( String label, String location, IMount mount )
 | 
			
		||||
    {
 | 
			
		||||
@@ -36,7 +36,9 @@ class MountWrapper
 | 
			
		||||
 | 
			
		||||
    MountWrapper( String label, String location, IWritableMount mount )
 | 
			
		||||
    {
 | 
			
		||||
        this( label, location, (IMount) mount );
 | 
			
		||||
        this.label = label;
 | 
			
		||||
        this.location = location;
 | 
			
		||||
        this.mount = mount;
 | 
			
		||||
        writableMount = mount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -154,7 +156,7 @@ class MountWrapper
 | 
			
		||||
        {
 | 
			
		||||
            if( mount.exists( path ) && !mount.isDirectory( path ) )
 | 
			
		||||
            {
 | 
			
		||||
                return mount.openChannelForRead( path );
 | 
			
		||||
                return mount.openForRead( path );
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
@@ -232,7 +234,7 @@ class MountWrapper
 | 
			
		||||
                        writableMount.makeDirectory( dir );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return writableMount.openChannelForWrite( path );
 | 
			
		||||
                return writableMount.openForWrite( path );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch( AccessDeniedException e )
 | 
			
		||||
@@ -262,7 +264,7 @@ class MountWrapper
 | 
			
		||||
                        writableMount.makeDirectory( dir );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return writableMount.openChannelForWrite( path );
 | 
			
		||||
                return writableMount.openForWrite( path );
 | 
			
		||||
            }
 | 
			
		||||
            else if( mount.isDirectory( path ) )
 | 
			
		||||
            {
 | 
			
		||||
@@ -270,7 +272,7 @@ class MountWrapper
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return writableMount.openChannelForAppend( path );
 | 
			
		||||
                return writableMount.openForAppend( path );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch( AccessDeniedException e )
 | 
			
		||||
 
 | 
			
		||||
@@ -223,15 +223,7 @@ public final class ResourceMount implements IMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return Channels.newInputStream( openChannelForRead( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        FileEntry file = get( path );
 | 
			
		||||
        if( file != null && !file.isDirectory() )
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ import dan200.computercraft.api.filesystem.IMount;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.nio.channels.ReadableByteChannel;
 | 
			
		||||
import java.nio.file.attribute.BasicFileAttributes;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -51,19 +50,11 @@ public class SubMount implements IMount
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public InputStream openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    public ReadableByteChannel openForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return parent.openForRead( getFullPath( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        return parent.openChannelForRead( getFullPath( path ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException
 | 
			
		||||
 
 | 
			
		||||
@@ -312,19 +312,6 @@ public class Terminal
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine whether this terminal has changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @return If this terminal is dirty.
 | 
			
		||||
     * @deprecated All {@code *Changed()} methods are deprecated: one should pass in a callback
 | 
			
		||||
     * instead.
 | 
			
		||||
     */
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public final boolean getChanged()
 | 
			
		||||
    {
 | 
			
		||||
        return m_changed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public final void setChanged()
 | 
			
		||||
    {
 | 
			
		||||
        m_changed = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,10 @@ import net.minecraft.data.DataGenerator;
 | 
			
		||||
import net.minecraft.data.DirectoryCache;
 | 
			
		||||
import net.minecraft.data.IDataProvider;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraft.world.storage.loot.LootParameterSets;
 | 
			
		||||
import net.minecraft.world.storage.loot.LootTable;
 | 
			
		||||
import net.minecraft.world.storage.loot.LootTableManager;
 | 
			
		||||
import net.minecraft.world.storage.loot.ValidationResults;
 | 
			
		||||
import net.minecraft.world.storage.loot.ValidationTracker;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
@@ -42,17 +43,17 @@ public abstract class LootTableProvider implements IDataProvider
 | 
			
		||||
    @Override
 | 
			
		||||
    public void act( @Nonnull DirectoryCache cache )
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        ValidationResults validation = new ValidationResults();
 | 
			
		||||
        Map<ResourceLocation, LootTable> tables = new HashMap<>();
 | 
			
		||||
        ValidationTracker validation = new ValidationTracker( LootParameterSets.GENERIC, x -> null, tables::get );
 | 
			
		||||
 | 
			
		||||
        registerLoot( ( id, table ) -> {
 | 
			
		||||
            if( tables.containsKey( id ) ) validation.addProblem( "Duplicate loot tables for " + id );
 | 
			
		||||
            if( tables.containsKey( id ) ) validation.func_227530_a_( "Duplicate loot tables for " + id );
 | 
			
		||||
            tables.put( id, table );
 | 
			
		||||
        } );
 | 
			
		||||
 | 
			
		||||
        tables.forEach( ( key, value ) -> LootTableManager.func_215302_a( validation, key, value, tables::get ) );
 | 
			
		||||
        tables.forEach( ( key, value ) -> LootTableManager.func_227508_a_( validation, key, value ) );
 | 
			
		||||
 | 
			
		||||
        Multimap<String, String> problems = validation.getProblems();
 | 
			
		||||
        Multimap<String, String> problems = validation.func_227527_a_();
 | 
			
		||||
        if( !problems.isEmpty() )
 | 
			
		||||
        {
 | 
			
		||||
            problems.forEach( ( child, problem ) ->
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,13 @@
 | 
			
		||||
package dan200.computercraft.shared;
 | 
			
		||||
 | 
			
		||||
import com.electronwill.nightconfig.core.CommentedConfig;
 | 
			
		||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
 | 
			
		||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
 | 
			
		||||
import com.google.common.base.CaseFormat;
 | 
			
		||||
import com.google.common.base.Converter;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.api.turtle.event.TurtleAction;
 | 
			
		||||
import dan200.computercraft.core.apis.AddressPredicate;
 | 
			
		||||
import dan200.computercraft.core.apis.http.AddressRule;
 | 
			
		||||
import dan200.computercraft.core.apis.http.websocket.Websocket;
 | 
			
		||||
import net.minecraftforge.common.ForgeConfigSpec;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
@@ -19,13 +20,14 @@ import net.minecraftforge.fml.ModLoadingContext;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
import net.minecraftforge.fml.config.ModConfig;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_BLACKLIST;
 | 
			
		||||
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_WHITELIST;
 | 
			
		||||
import static net.minecraftforge.common.ForgeConfigSpec.Builder;
 | 
			
		||||
import static net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
 | 
			
		||||
 | 
			
		||||
@@ -50,8 +52,7 @@ public final class Config
 | 
			
		||||
 | 
			
		||||
    private static final ConfigValue<Boolean> httpEnabled;
 | 
			
		||||
    private static final ConfigValue<Boolean> httpWebsocketEnabled;
 | 
			
		||||
    private static final ConfigValue<List<? extends String>> httpWhitelist;
 | 
			
		||||
    private static final ConfigValue<List<? extends String>> httpBlacklist;
 | 
			
		||||
    private static final ConfigValue<List<? extends UnmodifiableConfig>> httpRules;
 | 
			
		||||
 | 
			
		||||
    private static final ConfigValue<Integer> httpTimeout;
 | 
			
		||||
    private static final ConfigValue<Integer> httpMaxRequests;
 | 
			
		||||
@@ -151,25 +152,25 @@ public final class Config
 | 
			
		||||
            builder.push( "http" );
 | 
			
		||||
 | 
			
		||||
            httpEnabled = builder
 | 
			
		||||
                .comment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for more " +
 | 
			
		||||
                    "fine grained control than this)" )
 | 
			
		||||
                .define( "enabled", ComputerCraft.http_enable );
 | 
			
		||||
                .comment( "Enable the \"http\" API on Computers (see \"rules\" for more fine grained control than this)." )
 | 
			
		||||
                .define( "enabled", ComputerCraft.httpEnabled );
 | 
			
		||||
 | 
			
		||||
            httpWebsocketEnabled = builder
 | 
			
		||||
                .comment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." )
 | 
			
		||||
                .define( "websocket_enabled", ComputerCraft.http_websocket_enable );
 | 
			
		||||
                .define( "websocket_enabled", ComputerCraft.httpWebsocketEnabled );
 | 
			
		||||
 | 
			
		||||
            httpWhitelist = builder
 | 
			
		||||
                .comment( "A list of wildcards for domains or IP ranges that can be accessed through the \"http\" API on Computers.\n" +
 | 
			
		||||
                    "Set this to \"*\" to access to the entire internet. Example: \"*.pastebin.com\" will restrict access to just subdomains of pastebin.com.\n" +
 | 
			
		||||
                    "You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." )
 | 
			
		||||
                .defineList( "whitelist", Arrays.asList( DEFAULT_HTTP_WHITELIST ), x -> true );
 | 
			
		||||
 | 
			
		||||
            httpBlacklist = builder
 | 
			
		||||
                .comment( "A list of wildcards for domains or IP ranges that cannot be accessed through the \"http\" API on Computers.\n" +
 | 
			
		||||
                    "If this is empty then all whitelisted domains will be accessible. Example: \"*.github.com\" will block access to all subdomains of github.com.\n" +
 | 
			
		||||
                    "You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." )
 | 
			
		||||
                .defineList( "blacklist", Arrays.asList( DEFAULT_HTTP_BLACKLIST ), x -> true );
 | 
			
		||||
            httpRules = builder
 | 
			
		||||
                .comment( "A list of rules which control which domains or IPs are allowed through the \"http\" API on computers.\n" +
 | 
			
		||||
                    "Each rule is an item with a 'host' to match against, and an action. " +
 | 
			
		||||
                    "The host may be a domain name (\"pastebin.com\"),\n" +
 | 
			
		||||
                    "wildcard (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\"). 'action' maybe 'allow' or 'block'. If no rules" +
 | 
			
		||||
                    "match, the domain will be blocked." )
 | 
			
		||||
                .defineList( "rules",
 | 
			
		||||
                    Stream.concat(
 | 
			
		||||
                        Stream.of( ComputerCraft.DEFAULT_HTTP_DENY ).map( x -> makeRule( x, "deny" ) ),
 | 
			
		||||
                        Stream.of( ComputerCraft.DEFAULT_HTTP_ALLOW ).map( x -> makeRule( x, "allow" ) )
 | 
			
		||||
                    ).collect( Collectors.toList() ),
 | 
			
		||||
                    x -> x instanceof UnmodifiableConfig && parseRule( (UnmodifiableConfig) x ) != null );
 | 
			
		||||
 | 
			
		||||
            httpTimeout = builder
 | 
			
		||||
                .comment( "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." )
 | 
			
		||||
@@ -286,10 +287,10 @@ public final class Config
 | 
			
		||||
        ComputerCraft.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( maxMainComputerTime.get() );
 | 
			
		||||
 | 
			
		||||
        // HTTP
 | 
			
		||||
        ComputerCraft.http_enable = httpEnabled.get();
 | 
			
		||||
        ComputerCraft.http_websocket_enable = httpWebsocketEnabled.get();
 | 
			
		||||
        ComputerCraft.http_whitelist = new AddressPredicate( httpWhitelist.get() );
 | 
			
		||||
        ComputerCraft.http_blacklist = new AddressPredicate( httpBlacklist.get() );
 | 
			
		||||
        ComputerCraft.httpEnabled = httpEnabled.get();
 | 
			
		||||
        ComputerCraft.httpWebsocketEnabled = httpWebsocketEnabled.get();
 | 
			
		||||
        ComputerCraft.httpRules = Collections.unmodifiableList( httpRules.get().stream()
 | 
			
		||||
            .map( Config::parseRule ).filter( Objects::nonNull ).collect( Collectors.toList() ) );
 | 
			
		||||
 | 
			
		||||
        ComputerCraft.httpTimeout = httpTimeout.get();
 | 
			
		||||
        ComputerCraft.httpMaxRequests = httpMaxRequests.get();
 | 
			
		||||
@@ -324,7 +325,7 @@ public final class Config
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void sync( ModConfig.ConfigReloading event )
 | 
			
		||||
    public static void sync( ModConfig.Reloading event )
 | 
			
		||||
    {
 | 
			
		||||
        // Ensure file configs are reloaded. Forge should probably do this, so worth checking in the future.
 | 
			
		||||
        CommentedConfig config = event.getConfig().getConfigData();
 | 
			
		||||
@@ -346,4 +347,28 @@ public final class Config
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static UnmodifiableConfig makeRule( String host, String action )
 | 
			
		||||
    {
 | 
			
		||||
        com.electronwill.nightconfig.core.Config config = com.electronwill.nightconfig.core.Config.inMemory();
 | 
			
		||||
        config.add( "host", host );
 | 
			
		||||
        config.add( "action", action );
 | 
			
		||||
        return config;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private static AddressRule parseRule( UnmodifiableConfig builder )
 | 
			
		||||
    {
 | 
			
		||||
        Object hostObj = builder.get( "host" );
 | 
			
		||||
        Object actionObj = builder.get( "action" );
 | 
			
		||||
        if( !(hostObj instanceof String) || !(actionObj instanceof String) ) return null;
 | 
			
		||||
 | 
			
		||||
        String host = (String) hostObj, action = (String) actionObj;
 | 
			
		||||
        for( AddressRule.Action candiate : AddressRule.Action.values() )
 | 
			
		||||
        {
 | 
			
		||||
            if( candiate.name().equalsIgnoreCase( action ) ) return AddressRule.parse( host, candiate );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -84,17 +84,17 @@ public final class Registry
 | 
			
		||||
        // Computers
 | 
			
		||||
        ComputerCraft.Blocks.computerNormal = new BlockComputer(
 | 
			
		||||
            Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.0f ),
 | 
			
		||||
            ComputerFamily.Normal, TileComputer.FACTORY_NORMAL
 | 
			
		||||
            ComputerFamily.NORMAL, TileComputer.FACTORY_NORMAL
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        ComputerCraft.Blocks.computerAdvanced = new BlockComputer(
 | 
			
		||||
            Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.0f ),
 | 
			
		||||
            ComputerFamily.Advanced, TileComputer.FACTORY_ADVANCED
 | 
			
		||||
            ComputerFamily.ADVANCED, TileComputer.FACTORY_ADVANCED
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        ComputerCraft.Blocks.computerCommand = new BlockComputer(
 | 
			
		||||
            Block.Properties.create( Material.ROCK ).hardnessAndResistance( -1, 6000000.0F ),
 | 
			
		||||
            ComputerFamily.Command, TileCommandComputer.FACTORY
 | 
			
		||||
            ComputerFamily.COMMAND, TileCommandComputer.FACTORY
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        registry.registerAll(
 | 
			
		||||
@@ -106,12 +106,12 @@ public final class Registry
 | 
			
		||||
        // Turtles
 | 
			
		||||
        ComputerCraft.Blocks.turtleNormal = new BlockTurtle(
 | 
			
		||||
            Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.5f ),
 | 
			
		||||
            ComputerFamily.Normal, TileTurtle.FACTORY_NORMAL
 | 
			
		||||
            ComputerFamily.NORMAL, TileTurtle.FACTORY_NORMAL
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        ComputerCraft.Blocks.turtleAdvanced = new BlockTurtle(
 | 
			
		||||
            Block.Properties.create( Material.ROCK ).hardnessAndResistance( 2.5f ),
 | 
			
		||||
            ComputerFamily.Advanced, TileTurtle.FACTORY_ADVANCED
 | 
			
		||||
            ComputerFamily.ADVANCED, TileTurtle.FACTORY_ADVANCED
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        registry.registerAll(
 | 
			
		||||
@@ -234,8 +234,8 @@ public final class Registry
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Pocket computer
 | 
			
		||||
        ComputerCraft.Items.pocketComputerNormal = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.Normal );
 | 
			
		||||
        ComputerCraft.Items.pocketComputerAdvanced = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.Advanced );
 | 
			
		||||
        ComputerCraft.Items.pocketComputerNormal = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.NORMAL );
 | 
			
		||||
        ComputerCraft.Items.pocketComputerAdvanced = new ItemPocketComputer( defaultItem().maxStackSize( 1 ), ComputerFamily.ADVANCED );
 | 
			
		||||
 | 
			
		||||
        registry.registerAll(
 | 
			
		||||
            ComputerCraft.Items.pocketComputerNormal.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_computer_normal" ) ),
 | 
			
		||||
 
 | 
			
		||||
@@ -205,7 +205,7 @@ public final class CommandComputerCraft
 | 
			
		||||
                    int queued = 0;
 | 
			
		||||
                    for( ServerComputer computer : computers )
 | 
			
		||||
                    {
 | 
			
		||||
                        if( computer.getFamily() == ComputerFamily.Command && computer.isOn() )
 | 
			
		||||
                        if( computer.getFamily() == ComputerFamily.COMMAND && computer.isOn() )
 | 
			
		||||
                        {
 | 
			
		||||
                            computer.queueEvent( "computer_command", rest );
 | 
			
		||||
                            queued++;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +11,14 @@ import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.entity.player.PlayerEntity;
 | 
			
		||||
import net.minecraft.tileentity.TileEntity;
 | 
			
		||||
import net.minecraft.tileentity.TileEntityType;
 | 
			
		||||
import net.minecraft.util.ActionResultType;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.world.IBlockReader;
 | 
			
		||||
import net.minecraft.world.IWorldReader;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
import net.minecraft.world.server.ServerWorld;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
@@ -45,12 +47,13 @@ public abstract class BlockGeneric extends Block
 | 
			
		||||
        if( tile instanceof TileGeneric ) ((TileGeneric) tile).destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public final boolean onBlockActivated( BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public final ActionResultType onBlockActivated( BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        TileEntity tile = world.getTileEntity( pos );
 | 
			
		||||
        return tile instanceof TileGeneric && ((TileGeneric) tile).onActivate( player, hand, hit );
 | 
			
		||||
        return tile instanceof TileGeneric ? ((TileGeneric) tile).onActivate( player, hand, hit ) : ActionResultType.PASS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -70,7 +73,7 @@ public abstract class BlockGeneric extends Block
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public void tick( BlockState state, World world, BlockPos pos, Random rand )
 | 
			
		||||
    public void tick( BlockState state, ServerWorld world, BlockPos pos, Random rand )
 | 
			
		||||
    {
 | 
			
		||||
        TileEntity te = world.getTileEntity( pos );
 | 
			
		||||
        if( te instanceof TileGeneric ) ((TileGeneric) te).blockTick();
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import net.minecraft.network.NetworkManager;
 | 
			
		||||
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
 | 
			
		||||
import net.minecraft.tileentity.TileEntity;
 | 
			
		||||
import net.minecraft.tileentity.TileEntityType;
 | 
			
		||||
import net.minecraft.util.ActionResultType;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
@@ -37,9 +38,10 @@ public abstract class TileGeneric extends TileEntity
 | 
			
		||||
        getWorld().notifyBlockUpdate( pos, state, state, 3 );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
        return ActionResultType.PASS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onNeighbourChange( @Nonnull BlockPos neighbour )
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ public class TileCommandComputer extends TileComputer
 | 
			
		||||
{
 | 
			
		||||
    public static final NamedTileEntityType<TileCommandComputer> FACTORY = NamedTileEntityType.create(
 | 
			
		||||
        new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ),
 | 
			
		||||
        f -> new TileCommandComputer( ComputerFamily.Command, f )
 | 
			
		||||
        f -> new TileCommandComputer( ComputerFamily.COMMAND, f )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    public class CommandReceiver implements ICommandSource
 | 
			
		||||
 
 | 
			
		||||
@@ -27,12 +27,12 @@ public class TileComputer extends TileComputerBase
 | 
			
		||||
{
 | 
			
		||||
    public static final NamedTileEntityType<TileComputer> FACTORY_NORMAL = NamedTileEntityType.create(
 | 
			
		||||
        new ResourceLocation( ComputerCraft.MOD_ID, "computer_normal" ),
 | 
			
		||||
        f -> new TileComputer( ComputerFamily.Normal, f )
 | 
			
		||||
        f -> new TileComputer( ComputerFamily.NORMAL, f )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    public static final NamedTileEntityType<TileComputer> FACTORY_ADVANCED = NamedTileEntityType.create(
 | 
			
		||||
        new ResourceLocation( ComputerCraft.MOD_ID, "computer_advanced" ),
 | 
			
		||||
        f -> new TileComputer( ComputerFamily.Advanced, f )
 | 
			
		||||
        f -> new TileComputer( ComputerFamily.ADVANCED, f )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    private ComputerProxy m_proxy;
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ import net.minecraft.item.Items;
 | 
			
		||||
import net.minecraft.nbt.CompoundNBT;
 | 
			
		||||
import net.minecraft.tileentity.ITickableTileEntity;
 | 
			
		||||
import net.minecraft.tileentity.TileEntityType;
 | 
			
		||||
import net.minecraft.util.ActionResultType;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.INameable;
 | 
			
		||||
@@ -102,8 +103,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        ItemStack currentItem = player.getHeldItem( hand );
 | 
			
		||||
        if( !currentItem.isEmpty() && currentItem.getItem() == Items.NAME_TAG && canNameWithTag( player ) && currentItem.hasDisplayName() )
 | 
			
		||||
@@ -114,9 +116,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
 | 
			
		||||
                setLabel( currentItem.getDisplayName().getString() );
 | 
			
		||||
                currentItem.shrink( 1 );
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
            return ActionResultType.SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
        else if( !player.isSneaking() )
 | 
			
		||||
        else if( !player.isCrouching() )
 | 
			
		||||
        {
 | 
			
		||||
            // Regular right click to activate computer
 | 
			
		||||
            if( !getWorld().isRemote && isUsable( player, false ) )
 | 
			
		||||
@@ -124,9 +126,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
 | 
			
		||||
                createServerComputer().turnOn();
 | 
			
		||||
                new ComputerContainerData( createServerComputer() ).open( player, this );
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
            return ActionResultType.SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return ActionResultType.PASS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ package dan200.computercraft.shared.computer.core;
 | 
			
		||||
 | 
			
		||||
public enum ComputerFamily
 | 
			
		||||
{
 | 
			
		||||
    Normal,
 | 
			
		||||
    Advanced,
 | 
			
		||||
    Command
 | 
			
		||||
    NORMAL,
 | 
			
		||||
    ADVANCED,
 | 
			
		||||
    COMMAND
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
 | 
			
		||||
 | 
			
		||||
    public ServerComputer( World world, int computerID, String label, int instanceID, ComputerFamily family, int terminalWidth, int terminalHeight )
 | 
			
		||||
    {
 | 
			
		||||
        super( family != ComputerFamily.Normal, terminalWidth, terminalHeight );
 | 
			
		||||
        super( family != ComputerFamily.NORMAL, terminalWidth, terminalHeight );
 | 
			
		||||
        m_instanceID = instanceID;
 | 
			
		||||
 | 
			
		||||
        m_world = world;
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ public class ContainerViewComputer extends ContainerComputerBase implements ICon
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If we're a command computer then ensure we're in creative
 | 
			
		||||
        if( computer.getFamily() == ComputerFamily.Command )
 | 
			
		||||
        if( computer.getFamily() == ComputerFamily.COMMAND )
 | 
			
		||||
        {
 | 
			
		||||
            MinecraftServer server = player.getServer();
 | 
			
		||||
            if( server == null || !server.isCommandBlockEnabled() )
 | 
			
		||||
 
 | 
			
		||||
@@ -29,11 +29,11 @@ public final class ComputerItemFactory
 | 
			
		||||
    {
 | 
			
		||||
        switch( family )
 | 
			
		||||
        {
 | 
			
		||||
            case Normal:
 | 
			
		||||
            case NORMAL:
 | 
			
		||||
                return ComputerCraft.Items.computerNormal.create( id, label );
 | 
			
		||||
            case Advanced:
 | 
			
		||||
            case ADVANCED:
 | 
			
		||||
                return ComputerCraft.Items.computerAdvanced.create( id, label );
 | 
			
		||||
            case Command:
 | 
			
		||||
            case COMMAND:
 | 
			
		||||
                return ComputerCraft.Items.computerCommand.create( id, label );
 | 
			
		||||
            default:
 | 
			
		||||
                return ItemStack.EMPTY;
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ public abstract class ItemComputerBase extends BlockItem implements IComputerIte
 | 
			
		||||
    public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
 | 
			
		||||
    {
 | 
			
		||||
        ComputerFamily family = getFamily();
 | 
			
		||||
        if( family != ComputerFamily.Command )
 | 
			
		||||
        if( family != ComputerFamily.COMMAND )
 | 
			
		||||
        {
 | 
			
		||||
            int id = getComputerID( stack );
 | 
			
		||||
            if( id >= 0 )
 | 
			
		||||
 
 | 
			
		||||
@@ -116,8 +116,8 @@ public class JEIComputerCraft implements IModPlugin
 | 
			
		||||
        StringBuilder name = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
        // Add left and right upgrades to the identifier
 | 
			
		||||
        ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.Left );
 | 
			
		||||
        ITurtleUpgrade right = turtle.getUpgrade( stack, TurtleSide.Right );
 | 
			
		||||
        ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.LEFT );
 | 
			
		||||
        ITurtleUpgrade right = turtle.getUpgrade( stack, TurtleSide.RIGHT );
 | 
			
		||||
        if( left != null ) name.append( left.getUpgradeID() );
 | 
			
		||||
        if( left != null && right != null ) name.append( '|' );
 | 
			
		||||
        if( right != null ) name.append( right.getUpgradeID() );
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ import static net.minecraft.util.NonNullList.from;
 | 
			
		||||
 | 
			
		||||
class RecipeResolver implements IRecipeManagerPlugin
 | 
			
		||||
{
 | 
			
		||||
    static final ComputerFamily[] MAIN_FAMILIES = new ComputerFamily[] { ComputerFamily.Normal, ComputerFamily.Advanced };
 | 
			
		||||
    static final ComputerFamily[] MAIN_FAMILIES = new ComputerFamily[] { ComputerFamily.NORMAL, ComputerFamily.ADVANCED };
 | 
			
		||||
 | 
			
		||||
    private final Map<Item, List<UpgradeInfo>> upgradeItemLookup = new HashMap<>();
 | 
			
		||||
    private final List<UpgradeInfo> pocketUpgrades = new ArrayList<>();
 | 
			
		||||
@@ -150,8 +150,8 @@ class RecipeResolver implements IRecipeManagerPlugin
 | 
			
		||||
        {
 | 
			
		||||
            // Suggest possible upgrades which can be applied to this turtle
 | 
			
		||||
            ITurtleItem item = (ITurtleItem) stack.getItem();
 | 
			
		||||
            ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.Left );
 | 
			
		||||
            ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.Right );
 | 
			
		||||
            ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT );
 | 
			
		||||
            ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT );
 | 
			
		||||
            if( left != null && right != null ) return Collections.emptyList();
 | 
			
		||||
 | 
			
		||||
            List<Shaped> recipes = new ArrayList<>();
 | 
			
		||||
@@ -231,8 +231,8 @@ class RecipeResolver implements IRecipeManagerPlugin
 | 
			
		||||
            ITurtleItem item = (ITurtleItem) stack.getItem();
 | 
			
		||||
            List<Shaped> recipes = new ArrayList<>( 0 );
 | 
			
		||||
 | 
			
		||||
            ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.Left );
 | 
			
		||||
            ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.Right );
 | 
			
		||||
            ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT );
 | 
			
		||||
            ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT );
 | 
			
		||||
 | 
			
		||||
            // The turtle is facing towards us, so upgrades on the left are actually crafted on the right.
 | 
			
		||||
            if( left != null )
 | 
			
		||||
 
 | 
			
		||||
@@ -126,6 +126,6 @@ public class ItemDisk extends Item implements IMedia, IColouredItem
 | 
			
		||||
    public int getColour( @Nonnull ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        int colour = IColouredItem.getColourBasic( stack );
 | 
			
		||||
        return colour == -1 ? Colour.White.getHex() : colour;
 | 
			
		||||
        return colour == -1 ? Colour.WHITE.getHex() : colour;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -134,6 +134,6 @@ public class ItemTreasureDisk extends Item implements IMedia
 | 
			
		||||
    public static int getColour( @Nonnull ItemStack stack )
 | 
			
		||||
    {
 | 
			
		||||
        CompoundNBT nbt = stack.getTag();
 | 
			
		||||
        return nbt != null && nbt.contains( NBT_COLOUR ) ? nbt.getInt( NBT_COLOUR ) : Colour.Blue.getHex();
 | 
			
		||||
        return nbt != null && nbt.contains( NBT_COLOUR ) ? nbt.getInt( NBT_COLOUR ) : Colour.BLUE.getHex();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,7 @@ public class DiskRecipe extends SpecialRecipe
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ItemDisk.createFromIDAndColour( -1, null, tracker.hasColour() ? tracker.getColour() : Colour.Blue.getHex() );
 | 
			
		||||
        return ItemDisk.createFromIDAndColour( -1, null, tracker.hasColour() ? tracker.getColour() : Colour.BLUE.getHex() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -100,7 +100,7 @@ public class DiskRecipe extends SpecialRecipe
 | 
			
		||||
    @Override
 | 
			
		||||
    public ItemStack getRecipeOutput()
 | 
			
		||||
    {
 | 
			
		||||
        return ItemDisk.createFromIDAndColour( -1, null, Colour.Blue.getHex() );
 | 
			
		||||
        return ItemDisk.createFromIDAndColour( -1, null, Colour.BLUE.getHex() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
 
 | 
			
		||||
@@ -76,12 +76,10 @@ public final class NetworkHandler
 | 
			
		||||
    {
 | 
			
		||||
        for( ServerPlayerEntity player : ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayers() )
 | 
			
		||||
        {
 | 
			
		||||
            if( player.getEntityWorld() != world ) continue;
 | 
			
		||||
 | 
			
		||||
            double x = pos.x - player.posX;
 | 
			
		||||
            double y = pos.y - player.posY;
 | 
			
		||||
            double z = pos.z - player.posZ;
 | 
			
		||||
            if( x * x + y * y + z * z < range * range ) sendToPlayer( player, packet );
 | 
			
		||||
            if( player.getEntityWorld() == world && player.getDistanceSq( pos ) < range * range )
 | 
			
		||||
            {
 | 
			
		||||
                sendToPlayer( player, packet );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -99,26 +99,27 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        if( player.isSneaking() )
 | 
			
		||||
        if( player.isCrouching() )
 | 
			
		||||
        {
 | 
			
		||||
            // Try to put a disk into the drive
 | 
			
		||||
            ItemStack disk = player.getHeldItem( hand );
 | 
			
		||||
            if( disk.isEmpty() ) return false;
 | 
			
		||||
            if( disk.isEmpty() ) return ActionResultType.PASS;
 | 
			
		||||
            if( !getWorld().isRemote && getStackInSlot( 0 ).isEmpty() && MediaProviders.get( disk ) != null )
 | 
			
		||||
            {
 | 
			
		||||
                setDiskStack( disk );
 | 
			
		||||
                player.setHeldItem( hand, ItemStack.EMPTY );
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
            return ActionResultType.SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // Open the GUI
 | 
			
		||||
            if( !getWorld().isRemote ) NetworkHooks.openGui( (ServerPlayerEntity) player, this );
 | 
			
		||||
            return true;
 | 
			
		||||
            return ActionResultType.SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -250,11 +250,4 @@ public class BlockCable extends BlockGeneric implements IWaterLoggable
 | 
			
		||||
                .with( WEST, false ).with( UP, false ).with( DOWN, false );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public boolean hasCustomBreakingProgress( BlockState state )
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.entity.player.PlayerEntity;
 | 
			
		||||
import net.minecraft.item.ItemStack;
 | 
			
		||||
import net.minecraft.nbt.CompoundNBT;
 | 
			
		||||
import net.minecraft.util.ActionResultType;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
@@ -238,12 +239,14 @@ public class TileCable extends TileGeneric implements IPeripheralTile
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        if( !canAttachPeripheral() || player.isSneaking() ) return false;
 | 
			
		||||
        if( player.isCrouching() ) return ActionResultType.PASS;
 | 
			
		||||
        if( !canAttachPeripheral() ) return ActionResultType.FAIL;
 | 
			
		||||
 | 
			
		||||
        if( getWorld().isRemote ) return true;
 | 
			
		||||
        if( getWorld().isRemote ) return ActionResultType.SUCCESS;
 | 
			
		||||
 | 
			
		||||
        String oldName = m_peripheral.getConnectedName();
 | 
			
		||||
        togglePeripheralAccess();
 | 
			
		||||
@@ -262,7 +265,7 @@ public class TileCable extends TileGeneric implements IPeripheralTile
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return ActionResultType.SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ import dan200.computercraft.shared.wired.CapabilityWiredElement;
 | 
			
		||||
import net.minecraft.block.BlockState;
 | 
			
		||||
import net.minecraft.entity.player.PlayerEntity;
 | 
			
		||||
import net.minecraft.nbt.CompoundNBT;
 | 
			
		||||
import net.minecraft.util.ActionResultType;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
@@ -184,10 +185,11 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        if( getWorld().isRemote ) return true;
 | 
			
		||||
        if( getWorld().isRemote ) return ActionResultType.SUCCESS;
 | 
			
		||||
 | 
			
		||||
        // On server, we interacted if a peripheral was found
 | 
			
		||||
        Set<String> oldPeriphNames = getConnectedPeripheralNames();
 | 
			
		||||
@@ -200,7 +202,7 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
 | 
			
		||||
            sendPeripheralChanges( player, "chat.computercraft.wired_modem.peripheral_connected", periphNames );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return ActionResultType.SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void sendPeripheralChanges( PlayerEntity player, String kind, Collection<String> peripherals )
 | 
			
		||||
 
 | 
			
		||||
@@ -374,7 +374,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
 | 
			
		||||
            m_computer.queueEvent( event, arguments );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nullable
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        @Override
 | 
			
		||||
        public IWorkMonitor getMainThreadMonitor()
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,12 +18,10 @@ import net.minecraft.state.EnumProperty;
 | 
			
		||||
import net.minecraft.state.StateContainer;
 | 
			
		||||
import net.minecraft.state.properties.BlockStateProperties;
 | 
			
		||||
import net.minecraft.tileentity.TileEntity;
 | 
			
		||||
import net.minecraft.util.BlockRenderLayer;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
public class BlockMonitor extends BlockGeneric
 | 
			
		||||
@@ -38,28 +36,13 @@ public class BlockMonitor extends BlockGeneric
 | 
			
		||||
    public BlockMonitor( Properties settings, NamedTileEntityType<? extends TileGeneric> type )
 | 
			
		||||
    {
 | 
			
		||||
        super( settings, type );
 | 
			
		||||
        // TODO: Test underwater - do we need isSolid at all?
 | 
			
		||||
        setDefaultState( getStateContainer().getBaseState()
 | 
			
		||||
            .with( ORIENTATION, Direction.NORTH )
 | 
			
		||||
            .with( FACING, Direction.NORTH )
 | 
			
		||||
            .with( STATE, MonitorEdgeState.NONE ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public BlockRenderLayer getRenderLayer()
 | 
			
		||||
    {
 | 
			
		||||
        // We use the CUTOUT layer, as otherwise monitor rendering will cause flickering.
 | 
			
		||||
        return BlockRenderLayer.CUTOUT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public boolean isSolid( BlockState state )
 | 
			
		||||
    {
 | 
			
		||||
        // We override isSolid, as our overriding of getRenderLayer means that it would otherwise return false.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void fillStateContainer( StateContainer.Builder<Block, BlockState> builder )
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ public final class ClientMonitor extends ClientTerminal
 | 
			
		||||
                if( buffer != null ) return false;
 | 
			
		||||
 | 
			
		||||
                deleteBuffers();
 | 
			
		||||
                buffer = new VertexBuffer( FixedWidthFontRenderer.POSITION_COLOR_TEX );
 | 
			
		||||
                buffer = new VertexBuffer( FixedWidthFontRenderer.TYPE.getVertexFormat() );
 | 
			
		||||
                addMonitor();
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +75,7 @@ public final class ClientMonitor extends ClientTerminal
 | 
			
		||||
    {
 | 
			
		||||
        if( buffer != null )
 | 
			
		||||
        {
 | 
			
		||||
            buffer.deleteGlBuffers();
 | 
			
		||||
            buffer.close();
 | 
			
		||||
            buffer = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.monitor;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.platform.GLX;
 | 
			
		||||
import dan200.computercraft.ComputerCraft;
 | 
			
		||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
 | 
			
		||||
 | 
			
		||||
@@ -73,22 +72,7 @@ public enum MonitorRenderer
 | 
			
		||||
    public static MonitorRenderer current()
 | 
			
		||||
    {
 | 
			
		||||
        MonitorRenderer current = ComputerCraft.monitorRenderer;
 | 
			
		||||
        switch( current )
 | 
			
		||||
        {
 | 
			
		||||
            case BEST:
 | 
			
		||||
                return best();
 | 
			
		||||
            case VBO:
 | 
			
		||||
                if( !GLX.useVbo() )
 | 
			
		||||
                {
 | 
			
		||||
                    ComputerCraft.log.warn( "VBOs are not supported on your graphics card. Falling back to default." );
 | 
			
		||||
                    ComputerCraft.monitorRenderer = BEST;
 | 
			
		||||
                    return best();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return VBO;
 | 
			
		||||
            default:
 | 
			
		||||
                return current;
 | 
			
		||||
        }
 | 
			
		||||
        return current == MonitorRenderer.BEST ? best() : current;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static MonitorRenderer best()
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,11 @@ import net.minecraft.entity.player.PlayerEntity;
 | 
			
		||||
import net.minecraft.nbt.CompoundNBT;
 | 
			
		||||
import net.minecraft.tileentity.TileEntity;
 | 
			
		||||
import net.minecraft.tileentity.TileEntityType;
 | 
			
		||||
import net.minecraft.util.ActionResultType;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraft.util.math.AxisAlignedBB;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.world.World;
 | 
			
		||||
@@ -104,10 +106,11 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
 | 
			
		||||
        if( m_clientMonitor != null && m_xIndex == 0 && m_yIndex == 0 ) m_clientMonitor.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        if( !player.isSneaking() && getFront() == hit.getFace() )
 | 
			
		||||
        if( !player.isCrouching() && getFront() == hit.getFace() )
 | 
			
		||||
        {
 | 
			
		||||
            if( !getWorld().isRemote )
 | 
			
		||||
            {
 | 
			
		||||
@@ -117,10 +120,10 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
 | 
			
		||||
                    (float) (hit.getHitVec().z - hit.getPos().getZ())
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
            return ActionResultType.SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return ActionResultType.PASS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
@@ -651,13 +654,12 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
 | 
			
		||||
        m_computers.remove( computer );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public AxisAlignedBB getRenderBoundingBox()
 | 
			
		||||
    {
 | 
			
		||||
        TileMonitor start = getNeighbour( 0, 0 );
 | 
			
		||||
        TileMonitor end = getNeighbour( width - 1, height - 1 );
 | 
			
		||||
        TileMonitor end = getNeighbour( m_width - 1, m_height - 1 );
 | 
			
		||||
        if( start != null && end != null )
 | 
			
		||||
        {
 | 
			
		||||
            BlockPos startPos = start.getPos();
 | 
			
		||||
@@ -676,5 +678,4 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
 | 
			
		||||
            return new AxisAlignedBB( pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1 );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@ import net.minecraft.inventory.container.INamedContainerProvider;
 | 
			
		||||
import net.minecraft.item.*;
 | 
			
		||||
import net.minecraft.nbt.CompoundNBT;
 | 
			
		||||
import net.minecraft.util.*;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.Vec3d;
 | 
			
		||||
import net.minecraft.util.text.ITextComponent;
 | 
			
		||||
import net.minecraft.util.text.TranslationTextComponent;
 | 
			
		||||
import net.minecraftforge.common.capabilities.Capability;
 | 
			
		||||
@@ -94,13 +94,14 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        if( player.isSneaking() ) return false;
 | 
			
		||||
        if( player.isCrouching() ) return ActionResultType.PASS;
 | 
			
		||||
 | 
			
		||||
        if( !getWorld().isRemote ) NetworkHooks.openGui( (ServerPlayerEntity) player, this );
 | 
			
		||||
        return true;
 | 
			
		||||
        return ActionResultType.SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -424,11 +425,7 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
 | 
			
		||||
                setInventorySlotContents( i, ItemStack.EMPTY );
 | 
			
		||||
 | 
			
		||||
                // Spawn the item in the world
 | 
			
		||||
                BlockPos pos = getPos();
 | 
			
		||||
                double x = pos.getX() + 0.5;
 | 
			
		||||
                double y = pos.getY() + 0.75;
 | 
			
		||||
                double z = pos.getZ() + 0.5;
 | 
			
		||||
                WorldUtil.dropItemStack( stack, getWorld(), x, y, z );
 | 
			
		||||
                WorldUtil.dropItemStack( stack, getWorld(), new Vec3d( getPos() ).add( 0.5, 0.75, 0.5 ) );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ public class PocketAPI implements ILuaAPI
 | 
			
		||||
                            stack = InventoryUtil.storeItems( stack, new PlayerMainInvWrapper( inventory ), inventory.currentItem );
 | 
			
		||||
                            if( !stack.isEmpty() )
 | 
			
		||||
                            {
 | 
			
		||||
                                WorldUtil.dropItemStack( stack, player.getEntityWorld(), player.posX, player.posY, player.posZ );
 | 
			
		||||
                                WorldUtil.dropItemStack( stack, player.getEntityWorld(), player.getPositionVec() );
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@@ -111,7 +111,7 @@ public class PocketAPI implements ILuaAPI
 | 
			
		||||
                        stack = InventoryUtil.storeItems( stack, new PlayerMainInvWrapper( inventory ), inventory.currentItem );
 | 
			
		||||
                        if( stack.isEmpty() )
 | 
			
		||||
                        {
 | 
			
		||||
                            WorldUtil.dropItemStack( stack, player.getEntityWorld(), player.posX, player.posY, player.posZ );
 | 
			
		||||
                            WorldUtil.dropItemStack( stack, player.getEntityWorld(), player.getPositionVec() );
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,9 @@ public final class PocketComputerItemFactory
 | 
			
		||||
    {
 | 
			
		||||
        switch( family )
 | 
			
		||||
        {
 | 
			
		||||
            case Normal:
 | 
			
		||||
            case NORMAL:
 | 
			
		||||
                return ComputerCraft.Items.pocketComputerNormal.create( id, label, colour, upgrade );
 | 
			
		||||
            case Advanced:
 | 
			
		||||
            case ADVANCED:
 | 
			
		||||
                return ComputerCraft.Items.pocketComputerAdvanced.create( id, label, colour, upgrade );
 | 
			
		||||
            default:
 | 
			
		||||
                return ItemStack.EMPTY;
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ public final class PocketComputerUpgradeRecipe extends SpecialRecipe
 | 
			
		||||
    @Override
 | 
			
		||||
    public ItemStack getRecipeOutput()
 | 
			
		||||
    {
 | 
			
		||||
        return PocketComputerItemFactory.create( -1, null, -1, ComputerFamily.Normal, null );
 | 
			
		||||
        return PocketComputerItemFactory.create( -1, null, -1, ComputerFamily.NORMAL, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ import dan200.computercraft.api.media.IMedia;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IPeripheralTile;
 | 
			
		||||
import dan200.computercraft.core.computer.MainThread;
 | 
			
		||||
import dan200.computercraft.core.tracking.Tracking;
 | 
			
		||||
import dan200.computercraft.shared.Config;
 | 
			
		||||
import dan200.computercraft.shared.command.CommandComputerCraft;
 | 
			
		||||
import dan200.computercraft.shared.command.arguments.ArgumentSerializers;
 | 
			
		||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
 | 
			
		||||
@@ -36,7 +35,6 @@ import net.minecraft.world.storage.loot.conditions.LootConditionManager;
 | 
			
		||||
import net.minecraftforge.event.TickEvent;
 | 
			
		||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
 | 
			
		||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
 | 
			
		||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
 | 
			
		||||
import net.minecraftforge.fml.common.Mod;
 | 
			
		||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
 | 
			
		||||
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
 | 
			
		||||
@@ -133,12 +131,6 @@ public final class ComputerCraftProxyCommon
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @SubscribeEvent
 | 
			
		||||
        public static void onConfigChanged( ConfigChangedEvent.OnConfigChangedEvent event )
 | 
			
		||||
        {
 | 
			
		||||
            if( event.getModID().equals( ComputerCraft.MOD_ID ) ) Config.sync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @SubscribeEvent
 | 
			
		||||
        public static void onContainerOpen( PlayerContainerEvent.Open event )
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -133,11 +133,11 @@ public class TurtleAPI implements ILuaAPI
 | 
			
		||||
        }
 | 
			
		||||
        else if( side.equalsIgnoreCase( "left" ) )
 | 
			
		||||
        {
 | 
			
		||||
            return TurtleSide.Left;
 | 
			
		||||
            return TurtleSide.LEFT;
 | 
			
		||||
        }
 | 
			
		||||
        else if( side.equalsIgnoreCase( "right" ) )
 | 
			
		||||
        {
 | 
			
		||||
            return TurtleSide.Right;
 | 
			
		||||
            return TurtleSide.RIGHT;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -152,58 +152,58 @@ public class TurtleAPI implements ILuaAPI
 | 
			
		||||
        {
 | 
			
		||||
            case 0: // forward
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.Forward ) );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.FORWARD ) );
 | 
			
		||||
            case 1: // back
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.Back ) );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.BACK ) );
 | 
			
		||||
            case 2: // up
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.Up ) );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.UP ) );
 | 
			
		||||
            case 3: // down
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.Down ) );
 | 
			
		||||
                return tryCommand( context, new TurtleMoveCommand( MoveDirection.DOWN ) );
 | 
			
		||||
            case 4: // turnLeft
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleTurnCommand( TurnDirection.Left ) );
 | 
			
		||||
                return tryCommand( context, new TurtleTurnCommand( TurnDirection.LEFT ) );
 | 
			
		||||
            case 5: // turnRight
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleTurnCommand( TurnDirection.Right ) );
 | 
			
		||||
                return tryCommand( context, new TurtleTurnCommand( TurnDirection.RIGHT ) );
 | 
			
		||||
            case 6:
 | 
			
		||||
            {
 | 
			
		||||
                // dig
 | 
			
		||||
                TurtleSide side = parseSide( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.dig( InteractDirection.Forward, side ) );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.dig( InteractDirection.FORWARD, side ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 7:
 | 
			
		||||
            {
 | 
			
		||||
                // digUp
 | 
			
		||||
                TurtleSide side = parseSide( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.dig( InteractDirection.Up, side ) );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.dig( InteractDirection.UP, side ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 8:
 | 
			
		||||
            {
 | 
			
		||||
                // digDown
 | 
			
		||||
                TurtleSide side = parseSide( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.dig( InteractDirection.Down, side ) );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.dig( InteractDirection.DOWN, side ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 9: // place
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Forward, args ) );
 | 
			
		||||
                return tryCommand( context, new TurtlePlaceCommand( InteractDirection.FORWARD, args ) );
 | 
			
		||||
            case 10: // placeUp
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Up, args ) );
 | 
			
		||||
                return tryCommand( context, new TurtlePlaceCommand( InteractDirection.UP, args ) );
 | 
			
		||||
            case 11: // placeDown
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Down, args ) );
 | 
			
		||||
                return tryCommand( context, new TurtlePlaceCommand( InteractDirection.DOWN, args ) );
 | 
			
		||||
            case 12:
 | 
			
		||||
            {
 | 
			
		||||
                // drop
 | 
			
		||||
                int count = parseCount( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleDropCommand( InteractDirection.Forward, count ) );
 | 
			
		||||
                return tryCommand( context, new TurtleDropCommand( InteractDirection.FORWARD, count ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 13:
 | 
			
		||||
            {
 | 
			
		||||
@@ -229,72 +229,72 @@ public class TurtleAPI implements ILuaAPI
 | 
			
		||||
                return new Object[] { stack.isEmpty() ? 64 : Math.min( stack.getMaxStackSize(), 64 ) - stack.getCount() };
 | 
			
		||||
            }
 | 
			
		||||
            case 16: // detect
 | 
			
		||||
                return tryCommand( context, new TurtleDetectCommand( InteractDirection.Forward ) );
 | 
			
		||||
                return tryCommand( context, new TurtleDetectCommand( InteractDirection.FORWARD ) );
 | 
			
		||||
            case 17: // detectUp
 | 
			
		||||
                return tryCommand( context, new TurtleDetectCommand( InteractDirection.Up ) );
 | 
			
		||||
                return tryCommand( context, new TurtleDetectCommand( InteractDirection.UP ) );
 | 
			
		||||
            case 18: // detectDown
 | 
			
		||||
                return tryCommand( context, new TurtleDetectCommand( InteractDirection.Down ) );
 | 
			
		||||
                return tryCommand( context, new TurtleDetectCommand( InteractDirection.DOWN ) );
 | 
			
		||||
            case 19: // compare
 | 
			
		||||
                return tryCommand( context, new TurtleCompareCommand( InteractDirection.Forward ) );
 | 
			
		||||
                return tryCommand( context, new TurtleCompareCommand( InteractDirection.FORWARD ) );
 | 
			
		||||
            case 20: // compareUp
 | 
			
		||||
                return tryCommand( context, new TurtleCompareCommand( InteractDirection.Up ) );
 | 
			
		||||
                return tryCommand( context, new TurtleCompareCommand( InteractDirection.UP ) );
 | 
			
		||||
            case 21: // compareDown
 | 
			
		||||
                return tryCommand( context, new TurtleCompareCommand( InteractDirection.Down ) );
 | 
			
		||||
                return tryCommand( context, new TurtleCompareCommand( InteractDirection.DOWN ) );
 | 
			
		||||
            case 22:
 | 
			
		||||
            {
 | 
			
		||||
                // attack
 | 
			
		||||
                TurtleSide side = parseSide( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.attack( InteractDirection.Forward, side ) );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.attack( InteractDirection.FORWARD, side ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 23:
 | 
			
		||||
            {
 | 
			
		||||
                // attackUp
 | 
			
		||||
                TurtleSide side = parseSide( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.attack( InteractDirection.Up, side ) );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.attack( InteractDirection.UP, side ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 24:
 | 
			
		||||
            {
 | 
			
		||||
                // attackDown
 | 
			
		||||
                TurtleSide side = parseSide( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.attack( InteractDirection.Down, side ) );
 | 
			
		||||
                return tryCommand( context, TurtleToolCommand.attack( InteractDirection.DOWN, side ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 25:
 | 
			
		||||
            {
 | 
			
		||||
                // dropUp
 | 
			
		||||
                int count = parseCount( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleDropCommand( InteractDirection.Up, count ) );
 | 
			
		||||
                return tryCommand( context, new TurtleDropCommand( InteractDirection.UP, count ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 26:
 | 
			
		||||
            {
 | 
			
		||||
                // dropDown
 | 
			
		||||
                int count = parseCount( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleDropCommand( InteractDirection.Down, count ) );
 | 
			
		||||
                return tryCommand( context, new TurtleDropCommand( InteractDirection.DOWN, count ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 27:
 | 
			
		||||
            {
 | 
			
		||||
                // suck
 | 
			
		||||
                int count = parseCount( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleSuckCommand( InteractDirection.Forward, count ) );
 | 
			
		||||
                return tryCommand( context, new TurtleSuckCommand( InteractDirection.FORWARD, count ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 28:
 | 
			
		||||
            {
 | 
			
		||||
                // suckUp
 | 
			
		||||
                int count = parseCount( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleSuckCommand( InteractDirection.Up, count ) );
 | 
			
		||||
                return tryCommand( context, new TurtleSuckCommand( InteractDirection.UP, count ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 29:
 | 
			
		||||
            {
 | 
			
		||||
                // suckDown
 | 
			
		||||
                int count = parseCount( args, 0 );
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleSuckCommand( InteractDirection.Down, count ) );
 | 
			
		||||
                return tryCommand( context, new TurtleSuckCommand( InteractDirection.DOWN, count ) );
 | 
			
		||||
            }
 | 
			
		||||
            case 30: // getFuelLevel
 | 
			
		||||
                return new Object[] { m_turtle.isFuelNeeded() ? m_turtle.getFuelLevel() : "unlimited" };
 | 
			
		||||
@@ -324,16 +324,16 @@ public class TurtleAPI implements ILuaAPI
 | 
			
		||||
                return new Object[] { m_turtle.isFuelNeeded() ? m_turtle.getFuelLimit() : "unlimited" };
 | 
			
		||||
            case 36: // equipLeft
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleEquipCommand( TurtleSide.Left ) );
 | 
			
		||||
                return tryCommand( context, new TurtleEquipCommand( TurtleSide.LEFT ) );
 | 
			
		||||
            case 37: // equipRight
 | 
			
		||||
                m_environment.addTrackingChange( TrackingField.TURTLE_OPS );
 | 
			
		||||
                return tryCommand( context, new TurtleEquipCommand( TurtleSide.Right ) );
 | 
			
		||||
                return tryCommand( context, new TurtleEquipCommand( TurtleSide.RIGHT ) );
 | 
			
		||||
            case 38: // inspect
 | 
			
		||||
                return tryCommand( context, new TurtleInspectCommand( InteractDirection.Forward ) );
 | 
			
		||||
                return tryCommand( context, new TurtleInspectCommand( InteractDirection.FORWARD ) );
 | 
			
		||||
            case 39: // inspectUp
 | 
			
		||||
                return tryCommand( context, new TurtleInspectCommand( InteractDirection.Up ) );
 | 
			
		||||
                return tryCommand( context, new TurtleInspectCommand( InteractDirection.UP ) );
 | 
			
		||||
            case 40: // inspectDown
 | 
			
		||||
                return tryCommand( context, new TurtleInspectCommand( InteractDirection.Down ) );
 | 
			
		||||
                return tryCommand( context, new TurtleInspectCommand( InteractDirection.DOWN ) );
 | 
			
		||||
            case 41: // getItemDetail
 | 
			
		||||
            {
 | 
			
		||||
                // FIXME: There's a race condition here if the stack is being modified (mutating NBT, etc...)
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@ public class BlockTurtle extends BlockComputerBase<TileTurtle> implements IWater
 | 
			
		||||
    @Override
 | 
			
		||||
    public float getExplosionResistance( BlockState state, IWorldReader world, BlockPos pos, @Nullable Entity exploder, Explosion explosion )
 | 
			
		||||
    {
 | 
			
		||||
        if( getFamily() == ComputerFamily.Advanced || exploder instanceof LivingEntity || exploder instanceof DamagingProjectileEntity )
 | 
			
		||||
        if( getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof DamagingProjectileEntity )
 | 
			
		||||
        {
 | 
			
		||||
            return 2000;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,10 +33,7 @@ import net.minecraft.item.Items;
 | 
			
		||||
import net.minecraft.nbt.CompoundNBT;
 | 
			
		||||
import net.minecraft.nbt.ListNBT;
 | 
			
		||||
import net.minecraft.tileentity.TileEntityType;
 | 
			
		||||
import net.minecraft.util.Direction;
 | 
			
		||||
import net.minecraft.util.Hand;
 | 
			
		||||
import net.minecraft.util.NonNullList;
 | 
			
		||||
import net.minecraft.util.ResourceLocation;
 | 
			
		||||
import net.minecraft.util.*;
 | 
			
		||||
import net.minecraft.util.math.BlockPos;
 | 
			
		||||
import net.minecraft.util.math.BlockRayTraceResult;
 | 
			
		||||
import net.minecraft.util.math.Vec3d;
 | 
			
		||||
@@ -60,12 +57,12 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
 | 
			
		||||
 | 
			
		||||
    public static final NamedTileEntityType<TileTurtle> FACTORY_NORMAL = NamedTileEntityType.create(
 | 
			
		||||
        new ResourceLocation( ComputerCraft.MOD_ID, "turtle_normal" ),
 | 
			
		||||
        type -> new TileTurtle( type, ComputerFamily.Normal )
 | 
			
		||||
        type -> new TileTurtle( type, ComputerFamily.NORMAL )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    public static final NamedTileEntityType<TileTurtle> FACTORY_ADVANCED = NamedTileEntityType.create(
 | 
			
		||||
        new ResourceLocation( ComputerCraft.MOD_ID, "turtle_advanced" ),
 | 
			
		||||
        type -> new TileTurtle( type, ComputerFamily.Advanced )
 | 
			
		||||
        type -> new TileTurtle( type, ComputerFamily.ADVANCED )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    enum MoveState
 | 
			
		||||
@@ -164,8 +161,9 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
 | 
			
		||||
    {
 | 
			
		||||
        // Apply dye
 | 
			
		||||
        ItemStack currentItem = player.getHeldItem( hand );
 | 
			
		||||
@@ -186,7 +184,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
                return ActionResultType.SUCCESS;
 | 
			
		||||
            }
 | 
			
		||||
            else if( currentItem.getItem() == Items.WATER_BUCKET && m_brain.getColour() != -1 )
 | 
			
		||||
            {
 | 
			
		||||
@@ -203,7 +201,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
                return ActionResultType.SUCCESS;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -528,10 +526,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
 | 
			
		||||
        switch( side )
 | 
			
		||||
        {
 | 
			
		||||
            case RIGHT:
 | 
			
		||||
                upgrade = getUpgrade( TurtleSide.Right );
 | 
			
		||||
                upgrade = getUpgrade( TurtleSide.RIGHT );
 | 
			
		||||
                break;
 | 
			
		||||
            case LEFT:
 | 
			
		||||
                upgrade = getUpgrade( TurtleSide.Left );
 | 
			
		||||
                upgrade = getUpgrade( TurtleSide.LEFT );
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,20 +10,20 @@ import net.minecraft.util.Direction;
 | 
			
		||||
 | 
			
		||||
public enum InteractDirection
 | 
			
		||||
{
 | 
			
		||||
    Forward,
 | 
			
		||||
    Up,
 | 
			
		||||
    Down;
 | 
			
		||||
    FORWARD,
 | 
			
		||||
    UP,
 | 
			
		||||
    DOWN;
 | 
			
		||||
 | 
			
		||||
    public Direction toWorldDir( ITurtleAccess turtle )
 | 
			
		||||
    {
 | 
			
		||||
        switch( this )
 | 
			
		||||
        {
 | 
			
		||||
            case Forward:
 | 
			
		||||
            case FORWARD:
 | 
			
		||||
            default:
 | 
			
		||||
                return turtle.getDirection();
 | 
			
		||||
            case Up:
 | 
			
		||||
            case UP:
 | 
			
		||||
                return Direction.UP;
 | 
			
		||||
            case Down:
 | 
			
		||||
            case DOWN:
 | 
			
		||||
                return Direction.DOWN;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user