mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 14:37:39 +00:00
Compare commits
46 Commits
v1.80pr1.6
...
v1.80pr1.9
Author | SHA1 | Date | |
---|---|---|---|
![]() |
51e787f631 | ||
![]() |
8080699030 | ||
![]() |
e555f9f7f0 | ||
![]() |
822db6e9b5 | ||
![]() |
ac1f30ef43 | ||
![]() |
0fc1b8c46b | ||
![]() |
e7c19bcf55 | ||
![]() |
63ca8aca4c | ||
![]() |
2caa9c57fc | ||
![]() |
33fad2da15 | ||
![]() |
518eefbe10 | ||
![]() |
1ba73454c1 | ||
![]() |
ee4735c17c | ||
![]() |
b008edae90 | ||
![]() |
c6bd88f3ad | ||
![]() |
efa57521c7 | ||
![]() |
4700f8831b | ||
![]() |
9428bee316 | ||
![]() |
89c7183a1d | ||
![]() |
d2a9e7e458 | ||
![]() |
1774f1a079 | ||
![]() |
de1307913b | ||
![]() |
093132533d | ||
![]() |
0685be6bfa | ||
![]() |
f40733e9a6 | ||
![]() |
a3d1cff298 | ||
![]() |
b8957cab5c | ||
![]() |
3ac8dde779 | ||
![]() |
17dace979a | ||
![]() |
d405316a4b | ||
![]() |
7e18f2cead | ||
![]() |
000786a1a7 | ||
![]() |
0bf13562b9 | ||
![]() |
45a189e834 | ||
![]() |
0ce6f34a09 | ||
![]() |
4d984dc5ee | ||
![]() |
984d358930 | ||
![]() |
a95893b823 | ||
![]() |
81aaead032 | ||
![]() |
a2083bcff1 | ||
![]() |
052f2a16dc | ||
![]() |
fd10ed6f62 | ||
![]() |
c0bdd4ff1d | ||
![]() |
5f0addbc3e | ||
![]() |
c9589ad0e7 | ||
![]() |
b21c495815 |
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report some misbehaviour in the mod
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
## Before reporting
|
||||
- Search for the bug both here and [on the ComputerCraft issues page](https://github.com/dan200/ComputerCraft/issues?utf8=%E2%9C%93&q=is%3Aissue+)
|
||||
- If possible, try to reproduce on vanilla ComputerCraft. If it still occurs, [report on the ComputerCraft repo](https://github.com/dan200/ComputerCraft/issues/new) instead.
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Minecraft version
|
||||
- CC: Tweaked version
|
||||
- Detailed reproduction steps!** Sometimes I can spot a bug pretty easily, but often it's much more obscure. Anything you can give which will help reproduce it means it'll get fixed quicker.
|
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea or improvement
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
## Before reporting
|
||||
- Search for the suggestion both here and [on the ComputerCraft issues page](https://github.com/dan200/ComputerCraft/issues?utf8=%E2%9C%93&q=is%3Aissue+). It's possible someone's suggested it before!
|
||||
- Unless something is specific to CC:Tweaked, try to [suggest them on the ComputerCraft repo](https://github.com/dan200/ComputerCraft/issues/new). There's a lot more people watching it, so it allows the wider community to contribute.
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Explanation of how the feature/change chould work.
|
||||
- Some rationale/use case for a feature. I'd like to keep CC:T as minimal
|
9
.github/pull_request_template.md
vendored
Normal file
9
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<!--
|
||||
Unless this feature is specific to CC:Tweaked, try to [target the original ComputerCraft repo](https://github.com/dan200/ComputerCraft/) instead. There's a lot more people watching it, so it allows the wider community to contribute.
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Brief explanation of the changes you've made.
|
||||
- Rationale of why this change has been made/reasoning behind it.
|
||||
|
||||
The more information you can provide, the easier it is to review something now _and_ to see why a change was made, when the code needs updating in the future.
|
40
README.md
40
README.md
@@ -6,26 +6,44 @@ features of the mod. For a more stable experience, I recommend checking out the
|
||||
[original mod](https://github.com/dan200/ComputerCraft).
|
||||
|
||||
## What?
|
||||
CC: Tweaked does not aim to create a competing fork of ComputerCraft, nor am I planning to take it in in a vastly
|
||||
different direction to the original mod. In fact, CC: Tweaked aims to be a nurturing ground for various features, with
|
||||
a pull request against the original mod being the end goal.
|
||||
CC: Tweaked (or CC:T for short) does not aim to create a competing fork of ComputerCraft, nor am I planning to take it
|
||||
in in a vastly different direction to the original mod. In fact, CC:T aims to be a nurturing ground for various
|
||||
features, with a pull request against the original mod being the end goal.
|
||||
|
||||
CC: Tweaked also includes many pull requests from the community which have not yet been merged, offering a large number
|
||||
CC:T also includes many pull requests from the community which have not yet been merged, offering a large number
|
||||
of additional bug fixes and features over the original mod.
|
||||
|
||||
## Features
|
||||
CC: Tweaked contains the all features of the latest alpha, as well as numerous fixes, performance improvements and
|
||||
several additional features. I'd recommend checking out [the releases page](https://github.com/SquidDev-CC/CC-Tweaked/releases)
|
||||
to see the full changes, but here's a couple of the more interesting changes:
|
||||
|
||||
- Replace LuaJ with Cobalt.
|
||||
- Allow running multiple computers at the same time.
|
||||
- Websocket support in the HTTP library.
|
||||
- Wired modems and cables act more like multiparts.
|
||||
- Add map-like rendering for pocket computers and printed pages/books.
|
||||
- Adds the `/computercraft` command, offering various diagnostic tools for server owners. This allows operators to
|
||||
track which computers are hogging resources, turn on and shutdown multiple computers at once and interact with
|
||||
computers remotely.
|
||||
- Add full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is
|
||||
installed).
|
||||
- Extended binary file handles. They support file seeking, and reading new lines, allowing full (and accurate)
|
||||
emulation of the standard Lua `io` library.
|
||||
|
||||
## Relation to CCTweaks?
|
||||
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. However, I do plan
|
||||
to migrate some features of CCTweaks into CC: Tweaked.
|
||||
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said,
|
||||
several features have been included, such as full block modems, the Cobalt runtime and map-like rendering for pocket
|
||||
computers.
|
||||
|
||||
## Contributing
|
||||
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you do wish to contribute
|
||||
code, do consider submitting it to the ComputerCraft repository instead.
|
||||
code, do consider submitting it to the ComputerCraft repository first.
|
||||
|
||||
That being said, in order to start helping develop CC: Tweaked, you'll need to follow these steps:
|
||||
That being said, in order to start helping develop CC:T, you'll need to follow these steps:
|
||||
|
||||
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
|
||||
- **Setup Forge:** `./gradlew setupDecompWorkspace`
|
||||
- **Test your changes:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
||||
|
||||
If you want to run CC: Tweaked in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from
|
||||
`build/libs`.
|
||||
|
||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
||||
|
15
build.gradle
15
build.gradle
@@ -23,12 +23,12 @@ apply plugin: 'org.ajoberstar.grgit'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'maven'
|
||||
|
||||
version = "1.80pr1.6"
|
||||
version = "1.80pr1.9"
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked"
|
||||
|
||||
minecraft {
|
||||
version = "1.12.2-14.23.2.2634"
|
||||
version = "1.12.2-14.23.4.2749"
|
||||
runDir = "run"
|
||||
replace '${version}', project.version
|
||||
|
||||
@@ -37,7 +37,7 @@ minecraft {
|
||||
// stable_# stables are built at the discretion of the MCP team.
|
||||
// Use non-default mappings at your own risk. they may not allways work.
|
||||
// simply re-run your setup task after changing the mappings to update your workspace.
|
||||
mappings = "snapshot_20180324"
|
||||
mappings = "snapshot_20180724"
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ repositories {
|
||||
name = "squiddev"
|
||||
url = "https://dl.bintray.com/squiddev/maven"
|
||||
}
|
||||
|
||||
ivy { artifactPattern "https://asie.pl/files/mods/Charset/LibOnly/[module]-[revision](-[classifier]).[ext]" }
|
||||
}
|
||||
|
||||
configurations {
|
||||
@@ -60,8 +62,11 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
deobfProvided "mezz.jei:jei_1.12.2:4.8.5.159:api"
|
||||
deobfProvided "pl.asie:Charset-Lib:0.5.4.6"
|
||||
|
||||
runtime "mezz.jei:jei_1.12.2:4.8.5.159"
|
||||
shade 'org.squiddev:Cobalt:0.3.1'
|
||||
|
||||
shade 'org.squiddev:Cobalt:0.3.2'
|
||||
|
||||
testCompile 'junit:junit:4.11'
|
||||
|
||||
@@ -126,7 +131,7 @@ curseforge {
|
||||
project {
|
||||
id = '282001'
|
||||
releaseType = 'beta'
|
||||
changelog = ''
|
||||
changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${project.version})."
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ import dan200.computercraft.api.turtle.event.TurtleAction;
|
||||
import dan200.computercraft.core.apis.AddressPredicate;
|
||||
import dan200.computercraft.core.filesystem.ComboMount;
|
||||
import dan200.computercraft.core.filesystem.FileMount;
|
||||
import dan200.computercraft.core.filesystem.JarMount;
|
||||
import dan200.computercraft.core.filesystem.FileSystemMount;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.command.CommandComputer;
|
||||
@@ -97,7 +97,11 @@ import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.ProviderNotFoundException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -921,11 +925,12 @@ public class ComputerCraft
|
||||
{
|
||||
try
|
||||
{
|
||||
IMount jarMount = new JarMount( modJar, subPath );
|
||||
mounts.add( jarMount );
|
||||
FileSystem fs = FileSystems.newFileSystem( modJar.toPath(), ComputerCraft.class.getClassLoader() );
|
||||
mounts.add( new FileSystemMount( fs, subPath ) );
|
||||
}
|
||||
catch( IOException e )
|
||||
catch( IOException | ProviderNotFoundException | ServiceConfigurationError e )
|
||||
{
|
||||
ComputerCraft.log.error( "Could not load mount from mod jar", e );
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
@@ -943,7 +948,7 @@ public class ComputerCraft
|
||||
if( !resourcePack.isDirectory() )
|
||||
{
|
||||
// Mount a resource pack from a jar
|
||||
IMount resourcePackMount = new JarMount( resourcePack, subPath );
|
||||
IMount resourcePackMount = new FileSystemMount( FileSystems.getFileSystem( resourcePack.toURI() ), subPath );
|
||||
mounts.add( resourcePackMount );
|
||||
}
|
||||
else
|
||||
@@ -959,7 +964,7 @@ public class ComputerCraft
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
// Ignore
|
||||
ComputerCraft.log.error( "Could not load resource pack '" + resourcePack1 + "'", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1108,7 +1113,7 @@ public class ComputerCraft
|
||||
{
|
||||
return turtleProxy.getTurtleUpgrade( id );
|
||||
}
|
||||
|
||||
|
||||
public static ITurtleUpgrade getTurtleUpgrade( int legacyID )
|
||||
{
|
||||
return turtleProxy.getTurtleUpgrade( legacyID );
|
||||
@@ -1124,13 +1129,18 @@ public class ComputerCraft
|
||||
turtleProxy.addAllUpgradedTurtles( list );
|
||||
}
|
||||
|
||||
public static void setEntityDropConsumer( Entity entity, IEntityDropConsumer consumer )
|
||||
public static void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer )
|
||||
{
|
||||
turtleProxy.setEntityDropConsumer( entity, consumer );
|
||||
turtleProxy.setDropConsumer( entity, consumer );
|
||||
}
|
||||
|
||||
public static void clearEntityDropConsumer( Entity entity )
|
||||
public static void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer )
|
||||
{
|
||||
turtleProxy.clearEntityDropConsumer( entity );
|
||||
turtleProxy.setDropConsumer( world, pos, consumer );
|
||||
}
|
||||
|
||||
public static List<ItemStack> clearDropConsumer( )
|
||||
{
|
||||
return turtleProxy.clearDropConsumer();
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@ 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.util.List;
|
||||
|
||||
/**
|
||||
@@ -72,7 +74,25 @@ public interface IMount
|
||||
* @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.
|
||||
*
|
||||
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
|
||||
* @return A channel representing the contents of the file. If the channel implements
|
||||
* {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary positions when using binary
|
||||
* mode.
|
||||
* @throws IOException If the file does not exist, or could not be opened.
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("deprecation")
|
||||
default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
return Channels.newChannel( openForRead( path ) );
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@ 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;
|
||||
|
||||
/**
|
||||
* Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)}
|
||||
@@ -50,20 +52,54 @@ public interface IWritableMount extends IMount
|
||||
* @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.
|
||||
*
|
||||
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
|
||||
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one
|
||||
* will be able to seek to arbitrary positions when using binary mode.
|
||||
* @throws IOException If the file could not be opened for writing.
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("deprecation")
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one
|
||||
* will be able to seek to arbitrary positions when using binary mode.
|
||||
* @throws IOException If the file could not be opened for writing.
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("deprecation")
|
||||
default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
|
||||
{
|
||||
return Channels.newChannel( openForAppend( path ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the
|
||||
* mount, and write operations should fail once it reaches zero.
|
||||
|
@@ -6,25 +6,19 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
|
||||
public class GuiPrintout extends GuiContainer
|
||||
{
|
||||
private static final ResourceLocation background = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
|
||||
|
||||
private static final int xSize = 172;
|
||||
private static final int ySize = 209;
|
||||
|
||||
private final boolean m_book;
|
||||
private final int m_pages;
|
||||
private final TextBuffer[] m_text;
|
||||
@@ -34,23 +28,18 @@ public class GuiPrintout extends GuiContainer
|
||||
public GuiPrintout( ContainerHeldItem container )
|
||||
{
|
||||
super( container );
|
||||
m_book = (ItemPrintout.getType( container.getStack() ) == ItemPrintout.Type.Book);
|
||||
|
||||
String[] text = ItemPrintout.getText( container.getStack() );
|
||||
m_text = new TextBuffer[ text.length ];
|
||||
for( int i=0; i<m_text.length; ++i )
|
||||
{
|
||||
m_text[i] = new TextBuffer( text[i] );
|
||||
}
|
||||
for( int i = 0; i < m_text.length; ++i ) m_text[ i ] = new TextBuffer( text[ i ] );
|
||||
|
||||
String[] colours = ItemPrintout.getColours( container.getStack() );
|
||||
m_colours = new TextBuffer[ colours.length ];
|
||||
for( int i=0; i<m_colours.length; ++i )
|
||||
{
|
||||
m_colours[i] = new TextBuffer( colours[i] );
|
||||
}
|
||||
for( int i = 0; i < m_colours.length; ++i ) m_colours[ i ] = new TextBuffer( colours[ i ] );
|
||||
|
||||
m_pages = Math.max( m_text.length / ItemPrintout.LINES_PER_PAGE, 1 );
|
||||
m_page = 0;
|
||||
m_pages = Math.max( m_text.length / ItemPrintout.LINES_PER_PAGE, 1 );
|
||||
m_book = ItemPrintout.getType( container.getStack() ) == ItemPrintout.Type.Book;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,25 +67,19 @@ public class GuiPrintout extends GuiContainer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char c, int k) throws IOException
|
||||
protected void keyTyped( char c, int k ) throws IOException
|
||||
{
|
||||
super.keyTyped( c, k );
|
||||
|
||||
if( k == 205 )
|
||||
{
|
||||
// Right
|
||||
if( m_page < m_pages - 1 )
|
||||
{
|
||||
m_page = m_page + 1;
|
||||
}
|
||||
if( m_page < m_pages - 1 ) m_page++;
|
||||
}
|
||||
else if( k == 203 )
|
||||
{
|
||||
// Left
|
||||
if( m_page > 0 )
|
||||
{
|
||||
m_page = m_page - 1;
|
||||
}
|
||||
// Left
|
||||
if( m_page > 0 ) m_page--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,21 +89,15 @@ public class GuiPrintout extends GuiContainer
|
||||
super.handleMouseInput();
|
||||
|
||||
int mouseWheelChange = Mouse.getEventDWheel();
|
||||
if (mouseWheelChange < 0)
|
||||
if( mouseWheelChange < 0 )
|
||||
{
|
||||
// Up
|
||||
if( m_page < m_pages - 1 )
|
||||
{
|
||||
m_page = m_page + 1;
|
||||
}
|
||||
if( m_page < m_pages - 1 ) m_page++;
|
||||
}
|
||||
else if (mouseWheelChange > 0)
|
||||
else if( mouseWheelChange > 0 )
|
||||
{
|
||||
// Down
|
||||
if( m_page > 0 )
|
||||
{
|
||||
m_page = m_page - 1;
|
||||
}
|
||||
if( m_page > 0 ) m_page--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,78 +112,20 @@ public class GuiPrintout extends GuiContainer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float f)
|
||||
public void drawScreen( int mouseX, int mouseY, float f )
|
||||
{
|
||||
// Draw background
|
||||
zLevel = zLevel - 1;
|
||||
drawDefaultBackground();
|
||||
|
||||
zLevel = zLevel + 1;
|
||||
|
||||
// Draw the printout
|
||||
GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
this.mc.getTextureManager().bindTexture( background );
|
||||
|
||||
int startY = (height - ySize) / 2;
|
||||
//int startX = (width - xSize) / 2 - (m_page * 8);
|
||||
int startX = (width - (xSize + (m_pages - 1)*8)) / 2;
|
||||
|
||||
if( m_book )
|
||||
{
|
||||
// Border
|
||||
drawTexturedModalRect( startX - 8, startY - 8, xSize + 48, 0, 12, ySize + 24);
|
||||
drawTexturedModalRect( startX + xSize + (m_pages - 1)*8 - 4, startY - 8, xSize + 48 + 12, 0, 12, ySize + 24);
|
||||
|
||||
drawTexturedModalRect( startX, startY - 8, 0, ySize, xSize, 12);
|
||||
drawTexturedModalRect( startX, startY + ySize - 4, 0, ySize + 12, xSize, 12);
|
||||
for( int n=1; n<m_pages; ++n )
|
||||
{
|
||||
drawTexturedModalRect( startX + xSize + (n-1)*8, startY - 8, 0, ySize, 8, 12);
|
||||
drawTexturedModalRect( startX + xSize + (n-1)*8, startY + ySize - 4, 0, ySize + 12, 8, 12);
|
||||
}
|
||||
}
|
||||
|
||||
// Left half
|
||||
if( m_page == 0 )
|
||||
{
|
||||
drawTexturedModalRect( startX, startY, 24, 0, xSize / 2, ySize);
|
||||
drawTexturedModalRect( startX, startY, 0, 0, 12, ySize);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawTexturedModalRect( startX, startY, 0, 0, 12, ySize);
|
||||
for( int n=1; n<m_page; ++n )
|
||||
{
|
||||
drawTexturedModalRect( startX + n*8, startY, 12, 0, 12, ySize);
|
||||
}
|
||||
drawTexturedModalRect( startX + m_page*8, startY, 24, 0, xSize / 2, ySize);
|
||||
}
|
||||
|
||||
// Right half
|
||||
if( m_page == (m_pages - 1) )
|
||||
{
|
||||
drawTexturedModalRect( startX + m_page*8 + xSize/2, startY, 24 + xSize / 2, 0, xSize / 2, ySize);
|
||||
drawTexturedModalRect( startX + m_page*8 + (xSize - 12), startY, 24 + xSize + 12, 0, 12, ySize);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawTexturedModalRect( startX + (m_pages - 1)*8 + (xSize - 12), startY, 24 + xSize + 12, 0, 12, ySize);
|
||||
for( int n=m_pages-2; n>=m_page; --n )
|
||||
{
|
||||
drawTexturedModalRect( startX + n*8 + (xSize - 12), startY, 24 + xSize, 0, 12, ySize);
|
||||
}
|
||||
drawTexturedModalRect( startX + m_page*8 + xSize/2, startY, 24 + xSize / 2, 0, xSize / 2, ySize);
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer)ComputerCraft.getFixedWidthFontRenderer();
|
||||
int x = startX + m_page * 8 + 13;
|
||||
int y = startY + 11;
|
||||
for( int line=0; line<ItemPrintout.LINES_PER_PAGE; ++line )
|
||||
{
|
||||
int lineIdx = ItemPrintout.LINES_PER_PAGE * m_page + line;
|
||||
if( lineIdx >= 0 && lineIdx < m_text.length )
|
||||
{
|
||||
fontRenderer.drawString( m_text[lineIdx], x, y, m_colours[lineIdx], null, 0, 0, false, Palette.DEFAULT );
|
||||
}
|
||||
y = y + FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
}
|
||||
int startY = (height - Y_SIZE) / 2;
|
||||
int startX = (width - X_SIZE) / 2;
|
||||
|
||||
drawBorder( startX, startY, zLevel, m_page, m_pages, m_book );
|
||||
drawText( startX + X_TEXT_MARGIN, startY + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ package dan200.computercraft.client.proxy;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.ItemPocketRenderer;
|
||||
import dan200.computercraft.client.render.ItemPrintoutRenderer;
|
||||
import dan200.computercraft.client.render.RenderOverlayCable;
|
||||
import dan200.computercraft.client.render.TileEntityCableRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
@@ -497,6 +498,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
MinecraftForge.EVENT_BUS.register( handlers );
|
||||
MinecraftForge.EVENT_BUS.register( new RenderOverlayCable() );
|
||||
MinecraftForge.EVENT_BUS.register( new ItemPocketRenderer() );
|
||||
MinecraftForge.EVENT_BUS.register( new ItemPrintoutRenderer() );
|
||||
}
|
||||
|
||||
public class ForgeHandlers
|
||||
|
@@ -0,0 +1,197 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.event.RenderItemInFrameEvent;
|
||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
|
||||
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
|
||||
|
||||
public class ItemPrintoutRenderer
|
||||
{
|
||||
@SubscribeEvent
|
||||
public void onRenderInHand( RenderSpecificHandEvent event )
|
||||
{
|
||||
ItemStack stack = event.getItemStack();
|
||||
if( stack.getItem() != ComputerCraft.Items.printout ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
if( event.getHand() == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
|
||||
{
|
||||
renderPrintoutFirstPersonCentre(
|
||||
event.getInterpolatedPitch(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderPrintoutFirstPersonSide(
|
||||
event.getHand() == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a pocket computer to one side of the player.
|
||||
*
|
||||
* @param side The side to render on
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack)
|
||||
*/
|
||||
private void renderPrintoutFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
float offset = side == EnumHandSide.RIGHT ? 1f : -1f;
|
||||
GlStateManager.translate( offset * 0.125f, -0.125f, 0f );
|
||||
|
||||
// If the player is not invisible then render a single arm
|
||||
if( !minecraft.player.isInvisible() )
|
||||
{
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.rotate( offset * 10f, 0f, 0f, 1f );
|
||||
minecraft.getItemRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
|
||||
float f1 = MathHelper.sqrt( swingProgress );
|
||||
float f2 = MathHelper.sin( f1 * (float) Math.PI );
|
||||
float f3 = -0.5f * f2;
|
||||
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
|
||||
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
GlStateManager.translate( offset * f3, f4 - 0.3f * f2, f5 );
|
||||
GlStateManager.rotate( f2 * -45f, 1f, 0f, 0f );
|
||||
GlStateManager.rotate( offset * f2 * -30f, 0f, 1f, 0f );
|
||||
|
||||
renderPrintoutFirstPerson( stack );
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an item in the middle of the screen
|
||||
*
|
||||
* @param pitch The pitch of the player
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPerson(float, float, float)
|
||||
*/
|
||||
private void renderPrintoutFirstPersonCentre( float pitch, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
ItemRenderer itemRenderer = Minecraft.getMinecraft().getItemRenderer();
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
float swingRt = MathHelper.sqrt( swingProgress );
|
||||
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.translate( 0f, -tX / 2f, tZ );
|
||||
float pitchAngle = itemRenderer.getMapAngleFromPitch( pitch );
|
||||
GlStateManager.translate( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
|
||||
GlStateManager.rotate( pitchAngle * -85f, 1f, 0f, 0f );
|
||||
itemRenderer.renderArms();
|
||||
float rX = MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.rotate( rX * 20f, 1f, 0f, 0f );
|
||||
GlStateManager.scale( 2f, 2f, 2f );
|
||||
|
||||
renderPrintoutFirstPerson( stack );
|
||||
}
|
||||
|
||||
|
||||
private static void renderPrintoutFirstPerson( ItemStack stack )
|
||||
{
|
||||
// Setup various transformations. Note that these are partially adapated from the corresponding method
|
||||
// in ItemRenderer.renderMapFirstPerson
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
GlStateManager.rotate( 180f, 0f, 1f, 0f );
|
||||
GlStateManager.rotate( 180f, 0f, 0f, 1f );
|
||||
GlStateManager.scale( 0.42f, 0.42f, -0.42f );
|
||||
GlStateManager.translate( -0.5f, -0.48f, 0.0f );
|
||||
|
||||
drawPrintout( stack );
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderInFrame( RenderItemInFrameEvent event )
|
||||
{
|
||||
ItemStack stack = event.getItem();
|
||||
if( stack.getItem() != ComputerCraft.Items.printout ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
// Move a little bit forward to ensure we're not clipping with the frame
|
||||
GlStateManager.translate( 0.0f, 0.0f, -0.001f );
|
||||
GlStateManager.rotate( 180f, 0f, 0f, 1f );
|
||||
GlStateManager.scale( 0.95f, 0.95f, -0.95f );
|
||||
GlStateManager.translate( -0.5f, -0.5f, 0.0f );
|
||||
|
||||
drawPrintout( stack );
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
private static void drawPrintout( ItemStack stack )
|
||||
{
|
||||
int pages = ItemPrintout.getPageCount( stack );
|
||||
boolean book = ItemPrintout.getType( stack ) == ItemPrintout.Type.Book;
|
||||
|
||||
double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
||||
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
||||
|
||||
// Non-books will be left aligned
|
||||
if( !book ) width += offsetAt( pages );
|
||||
|
||||
double visualWidth = width, visualHeight = height;
|
||||
|
||||
// Meanwhile books will be centred
|
||||
if( book )
|
||||
{
|
||||
visualWidth += 2 * COVER_SIZE + 2 * offsetAt( pages );
|
||||
visualHeight += 2 * COVER_SIZE;
|
||||
}
|
||||
|
||||
double max = Math.max( visualHeight, visualWidth );
|
||||
|
||||
// Scale the printout to fit correctly.
|
||||
double scale = 1.0 / max;
|
||||
GlStateManager.scale( scale, scale, scale );
|
||||
GlStateManager.translate( (max - width) / 2.0f, (max - height) / 2.0f, 0.0f );
|
||||
|
||||
drawBorder( 0, 0, -0.01, 0, pages, book );
|
||||
drawText( X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) );
|
||||
}
|
||||
}
|
@@ -0,0 +1,169 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
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.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
|
||||
|
||||
public class PrintoutRenderer
|
||||
{
|
||||
private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
|
||||
private static final double BG_SIZE = 256.0;
|
||||
|
||||
/**
|
||||
* Width of a page
|
||||
*/
|
||||
public static final int X_SIZE = 172;
|
||||
|
||||
/**
|
||||
* Height of a page
|
||||
*/
|
||||
public static final int Y_SIZE = 209;
|
||||
|
||||
/**
|
||||
* Padding between the left and right of a page and the text
|
||||
*/
|
||||
public static final int X_TEXT_MARGIN = 13;
|
||||
|
||||
/**
|
||||
* Padding between the top and bottom of a page and the text
|
||||
*/
|
||||
public static final int Y_TEXT_MARGIN = 11;
|
||||
|
||||
/**
|
||||
* Width of the extra page texture
|
||||
*/
|
||||
private static final int X_FOLD_SIZE = 12;
|
||||
|
||||
/**
|
||||
* Size of the leather cover
|
||||
*/
|
||||
public static final int COVER_SIZE = 12;
|
||||
|
||||
private static final int COVER_Y = Y_SIZE;
|
||||
private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE;
|
||||
|
||||
public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
|
||||
{
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
|
||||
for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line )
|
||||
{
|
||||
fontRenderer.drawString( text[ start + line ], x, y + line * FONT_HEIGHT, colours[ start + line ], null, 0, 0, false, Palette.DEFAULT );
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawText( int x, int y, int start, String[] text, String[] colours )
|
||||
{
|
||||
GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.enableTexture2D();
|
||||
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
|
||||
for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line )
|
||||
{
|
||||
fontRenderer.drawString( new TextBuffer( text[ start + line ] ), x, y + line * FONT_HEIGHT, new TextBuffer( colours[ start + line ] ), null, 0, 0, false, Palette.DEFAULT );
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook )
|
||||
{
|
||||
GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.enableTexture2D();
|
||||
|
||||
Minecraft.getMinecraft().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;
|
||||
|
||||
if( isBook )
|
||||
{
|
||||
// Border
|
||||
double offset = offsetAt( pages );
|
||||
final double left = x - 4 - offset;
|
||||
final double 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 );
|
||||
|
||||
// Draw centre panel (just stretched texture, sorry).
|
||||
drawTexture( buffer,
|
||||
x - offset, y, z - 0.02, X_SIZE + offset * 2, Y_SIZE,
|
||||
COVER_X + COVER_SIZE / 2, COVER_SIZE, COVER_SIZE, Y_SIZE
|
||||
);
|
||||
|
||||
double 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 );
|
||||
borderX += thisWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Left half
|
||||
drawTexture( buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2, Y_SIZE );
|
||||
for( int n = 0; n <= leftPages; n++ )
|
||||
{
|
||||
drawTexture( buffer,
|
||||
x - offsetAt( n ), y, z - 1e-3 * n,
|
||||
// Use the left "bold" fold for the outermost page
|
||||
n == leftPages ? 0 : X_FOLD_SIZE, 0,
|
||||
X_FOLD_SIZE, Y_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
// Right half
|
||||
drawTexture( buffer, x + X_SIZE / 2, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2, 0, X_SIZE / 2, Y_SIZE );
|
||||
for( int n = 0; n <= rightPages; n++ )
|
||||
{
|
||||
drawTexture( buffer,
|
||||
x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3 * 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 )
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double width, double height, double u, double v, double tWidth, double 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();
|
||||
}
|
||||
|
||||
public static double offsetAt( int page )
|
||||
{
|
||||
return 32 * (1 - Math.pow( 1.2, -page ));
|
||||
}
|
||||
}
|
@@ -71,7 +71,7 @@ public class TileEntityCableRenderer extends TileEntitySpecialRenderer<TileCable
|
||||
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
|
||||
buffer.noColor();
|
||||
|
||||
ForgeHooksClient.setRenderLayer( block.getBlockLayer() );
|
||||
ForgeHooksClient.setRenderLayer( block.getRenderLayer() );
|
||||
|
||||
// See BlockRendererDispatcher#renderBlockDamage
|
||||
TextureAtlasSprite breakingTexture = mc.getTextureMapBlocks().getAtlasSprite( "minecraft:blocks/destroy_stage_" + destroyStage );
|
||||
|
@@ -3,14 +3,17 @@ package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerOwned;
|
||||
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.Set;
|
||||
|
||||
public abstract class ComputerAccess implements IComputerAccess
|
||||
public abstract class ComputerAccess implements IComputerAccess, IComputerOwned
|
||||
{
|
||||
private final IAPIEnvironment m_environment;
|
||||
private final Set<String> m_mounts = new HashSet<>();
|
||||
@@ -133,6 +136,13 @@ public abstract class ComputerAccess implements IComputerAccess
|
||||
m_environment.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Computer getComputer()
|
||||
{
|
||||
return m_environment.getComputer();
|
||||
}
|
||||
|
||||
private String findFreeLocation( String desiredLoc )
|
||||
{
|
||||
try
|
||||
|
@@ -9,19 +9,23 @@ package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.BinaryOutputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedOutputHandle;
|
||||
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.BinaryWritableHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedWritableHandle;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.filesystem.FileSystemWrapper;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
|
||||
@@ -221,38 +225,38 @@ public class FSAPI implements ILuaAPI
|
||||
case "r":
|
||||
{
|
||||
// Open the file for reading, then create a wrapper around the reader
|
||||
InputStream reader = m_fileSystem.openForRead( path );
|
||||
return new Object[] { new EncodedInputHandle( reader ) };
|
||||
FileSystemWrapper<BufferedReader> reader = m_fileSystem.openForRead( path, EncodedReadableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedReadableHandle( reader.get(), reader ) };
|
||||
}
|
||||
case "w":
|
||||
{
|
||||
// Open the file for writing, then create a wrapper around the writer
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, false );
|
||||
return new Object[] { new EncodedOutputHandle( writer ) };
|
||||
FileSystemWrapper<BufferedWriter> writer = m_fileSystem.openForWrite( path, false, EncodedWritableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "a":
|
||||
{
|
||||
// Open the file for appending, then create a wrapper around the writer
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, true );
|
||||
return new Object[] { new EncodedOutputHandle( writer ) };
|
||||
FileSystemWrapper<BufferedWriter> writer = m_fileSystem.openForWrite( path, true, EncodedWritableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "rb":
|
||||
{
|
||||
// Open the file for binary reading, then create a wrapper around the reader
|
||||
InputStream reader = m_fileSystem.openForRead( path );
|
||||
return new Object[] { new BinaryInputHandle( reader ) };
|
||||
FileSystemWrapper<ReadableByteChannel> reader = m_fileSystem.openForRead( path, Function.identity() );
|
||||
return new Object[] { new BinaryReadableHandle( reader.get(), reader ) };
|
||||
}
|
||||
case "wb":
|
||||
{
|
||||
// Open the file for binary writing, then create a wrapper around the writer
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, false );
|
||||
return new Object[] { new BinaryOutputHandle( writer ) };
|
||||
FileSystemWrapper<WritableByteChannel> writer = m_fileSystem.openForWrite( path, false, Function.identity() );
|
||||
return new Object[] { new BinaryWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "ab":
|
||||
{
|
||||
// Open the file for binary appending, then create a wrapper around the reader
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, true );
|
||||
return new Object[] { new BinaryOutputHandle( writer ) };
|
||||
FileSystemWrapper<WritableByteChannel> writer = m_fileSystem.openForWrite( path, true, Function.identity() );
|
||||
return new Object[] { new BinaryWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported mode" );
|
||||
|
@@ -9,17 +9,19 @@ package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
import dan200.computercraft.core.computer.IComputerOwned;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
public interface IAPIEnvironment
|
||||
public interface IAPIEnvironment extends IComputerOwned
|
||||
{
|
||||
interface IPeripheralChangeListener
|
||||
{
|
||||
void onPeripheralChanged( int side, IPeripheral newPeripheral );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
Computer getComputer();
|
||||
int getComputerID();
|
||||
IComputerEnvironment getComputerEnvironment();
|
||||
|
@@ -64,7 +64,8 @@ public class TermAPI implements ILuaAPI
|
||||
"setPaletteColour",
|
||||
"setPaletteColor",
|
||||
"getPaletteColour",
|
||||
"getPaletteColor"
|
||||
"getPaletteColor",
|
||||
"getCursorBlink",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -284,6 +285,9 @@ public class TermAPI implements ILuaAPI
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 23:
|
||||
// getCursorBlink
|
||||
return new Object[] { m_terminal.getCursorBlink() };
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
|
@@ -0,0 +1,91 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.NonWritableChannelException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
|
||||
/**
|
||||
* A seekable, readable byte channel which is backed by a simple byte array.
|
||||
*/
|
||||
public class ArrayByteChannel implements SeekableByteChannel
|
||||
{
|
||||
private boolean closed = false;
|
||||
private int position = 0;
|
||||
|
||||
private final byte[] backing;
|
||||
|
||||
public ArrayByteChannel( byte[] backing )
|
||||
{
|
||||
this.backing = backing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read( ByteBuffer destination ) throws IOException
|
||||
{
|
||||
if( closed ) throw new ClosedChannelException();
|
||||
Preconditions.checkNotNull( destination, "destination" );
|
||||
|
||||
if( position >= backing.length ) return -1;
|
||||
|
||||
int remaining = Math.min( backing.length - position, destination.remaining() );
|
||||
destination.put( backing, position, remaining );
|
||||
position += remaining;
|
||||
return remaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write( ByteBuffer src ) throws IOException
|
||||
{
|
||||
if( closed ) throw new ClosedChannelException();
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException
|
||||
{
|
||||
if( closed ) throw new ClosedChannelException();
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position( long newPosition ) throws IOException
|
||||
{
|
||||
if( closed ) throw new ClosedChannelException();
|
||||
if( newPosition < 0 || newPosition > Integer.MAX_VALUE )
|
||||
{
|
||||
throw new IllegalArgumentException( "Position out of bounds" );
|
||||
}
|
||||
position = (int) newPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException
|
||||
{
|
||||
if( closed ) throw new ClosedChannelException();
|
||||
return backing.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate( long size ) throws IOException
|
||||
{
|
||||
if( closed ) throw new ClosedChannelException();
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return !closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
closed = true;
|
||||
}
|
||||
}
|
@@ -1,122 +0,0 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
||||
|
||||
public class BinaryInputHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final InputStream m_stream;
|
||||
|
||||
public BinaryInputHandle( InputStream reader )
|
||||
{
|
||||
super( reader );
|
||||
this.m_stream = reader;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
"read",
|
||||
"readAll",
|
||||
"close",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
// read
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
if( args.length > 0 && args[ 0 ] != null )
|
||||
{
|
||||
int count = getInt( args, 0 );
|
||||
if( count < 0 )
|
||||
{
|
||||
// Whilst this may seem absurd to allow reading 0 bytes, PUC Lua it so
|
||||
// it seems best to remain somewhat consistent.
|
||||
throw new LuaException( "Cannot read a negative number of bytes" );
|
||||
}
|
||||
else if( count <= BUFFER_SIZE )
|
||||
{
|
||||
// If we've got a small count, then allocate that and read it.
|
||||
byte[] bytes = new byte[ count ];
|
||||
int read = m_stream.read( bytes );
|
||||
|
||||
if( read < 0 ) return null;
|
||||
if( read < count ) bytes = Arrays.copyOf( bytes, read );
|
||||
return new Object[] { bytes };
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] buffer = new byte[ BUFFER_SIZE ];
|
||||
|
||||
// Read the initial set of bytes, failing if none are read.
|
||||
int read = m_stream.read( buffer, 0, Math.min( buffer.length, count ) );
|
||||
if( read == -1 ) return null;
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream( read );
|
||||
count -= read;
|
||||
out.write( buffer, 0, read );
|
||||
|
||||
// Otherwise read until we either reach the limit or we no longer consume
|
||||
// the full buffer.
|
||||
while( read >= buffer.length && count > 0 )
|
||||
{
|
||||
read = m_stream.read( buffer, 0, Math.min( BUFFER_SIZE, count ) );
|
||||
if( read == -1 ) break;
|
||||
count -= read;
|
||||
out.write( buffer, 0, read );
|
||||
}
|
||||
|
||||
return new Object[] { out.toByteArray() };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int b = m_stream.read();
|
||||
return b == -1 ? null : new Object[] { b };
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 1:
|
||||
// readAll
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
byte[] out = ByteStreams.toByteArray( m_stream );
|
||||
return out == null ? null : new Object[] { out };
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 2:
|
||||
//close
|
||||
close();
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
||||
|
||||
public class BinaryReadableHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private static final String[] METHOD_NAMES = new String[] { "read", "readAll", "readLine", "close" };
|
||||
private static final String[] METHOD_SEEK_NAMES = ObjectArrays.concat( METHOD_NAMES, new String[] { "seek" }, String.class );
|
||||
|
||||
private final ReadableByteChannel m_reader;
|
||||
private final SeekableByteChannel m_seekable;
|
||||
private final ByteBuffer single = ByteBuffer.allocate( 1 );
|
||||
|
||||
public BinaryReadableHandle( ReadableByteChannel channel, Closeable closeable )
|
||||
{
|
||||
super( closeable );
|
||||
this.m_reader = channel;
|
||||
this.m_seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
|
||||
}
|
||||
|
||||
public BinaryReadableHandle( ReadableByteChannel channel )
|
||||
{
|
||||
this( channel, channel );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return m_seekable == null ? METHOD_NAMES : METHOD_SEEK_NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
// read
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
if( args.length > 0 && args[ 0 ] != null )
|
||||
{
|
||||
int count = getInt( args, 0 );
|
||||
if( count < 0 )
|
||||
{
|
||||
throw new LuaException( "Cannot read a negative number of bytes" );
|
||||
}
|
||||
else if( count == 0 && m_seekable != null )
|
||||
{
|
||||
return m_seekable.position() >= m_seekable.size() ? null : new Object[] { "" };
|
||||
}
|
||||
|
||||
if( count <= BUFFER_SIZE )
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocate( count );
|
||||
|
||||
int read = m_reader.read( buffer );
|
||||
if( read < 0 ) return null;
|
||||
return new Object[] { read < count ? Arrays.copyOf( buffer.array(), read ) : buffer.array() };
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocate( BUFFER_SIZE );
|
||||
|
||||
int read = m_reader.read( buffer );
|
||||
if( read < 0 ) return null;
|
||||
int totalRead = read;
|
||||
|
||||
// If we failed to read "enough" here, let's just abort
|
||||
if( totalRead >= count || read < BUFFER_SIZE )
|
||||
{
|
||||
return new Object[] { Arrays.copyOf( buffer.array(), read ) };
|
||||
}
|
||||
|
||||
// Build up an array of ByteBuffers. Hopefully this means we can perform less allocation
|
||||
// than doubling up the buffer each time.
|
||||
List<ByteBuffer> parts = new ArrayList<>( 4 );
|
||||
parts.add( buffer );
|
||||
while( totalRead < count && read >= BUFFER_SIZE )
|
||||
{
|
||||
buffer = ByteBuffer.allocate( BUFFER_SIZE );
|
||||
totalRead += read = m_reader.read( buffer );
|
||||
parts.add( buffer );
|
||||
}
|
||||
|
||||
// Now just copy all the bytes across!
|
||||
byte[] bytes = new byte[ totalRead ];
|
||||
int pos = 0;
|
||||
for( ByteBuffer part : parts )
|
||||
{
|
||||
System.arraycopy( part.array(), 0, bytes, pos, part.position() );
|
||||
pos += part.position();
|
||||
}
|
||||
return new Object[] { bytes };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
single.clear();
|
||||
int b = m_reader.read( single );
|
||||
return b == -1 ? null : new Object[] { single.get( 0 ) };
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 1:
|
||||
// readAll
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
int expected = 32;
|
||||
if( m_seekable != null )
|
||||
{
|
||||
expected = Math.max( expected, (int) (m_seekable.size() - m_seekable.position()) );
|
||||
}
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream( expected );
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocate( 8192 );
|
||||
boolean readAnything = false;
|
||||
while( true )
|
||||
{
|
||||
buf.clear();
|
||||
int r = m_reader.read( buf );
|
||||
if( r == -1 ) break;
|
||||
|
||||
readAnything = true;
|
||||
stream.write( buf.array(), 0, r );
|
||||
}
|
||||
return readAnything ? new Object[] { stream.toByteArray() } : null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// readLine
|
||||
checkOpen();
|
||||
boolean withTrailing = optBoolean( args, 0, false );
|
||||
try
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
boolean readAnything = false;
|
||||
while( true )
|
||||
{
|
||||
single.clear();
|
||||
int r = m_reader.read( single );
|
||||
if( r == -1 ) break;
|
||||
|
||||
readAnything = true;
|
||||
byte b = single.get( 0 );
|
||||
if( b == '\n' )
|
||||
{
|
||||
if( withTrailing ) stream.write( b );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.write( b );
|
||||
}
|
||||
}
|
||||
|
||||
return readAnything ? new Object[] { stream.toByteArray() } : null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
//close
|
||||
close();
|
||||
return null;
|
||||
case 4:
|
||||
// seek
|
||||
checkOpen();
|
||||
return handleSeek( m_seekable, args );
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,33 +1,45 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.ArgumentHelper;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
|
||||
public class BinaryOutputHandle extends HandleGeneric
|
||||
public class BinaryWritableHandle extends HandleGeneric
|
||||
{
|
||||
private final OutputStream m_writer;
|
||||
private static final String[] METHOD_NAMES = new String[] { "write", "flush", "close" };
|
||||
private static final String[] METHOD_SEEK_NAMES = ObjectArrays.concat( METHOD_NAMES, new String[] { "seek" }, String.class );
|
||||
|
||||
public BinaryOutputHandle( OutputStream writer )
|
||||
private final WritableByteChannel m_writer;
|
||||
private final SeekableByteChannel m_seekable;
|
||||
private final ByteBuffer single = ByteBuffer.allocate( 1 );
|
||||
|
||||
public BinaryWritableHandle( WritableByteChannel channel, Closeable closeable )
|
||||
{
|
||||
super( writer );
|
||||
this.m_writer = writer;
|
||||
super( closeable );
|
||||
this.m_writer = channel;
|
||||
this.m_seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
|
||||
}
|
||||
|
||||
public BinaryWritableHandle( WritableByteChannel channel )
|
||||
{
|
||||
this( channel, channel );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
"write",
|
||||
"flush",
|
||||
"close",
|
||||
};
|
||||
return m_seekable == null ? METHOD_NAMES : METHOD_SEEK_NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,12 +55,16 @@ public class BinaryOutputHandle extends HandleGeneric
|
||||
if( args.length > 0 && args[ 0 ] instanceof Number )
|
||||
{
|
||||
int number = ((Number) args[ 0 ]).intValue();
|
||||
m_writer.write( number );
|
||||
single.clear();
|
||||
single.put( (byte) number );
|
||||
single.flip();
|
||||
|
||||
m_writer.write( single );
|
||||
}
|
||||
else if( args.length > 0 && args[ 0 ] instanceof String )
|
||||
{
|
||||
String value = (String) args[ 0 ];
|
||||
m_writer.write( StringUtil.encodeString( value ) );
|
||||
m_writer.write( ByteBuffer.wrap( StringUtil.encodeString( value ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,7 +81,9 @@ public class BinaryOutputHandle extends HandleGeneric
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
m_writer.flush();
|
||||
// Technically this is not needed
|
||||
if( m_writer instanceof FileChannel ) ((FileChannel) m_writer).force( false );
|
||||
|
||||
return null;
|
||||
}
|
||||
catch( IOException e )
|
||||
@@ -76,6 +94,10 @@ public class BinaryOutputHandle extends HandleGeneric
|
||||
//close
|
||||
close();
|
||||
return null;
|
||||
case 3:
|
||||
// seek
|
||||
checkOpen();
|
||||
return handleSeek( m_seekable, args );
|
||||
default:
|
||||
return null;
|
||||
}
|
@@ -4,45 +4,32 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
|
||||
|
||||
public class EncodedInputHandle extends HandleGeneric
|
||||
public class EncodedReadableHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final BufferedReader m_reader;
|
||||
private BufferedReader m_reader;
|
||||
|
||||
public EncodedInputHandle( BufferedReader reader )
|
||||
public EncodedReadableHandle( @Nonnull BufferedReader reader, @Nonnull Closeable closable )
|
||||
{
|
||||
super( reader );
|
||||
super( closable );
|
||||
this.m_reader = reader;
|
||||
}
|
||||
|
||||
public EncodedInputHandle( InputStream stream )
|
||||
public EncodedReadableHandle( @Nonnull BufferedReader reader )
|
||||
{
|
||||
this( stream, "UTF-8" );
|
||||
}
|
||||
|
||||
public EncodedInputHandle( InputStream stream, String encoding )
|
||||
{
|
||||
this( makeReader( stream, encoding ) );
|
||||
}
|
||||
|
||||
private static BufferedReader makeReader( InputStream stream, String encoding )
|
||||
{
|
||||
if( encoding == null ) encoding = "UTF-8";
|
||||
InputStreamReader streamReader;
|
||||
try
|
||||
{
|
||||
streamReader = new InputStreamReader( stream, encoding );
|
||||
}
|
||||
catch( UnsupportedEncodingException e )
|
||||
{
|
||||
streamReader = new InputStreamReader( stream );
|
||||
}
|
||||
return new BufferedReader( streamReader );
|
||||
this( reader, reader );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -63,13 +50,17 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// readLine
|
||||
checkOpen();
|
||||
boolean withTrailing = optBoolean( args, 0, false );
|
||||
try
|
||||
{
|
||||
String line = m_reader.readLine();
|
||||
if( line != null )
|
||||
{
|
||||
// While this is technically inaccurate, it's better than nothing
|
||||
if( withTrailing ) line += "\n";
|
||||
return new Object[] { line };
|
||||
}
|
||||
else
|
||||
@@ -81,6 +72,7 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
// readAll
|
||||
checkOpen();
|
||||
@@ -108,7 +100,6 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
close();
|
||||
return null;
|
||||
case 3:
|
||||
// read
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
@@ -161,4 +152,14 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferedReader openUtf8( ReadableByteChannel channel )
|
||||
{
|
||||
return open( channel, StandardCharsets.UTF_8 );
|
||||
}
|
||||
|
||||
public static BufferedReader open( ReadableByteChannel channel, Charset charset )
|
||||
{
|
||||
return new BufferedReader( Channels.newReader( channel, charset.newDecoder(), -1 ) );
|
||||
}
|
||||
}
|
@@ -4,41 +4,27 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class EncodedOutputHandle extends HandleGeneric
|
||||
public class EncodedWritableHandle extends HandleGeneric
|
||||
{
|
||||
private final BufferedWriter m_writer;
|
||||
private BufferedWriter m_writer;
|
||||
|
||||
public EncodedOutputHandle( BufferedWriter writer )
|
||||
public EncodedWritableHandle( @Nonnull BufferedWriter writer, @Nonnull Closeable closable )
|
||||
{
|
||||
super( writer );
|
||||
super( closable );
|
||||
this.m_writer = writer;
|
||||
}
|
||||
|
||||
public EncodedOutputHandle( OutputStream stream )
|
||||
public EncodedWritableHandle( @Nonnull BufferedWriter writer )
|
||||
{
|
||||
this( stream, "UTF-8" );
|
||||
}
|
||||
|
||||
public EncodedOutputHandle( OutputStream stream, String encoding )
|
||||
{
|
||||
this( makeWriter( stream, encoding ) );
|
||||
}
|
||||
|
||||
private static BufferedWriter makeWriter( OutputStream stream, String encoding )
|
||||
{
|
||||
if( encoding == null ) encoding = "UTF-8";
|
||||
OutputStreamWriter streamWriter;
|
||||
try
|
||||
{
|
||||
streamWriter = new OutputStreamWriter( stream, encoding );
|
||||
}
|
||||
catch( UnsupportedEncodingException e )
|
||||
{
|
||||
streamWriter = new OutputStreamWriter( stream );
|
||||
}
|
||||
return new BufferedWriter( streamWriter );
|
||||
this( writer, writer );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -125,4 +111,15 @@ public class EncodedOutputHandle extends HandleGeneric
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static BufferedWriter openUtf8( WritableByteChannel channel )
|
||||
{
|
||||
return open( channel, StandardCharsets.UTF_8 );
|
||||
}
|
||||
|
||||
public static BufferedWriter open( WritableByteChannel channel, Charset charset )
|
||||
{
|
||||
return new BufferedWriter( Channels.newWriter( channel, charset.newEncoder(), -1 ) );
|
||||
}
|
||||
}
|
@@ -3,17 +3,22 @@ package dan200.computercraft.core.apis.handles;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optString;
|
||||
|
||||
public abstract class HandleGeneric implements ILuaObject
|
||||
{
|
||||
protected final Closeable m_closable;
|
||||
protected boolean m_open = true;
|
||||
private Closeable m_closable;
|
||||
private boolean m_open = true;
|
||||
|
||||
public HandleGeneric( Closeable m_closable )
|
||||
protected HandleGeneric( @Nonnull Closeable closable )
|
||||
{
|
||||
this.m_closable = m_closable;
|
||||
this.m_closable = closable;
|
||||
}
|
||||
|
||||
protected void checkOpen() throws LuaException
|
||||
@@ -21,7 +26,7 @@ public abstract class HandleGeneric implements ILuaObject
|
||||
if( !m_open ) throw new LuaException( "attempt to use a closed file" );
|
||||
}
|
||||
|
||||
protected void close()
|
||||
protected final void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -31,5 +36,48 @@ public abstract class HandleGeneric implements ILuaObject
|
||||
catch( IOException ignored )
|
||||
{
|
||||
}
|
||||
m_closable = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared implementation for various file handle types
|
||||
*
|
||||
* @param channel The channel to seek in
|
||||
* @param args The Lua arguments to process, like Lua's {@code file:seek}.
|
||||
* @return The new position of the file, or null if some error occured.
|
||||
* @throws LuaException If the arguments were invalid
|
||||
* @see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:seek">{@code file:seek} in the Lua manual.</a>
|
||||
*/
|
||||
protected static Object[] handleSeek( SeekableByteChannel channel, Object[] args ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
String whence = optString( args, 0, "cur" );
|
||||
long offset = optInt( args, 1, 0 );
|
||||
switch( whence )
|
||||
{
|
||||
case "set":
|
||||
channel.position( offset );
|
||||
break;
|
||||
case "cur":
|
||||
channel.position( channel.position() + offset );
|
||||
break;
|
||||
case "end":
|
||||
channel.position( channel.size() + offset );
|
||||
break;
|
||||
default:
|
||||
throw new LuaException( "bad argument #1 to 'seek' (invalid option '" + whence + "'" );
|
||||
}
|
||||
|
||||
return new Object[] { channel.position() };
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
return new Object[] { false, "Position is negative" };
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,13 +13,17 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
||||
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -182,6 +186,10 @@ public class HTTPRequest implements Runnable
|
||||
byte[] result = ByteStreams.toByteArray( is );
|
||||
is.close();
|
||||
|
||||
String encoding = connection.getContentEncoding();
|
||||
Charset charset = encoding != null && Charset.isSupported( encoding )
|
||||
? Charset.forName( encoding ) : StandardCharsets.UTF_8;
|
||||
|
||||
// We've got some sort of response, so let's build a resulting object.
|
||||
Joiner joiner = Joiner.on( ',' );
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
@@ -193,9 +201,11 @@ public class HTTPRequest implements Runnable
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD,
|
||||
getHeaderSize( connection.getHeaderFields() ) + result.length );
|
||||
|
||||
InputStream contents = new ByteArrayInputStream( result );
|
||||
SeekableByteChannel contents = new ArrayByteChannel( result );
|
||||
ILuaObject stream = wrapStream(
|
||||
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ),
|
||||
m_binary
|
||||
? new BinaryReadableHandle( contents )
|
||||
: new EncodedReadableHandle( EncodedReadableHandle.open( contents, charset ) ),
|
||||
connection.getResponseCode(), headers
|
||||
);
|
||||
|
||||
|
@@ -13,7 +13,9 @@ import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.apis.HTTPAPI;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
@@ -26,6 +28,8 @@ import javax.annotation.Nullable;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
||||
|
||||
public class WebsocketConnection extends SimpleChannelInboundHandler<Object> implements ILuaObject, Closeable
|
||||
{
|
||||
public static final String SUCCESS_EVENT = "websocket_success";
|
||||
@@ -174,8 +178,11 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
||||
{
|
||||
checkOpen();
|
||||
String text = arguments.length > 0 && arguments[0] != null ? arguments[0].toString() : "";
|
||||
boolean binary = optBoolean(arguments, 1, false);
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_OUTGOING, text.length() );
|
||||
channel.writeAndFlush( new TextWebSocketFrame( text ) );
|
||||
channel.writeAndFlush( binary
|
||||
? new BinaryWebSocketFrame( Unpooled.wrappedBuffer( StringUtil.encodeString( text ) ) )
|
||||
: new TextWebSocketFrame( text ) );
|
||||
return null;
|
||||
}
|
||||
case 2:
|
||||
|
@@ -0,0 +1,9 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IComputerOwned
|
||||
{
|
||||
@Nullable
|
||||
Computer getComputer();
|
||||
}
|
@@ -11,6 +11,7 @@ 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.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -114,6 +115,7 @@ public class ComboMount implements IMount
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
for( int i=m_parts.length-1; i>=0; --i )
|
||||
@@ -126,4 +128,19 @@ public class ComboMount implements IMount
|
||||
}
|
||||
throw new IOException( "/" + 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 IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
}
|
||||
|
@@ -9,34 +9,36 @@ package dan200.computercraft.core.filesystem;
|
||||
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;
|
||||
|
||||
public class EmptyMount implements IMount
|
||||
{
|
||||
{
|
||||
public EmptyMount()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// IMount implementation
|
||||
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
return path.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
return path.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getSize( @Nonnull String path )
|
||||
{
|
||||
@@ -45,8 +47,17 @@ public class EmptyMount implements IMount
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream openForRead( @Nonnull String path )
|
||||
@Deprecated
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
return null;
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
}
|
||||
|
@@ -10,64 +10,43 @@ import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
|
||||
public class FileMount implements IWritableMount
|
||||
{
|
||||
private static int MINIMUM_FILE_SIZE = 500;
|
||||
|
||||
private class CountingOutputStream extends OutputStream
|
||||
private static final int MINIMUM_FILE_SIZE = 500;
|
||||
|
||||
private class WritableCountingChannel implements WritableByteChannel
|
||||
{
|
||||
private OutputStream m_innerStream;
|
||||
private long m_ignoredBytesLeft;
|
||||
|
||||
public CountingOutputStream( OutputStream innerStream, long bytesToIgnore )
|
||||
|
||||
private final WritableByteChannel m_inner;
|
||||
long m_ignoredBytesLeft;
|
||||
|
||||
WritableCountingChannel( WritableByteChannel inner, long bytesToIgnore )
|
||||
{
|
||||
m_innerStream = innerStream;
|
||||
m_inner = inner;
|
||||
m_ignoredBytesLeft = bytesToIgnore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
m_innerStream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException
|
||||
{
|
||||
m_innerStream.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write( @Nonnull byte[] b ) throws IOException
|
||||
{
|
||||
count( b.length );
|
||||
m_innerStream.write( b );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write( @Nonnull byte[] b, int off, int len ) throws IOException
|
||||
{
|
||||
count( len );
|
||||
m_innerStream.write( b, off, len );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write( int b ) throws IOException
|
||||
public int write( @Nonnull ByteBuffer b ) throws IOException
|
||||
{
|
||||
count( 1 );
|
||||
m_innerStream.write( b );
|
||||
count( b.remaining() );
|
||||
return m_inner.write( b );
|
||||
}
|
||||
|
||||
private void count( long n ) throws IOException
|
||||
void count( long n ) throws IOException
|
||||
{
|
||||
m_ignoredBytesLeft -= n;
|
||||
if( m_ignoredBytesLeft < 0 )
|
||||
{
|
||||
long newBytes = -m_ignoredBytesLeft;
|
||||
m_ignoredBytesLeft = 0;
|
||||
|
||||
|
||||
long bytesLeft = m_capacity - m_usedSpace;
|
||||
if( newBytes > bytesLeft )
|
||||
{
|
||||
@@ -79,12 +58,79 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return m_inner.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
m_inner.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class SeekableCountingChannel extends WritableCountingChannel implements SeekableByteChannel
|
||||
{
|
||||
private final SeekableByteChannel m_inner;
|
||||
|
||||
SeekableCountingChannel( SeekableByteChannel inner, long bytesToIgnore )
|
||||
{
|
||||
super( inner, bytesToIgnore );
|
||||
this.m_inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position( long newPosition ) throws IOException
|
||||
{
|
||||
if( !isOpen() ) throw new ClosedChannelException();
|
||||
if( newPosition < 0 ) throw new IllegalArgumentException();
|
||||
|
||||
long delta = newPosition - m_inner.position();
|
||||
if( delta < 0 )
|
||||
{
|
||||
m_ignoredBytesLeft -= delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
count( delta );
|
||||
}
|
||||
|
||||
return m_inner.position( newPosition );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate( long size ) throws IOException
|
||||
{
|
||||
throw new IOException( "Not yet implemented" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read( ByteBuffer dst ) throws IOException
|
||||
{
|
||||
if( !m_inner.isOpen() ) throw new ClosedChannelException();
|
||||
throw new NonReadableChannelException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException
|
||||
{
|
||||
return m_inner.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException
|
||||
{
|
||||
return m_inner.size();
|
||||
}
|
||||
}
|
||||
|
||||
private File m_rootPath;
|
||||
private long m_capacity;
|
||||
private long m_usedSpace;
|
||||
|
||||
|
||||
public FileMount( File rootPath, long capacity )
|
||||
{
|
||||
m_rootPath = rootPath;
|
||||
@@ -93,7 +139,7 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
|
||||
// IMount implementation
|
||||
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
@@ -107,7 +153,7 @@ public class FileMount implements IWritableMount
|
||||
return file.exists();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
@@ -121,7 +167,7 @@ public class FileMount implements IWritableMount
|
||||
return file.exists() && file.isDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||
{
|
||||
@@ -150,7 +196,7 @@ public class FileMount implements IWritableMount
|
||||
{
|
||||
throw new IOException( "/" + path + ": Not a directory" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,9 +226,10 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
if( created() )
|
||||
@@ -193,11 +240,26 @@ public class FileMount implements IWritableMount
|
||||
return new FileInputStream( file );
|
||||
}
|
||||
}
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
if( created() )
|
||||
{
|
||||
File file = getRealPath( path );
|
||||
if( file.exists() && !file.isDirectory() )
|
||||
{
|
||||
return FileChannel.open( file.toPath(), StandardOpenOption.READ );
|
||||
}
|
||||
}
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
|
||||
// IWritableMount implementation
|
||||
|
||||
|
||||
@Override
|
||||
public void makeDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
@@ -224,7 +286,7 @@ public class FileMount implements IWritableMount
|
||||
{
|
||||
throw new IOException( "/" + path + ": Out of space" );
|
||||
}
|
||||
|
||||
|
||||
boolean success = file.mkdirs();
|
||||
if( success )
|
||||
{
|
||||
@@ -236,7 +298,7 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void delete( @Nonnull String path ) throws IOException
|
||||
{
|
||||
@@ -244,7 +306,7 @@ public class FileMount implements IWritableMount
|
||||
{
|
||||
throw new IOException( "/" + path + ": Access denied" );
|
||||
}
|
||||
|
||||
|
||||
if( created() )
|
||||
{
|
||||
File file = getRealPath( path );
|
||||
@@ -254,7 +316,7 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void deleteRecursively( File file ) throws IOException
|
||||
{
|
||||
// Empty directories first
|
||||
@@ -266,7 +328,7 @@ public class FileMount implements IWritableMount
|
||||
deleteRecursively( new File( file, aChildren ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Then delete
|
||||
long fileSize = file.isDirectory() ? 0 : file.length();
|
||||
boolean success = file.delete();
|
||||
@@ -279,10 +341,26 @@ public class FileMount implements IWritableMount
|
||||
throw new IOException( "Access denied" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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
|
||||
{
|
||||
create();
|
||||
File file = getRealPath( path );
|
||||
@@ -308,13 +386,14 @@ public class FileMount implements IWritableMount
|
||||
m_usedSpace -= Math.max( file.length(), MINIMUM_FILE_SIZE );
|
||||
m_usedSpace += MINIMUM_FILE_SIZE;
|
||||
}
|
||||
return new CountingOutputStream( new FileOutputStream( file, false ), MINIMUM_FILE_SIZE );
|
||||
return new SeekableCountingChannel( Files.newByteChannel( file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE ),
|
||||
MINIMUM_FILE_SIZE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream openForAppend( @Nonnull String path ) throws IOException
|
||||
public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
|
||||
{
|
||||
if( created() )
|
||||
{
|
||||
@@ -329,7 +408,9 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CountingOutputStream( new FileOutputStream( file, true ), Math.max( MINIMUM_FILE_SIZE - file.length(), 0 ) );
|
||||
// Allowing seeking when appending is not recommended, so we use a separate channel.
|
||||
return new WritableCountingChannel( Files.newByteChannel( file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND ),
|
||||
Math.max( MINIMUM_FILE_SIZE - file.length(), 0 ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -337,23 +418,23 @@ public class FileMount implements IWritableMount
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getRemainingSpace()
|
||||
{
|
||||
return Math.max( m_capacity - m_usedSpace, 0 );
|
||||
}
|
||||
|
||||
|
||||
public File getRealPath( String path )
|
||||
{
|
||||
return new File( m_rootPath, path );
|
||||
}
|
||||
|
||||
|
||||
private boolean created()
|
||||
{
|
||||
return m_rootPath.exists();
|
||||
}
|
||||
|
||||
|
||||
private void create() throws IOException
|
||||
{
|
||||
if( !m_rootPath.exists() )
|
||||
@@ -365,7 +446,7 @@ public class FileMount implements IWritableMount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private long measureUsedSpace( File file )
|
||||
{
|
||||
if( !file.exists() )
|
||||
|
@@ -6,13 +6,23 @@
|
||||
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
|
||||
import java.io.*;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileSystem
|
||||
@@ -146,14 +156,14 @@ public class FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream openForRead( String path ) throws FileSystemException
|
||||
public ReadableByteChannel openForRead( String path ) throws FileSystemException
|
||||
{
|
||||
path = toLocal( path );
|
||||
try
|
||||
{
|
||||
if( m_mount.exists( path ) && !m_mount.isDirectory( path ) )
|
||||
{
|
||||
return m_mount.openForRead( path );
|
||||
return m_mount.openChannelForRead( path );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -209,13 +219,17 @@ public class FileSystem
|
||||
m_writableMount.delete( path );
|
||||
}
|
||||
}
|
||||
catch( AccessDeniedException e )
|
||||
{
|
||||
throw new FileSystemException( "Access denied" );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new FileSystemException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream openForWrite( String path ) throws FileSystemException
|
||||
public WritableByteChannel openForWrite( String path ) throws FileSystemException
|
||||
{
|
||||
if( m_writableMount == null )
|
||||
{
|
||||
@@ -238,16 +252,20 @@ public class FileSystem
|
||||
m_writableMount.makeDirectory( dir );
|
||||
}
|
||||
}
|
||||
return m_writableMount.openForWrite( path );
|
||||
return m_writableMount.openChannelForWrite( path );
|
||||
}
|
||||
}
|
||||
catch( AccessDeniedException e )
|
||||
{
|
||||
throw new FileSystemException( "Access denied" );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new FileSystemException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream openForAppend( String path ) throws FileSystemException
|
||||
public WritableByteChannel openForAppend( String path ) throws FileSystemException
|
||||
{
|
||||
if( m_writableMount == null )
|
||||
{
|
||||
@@ -266,7 +284,7 @@ public class FileSystem
|
||||
m_writableMount.makeDirectory( dir );
|
||||
}
|
||||
}
|
||||
return m_writableMount.openForWrite( path );
|
||||
return m_writableMount.openChannelForWrite( path );
|
||||
}
|
||||
else if( m_mount.isDirectory( path ) )
|
||||
{
|
||||
@@ -274,9 +292,13 @@ public class FileSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_writableMount.openForAppend( path );
|
||||
return m_writableMount.openChannelForAppend( path );
|
||||
}
|
||||
}
|
||||
catch( AccessDeniedException e )
|
||||
{
|
||||
throw new FileSystemException( "Access denied" );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new FileSystemException( e.getMessage() );
|
||||
@@ -291,10 +313,12 @@ public class FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private final FileSystemMount m_wrapper = new FileSystemMount( this );
|
||||
private final FileSystemWrapperMount m_wrapper = new FileSystemWrapperMount( this );
|
||||
private final Map<String, MountWrapper> m_mounts = new HashMap<>();
|
||||
private final Set<Closeable> m_openFiles = Collections.newSetFromMap( new WeakHashMap<Closeable, Boolean>() );
|
||||
|
||||
|
||||
private final HashMap<WeakReference<FileSystemWrapper<?>>, Closeable> m_openFiles = new HashMap<>();
|
||||
private final ReferenceQueue<FileSystemWrapper<?>> m_openFileQueue = new ReferenceQueue<>();
|
||||
|
||||
public FileSystem( String rootLabel, IMount rootMount ) throws FileSystemException
|
||||
{
|
||||
mount( rootLabel, "", rootMount );
|
||||
@@ -310,24 +334,15 @@ public class FileSystem
|
||||
// Close all dangling open files
|
||||
synchronized( m_openFiles )
|
||||
{
|
||||
for( Closeable file : m_openFiles )
|
||||
{
|
||||
try {
|
||||
file.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
for( Closeable file : m_openFiles.values() ) closeQuietly( file );
|
||||
m_openFiles.clear();
|
||||
while( m_openFileQueue.poll() != null ) ;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void mount( String label, String location, IMount mount ) throws FileSystemException
|
||||
{
|
||||
if( mount == null )
|
||||
{
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if( mount == null ) throw new NullPointerException();
|
||||
location = sanitizePath( location );
|
||||
if( location.contains( ".." ) ) {
|
||||
throw new FileSystemException( "Cannot mount below the root" );
|
||||
@@ -348,24 +363,18 @@ public class FileSystem
|
||||
}
|
||||
mount( new MountWrapper( label, location, mount ) );
|
||||
}
|
||||
|
||||
|
||||
private synchronized void mount( MountWrapper wrapper )
|
||||
{
|
||||
String location = wrapper.getLocation();
|
||||
if( m_mounts.containsKey( location ) )
|
||||
{
|
||||
m_mounts.remove( location );
|
||||
}
|
||||
m_mounts.remove( location );
|
||||
m_mounts.put( location, wrapper );
|
||||
}
|
||||
|
||||
public synchronized void unmount( String path )
|
||||
{
|
||||
path = sanitizePath( path );
|
||||
if( m_mounts.containsKey( path ) )
|
||||
{
|
||||
m_mounts.remove( path );
|
||||
}
|
||||
m_mounts.remove( path );
|
||||
}
|
||||
|
||||
public synchronized String combine( String path, String childPath )
|
||||
@@ -599,108 +608,85 @@ public class FileSystem
|
||||
else
|
||||
{
|
||||
// Copy a file:
|
||||
InputStream source = null;
|
||||
OutputStream destination = null;
|
||||
try
|
||||
try( ReadableByteChannel source = sourceMount.openForRead( sourcePath );
|
||||
WritableByteChannel destination = destinationMount.openForWrite( destinationPath ) )
|
||||
{
|
||||
// Open both files
|
||||
source = sourceMount.openForRead( sourcePath );
|
||||
destination = destinationMount.openForWrite( destinationPath );
|
||||
|
||||
// Copy bytes as fast as we can
|
||||
byte[] buffer = new byte[1024];
|
||||
while( true )
|
||||
{
|
||||
int bytesRead = source.read( buffer );
|
||||
if( bytesRead >= 0 )
|
||||
{
|
||||
destination.write( buffer, 0, bytesRead );
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
ByteStreams.copy( source, destination );
|
||||
}
|
||||
catch( AccessDeniedException e )
|
||||
{
|
||||
throw new FileSystemException( "Access denied" );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new FileSystemException( e.getMessage() );
|
||||
}
|
||||
finally
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup()
|
||||
{
|
||||
synchronized( m_openFiles )
|
||||
{
|
||||
Reference<?> ref;
|
||||
while( (ref = m_openFileQueue.poll()) != null )
|
||||
{
|
||||
// Close both files
|
||||
if( source != null )
|
||||
{
|
||||
try {
|
||||
source.close();
|
||||
} catch( IOException e ) {
|
||||
// nobody cares
|
||||
}
|
||||
}
|
||||
if( destination != null )
|
||||
{
|
||||
try {
|
||||
destination.close();
|
||||
} catch( IOException e ) {
|
||||
// nobody cares
|
||||
}
|
||||
}
|
||||
Closeable file = m_openFiles.remove( ref );
|
||||
if( file != null ) closeQuietly( file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized <T> T openFile( T file, Closeable handle ) throws FileSystemException
|
||||
private synchronized <T extends Closeable> FileSystemWrapper<T> openFile( @Nonnull T file ) throws FileSystemException
|
||||
{
|
||||
synchronized( m_openFiles )
|
||||
{
|
||||
if( ComputerCraft.maximumFilesOpen > 0 &&
|
||||
m_openFiles.size() >= ComputerCraft.maximumFilesOpen )
|
||||
{
|
||||
if( handle != null )
|
||||
{
|
||||
try {
|
||||
handle.close();
|
||||
} catch ( IOException ignored ) {
|
||||
// shrug
|
||||
}
|
||||
}
|
||||
throw new FileSystemException("Too many files already open");
|
||||
closeQuietly( file );
|
||||
throw new FileSystemException( "Too many files already open" );
|
||||
}
|
||||
|
||||
m_openFiles.add( handle );
|
||||
return file;
|
||||
FileSystemWrapper<T> wrapper = new FileSystemWrapper<>( this, file, m_openFileQueue );
|
||||
m_openFiles.put( wrapper.self, file );
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void closeFile( Closeable handle ) throws IOException
|
||||
synchronized void removeFile( FileSystemWrapper<?> handle )
|
||||
{
|
||||
synchronized( m_openFiles )
|
||||
{
|
||||
m_openFiles.remove( handle );
|
||||
handle.close();
|
||||
m_openFiles.remove( handle.self );
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized InputStream openForRead( String path ) throws FileSystemException
|
||||
|
||||
public synchronized <T extends Closeable> FileSystemWrapper<T> openForRead( String path, Function<ReadableByteChannel, T> open ) throws FileSystemException
|
||||
{
|
||||
path = sanitizePath ( path );
|
||||
cleanup();
|
||||
|
||||
path = sanitizePath( path );
|
||||
MountWrapper mount = getMount( path );
|
||||
InputStream stream = mount.openForRead( path );
|
||||
if( stream != null )
|
||||
ReadableByteChannel channel = mount.openForRead( path );
|
||||
if( channel != null )
|
||||
{
|
||||
return openFile( new ClosingInputStream( stream ), stream );
|
||||
return openFile( open.apply( channel ) );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized OutputStream openForWrite( String path, boolean append ) throws FileSystemException
|
||||
public synchronized <T extends Closeable> FileSystemWrapper<T> openForWrite( String path, boolean append, Function<WritableByteChannel, T> open ) throws FileSystemException
|
||||
{
|
||||
path = sanitizePath ( path );
|
||||
cleanup();
|
||||
|
||||
path = sanitizePath( path );
|
||||
MountWrapper mount = getMount( path );
|
||||
OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path );
|
||||
if( stream != null )
|
||||
WritableByteChannel channel = append ? mount.openForAppend( path ) : mount.openForWrite( path );
|
||||
if( channel != null )
|
||||
{
|
||||
return openFile( new ClosingOutputStream( stream ), stream );
|
||||
return openFile( open.apply( channel ) );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -865,33 +851,14 @@ public class FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private class ClosingInputStream extends FilterInputStream
|
||||
private static void closeQuietly( Closeable c )
|
||||
{
|
||||
protected ClosingInputStream( InputStream in )
|
||||
try
|
||||
{
|
||||
super( in );
|
||||
c.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
catch( IOException ignored )
|
||||
{
|
||||
super.close();
|
||||
closeFile( in );
|
||||
}
|
||||
}
|
||||
|
||||
private class ClosingOutputStream extends FilterOutputStream
|
||||
{
|
||||
protected ClosingOutputStream( OutputStream out )
|
||||
{
|
||||
super( out );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
closeFile( out );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,185 +1,132 @@
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileSystemMount implements IFileSystem
|
||||
public class FileSystemMount implements IMount
|
||||
{
|
||||
private final FileSystem m_filesystem;
|
||||
private final Entry rootEntry;
|
||||
|
||||
public FileSystemMount( FileSystem m_filesystem )
|
||||
public FileSystemMount( FileSystem fileSystem, String root ) throws IOException
|
||||
{
|
||||
this.m_filesystem = m_filesystem;
|
||||
}
|
||||
Path rootPath = fileSystem.getPath( root );
|
||||
rootEntry = new Entry( "", rootPath );
|
||||
|
||||
@Override
|
||||
public void makeDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
Queue<Entry> entries = new ArrayDeque<>();
|
||||
entries.add( rootEntry );
|
||||
while( !entries.isEmpty() )
|
||||
{
|
||||
m_filesystem.makeDir( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
Entry entry = entries.remove();
|
||||
try( Stream<Path> childStream = Files.list( entry.path ) )
|
||||
{
|
||||
Iterator<Path> children = childStream.iterator();
|
||||
while( children.hasNext() )
|
||||
{
|
||||
Path childPath = children.next();
|
||||
Entry child = new Entry( childPath.getFileName().toString(), childPath );
|
||||
entry.children.put( child.name, child );
|
||||
if( child.directory ) entries.add( child );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete( @Nonnull String path ) throws IOException
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.delete( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream openForWrite( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForWrite( path, false );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream openForAppend( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForWrite( path, true );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
return getFile( path ) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingSpace() throws IOException
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.getFreeSpace( "/" );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
Entry entry = getFile( path );
|
||||
return entry != null && entry.directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
Collections.addAll( contents, m_filesystem.list( path ) );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
Entry entry = getFile( path );
|
||||
if( entry == null || !entry.directory ) throw new IOException( "/" + path + ": Not a directory" );
|
||||
|
||||
contents.addAll( entry.children.keySet() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.getSize( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
Entry file = getFile( path );
|
||||
if( file == null ) throw new IOException( "/" + path + ": No such file" );
|
||||
return file.size;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForRead( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
Entry file = getFile( path );
|
||||
if( file == null || file.directory ) throw new IOException( "/" + path + ": No such file" );
|
||||
|
||||
return Files.newInputStream( file.path, StandardOpenOption.READ );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String combine( String path, String child )
|
||||
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
return m_filesystem.combine( path, child );
|
||||
Entry file = getFile( path );
|
||||
if( file == null || file.directory ) throw new IOException( "/" + path + ": No such file" );
|
||||
|
||||
return Files.newByteChannel( file.path, StandardOpenOption.READ );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy( String from, String to ) throws IOException
|
||||
private Entry getFile( String path )
|
||||
{
|
||||
try
|
||||
if( path.equals( "" ) ) return rootEntry;
|
||||
if( !path.contains( "/" ) ) return rootEntry.children.get( path );
|
||||
|
||||
String[] components = path.split( "/" );
|
||||
Entry entry = rootEntry;
|
||||
for( String component : components )
|
||||
{
|
||||
m_filesystem.copy( from, to );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
if( entry == null || entry.children == null ) return null;
|
||||
entry = entry.children.get( component );
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move( String from, String to ) throws IOException
|
||||
private static class Entry
|
||||
{
|
||||
try
|
||||
final String name;
|
||||
final Path path;
|
||||
|
||||
final boolean directory;
|
||||
final long size;
|
||||
final Map<String, Entry> children;
|
||||
|
||||
private Entry( String name, Path path ) throws IOException
|
||||
{
|
||||
m_filesystem.move( from, to );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
if( name.endsWith( "/" ) || name.endsWith( "\\" ) ) name = name.substring( 0, name.length() - 1 );
|
||||
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
|
||||
BasicFileAttributes attributes = Files.readAttributes( path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS );
|
||||
this.directory = attributes.isDirectory();
|
||||
this.size = directory ? 0 : attributes.size();
|
||||
this.children = directory ? new HashMap<>() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* An alternative closeable implementation that will free up resources in the filesystem.
|
||||
*
|
||||
* In an ideal world, we'd just wrap the closeable. However, as we do some {@code instanceof} checks
|
||||
* on the stream, it's not really possible as it'd require numerous instances.
|
||||
*
|
||||
* @param <T> The stream to wrap.
|
||||
*/
|
||||
public class FileSystemWrapper<T extends Closeable> implements Closeable
|
||||
{
|
||||
private final FileSystem fileSystem;
|
||||
private final T closeable;
|
||||
final WeakReference<FileSystemWrapper<?>> self;
|
||||
|
||||
FileSystemWrapper( FileSystem fileSystem, T closeable, ReferenceQueue<FileSystemWrapper<?>> queue )
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
this.closeable = closeable;
|
||||
this.self = new WeakReference<>( this, queue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
fileSystem.removeFile( this );
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public T get()
|
||||
{
|
||||
return closeable;
|
||||
}
|
||||
}
|
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class FileSystemWrapperMount implements IFileSystem
|
||||
{
|
||||
private final FileSystem m_filesystem;
|
||||
|
||||
public FileSystemWrapperMount( FileSystem m_filesystem )
|
||||
{
|
||||
this.m_filesystem = m_filesystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.makeDir( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.delete( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
// FIXME: Think of a better way of implementing this, so closing this will close on the computer.
|
||||
return m_filesystem.openForRead( path, Function.identity() ).get();
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForWrite( path, false, Function.identity() ).get();
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.openForWrite( path, true, Function.identity() ).get();
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.getFreeSpace( "/" );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
Collections.addAll( contents, m_filesystem.list( path ) );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize( @Nonnull String path ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.getSize( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine( String path, String child )
|
||||
{
|
||||
return m_filesystem.combine( path, child );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy( String from, String to ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.copy( from, to );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move( String from, String to ) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
m_filesystem.move( from, to );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.filesystem;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class JarMount implements IMount
|
||||
{
|
||||
private class FileInZip
|
||||
{
|
||||
private String m_path;
|
||||
private boolean m_directory;
|
||||
private long m_size;
|
||||
private Map<String, FileInZip> m_children;
|
||||
|
||||
public FileInZip( String path, boolean directory, long size )
|
||||
{
|
||||
m_path = path;
|
||||
m_directory = directory;
|
||||
m_size = m_directory ? 0 : size;
|
||||
m_children = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public String getPath()
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
public boolean isDirectory()
|
||||
{
|
||||
return m_directory;
|
||||
}
|
||||
|
||||
public long getSize()
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
public void list( List<String> contents )
|
||||
{
|
||||
contents.addAll( m_children.keySet() );
|
||||
}
|
||||
|
||||
public void insertChild( FileInZip child )
|
||||
{
|
||||
String localPath = FileSystem.toLocal( child.getPath(), m_path );
|
||||
m_children.put( localPath, child );
|
||||
}
|
||||
|
||||
public FileInZip getFile( String path )
|
||||
{
|
||||
// If we've reached the target, return this
|
||||
if( path.equals( m_path ) )
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
// Otherwise, get the next component of the path
|
||||
String localPath = FileSystem.toLocal( path, m_path );
|
||||
int slash = localPath.indexOf("/");
|
||||
if( slash >= 0 )
|
||||
{
|
||||
localPath = localPath.substring( 0, slash );
|
||||
}
|
||||
|
||||
// And recurse down using it
|
||||
FileInZip subFile = m_children.get( localPath );
|
||||
if( subFile != null )
|
||||
{
|
||||
return subFile.getFile( path );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public FileInZip getParent( String path )
|
||||
{
|
||||
if( path.length() == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInZip file = getFile( FileSystem.getDirectory( path ) );
|
||||
if( file.isDirectory() )
|
||||
{
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ZipFile m_zipFile;
|
||||
private FileInZip m_root;
|
||||
private String m_rootPath;
|
||||
|
||||
public JarMount( File jarFile, String subPath ) throws IOException
|
||||
{
|
||||
if( !jarFile.exists() || jarFile.isDirectory() )
|
||||
{
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
// Open the zip file
|
||||
try
|
||||
{
|
||||
m_zipFile = new ZipFile( jarFile );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
throw new IOException( "Error loading zip file" );
|
||||
}
|
||||
|
||||
if( m_zipFile.getEntry( subPath ) == null )
|
||||
{
|
||||
m_zipFile.close();
|
||||
throw new IOException( "Zip does not contain path" );
|
||||
}
|
||||
|
||||
// Read in all the entries
|
||||
Enumeration<? extends ZipEntry> zipEntries = m_zipFile.entries();
|
||||
while( zipEntries.hasMoreElements() )
|
||||
{
|
||||
ZipEntry entry = zipEntries.nextElement();
|
||||
String entryName = entry.getName();
|
||||
if( entryName.startsWith( subPath ) )
|
||||
{
|
||||
entryName = FileSystem.toLocal( entryName, subPath );
|
||||
if( m_root == null )
|
||||
{
|
||||
if( entryName.equals( "" ) )
|
||||
{
|
||||
m_root = new FileInZip( entryName, entry.isDirectory(), entry.getSize() );
|
||||
m_rootPath = subPath;
|
||||
if( !m_root.isDirectory() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle this case. The code currently assumes we find the root before anything else
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FileInZip parent = m_root.getParent( entryName );
|
||||
if( parent != null )
|
||||
{
|
||||
parent.insertChild( new FileInZip( entryName, entry.isDirectory(), entry.getSize() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle this case. The code currently assumes we find folders before their contents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IMount implementation
|
||||
|
||||
@Override
|
||||
public boolean exists( @Nonnull String path )
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
return file != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory( @Nonnull String path )
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
if( file != null )
|
||||
{
|
||||
return file.isDirectory();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
if( file != null && file.isDirectory() )
|
||||
{
|
||||
file.list( contents );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException( "/" + path + ": Not a directory" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize( @Nonnull String path ) throws IOException
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
if( file != null )
|
||||
{
|
||||
return file.getSize();
|
||||
}
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
FileInZip file = m_root.getFile( path );
|
||||
if( file != null && !file.isDirectory() )
|
||||
{
|
||||
try
|
||||
{
|
||||
String fullPath = m_rootPath;
|
||||
if( path.length() > 0 )
|
||||
{
|
||||
fullPath = fullPath + "/" + path;
|
||||
}
|
||||
ZipEntry entry = m_zipFile.getEntry( fullPath );
|
||||
if( entry != null )
|
||||
{
|
||||
return m_zipFile.getInputStream( entry );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
// treat errors as non-existance of file
|
||||
}
|
||||
}
|
||||
throw new IOException( "/" + path + ": No such file" );
|
||||
}
|
||||
}
|
@@ -11,6 +11,7 @@ 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;
|
||||
|
||||
public class SubMount implements IMount
|
||||
@@ -52,11 +53,19 @@ public class SubMount implements IMount
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
return m_parent.openForRead( getFullPath( path ) );
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
|
||||
{
|
||||
return m_parent.openChannelForRead( getFullPath( path ) );
|
||||
}
|
||||
|
||||
private String getFullPath( String path )
|
||||
{
|
||||
if( path.length() == 0 )
|
||||
|
@@ -128,7 +128,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
|
||||
|
||||
// Remove globals we don't want to expose
|
||||
m_globals.rawset( "collectgarbage", Constants.NIL );
|
||||
// m_globals.rawset( "collectgarbage", Constants.NIL );
|
||||
m_globals.rawset( "dofile", Constants.NIL );
|
||||
m_globals.rawset( "loadfile", Constants.NIL );
|
||||
m_globals.rawset( "print", Constants.NIL );
|
||||
|
@@ -20,10 +20,10 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.*;
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
{
|
||||
if( arguments.size() == 0 )
|
||||
{
|
||||
TextTable table = new TextTable( DUMP_LIST_ID, "Instance", "Id", "On", "Position" );
|
||||
TextTable table = new TextTable( DUMP_LIST_ID, "Computer", "On", "Position" );
|
||||
|
||||
List<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
|
||||
@@ -92,8 +92,7 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
table.addRow(
|
||||
linkComputer( computer ),
|
||||
text( Integer.toString( computer.getID() ) ),
|
||||
linkComputer( context, computer, computer.getID() ),
|
||||
bool( computer.isOn() ),
|
||||
linkPosition( context, computer )
|
||||
);
|
||||
@@ -149,26 +148,15 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
Set<ServerComputer> computers = Sets.newHashSet();
|
||||
if( arguments.size() > 0 )
|
||||
{
|
||||
for( String arg : arguments )
|
||||
withComputers( arguments, computers -> {
|
||||
int shutdown = 0;
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
computers.addAll( ComputerSelector.getComputers( arg ) );
|
||||
if( computer.isOn() ) shutdown++;
|
||||
computer.unload();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
computers.addAll( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
}
|
||||
|
||||
int shutdown = 0;
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
if( computer.isOn() ) shutdown++;
|
||||
computer.unload();
|
||||
}
|
||||
context.getSender().sendMessage( text( "Shutdown " + shutdown + " / " + computers.size() + " computers" ) );
|
||||
context.getSender().sendMessage( text( "Shutdown " + shutdown + " / " + computers.size() + " computers" ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -190,26 +178,15 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
@Override
|
||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||
{
|
||||
Set<ServerComputer> computers = Sets.newHashSet();
|
||||
if( arguments.size() > 0 )
|
||||
{
|
||||
for( String arg : arguments )
|
||||
withComputers( arguments, computers -> {
|
||||
int on = 0;
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
computers.addAll( ComputerSelector.getComputers( arg ) );
|
||||
if( !computer.isOn() ) on++;
|
||||
computer.turnOn();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
computers.addAll( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
}
|
||||
|
||||
int on = 0;
|
||||
for( ServerComputer computer : computers )
|
||||
{
|
||||
if( !computer.isOn() ) on++;
|
||||
computer.turnOn();
|
||||
}
|
||||
context.getSender().sendMessage( text( "Turned on " + on + " / " + computers.size() + " computers" ) );
|
||||
context.getSender().sendMessage( text( "Turned on " + on + " / " + computers.size() + " computers" ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -406,13 +383,46 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
return root;
|
||||
}
|
||||
|
||||
private static ITextComponent linkComputer( ServerComputer computer )
|
||||
private static ITextComponent linkComputer( CommandContext context, ServerComputer serverComputer, int computerId )
|
||||
{
|
||||
return link(
|
||||
text( Integer.toString( computer.getInstanceID() ) ),
|
||||
"/computercraft dump " + computer.getInstanceID(),
|
||||
"View more info about this computer"
|
||||
);
|
||||
ITextComponent out = new TextComponentString( "" );
|
||||
|
||||
// Append the computer instance
|
||||
if( serverComputer == null )
|
||||
{
|
||||
out.appendSibling( text( "?" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.appendSibling( link(
|
||||
text( Integer.toString( serverComputer.getInstanceID() ) ),
|
||||
"/computercraft dump " + serverComputer.getInstanceID(),
|
||||
"View more info about this computer"
|
||||
) );
|
||||
}
|
||||
|
||||
// And ID
|
||||
out.appendText( " (id " + computerId + ")" );
|
||||
|
||||
// And, if we're a player, some useful links
|
||||
if( serverComputer != null && UserLevel.OP.canExecute( context ) && context.fromPlayer() )
|
||||
{
|
||||
out
|
||||
.appendText( " " )
|
||||
.appendSibling( link(
|
||||
text( "\u261b" ),
|
||||
"/computercraft tp " + serverComputer.getInstanceID(),
|
||||
"Teleport to this computer"
|
||||
) )
|
||||
.appendText( " " )
|
||||
.appendSibling( link(
|
||||
text( "\u20e2" ),
|
||||
"/computercraft view " + serverComputer.getInstanceID(),
|
||||
"View this computer"
|
||||
) );
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static ITextComponent linkPosition( CommandContext context, ServerComputer computer )
|
||||
@@ -458,9 +468,6 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
if( server.getID() > maxId ) maxId = server.getID();
|
||||
}
|
||||
|
||||
ICommandSender sender = context.getSender();
|
||||
boolean isPlayer = sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer);
|
||||
|
||||
timings.sort( Comparator.<ComputerTracker, Long>comparing( x -> x.get( field ) ).reversed() );
|
||||
|
||||
boolean defaultLayout = field == TrackingField.TASKS || field == TrackingField.TOTAL_TIME
|
||||
@@ -476,26 +483,7 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
Computer computer = entry.getComputer();
|
||||
ServerComputer serverComputer = computer == null ? null : lookup.get( computer );
|
||||
|
||||
ITextComponent computerComponent = new TextComponentString( "" )
|
||||
.appendSibling( serverComputer == null ? text( "?" ) : linkComputer( serverComputer ) )
|
||||
.appendText( " (id " + entry.getComputerId() + ")" );
|
||||
|
||||
if( serverComputer != null && UserLevel.OP.canExecute( context ) && isPlayer )
|
||||
{
|
||||
computerComponent
|
||||
.appendText( " " )
|
||||
.appendSibling( link(
|
||||
text( "\u261b" ),
|
||||
"/computercraft tp " + serverComputer.getInstanceID(),
|
||||
"Teleport to this computer"
|
||||
) )
|
||||
.appendText( " " )
|
||||
.appendSibling( link(
|
||||
text( "\u20e2" ),
|
||||
"/computercraft view " + serverComputer.getInstanceID(),
|
||||
"View this computer"
|
||||
) );
|
||||
}
|
||||
ITextComponent computerComponent = linkComputer( context, serverComputer, entry.getComputerId() );
|
||||
|
||||
if( defaultLayout )
|
||||
{
|
||||
@@ -515,4 +503,30 @@ public final class CommandComputerCraft extends CommandDelegate
|
||||
|
||||
table.displayTo( context.getSender() );
|
||||
}
|
||||
|
||||
private static void withComputers( List<String> selectors, Consumer<Collection<ServerComputer>> action ) throws CommandException
|
||||
{
|
||||
Set<ServerComputer> computers = Sets.newHashSet();
|
||||
List<String> failed = new ArrayList<>();
|
||||
if( selectors.isEmpty() )
|
||||
{
|
||||
computers.addAll( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( String selector : selectors )
|
||||
{
|
||||
List<ServerComputer> selected = ComputerSelector.getComputers( selector );
|
||||
computers.addAll( selected );
|
||||
if( selected.isEmpty() ) failed.add( selector );
|
||||
}
|
||||
}
|
||||
|
||||
action.accept( computers );
|
||||
|
||||
if( !failed.isEmpty() )
|
||||
{
|
||||
throw new CommandException( "Could not find computers matching " + String.join( ", ", failed ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,30 +12,22 @@ import java.util.function.Predicate;
|
||||
|
||||
public final class ComputerSelector
|
||||
{
|
||||
private static List<ServerComputer> getComputers( Predicate<ServerComputer> predicate, String selector ) throws CommandException
|
||||
private static List<ServerComputer> getComputers( Predicate<ServerComputer> predicate ) throws CommandException
|
||||
{
|
||||
// We copy it to prevent concurrent modifications.
|
||||
List<ServerComputer> computers = Lists.newArrayList( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
List<ServerComputer> candidates = Lists.newArrayList();
|
||||
for( ServerComputer searchComputer : computers )
|
||||
{
|
||||
if( predicate.test( searchComputer ) ) candidates.add( searchComputer );
|
||||
}
|
||||
|
||||
if( candidates.isEmpty() )
|
||||
{
|
||||
throw new CommandException( "No computer matching " + selector );
|
||||
}
|
||||
else
|
||||
{
|
||||
return candidates;
|
||||
}
|
||||
ArrayList<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||
computers.removeIf( predicate.negate() );
|
||||
return computers;
|
||||
}
|
||||
|
||||
public static ServerComputer getComputer( String selector ) throws CommandException
|
||||
{
|
||||
List<ServerComputer> computers = getComputers( selector );
|
||||
if( computers.size() == 1 )
|
||||
if( computers.size() == 0 )
|
||||
{
|
||||
throw new CommandException( "No computer matching " + selector );
|
||||
}
|
||||
else if( computers.size() == 1 )
|
||||
{
|
||||
return computers.get( 0 );
|
||||
}
|
||||
@@ -46,7 +38,7 @@ public final class ComputerSelector
|
||||
|
||||
for( int i = 0; i < computers.size(); i++ )
|
||||
{
|
||||
if( i > 1 ) builder.append( ", " );
|
||||
if( i > 0 ) builder.append( ", " );
|
||||
builder.append( computers.get( i ).getInstanceID() );
|
||||
}
|
||||
builder.append( ")" );
|
||||
@@ -71,17 +63,17 @@ public final class ComputerSelector
|
||||
throw new CommandException( "'" + selector + "' is not a valid number" );
|
||||
}
|
||||
|
||||
return getComputers( x -> x.getID() == id, selector );
|
||||
return getComputers( x -> x.getID() == id );
|
||||
}
|
||||
else if( selector.length() > 0 && selector.charAt( 0 ) == '@' )
|
||||
{
|
||||
String label = selector.substring( 1 );
|
||||
return getComputers( x -> Objects.equals( label, x.getLabel() ), selector );
|
||||
return getComputers( x -> Objects.equals( label, x.getLabel() ) );
|
||||
}
|
||||
else if( selector.length() > 0 && selector.charAt( 0 ) == '~' )
|
||||
{
|
||||
String familyName = selector.substring( 1 );
|
||||
return getComputers( x -> x.getFamily().name().equalsIgnoreCase( familyName ), selector );
|
||||
return getComputers( x -> x.getFamily().name().equalsIgnoreCase( familyName ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -96,14 +88,7 @@ public final class ComputerSelector
|
||||
}
|
||||
|
||||
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance );
|
||||
if( computer == null )
|
||||
{
|
||||
throw new CommandException( "No such computer for instance id " + instance );
|
||||
}
|
||||
else
|
||||
{
|
||||
return Collections.singletonList( computer );
|
||||
}
|
||||
return computer == null ? Collections.emptyList() : Collections.singletonList( computer );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,9 @@ package dan200.computercraft.shared.command.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -90,4 +92,9 @@ public final class CommandContext
|
||||
{
|
||||
return sender;
|
||||
}
|
||||
|
||||
public boolean fromPlayer()
|
||||
{
|
||||
return sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer);
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured
|
||||
*/
|
||||
public class TextFormatter
|
||||
{
|
||||
private static final int SPACE_WIDTH = 4;
|
||||
private static final char PADDING_CHAR = '\u02cc';
|
||||
|
||||
/**
|
||||
@@ -54,7 +53,7 @@ public class TextFormatter
|
||||
8, 4,
|
||||
};
|
||||
|
||||
private static int getWidth( int codePoint )
|
||||
public static int getWidth( int codePoint )
|
||||
{
|
||||
// Escape codes
|
||||
if( codePoint == 167 ) return -1;
|
||||
@@ -73,7 +72,7 @@ public class TextFormatter
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getWidth( ITextComponent component )
|
||||
public static int getWidth( ITextComponent component )
|
||||
{
|
||||
int total = 0;
|
||||
if( component instanceof TextComponentString )
|
||||
@@ -132,19 +131,17 @@ public class TextFormatter
|
||||
int width = getWidthFor( entry, sender );
|
||||
int delta = maxWidth - width;
|
||||
|
||||
if( delta > 0 )
|
||||
int spaceWidth = getWidthFor( ' ', sender );
|
||||
int spaces = delta / spaceWidth;
|
||||
int extra = delta % spaces;
|
||||
|
||||
// Append a fixed number of spaces
|
||||
if( spaces > 0 ) out.appendSibling( new TextComponentString( StringUtils.repeat( ' ', spaces ) ) );
|
||||
|
||||
// Append several minor characters to pad to a full string
|
||||
if( extra > 0 )
|
||||
{
|
||||
int spaces = delta / SPACE_WIDTH;
|
||||
int extra = delta % SPACE_WIDTH;
|
||||
|
||||
// Append a fixed number of spaces
|
||||
if( spaces > 0 ) out.appendSibling( new TextComponentString( StringUtils.repeat( ' ', spaces ) ) );
|
||||
|
||||
// Append several minor characters to pad to a full string
|
||||
if( extra > 0 )
|
||||
{
|
||||
out.appendSibling( coloured( StringUtils.repeat( PADDING_CHAR, extra ), TextFormatting.GRAY ) );
|
||||
}
|
||||
out.appendSibling( coloured( StringUtils.repeat( PADDING_CHAR, extra ), TextFormatting.GRAY ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ public class TextTable
|
||||
this.columns = header.length;
|
||||
}
|
||||
|
||||
public TextTable(int id)
|
||||
public TextTable( int id )
|
||||
{
|
||||
this.id = id;
|
||||
this.header = null;
|
||||
@@ -90,14 +90,15 @@ public class TextTable
|
||||
ITextComponent[] row = rows.get( y );
|
||||
for( int i = 0; i < row.length; i++ )
|
||||
{
|
||||
int width = getWidthFor( row[i], sender ) + 3;
|
||||
int width = getWidthFor( row[i], sender );
|
||||
if( width > maxWidths[i] ) maxWidths[i] = width;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a small amount of extra padding. We include this here instead of the separator to allow
|
||||
// for "extra" characters
|
||||
for( int i = 0; i < maxWidths.length; i++ ) maxWidths[i] += 4;
|
||||
// Add a small amount of extra padding. This defaults to 3 spaces for players
|
||||
// and 1 for everyone else.
|
||||
int padding = isPlayer( sender ) ? getWidth( ' ' ) * 3 : 1;
|
||||
for( int i = 0; i < maxWidths.length; i++ ) maxWidths[i] += padding;
|
||||
|
||||
int totalWidth = (columns - 1) * getWidthFor( SEPARATOR, sender );
|
||||
for( int x : maxWidths ) totalWidth += x;
|
||||
@@ -161,7 +162,7 @@ public class TextTable
|
||||
for( int i = 0; i < out.size(); i++ )
|
||||
{
|
||||
if( i > 0 ) result.appendSibling( LINE );
|
||||
result.appendSibling( out.get( 0 ) );
|
||||
result.appendSibling( out.get( i ) );
|
||||
}
|
||||
sender.sendMessage( result );
|
||||
}
|
||||
|
@@ -54,6 +54,11 @@ public class ServerTerminal implements ITerminal
|
||||
}
|
||||
}
|
||||
|
||||
protected void markTerminalChanged()
|
||||
{
|
||||
m_terminalChanged = true;
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
m_terminalChangedLastFrame = m_terminalChanged || (m_terminal != null && m_terminal.getChanged());
|
||||
|
@@ -42,7 +42,7 @@ public class BlockCommandComputer extends BlockComputerBase
|
||||
super( Material.IRON );
|
||||
setBlockUnbreakable();
|
||||
setResistance( 6000000.0F );
|
||||
setUnlocalizedName( "computercraft:command_computer" );
|
||||
setTranslationKey( "computercraft:command_computer" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||
@@ -62,7 +62,7 @@ public class BlockCommandComputer extends BlockComputerBase
|
||||
@Deprecated
|
||||
public IBlockState getStateFromMeta( int meta )
|
||||
{
|
||||
EnumFacing dir = EnumFacing.getFront( meta & 0x7 );
|
||||
EnumFacing dir = EnumFacing.byIndex( meta & 0x7 );
|
||||
if( dir.getAxis() == EnumFacing.Axis.Y )
|
||||
{
|
||||
dir = EnumFacing.NORTH;
|
||||
|
@@ -44,7 +44,7 @@ public class BlockComputer extends BlockComputerBase
|
||||
{
|
||||
super( Material.ROCK );
|
||||
setHardness( 2.0f );
|
||||
setUnlocalizedName( "computercraft:computer" );
|
||||
setTranslationKey( "computercraft:computer" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||
@@ -65,7 +65,7 @@ public class BlockComputer extends BlockComputerBase
|
||||
@Deprecated
|
||||
public IBlockState getStateFromMeta( int meta )
|
||||
{
|
||||
EnumFacing dir = EnumFacing.getFront( meta & 0x7 );
|
||||
EnumFacing dir = EnumFacing.byIndex( meta & 0x7 );
|
||||
if( dir.getAxis() == EnumFacing.Axis.Y )
|
||||
{
|
||||
dir = EnumFacing.NORTH;
|
||||
|
@@ -154,7 +154,7 @@ public class ServerComputer extends ServerTerminal
|
||||
return packet;
|
||||
}
|
||||
|
||||
private ComputerCraftPacket createTerminalPacket() {
|
||||
protected ComputerCraftPacket createTerminalPacket() {
|
||||
ComputerCraftPacket packet = new ComputerCraftPacket();
|
||||
packet.m_packetType = ComputerCraftPacket.ComputerTerminalChanged;
|
||||
packet.m_dataInt = new int[] { getInstanceID() };
|
||||
@@ -465,7 +465,7 @@ public class ServerComputer extends ServerTerminal
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInteracting( EntityPlayer player )
|
||||
protected boolean isInteracting( EntityPlayer player )
|
||||
{
|
||||
if( player == null ) return false;
|
||||
|
||||
|
@@ -24,7 +24,7 @@ public class ItemCommandComputer extends ItemComputer
|
||||
super( block );
|
||||
setMaxStackSize( 64 );
|
||||
setHasSubtypes( true );
|
||||
setUnlocalizedName( "computercraft:command_computer" );
|
||||
setTranslationKey( "computercraft:command_computer" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ public class ItemComputer extends ItemComputerBase
|
||||
super( block );
|
||||
setMaxStackSize( 64 );
|
||||
setHasSubtypes( true );
|
||||
setUnlocalizedName( "computercraft:computer" );
|
||||
setTranslationKey( "computercraft:computer" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public class ItemComputer extends ItemComputerBase
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
||||
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||
{
|
||||
switch( getFamily( stack ) )
|
||||
{
|
||||
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.shared.integration.charset;
|
||||
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import pl.asie.charset.api.wires.IBundledEmitter;
|
||||
import pl.asie.charset.api.wires.IBundledReceiver;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static dan200.computercraft.shared.integration.charset.IntegrationCharset.CAPABILITY_EMITTER;
|
||||
import static dan200.computercraft.shared.integration.charset.IntegrationCharset.CAPABILITY_RECEIVER;
|
||||
|
||||
final class BundledCapabilityProvider implements ICapabilityProvider
|
||||
{
|
||||
private final TileGeneric tile;
|
||||
private IBundledReceiver receiver;
|
||||
private IBundledEmitter[] emitters;
|
||||
|
||||
BundledCapabilityProvider( TileGeneric tile )
|
||||
{
|
||||
this.tile = tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing side )
|
||||
{
|
||||
return capability == CAPABILITY_EMITTER || capability == CAPABILITY_RECEIVER;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getCapability( @Nonnull Capability<T> capability, @Nullable EnumFacing side )
|
||||
{
|
||||
if( capability == CAPABILITY_RECEIVER )
|
||||
{
|
||||
IBundledReceiver receiver = this.receiver;
|
||||
if( receiver == null ) receiver = this.receiver = tile::onNeighbourChange;
|
||||
|
||||
return CAPABILITY_RECEIVER.cast( receiver );
|
||||
}
|
||||
else if( capability == CAPABILITY_EMITTER )
|
||||
{
|
||||
IBundledEmitter[] emitters = this.emitters;
|
||||
if( emitters == null ) emitters = this.emitters = new IBundledEmitter[7];
|
||||
|
||||
int index = side == null ? 6 : side.getIndex();
|
||||
IBundledEmitter emitter = emitters[index];
|
||||
if( emitter == null )
|
||||
{
|
||||
if( side == null )
|
||||
{
|
||||
emitter = emitters[index] = () -> {
|
||||
int flags = 0;
|
||||
for( EnumFacing facing : EnumFacing.VALUES ) flags |= tile.getBundledRedstoneOutput( facing );
|
||||
return toBytes( flags );
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
emitter = emitters[index] = () -> toBytes( tile.getBundledRedstoneOutput( side ) );
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
return CAPABILITY_EMITTER.cast( emitter );
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] toBytes( int flag )
|
||||
{
|
||||
byte[] channels = new byte[16];
|
||||
for( int i = 0; i < 16; i++ ) channels[i] = (flag & (1 << i)) == 0 ? (byte) 0 : 15;
|
||||
return channels;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.shared.integration.charset;
|
||||
|
||||
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static dan200.computercraft.shared.integration.charset.IntegrationCharset.CAPABILITY_EMITTER;
|
||||
|
||||
public class BundledRedstoneProvider implements IBundledRedstoneProvider
|
||||
{
|
||||
@Override
|
||||
public int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile == null || !tile.hasCapability( CAPABILITY_EMITTER, side ) ) return -1;
|
||||
|
||||
byte[] signal = tile.getCapability( CAPABILITY_EMITTER, side ).getBundledSignal();
|
||||
if( signal == null ) return -1;
|
||||
|
||||
int flag = 0;
|
||||
for( int i = 0; i < signal.length; i++ ) flag |= signal[i] > 0 ? (1 << i) : 0;
|
||||
return flag;
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.shared.integration.charset;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import pl.asie.charset.api.wires.IBundledEmitter;
|
||||
import pl.asie.charset.api.wires.IBundledReceiver;
|
||||
|
||||
public final class IntegrationCharset
|
||||
{
|
||||
private static final ResourceLocation CAPABILITY_KEY = new ResourceLocation( ComputerCraft.MOD_ID, "charset" );
|
||||
|
||||
@CapabilityInject( IBundledEmitter.class )
|
||||
public static final Capability<IBundledEmitter> CAPABILITY_EMITTER = null;
|
||||
|
||||
@CapabilityInject( IBundledReceiver.class )
|
||||
public static final Capability<IBundledReceiver> CAPABILITY_RECEIVER = null;
|
||||
|
||||
private IntegrationCharset()
|
||||
{
|
||||
}
|
||||
|
||||
public static void register()
|
||||
{
|
||||
if( CAPABILITY_EMITTER == null || CAPABILITY_RECEIVER == null ) return;
|
||||
|
||||
MinecraftForge.EVENT_BUS.register( new IntegrationCharset() );
|
||||
ComputerCraftAPI.registerBundledRedstoneProvider( new BundledRedstoneProvider() );
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void attachGenericCapabilities( AttachCapabilitiesEvent<TileEntity> event)
|
||||
{
|
||||
TileEntity tile = event.getObject();
|
||||
if(tile instanceof TileGeneric)
|
||||
{
|
||||
event.addCapability( CAPABILITY_KEY, new BundledCapabilityProvider( (TileGeneric) tile ) );
|
||||
}
|
||||
}
|
||||
}
|
@@ -33,7 +33,7 @@ public class ItemDiskLegacy extends Item
|
||||
{
|
||||
setMaxStackSize( 1 );
|
||||
setHasSubtypes( true );
|
||||
setUnlocalizedName( "computercraft:disk" );
|
||||
setTranslationKey( "computercraft:disk" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -39,7 +39,7 @@ public class ItemPrintout extends Item
|
||||
{
|
||||
setMaxStackSize( 1 );
|
||||
setHasSubtypes( true );
|
||||
setUnlocalizedName( "computercraft:page" );
|
||||
setTranslationKey( "computercraft:page" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class ItemPrintout extends Item
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
||||
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||
{
|
||||
Type type = getType( stack );
|
||||
switch( type )
|
||||
|
@@ -34,7 +34,7 @@ public class ItemTreasureDisk extends Item
|
||||
{
|
||||
setMaxStackSize( 1 );
|
||||
setHasSubtypes( true );
|
||||
setUnlocalizedName( "computercraft:treasure_disk" );
|
||||
setTranslationKey( "computercraft:treasure_disk" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -55,7 +55,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
public BlockCable()
|
||||
{
|
||||
setHardness( 1.5f );
|
||||
setUnlocalizedName( "computercraft:cable" );
|
||||
setTranslationKey( "computercraft:cable" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.MODEM, BlockCableModemVariant.None )
|
||||
@@ -94,12 +94,12 @@ public class BlockCable extends BlockPeripheralBase
|
||||
if( meta < 6 )
|
||||
{
|
||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.NONE );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.getFront( meta ) ) );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.byIndex( meta ) ) );
|
||||
}
|
||||
else if( meta < 12 )
|
||||
{
|
||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.ANY );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.getFront( meta - 6 ) ) );
|
||||
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.byIndex( meta - 6 ) ) );
|
||||
}
|
||||
else if( meta == 13 )
|
||||
{
|
||||
@@ -296,7 +296,7 @@ public class BlockCable extends BlockPeripheralBase
|
||||
}
|
||||
}
|
||||
|
||||
return result == null ? null : new RayTraceResult( result.hitVec.addVector( pos.getX(), pos.getY(), pos.getZ() ), result.sideHit, pos );
|
||||
return result == null ? null : new RayTraceResult( result.hitVec.add( pos.getX(), pos.getY(), pos.getZ() ), result.sideHit, pos );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -44,7 +44,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
||||
public BlockPeripheral()
|
||||
{
|
||||
setHardness( 2.0f );
|
||||
setUnlocalizedName( "computercraft:peripheral" );
|
||||
setTranslationKey( "computercraft:peripheral" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||
@@ -55,7 +55,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
||||
@Override
|
||||
@Nonnull
|
||||
@SideOnly( Side.CLIENT)
|
||||
public BlockRenderLayer getBlockLayer()
|
||||
public BlockRenderLayer getRenderLayer()
|
||||
{
|
||||
return BlockRenderLayer.CUTOUT;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
||||
if( meta >= 2 && meta <= 5 )
|
||||
{
|
||||
state = state.withProperty( Properties.VARIANT, BlockPeripheralVariant.DiskDriveEmpty );
|
||||
state = state.withProperty( Properties.FACING, EnumFacing.getFront( meta ) );
|
||||
state = state.withProperty( Properties.FACING, EnumFacing.byIndex( meta ) );
|
||||
}
|
||||
else if( meta <= 9 )
|
||||
{
|
||||
@@ -93,7 +93,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
||||
else
|
||||
{
|
||||
state = state.withProperty( Properties.VARIANT, BlockPeripheralVariant.WirelessModemOff );
|
||||
state = state.withProperty( Properties.FACING, EnumFacing.getFront( meta - 4 ) );
|
||||
state = state.withProperty( Properties.FACING, EnumFacing.byIndex( meta - 4 ) );
|
||||
}
|
||||
}
|
||||
else if( meta == 10 )
|
||||
@@ -651,7 +651,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
||||
public boolean causesSuffocation(IBlockState state)
|
||||
{
|
||||
// This normally uses the default state
|
||||
return blockMaterial.blocksMovement() && state.isOpaqueCube();
|
||||
return material.blocksMovement() && state.isOpaqueCube();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -34,7 +34,7 @@ public class BlockWiredModemFull extends BlockPeripheralBase
|
||||
public BlockWiredModemFull()
|
||||
{
|
||||
setHardness( 1.5f );
|
||||
setUnlocalizedName( "computercraft:wired_modem_full" );
|
||||
setTranslationKey( "computercraft:wired_modem_full" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( blockState.getBaseState()
|
||||
.withProperty( Properties.MODEM_ON, false )
|
||||
|
@@ -21,7 +21,7 @@ public class ItemAdvancedModem extends ItemPeripheralBase
|
||||
public ItemAdvancedModem( Block block )
|
||||
{
|
||||
super( block );
|
||||
setUnlocalizedName( "computercraft:advanced_modem" );
|
||||
setTranslationKey( "computercraft:advanced_modem" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ public class ItemCable extends ItemPeripheralBase
|
||||
public ItemCable( Block block )
|
||||
{
|
||||
super( block );
|
||||
setUnlocalizedName( "computercraft:cable" );
|
||||
setTranslationKey( "computercraft:cable" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -20,7 +20,7 @@ public class ItemPeripheral extends ItemPeripheralBase
|
||||
public ItemPeripheral( Block block )
|
||||
{
|
||||
super( block );
|
||||
setUnlocalizedName( "computercraft:peripheral" );
|
||||
setTranslationKey( "computercraft:peripheral" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -59,7 +59,7 @@ public abstract class ItemPeripheralBase extends ItemBlock implements IPeriphera
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
||||
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||
{
|
||||
PeripheralType type = getPeripheralType( stack );
|
||||
switch( type )
|
||||
|
@@ -112,12 +112,12 @@ public abstract class TilePeripheralBase extends TileGeneric
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int getAnim()
|
||||
public int getAnim()
|
||||
{
|
||||
return m_anim;
|
||||
}
|
||||
|
||||
public synchronized void setAnim( int anim )
|
||||
public void setAnim( int anim )
|
||||
{
|
||||
if( anim != m_anim )
|
||||
{
|
||||
@@ -127,12 +127,12 @@ public abstract class TilePeripheralBase extends TileGeneric
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update()
|
||||
public void update()
|
||||
{
|
||||
if( m_changed )
|
||||
{
|
||||
updateBlock();
|
||||
m_changed = false;
|
||||
updateBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public abstract class TilePeripheralBase extends TileGeneric
|
||||
super.readFromNBT(nbttagcompound);
|
||||
if( nbttagcompound.hasKey( "dir" ) )
|
||||
{
|
||||
m_dir = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
||||
m_dir = EnumFacing.byIndex( nbttagcompound.getInteger( "dir" ) );
|
||||
}
|
||||
if( nbttagcompound.hasKey( "anim" ) )
|
||||
{
|
||||
@@ -174,7 +174,7 @@ public abstract class TilePeripheralBase extends TileGeneric
|
||||
public void readDescription( @Nonnull NBTTagCompound nbttagcompound )
|
||||
{
|
||||
super.readDescription( nbttagcompound );
|
||||
m_dir = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
||||
m_dir = EnumFacing.byIndex( nbttagcompound.getInteger( "dir" ) );
|
||||
m_anim = nbttagcompound.getInteger( "anim" );
|
||||
if( nbttagcompound.hasKey( "label" ) )
|
||||
{
|
||||
|
@@ -80,12 +80,9 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
public void destroy()
|
||||
{
|
||||
ejectContents( true );
|
||||
synchronized( this )
|
||||
if( m_recordPlaying )
|
||||
{
|
||||
if( m_recordPlaying )
|
||||
{
|
||||
stopRecord();
|
||||
}
|
||||
stopRecord();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,13 +167,10 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
super.update();
|
||||
|
||||
// Ejection
|
||||
synchronized( this )
|
||||
if( m_ejectQueued )
|
||||
{
|
||||
if( m_ejectQueued )
|
||||
{
|
||||
ejectContents( false );
|
||||
m_ejectQueued = false;
|
||||
}
|
||||
ejectContents( false );
|
||||
m_ejectQueued = false;
|
||||
}
|
||||
|
||||
// Music
|
||||
@@ -391,10 +385,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
synchronized( this )
|
||||
{
|
||||
setInventorySlotContents( 0, ItemStack.EMPTY );
|
||||
}
|
||||
setInventorySlotContents( 0, ItemStack.EMPTY );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -425,18 +416,12 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
@Nonnull
|
||||
public ItemStack getDiskStack()
|
||||
{
|
||||
synchronized( this )
|
||||
{
|
||||
return getStackInSlot( 0 );
|
||||
}
|
||||
return getStackInSlot( 0 );
|
||||
}
|
||||
|
||||
public void setDiskStack( @Nonnull ItemStack stack )
|
||||
{
|
||||
synchronized( this )
|
||||
{
|
||||
setInventorySlotContents( 0, stack );
|
||||
}
|
||||
setInventorySlotContents( 0, stack );
|
||||
}
|
||||
|
||||
public IMedia getDiskMedia()
|
||||
@@ -569,7 +554,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void updateAnim()
|
||||
private void updateAnim()
|
||||
{
|
||||
if( !m_diskStack.isEmpty() )
|
||||
{
|
||||
@@ -605,8 +590,8 @@ public class TileDiskDrive extends TilePeripheralBase
|
||||
if( !destroyed )
|
||||
{
|
||||
EnumFacing dir = getDirection();
|
||||
xOff = dir.getFrontOffsetX();
|
||||
zOff = dir.getFrontOffsetZ();
|
||||
xOff = dir.getXOffset();
|
||||
zOff = dir.getZOffset();
|
||||
}
|
||||
|
||||
BlockPos pos = getPos();
|
||||
|
@@ -33,7 +33,7 @@ public class BlockAdvancedModem extends BlockPeripheralBase
|
||||
public BlockAdvancedModem()
|
||||
{
|
||||
setHardness( 2.0f );
|
||||
setUnlocalizedName( "computercraft:advanced_modem" );
|
||||
setTranslationKey( "computercraft:advanced_modem" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||
@@ -54,7 +54,7 @@ public class BlockAdvancedModem extends BlockPeripheralBase
|
||||
public IBlockState getStateFromMeta( int meta )
|
||||
{
|
||||
IBlockState state = getDefaultState();
|
||||
state = state.withProperty( Properties.FACING, EnumFacing.getFront( meta ) );
|
||||
state = state.withProperty( Properties.FACING, EnumFacing.byIndex( meta ) );
|
||||
state = state.withProperty( Properties.ON, false );
|
||||
return state;
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ public abstract class ModemPeripheral
|
||||
m_open = false;
|
||||
}
|
||||
|
||||
public synchronized boolean pollChanged()
|
||||
public boolean pollChanged()
|
||||
{
|
||||
if( m_changed )
|
||||
{
|
||||
@@ -85,9 +85,9 @@ public abstract class ModemPeripheral
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean isActive()
|
||||
public boolean isActive()
|
||||
{
|
||||
return (m_computer != null) && m_open;
|
||||
return m_computer != null && m_open;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -108,10 +108,11 @@ public class TileCable extends TileModemBase
|
||||
m_node = m_cable.getNode();
|
||||
return new WiredModemPeripheral( m_cable )
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
protected boolean canSeePeripheral( @Nonnull String peripheralName )
|
||||
protected WiredModemLocalPeripheral getLocalPeripheral()
|
||||
{
|
||||
return !peripheralName.equals( m_peripheral.getConnectedName() );
|
||||
return m_peripheral;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -37,7 +37,7 @@ public abstract class TileModemBase extends TilePeripheralBase
|
||||
protected abstract ModemPeripheral createPeripheral();
|
||||
|
||||
@Override
|
||||
public synchronized void destroy()
|
||||
public void destroy()
|
||||
{
|
||||
if( m_modem != null )
|
||||
{
|
||||
|
@@ -401,12 +401,14 @@ public class TileWiredModemFull extends TilePeripheralBase
|
||||
WiredModemPeripheral peripheral = m_modems[side.ordinal()];
|
||||
if( peripheral == null )
|
||||
{
|
||||
WiredModemLocalPeripheral localPeripheral = m_peripherals[side.ordinal()];
|
||||
peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_element )
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
protected boolean canSeePeripheral( @Nonnull String peripheralName )
|
||||
protected WiredModemLocalPeripheral getLocalPeripheral()
|
||||
{
|
||||
return !peripheralName.equals( m_peripherals[side.ordinal()].getConnectedName() );
|
||||
return localPeripheral;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -10,6 +10,8 @@ import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.network.wired.IWiredSender;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerOwned;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -56,7 +58,8 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
return modem.getWorld();
|
||||
}
|
||||
|
||||
protected abstract boolean canSeePeripheral( @Nonnull String peripheralName );
|
||||
@Nonnull
|
||||
protected abstract WiredModemLocalPeripheral getLocalPeripheral();
|
||||
//endregion
|
||||
|
||||
//region IPeripheral
|
||||
@@ -65,13 +68,14 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
String[] methods = super.getMethodNames();
|
||||
String[] newMethods = new String[methods.length + 5];
|
||||
String[] newMethods = new String[methods.length + 6];
|
||||
System.arraycopy( methods, 0, newMethods, 0, methods.length );
|
||||
newMethods[methods.length] = "getNamesRemote";
|
||||
newMethods[methods.length + 1] = "isPresentRemote";
|
||||
newMethods[methods.length + 2] = "getTypeRemote";
|
||||
newMethods[methods.length + 3] = "getMethodsRemote";
|
||||
newMethods[methods.length + 4] = "callRemote";
|
||||
newMethods[methods.length + 5] = "getNameLocal";
|
||||
return newMethods;
|
||||
}
|
||||
|
||||
@@ -135,6 +139,12 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 );
|
||||
return callMethodRemote( remoteName, context, methodName, methodArgs );
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
// getNameLocal
|
||||
String local = getLocalPeripheral().getConnectedName();
|
||||
return local == null ? null : new Object[]{ local };
|
||||
}
|
||||
default:
|
||||
{
|
||||
// The regular modem methods
|
||||
@@ -217,7 +227,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
|
||||
private void attachPeripheralImpl( String periphName, IPeripheral peripheral )
|
||||
{
|
||||
if( !peripheralWrappers.containsKey( periphName ) && canSeePeripheral( periphName ) )
|
||||
if( !peripheralWrappers.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) )
|
||||
{
|
||||
RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, getComputer(), periphName );
|
||||
peripheralWrappers.put( periphName, wrapper );
|
||||
@@ -265,7 +275,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
throw new LuaException( "No peripheral: " + remoteName );
|
||||
}
|
||||
|
||||
private static class RemotePeripheralWrapper implements IComputerAccess
|
||||
private static class RemotePeripheralWrapper implements IComputerAccess, IComputerOwned
|
||||
{
|
||||
private final WiredModemElement m_element;
|
||||
private final IPeripheral m_peripheral;
|
||||
@@ -400,5 +410,12 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
||||
return m_element.getRemotePeripherals().get( name );
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Computer getComputer()
|
||||
{
|
||||
return m_computer instanceof IComputerOwned ? ((IComputerOwned) m_computer).getComputer() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -66,7 +66,8 @@ public class MonitorPeripheral implements IPeripheral
|
||||
"setPaletteColor",
|
||||
"getPaletteColour",
|
||||
"getPaletteColor",
|
||||
"getTextScale"
|
||||
"getTextScale",
|
||||
"getCursorBlink",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -245,8 +246,12 @@ public class MonitorPeripheral implements IPeripheral
|
||||
// getTextScale
|
||||
return new Object[] { monitor.getTextScale() / 2.0 };
|
||||
}
|
||||
case 25:
|
||||
// getCursorBlink
|
||||
return new Object[] { terminal.getCursorBlink() };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -344,7 +344,7 @@ public class TileMonitor extends TilePeripheralBase
|
||||
|
||||
public EnumFacing getFront()
|
||||
{
|
||||
return m_dir <= 5 ? EnumFacing.getFront( m_dir ) : (m_dir <= 11 ? EnumFacing.DOWN : EnumFacing.UP);
|
||||
return m_dir <= 5 ? EnumFacing.byIndex( m_dir ) : (m_dir <= 11 ? EnumFacing.DOWN : EnumFacing.UP);
|
||||
}
|
||||
|
||||
public EnumFacing getRight()
|
||||
|
@@ -190,10 +190,7 @@ public class TilePrinter extends TilePeripheralBase
|
||||
@Override
|
||||
public ItemStack getStackInSlot(int i)
|
||||
{
|
||||
synchronized( m_inventory )
|
||||
{
|
||||
return m_inventory.get( i );
|
||||
}
|
||||
return m_inventory.get( i );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -8,10 +8,11 @@ package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaTask;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import net.minecraft.network.play.server.SPacketCustomSound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
@@ -20,33 +21,35 @@ import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optReal;
|
||||
|
||||
public class SpeakerPeripheral implements IPeripheral {
|
||||
private TileSpeaker m_speaker;
|
||||
public class SpeakerPeripheral implements IPeripheral
|
||||
{
|
||||
private final TileSpeaker m_speaker;
|
||||
private long m_clock;
|
||||
private long m_lastPlayTime;
|
||||
private int m_notesThisTick;
|
||||
private final AtomicInteger m_notesThisTick;
|
||||
|
||||
public SpeakerPeripheral()
|
||||
{
|
||||
m_clock = 0;
|
||||
m_lastPlayTime = 0;
|
||||
m_notesThisTick = 0;
|
||||
this( null );
|
||||
}
|
||||
|
||||
SpeakerPeripheral(TileSpeaker speaker)
|
||||
SpeakerPeripheral( TileSpeaker speaker )
|
||||
{
|
||||
this();
|
||||
m_clock = 0;
|
||||
m_lastPlayTime = 0;
|
||||
m_notesThisTick = new AtomicInteger( );
|
||||
m_speaker = speaker;
|
||||
}
|
||||
|
||||
public synchronized void update()
|
||||
public void update()
|
||||
{
|
||||
m_clock++;
|
||||
m_notesThisTick = 0;
|
||||
m_notesThisTick.set( 0 );
|
||||
}
|
||||
|
||||
public World getWorld()
|
||||
@@ -59,9 +62,9 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
return m_speaker.getPos();
|
||||
}
|
||||
|
||||
public synchronized boolean madeSound(long ticks)
|
||||
public boolean madeSound(long ticks)
|
||||
{
|
||||
return (m_clock - m_lastPlayTime <= ticks) ;
|
||||
return m_clock - m_lastPlayTime <= ticks;
|
||||
}
|
||||
|
||||
/* IPeripheral implementation */
|
||||
@@ -69,15 +72,9 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
@Override
|
||||
public boolean equals( IPeripheral other )
|
||||
{
|
||||
if( other != null && other instanceof SpeakerPeripheral )
|
||||
{
|
||||
SpeakerPeripheral otherSpeaker = (SpeakerPeripheral) other;
|
||||
return otherSpeaker.m_speaker == m_speaker;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( other == this ) return true;
|
||||
if( !(other instanceof SpeakerPeripheral) ) return false;
|
||||
return m_speaker == ((SpeakerPeripheral) other).m_speaker;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -92,8 +89,8 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
"playSound", // Plays sound at resourceLocator
|
||||
"playNote" // Plays note
|
||||
"playSound", // Plays sound at resourceLocator
|
||||
"playNote" // Plays note
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,18 +102,22 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
// playSound
|
||||
case 0:
|
||||
{
|
||||
return playSound(args, context, false);
|
||||
String name = getString( args, 0 );
|
||||
float volume = (float) optReal( args, 1, 1.0 );
|
||||
float pitch = (float) optReal( args, 2, 1.0 );
|
||||
|
||||
return new Object[] { playSound( context, name, volume, pitch, false ) };
|
||||
}
|
||||
|
||||
// playNote
|
||||
case 1:
|
||||
{
|
||||
return playNote(args, context);
|
||||
return playNote( args, context );
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new LuaException("Method index out of range!");
|
||||
throw new LuaException( "Method index out of range!" );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -125,75 +126,53 @@ public class SpeakerPeripheral implements IPeripheral {
|
||||
@Nonnull
|
||||
private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException
|
||||
{
|
||||
String name = getString(arguments, 0);
|
||||
String name = getString( arguments, 0 );
|
||||
float volume = (float) optReal( arguments, 1, 1.0 );
|
||||
float pitch = (float) optReal( arguments, 2, 1.0 );
|
||||
|
||||
String noteName = "block.note." + name;
|
||||
|
||||
// Check if sound exists
|
||||
if ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + name ) ) )
|
||||
// Check if the note exists
|
||||
if( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( noteName ) ) )
|
||||
{
|
||||
throw new LuaException("Invalid instrument, \"" + arguments[0] + "\"!");
|
||||
throw new LuaException( "Invalid instrument, \"" + name + "\"!" );
|
||||
}
|
||||
|
||||
// If the resource location for note block notes changes, this method call will need to be updated
|
||||
Object[] returnValue = playSound(
|
||||
new Object[] {
|
||||
"block.note." + name,
|
||||
(double)Math.min( volume, 3f ),
|
||||
Math.pow( 2.0f, ( pitch - 12.0f ) / 12.0f)
|
||||
}, context, true
|
||||
);
|
||||
boolean success = playSound( context, noteName, volume, (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true );
|
||||
|
||||
if( returnValue[0] instanceof Boolean && (Boolean) returnValue[0] )
|
||||
{
|
||||
m_notesThisTick++;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
if( success ) m_notesThisTick.incrementAndGet();
|
||||
return new Object[] { success };
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private synchronized Object[] playSound( Object[] arguments, ILuaContext context, boolean isNote ) throws LuaException
|
||||
private synchronized boolean playSound( ILuaContext context, String name, float volume, float pitch, boolean isNote ) throws LuaException
|
||||
{
|
||||
String name = getString(arguments, 0);
|
||||
float volume = (float) optReal( arguments, 1, 1.0 );
|
||||
float pitch = (float) optReal( arguments, 2, 1.0 );
|
||||
|
||||
ResourceLocation resourceName = new ResourceLocation( name );
|
||||
|
||||
if( m_clock - m_lastPlayTime >= TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS || ( ( m_clock - m_lastPlayTime == 0 ) && ( m_notesThisTick < ComputerCraft.maxNotesPerTick ) && isNote ) )
|
||||
if( m_clock - m_lastPlayTime < TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS &&
|
||||
(!isNote || m_clock - m_lastPlayTime != 0 || m_notesThisTick.get() >= ComputerCraft.maxNotesPerTick) )
|
||||
{
|
||||
if( SoundEvent.REGISTRY.containsKey(resourceName) )
|
||||
{
|
||||
final World world = getWorld();
|
||||
final BlockPos pos = getPos();
|
||||
final ResourceLocation resource = resourceName;
|
||||
final float vol = volume;
|
||||
final float soundPitch = pitch;
|
||||
|
||||
context.issueMainThreadTask(new ILuaTask()
|
||||
{
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] execute()
|
||||
{
|
||||
world.playSound( null, pos, SoundEvent.REGISTRY.getObject( resource ), SoundCategory.RECORDS, Math.min( vol, 3f ), soundPitch );
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
m_lastPlayTime = m_clock;
|
||||
return new Object[]{true}; // Success, return true
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Object[]{false}; // Failed - sound not existent, return false
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Object[]{false}; // Failed - rate limited, return false
|
||||
// Rate limiting occurs when we've already played a sound within the last tick, or we've
|
||||
// played more notes than allowable within the current tick.
|
||||
return false;
|
||||
}
|
||||
|
||||
World world = getWorld();
|
||||
BlockPos pos = getPos();
|
||||
|
||||
context.issueMainThreadTask( () -> {
|
||||
MinecraftServer server = world.getMinecraftServer();
|
||||
if( server == null ) return null;
|
||||
|
||||
double x = pos.getX() + 0.5, y = pos.getY() + 0.5, z = pos.getZ() + 0.5;
|
||||
float adjVolume = Math.min( volume, 3.0f );
|
||||
server.getPlayerList().sendToAllNearExcept(
|
||||
null, x, y, z, adjVolume > 1.0f ? 16 * adjVolume : 16.0, world.provider.getDimension(),
|
||||
new SPacketCustomSound( name, SoundCategory.RECORDS, x, y, z, adjVolume, pitch )
|
||||
);
|
||||
return null;
|
||||
} );
|
||||
|
||||
m_lastPlayTime = m_clock;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -25,7 +25,7 @@ public class TileSpeaker extends TilePeripheralBase
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update()
|
||||
public void update()
|
||||
{
|
||||
m_peripheral.update();
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -138,7 +139,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
{
|
||||
if( this.m_upgrade == upgrade ) return;
|
||||
|
||||
synchronized (this)
|
||||
synchronized( this )
|
||||
{
|
||||
ComputerCraft.Items.pocketComputer.setUpgrade( m_stack, upgrade );
|
||||
if( m_entity instanceof EntityPlayer ) ((EntityPlayer) m_entity).inventory.markDirty();
|
||||
@@ -156,6 +157,9 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
setPosition( entity.getPosition() );
|
||||
}
|
||||
|
||||
// If a new entity has picked it up then rebroadcast the terminal to them
|
||||
if( entity != m_entity && entity instanceof EntityPlayerMP ) markTerminalChanged();
|
||||
|
||||
m_entity = entity;
|
||||
m_stack = stack;
|
||||
|
||||
@@ -165,4 +169,19 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
invalidatePeripheral();
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastState( boolean force )
|
||||
{
|
||||
super.broadcastState( force );
|
||||
|
||||
if( (hasTerminalChanged() || force) && m_entity instanceof EntityPlayerMP )
|
||||
{
|
||||
// Broadcast the state to the current entity if they're not already interacting with it.
|
||||
EntityPlayerMP player = (EntityPlayerMP) m_entity;
|
||||
if( player.connection != null && !isInteracting( player ) )
|
||||
{
|
||||
ComputerCraft.sendToPlayer( player, createTerminalPacket() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
{
|
||||
setMaxStackSize( 1 );
|
||||
setHasSubtypes( true );
|
||||
setUnlocalizedName( "computercraft:pocket_computer" );
|
||||
setTranslationKey( "computercraft:pocket_computer" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
||||
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||
{
|
||||
switch( getFamily( stack ) )
|
||||
{
|
||||
@@ -209,7 +209,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
@Override
|
||||
public String getItemStackDisplayName( @Nonnull ItemStack stack )
|
||||
{
|
||||
String baseString = getUnlocalizedName( stack );
|
||||
String baseString = getTranslationKey( stack );
|
||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||
if( upgrade != null )
|
||||
{
|
||||
|
@@ -21,7 +21,6 @@ import dan200.computercraft.shared.turtle.items.ItemTurtleNormal;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.IEntityDropConsumer;
|
||||
import dan200.computercraft.shared.util.ImpostorRecipe;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -33,33 +32,49 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.registry.EntityRegistry;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import net.minecraftforge.registries.IForgeRegistry;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
{
|
||||
{
|
||||
private Map<Integer, ITurtleUpgrade> m_legacyTurtleUpgrades;
|
||||
private Map<String, ITurtleUpgrade> m_turtleUpgrades;
|
||||
private Map<Entity, IEntityDropConsumer> m_dropConsumers;
|
||||
|
||||
private Function<ItemStack, ItemStack> dropConsumer;
|
||||
private List<ItemStack> remainingDrops;
|
||||
private WeakReference<World> dropWorld;
|
||||
private BlockPos dropPos;
|
||||
private AxisAlignedBB dropBounds;
|
||||
private WeakReference<Entity> dropEntity;
|
||||
|
||||
public CCTurtleProxyCommon()
|
||||
{
|
||||
m_legacyTurtleUpgrades = new HashMap<>();
|
||||
m_turtleUpgrades = new HashMap<>();
|
||||
m_dropConsumers = new WeakHashMap<>();
|
||||
}
|
||||
|
||||
|
||||
// ICCTurtleProxy implementation
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void preInit()
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.register( this );
|
||||
@@ -75,8 +90,8 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
// RecipeSorter.register( "computercraft:turtle", TurtleRecipe.class, RecipeSorter.Category.SHAPED, "after:minecraft:shapeless" );
|
||||
// RecipeSorter.register( "computercraft:turtle_upgrade", TurtleUpgradeRecipe.class, RecipeSorter.Category.SHAPED, "after:minecraft:shapeless" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
registerForgeHandlers();
|
||||
@@ -94,7 +109,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
ComputerCraft.log.error( message );
|
||||
throw new RuntimeException( message );
|
||||
}
|
||||
|
||||
|
||||
// Register
|
||||
registerTurtleUpgradeInternal( upgrade );
|
||||
}
|
||||
@@ -110,7 +125,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
{
|
||||
return m_legacyTurtleUpgrades.get( legacyId );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ITurtleUpgrade getTurtleUpgrade( @Nonnull ItemStack stack )
|
||||
{
|
||||
@@ -126,7 +141,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ComputerCraft.log.error("Error getting computer upgrade item", e);
|
||||
ComputerCraft.log.error( "Error getting computer upgrade item", e );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -148,7 +163,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addAllUpgradedTurtles( ComputerFamily family, NonNullList<ItemStack> list )
|
||||
{
|
||||
ItemStack basicStack = TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null );
|
||||
@@ -169,7 +184,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
|
||||
private void addUpgradedTurtle( ComputerFamily family, ITurtleUpgrade upgrade, List<ItemStack> list )
|
||||
{
|
||||
if ( isUpgradeSuitableForFamily( family, upgrade ) )
|
||||
if( isUpgradeSuitableForFamily( family, upgrade ) )
|
||||
{
|
||||
ItemStack stack = TurtleItemFactory.create( -1, null, -1, family, upgrade, null, 0, null );
|
||||
if( !stack.isEmpty() )
|
||||
@@ -178,54 +193,65 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addAllUpgradedTurtles( NonNullList<ItemStack> list )
|
||||
{
|
||||
addAllUpgradedTurtles( ComputerFamily.Normal, list );
|
||||
addAllUpgradedTurtles( ComputerFamily.Advanced, list );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setEntityDropConsumer( Entity entity, IEntityDropConsumer consumer )
|
||||
public void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer )
|
||||
{
|
||||
if( !m_dropConsumers.containsKey( entity ) )
|
||||
{
|
||||
boolean captured = entity.captureDrops;
|
||||
|
||||
if( !captured )
|
||||
{
|
||||
entity.captureDrops = true;
|
||||
ArrayList<EntityItem> items = entity.capturedDrops;
|
||||
|
||||
if( items == null || items.size() == 0 )
|
||||
{
|
||||
m_dropConsumers.put( entity, consumer );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropConsumer = consumer;
|
||||
remainingDrops = new ArrayList<>();
|
||||
dropEntity = new WeakReference<>( entity );
|
||||
dropWorld = new WeakReference<>( entity.world );
|
||||
dropPos = null;
|
||||
dropBounds = new AxisAlignedBB( entity.getPosition() ).grow( 2, 2, 2 );
|
||||
|
||||
entity.captureDrops = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearEntityDropConsumer( Entity entity )
|
||||
public void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer )
|
||||
{
|
||||
if( m_dropConsumers.containsKey( entity ) )
|
||||
dropConsumer = consumer;
|
||||
remainingDrops = new ArrayList<>();
|
||||
dropEntity = null;
|
||||
dropWorld = new WeakReference<>( world );
|
||||
dropPos = pos;
|
||||
dropBounds = new AxisAlignedBB( pos ).grow( 2, 2, 2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> clearDropConsumer()
|
||||
{
|
||||
if( dropEntity != null )
|
||||
{
|
||||
boolean captured = entity.captureDrops;
|
||||
|
||||
if( captured )
|
||||
Entity entity = dropEntity.get();
|
||||
if( entity != null )
|
||||
{
|
||||
entity.captureDrops = false;
|
||||
ArrayList<EntityItem> items = entity.capturedDrops;
|
||||
|
||||
if( items != null )
|
||||
if( entity.capturedDrops != null )
|
||||
{
|
||||
dispatchEntityDrops( entity, items );
|
||||
items.clear();
|
||||
for( EntityItem entityItem : entity.capturedDrops ) handleDrops( entityItem.getItem() );
|
||||
entity.capturedDrops.clear();
|
||||
}
|
||||
}
|
||||
m_dropConsumers.remove( entity );
|
||||
}
|
||||
|
||||
List<ItemStack> remainingStacks = remainingDrops;
|
||||
|
||||
dropConsumer = null;
|
||||
remainingDrops = null;
|
||||
dropEntity = null;
|
||||
dropWorld = null;
|
||||
dropPos = null;
|
||||
dropBounds = null;
|
||||
|
||||
return remainingStacks;
|
||||
}
|
||||
|
||||
private void registerTurtleUpgradeInternal( ITurtleUpgrade upgrade )
|
||||
@@ -289,7 +315,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
{
|
||||
IForgeRegistry<Item> registry = event.getRegistry();
|
||||
|
||||
registry.register( new ItemTurtleLegacy( ComputerCraft.Blocks.turtle).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ) ) );
|
||||
registry.register( new ItemTurtleLegacy( ComputerCraft.Blocks.turtle ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ) ) );
|
||||
registry.register( new ItemTurtleNormal( ComputerCraft.Blocks.turtleExpanded ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_expanded" ) ) );
|
||||
registry.register( new ItemTurtleAdvanced( ComputerCraft.Blocks.turtleAdvanced ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_advanced" ) ) );
|
||||
}
|
||||
@@ -362,7 +388,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
private void registerUpgrades()
|
||||
{
|
||||
// Upgrades
|
||||
ComputerCraft.Upgrades.wirelessModem = new TurtleModem( false, new ResourceLocation( "computercraft", "wireless_modem" ), 1 );
|
||||
ComputerCraft.Upgrades.wirelessModem = new TurtleModem( false, new ResourceLocation( "computercraft", "wireless_modem" ), 1 );
|
||||
registerTurtleUpgradeInternal( ComputerCraft.Upgrades.wirelessModem );
|
||||
|
||||
ComputerCraft.Upgrades.craftingTable = new TurtleCraftingTable( 2 );
|
||||
@@ -396,10 +422,10 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
// We have to use mappings.getAllMappings() as the mod ID is upper case but the domain lower.
|
||||
for( RegistryEvent.MissingMappings.Mapping<Item> mapping : mappings.getAllMappings() )
|
||||
{
|
||||
String domain = mapping.key.getResourceDomain();
|
||||
String domain = mapping.key.getNamespace();
|
||||
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||
|
||||
String key = mapping.key.getResourcePath();
|
||||
String key = mapping.key.getPath();
|
||||
if( key.equalsIgnoreCase( "CC-Turtle" ) )
|
||||
{
|
||||
mapping.remap( Item.getItemFromBlock( ComputerCraft.Blocks.turtle ) );
|
||||
@@ -421,10 +447,10 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
// We have to use mappings.getAllMappings() as the mod ID is upper case but the domain lower.
|
||||
for( RegistryEvent.MissingMappings.Mapping<Block> mapping : mappings.getAllMappings() )
|
||||
{
|
||||
String domain = mapping.key.getResourceDomain();
|
||||
String domain = mapping.key.getNamespace();
|
||||
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||
|
||||
String key = mapping.key.getResourcePath();
|
||||
String key = mapping.key.getPath();
|
||||
if( key.equalsIgnoreCase( "CC-Turtle" ) )
|
||||
{
|
||||
mapping.remap( ComputerCraft.Blocks.turtle );
|
||||
@@ -447,26 +473,60 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
GameRegistry.registerTileEntity( TileTurtleExpanded.class, ComputerCraft.LOWER_ID + " : " + "turtleex" );
|
||||
GameRegistry.registerTileEntity( TileTurtleAdvanced.class, ComputerCraft.LOWER_ID + " : " + "turtleadv" );
|
||||
}
|
||||
|
||||
|
||||
private void registerForgeHandlers()
|
||||
{
|
||||
ForgeHandlers handlers = new ForgeHandlers();
|
||||
MinecraftForge.EVENT_BUS.register( handlers );
|
||||
}
|
||||
|
||||
public class ForgeHandlers
|
||||
{
|
||||
private ForgeHandlers()
|
||||
{
|
||||
}
|
||||
|
||||
// Forge event responses
|
||||
@SubscribeEvent
|
||||
private void handleDrops(ItemStack stack)
|
||||
{
|
||||
ItemStack remaining = dropConsumer.apply(stack);
|
||||
if (!remaining.isEmpty()) remainingDrops.add(remaining);
|
||||
}
|
||||
|
||||
private class ForgeHandlers
|
||||
{
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onEntityLivingDrops( LivingDropsEvent event )
|
||||
{
|
||||
dispatchEntityDrops( event.getEntity(), event.getDrops() );
|
||||
// Capture any mob drops for the current entity
|
||||
if( dropEntity != null && event.getEntity() == dropEntity.get() )
|
||||
{
|
||||
List<EntityItem> drops = event.getDrops();
|
||||
for( EntityItem entityItem : drops ) handleDrops( entityItem.getItem() );
|
||||
drops.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onHarvestDrops( BlockEvent.HarvestDropsEvent event )
|
||||
{
|
||||
// Capture block drops for the current entity
|
||||
if( dropWorld != null && dropWorld.get() == event.getWorld()
|
||||
&& dropPos != null && dropPos.equals( event.getPos() ) )
|
||||
{
|
||||
for( ItemStack item : event.getDrops() )
|
||||
{
|
||||
if( event.getWorld().rand.nextFloat() < event.getDropChance() ) handleDrops( item );
|
||||
}
|
||||
event.getDrops().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onEntitySpawn( EntityJoinWorldEvent event )
|
||||
{
|
||||
// Capture any nearby item spawns
|
||||
if( dropWorld != null && dropWorld.get() == event.getWorld() && event.getEntity() instanceof EntityItem
|
||||
&& dropBounds.contains( event.getEntity().getPositionVector() ) )
|
||||
{
|
||||
handleDrops( ((EntityItem) event.getEntity()).getItem() );
|
||||
event.setCanceled( true );
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTurtleAction( TurtleActionEvent event) {
|
||||
if( ComputerCraft.turtleDisabledActions.contains( event.getAction() ) )
|
||||
@@ -475,18 +535,5 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchEntityDrops( Entity entity, java.util.List<EntityItem> drops )
|
||||
{
|
||||
IEntityDropConsumer consumer = m_dropConsumers.get( entity );
|
||||
if( consumer != null )
|
||||
{
|
||||
// All checks have passed, lets dispatch the drops
|
||||
for(EntityItem entityItem : drops)
|
||||
{
|
||||
consumer.consumeDrop( entity, entityItem.getItem() );
|
||||
}
|
||||
drops.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import dan200.computercraft.shared.computer.core.*;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||
import dan200.computercraft.shared.integration.charset.IntegrationCharset;
|
||||
import dan200.computercraft.shared.media.common.DefaultMediaProvider;
|
||||
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
|
||||
@@ -76,6 +77,7 @@ import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
|
||||
@@ -83,6 +85,7 @@ import net.minecraftforge.fml.common.network.IGuiHandler;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import net.minecraftforge.registries.IForgeRegistry;
|
||||
import pl.asie.charset.ModCharset;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
@@ -117,6 +120,8 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
{
|
||||
registerTileEntities();
|
||||
registerForgeHandlers();
|
||||
|
||||
if( Loader.isModLoaded( ModCharset.MODID ) ) IntegrationCharset.register();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -414,10 +419,10 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
// We have to use mappings.getAllMappings() as the mod ID is upper case but the domain lower.
|
||||
for( RegistryEvent.MissingMappings.Mapping<Item> mapping : mappings.getAllMappings() )
|
||||
{
|
||||
String domain = mapping.key.getResourceDomain();
|
||||
String domain = mapping.key.getNamespace();
|
||||
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||
|
||||
String key = mapping.key.getResourcePath();
|
||||
String key = mapping.key.getPath();
|
||||
if( key.equalsIgnoreCase( "CC-Computer" ) )
|
||||
{
|
||||
mapping.remap( Item.getItemFromBlock( ComputerCraft.Blocks.computer ) );
|
||||
@@ -451,10 +456,10 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
||||
// We have to use mappings.getAllMappings() as the mod ID is upper case but the domain lower.
|
||||
for( RegistryEvent.MissingMappings.Mapping<Block> mapping : mappings.getAllMappings() )
|
||||
{
|
||||
String domain = mapping.key.getResourceDomain();
|
||||
String domain = mapping.key.getNamespace();
|
||||
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||
|
||||
String key = mapping.key.getResourcePath();
|
||||
String key = mapping.key.getPath();
|
||||
if( key.equalsIgnoreCase( "CC-Computer" ) )
|
||||
{
|
||||
mapping.remap( ComputerCraft.Blocks.computer );
|
||||
|
@@ -7,12 +7,16 @@
|
||||
package dan200.computercraft.shared.proxy;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.shared.util.IEntityDropConsumer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface ICCTurtleProxy
|
||||
{
|
||||
@@ -25,6 +29,7 @@ public interface ICCTurtleProxy
|
||||
ITurtleUpgrade getTurtleUpgrade( @Nonnull ItemStack item );
|
||||
void addAllUpgradedTurtles( NonNullList<ItemStack> list );
|
||||
|
||||
void setEntityDropConsumer( Entity entity, IEntityDropConsumer consumer );
|
||||
void clearEntityDropConsumer( Entity entity );
|
||||
void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer );
|
||||
void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer );
|
||||
List<ItemStack> clearDropConsumer();
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@ public class BlockTurtle extends BlockComputerBase
|
||||
{
|
||||
super( Material.IRON );
|
||||
setHardness( 2.5f );
|
||||
setUnlocalizedName( "computercraft:turtle" );
|
||||
setTranslationKey( "computercraft:turtle" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
setDefaultState( this.blockState.getBaseState()
|
||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||
|
@@ -127,6 +127,8 @@ public class TurtleBrain implements ITurtleAccess
|
||||
private TurtleAnimation m_animation;
|
||||
private int m_animationProgress;
|
||||
private int m_lastAnimationProgress;
|
||||
|
||||
TurtlePlayer m_cachedPlayer;
|
||||
|
||||
public TurtleBrain( TileTurtle turtle )
|
||||
{
|
||||
@@ -224,7 +226,7 @@ public class TurtleBrain implements ITurtleAccess
|
||||
public void readFromNBT( NBTTagCompound nbttagcompound )
|
||||
{
|
||||
// Read state
|
||||
m_direction = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
||||
m_direction = EnumFacing.byIndex( nbttagcompound.getInteger( "dir" ) );
|
||||
m_selectedSlot = nbttagcompound.getInteger( "selectedSlot" );
|
||||
if( nbttagcompound.hasKey( "fuelLevel" ) )
|
||||
{
|
||||
@@ -368,8 +370,8 @@ public class TurtleBrain implements ITurtleAccess
|
||||
// Write overlay
|
||||
if( m_overlay != null )
|
||||
{
|
||||
nbttagcompound.setString( "overlay_mod", m_overlay.getResourceDomain() );
|
||||
nbttagcompound.setString( "overlay_path", m_overlay.getResourcePath() );
|
||||
nbttagcompound.setString( "overlay_mod", m_overlay.getNamespace() );
|
||||
nbttagcompound.setString( "overlay_path", m_overlay.getPath() );
|
||||
}
|
||||
|
||||
// Write NBT
|
||||
@@ -427,8 +429,8 @@ public class TurtleBrain implements ITurtleAccess
|
||||
// Overlay
|
||||
if( m_overlay != null )
|
||||
{
|
||||
nbttagcompound.setString( "overlay_mod", m_overlay.getResourceDomain() );
|
||||
nbttagcompound.setString( "overlay_path", m_overlay.getResourcePath() );
|
||||
nbttagcompound.setString( "overlay_mod", m_overlay.getNamespace() );
|
||||
nbttagcompound.setString( "overlay_path", m_overlay.getPath() );
|
||||
}
|
||||
|
||||
// Animation
|
||||
@@ -503,7 +505,7 @@ public class TurtleBrain implements ITurtleAccess
|
||||
m_lastAnimationProgress = 0;
|
||||
}
|
||||
|
||||
m_direction = EnumFacing.getFront( nbttagcompound.getInteger( "direction" ) );
|
||||
m_direction = EnumFacing.byIndex( nbttagcompound.getInteger( "direction" ) );
|
||||
m_fuelLevel = nbttagcompound.getInteger( "fuelLevel" );
|
||||
}
|
||||
|
||||
@@ -996,9 +998,9 @@ public class TurtleBrain implements ITurtleAccess
|
||||
|
||||
double distance = -1.0 + getAnimationFraction( f );
|
||||
return new Vec3d(
|
||||
distance * dir.getFrontOffsetX(),
|
||||
distance * dir.getFrontOffsetY(),
|
||||
distance * dir.getFrontOffsetZ()
|
||||
distance * dir.getXOffset(),
|
||||
distance * dir.getYOffset(),
|
||||
distance * dir.getZOffset()
|
||||
);
|
||||
}
|
||||
default:
|
||||
@@ -1182,31 +1184,31 @@ public class TurtleBrain implements ITurtleAccess
|
||||
|
||||
float pushFrac = 1.0f - ((float)(m_animationProgress + 1) / (float)ANIM_DURATION);
|
||||
float push = Math.max( pushFrac + 0.0125f, 0.0f );
|
||||
if (moveDir.getFrontOffsetX() < 0)
|
||||
if (moveDir.getXOffset() < 0)
|
||||
{
|
||||
minX += moveDir.getFrontOffsetX() * push;
|
||||
minX += moveDir.getXOffset() * push;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxX -= moveDir.getFrontOffsetX() * push;
|
||||
maxX -= moveDir.getXOffset() * push;
|
||||
}
|
||||
|
||||
if (moveDir.getFrontOffsetY() < 0)
|
||||
if (moveDir.getYOffset() < 0)
|
||||
{
|
||||
minY += moveDir.getFrontOffsetY() * push;
|
||||
minY += moveDir.getYOffset() * push;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxY -= moveDir.getFrontOffsetY() * push;
|
||||
maxY -= moveDir.getYOffset() * push;
|
||||
}
|
||||
|
||||
if (moveDir.getFrontOffsetZ() < 0)
|
||||
if (moveDir.getZOffset() < 0)
|
||||
{
|
||||
minZ += moveDir.getFrontOffsetZ() * push;
|
||||
minZ += moveDir.getZOffset() * push;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxZ -= moveDir.getFrontOffsetZ() * push;
|
||||
maxZ -= moveDir.getZOffset() * push;
|
||||
}
|
||||
|
||||
AxisAlignedBB aabb = new AxisAlignedBB( minX, minY, minZ, maxX, maxY, maxZ );
|
||||
@@ -1214,9 +1216,9 @@ public class TurtleBrain implements ITurtleAccess
|
||||
if( !list.isEmpty() )
|
||||
{
|
||||
double pushStep = 1.0f / ANIM_DURATION;
|
||||
double pushStepX = moveDir.getFrontOffsetX() * pushStep;
|
||||
double pushStepY = moveDir.getFrontOffsetY() * pushStep;
|
||||
double pushStepZ = moveDir.getFrontOffsetZ() * pushStep;
|
||||
double pushStepX = moveDir.getXOffset() * pushStep;
|
||||
double pushStepY = moveDir.getYOffset() * pushStep;
|
||||
double pushStepZ = moveDir.getZOffset() * pushStep;
|
||||
for (Entity entity : list)
|
||||
{
|
||||
entity.move( MoverType.PISTON, pushStepX, pushStepY, pushStepZ );
|
||||
|
@@ -126,7 +126,7 @@ public class TurtleCompareCommand implements ITurtleCommand
|
||||
{
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
else if( selectedStack.getUnlocalizedName().equals( lookAtStack.getUnlocalizedName() ) )
|
||||
else if( selectedStack.getTranslationKey().equals( lookAtStack.getTranslationKey() ) )
|
||||
{
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
|
@@ -87,9 +87,9 @@ public class TurtleMoveCommand implements ITurtleCommand
|
||||
if( entityBB != null )
|
||||
{
|
||||
AxisAlignedBB pushedBB = entityBB.offset(
|
||||
direction.getFrontOffsetX(),
|
||||
direction.getFrontOffsetY(),
|
||||
direction.getFrontOffsetZ()
|
||||
direction.getXOffset(),
|
||||
direction.getYOffset(),
|
||||
direction.getZOffset()
|
||||
);
|
||||
if( !oldWorld.getCollisionBoxes( null, pushedBB ).isEmpty() )
|
||||
{
|
||||
|
@@ -30,7 +30,6 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
@@ -38,6 +37,7 @@ import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public class TurtlePlaceCommand implements ITurtleCommand
|
||||
{
|
||||
@@ -180,12 +180,12 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
|
||||
public static TurtlePlayer createPlayer( ITurtleAccess turtle, BlockPos position, EnumFacing direction )
|
||||
{
|
||||
TurtlePlayer turtlePlayer = new TurtlePlayer( turtle );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.get( turtle );
|
||||
orientPlayer( turtle, turtlePlayer, position, direction );
|
||||
return turtlePlayer;
|
||||
}
|
||||
|
||||
public static void orientPlayer( ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, EnumFacing direction )
|
||||
private static void orientPlayer( ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, EnumFacing direction )
|
||||
{
|
||||
turtlePlayer.posX = position.getX() + 0.5;
|
||||
turtlePlayer.posY = position.getY() + 0.5;
|
||||
@@ -194,9 +194,9 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
// Stop intersection with the turtle itself
|
||||
if( turtle.getPosition().equals( position ) )
|
||||
{
|
||||
turtlePlayer.posX += 0.48 * direction.getFrontOffsetX();
|
||||
turtlePlayer.posY += 0.48 * direction.getFrontOffsetY();
|
||||
turtlePlayer.posZ += 0.48 * direction.getFrontOffsetZ();
|
||||
turtlePlayer.posX += 0.48 * direction.getXOffset();
|
||||
turtlePlayer.posY += 0.48 * direction.getYOffset();
|
||||
turtlePlayer.posZ += 0.48 * direction.getZOffset();
|
||||
}
|
||||
|
||||
if( direction.getAxis() != EnumFacing.Axis.Y )
|
||||
@@ -241,14 +241,10 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
// Start claiming entity drops
|
||||
Entity hitEntity = hit.getKey();
|
||||
Vec3d hitPos = hit.getValue();
|
||||
ComputerCraft.setEntityDropConsumer( hitEntity, ( entity, drop ) ->
|
||||
{
|
||||
ItemStack remainder = InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
WorldUtil.dropItemStack( remainder, world, position, turtle.getDirection().getOpposite() );
|
||||
}
|
||||
} );
|
||||
ComputerCraft.setDropConsumer(
|
||||
hitEntity,
|
||||
drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() )
|
||||
);
|
||||
|
||||
// Place on the entity
|
||||
boolean placed = false;
|
||||
@@ -285,7 +281,11 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
}
|
||||
|
||||
// Stop claiming drops
|
||||
ComputerCraft.clearEntityDropConsumer( hitEntity );
|
||||
List<ItemStack> remainingDrops = ComputerCraft.clearDropConsumer();
|
||||
for( ItemStack remaining : remainingDrops )
|
||||
{
|
||||
WorldUtil.dropItemStack( remaining, world, position, turtle.getDirection().getOpposite() );
|
||||
}
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
||||
@@ -367,9 +367,9 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
orientPlayer( turtle, turtlePlayer, playerPosition, playerDir );
|
||||
|
||||
// Calculate where the turtle would hit the block
|
||||
float hitX = 0.5f + side.getFrontOffsetX() * 0.5f;
|
||||
float hitY = 0.5f + side.getFrontOffsetY() * 0.5f;
|
||||
float hitZ = 0.5f + side.getFrontOffsetZ() * 0.5f;
|
||||
float hitX = 0.5f + side.getXOffset() * 0.5f;
|
||||
float hitY = 0.5f + side.getYOffset() * 0.5f;
|
||||
float hitZ = 0.5f + side.getZOffset() * 0.5f;
|
||||
if( Math.abs( hitY - 0.5f ) < 0.01f )
|
||||
{
|
||||
hitY = 0.45f;
|
||||
@@ -382,7 +382,7 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
|
||||
// Do the deploying (put everything in the players inventory)
|
||||
boolean placed = false;
|
||||
|
||||
TileEntity existingTile = turtle.getWorld().getTileEntity( position );
|
||||
|
||||
// See PlayerInteractionManager.processRightClickBlock
|
||||
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, EnumHand.MAIN_HAND, position, side, new Vec3d( hitX, hitY, hitZ ) );
|
||||
@@ -426,12 +426,11 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
{
|
||||
World world = turtle.getWorld();
|
||||
TileEntity tile = world.getTileEntity( position );
|
||||
if( tile == null )
|
||||
if( tile == null || tile == existingTile )
|
||||
{
|
||||
BlockPos newPosition = WorldUtil.moveCoords( position, side );
|
||||
tile = world.getTileEntity( newPosition );
|
||||
tile = world.getTileEntity( WorldUtil.moveCoords( position, side ) );
|
||||
}
|
||||
if( tile != null && tile instanceof TileEntitySign )
|
||||
if( tile instanceof TileEntitySign )
|
||||
{
|
||||
TileEntitySign signTile = (TileEntitySign) tile;
|
||||
String s = (String)extraArguments[0];
|
||||
|
@@ -36,16 +36,31 @@ public class TurtlePlayer extends FakePlayer
|
||||
"[ComputerCraft]"
|
||||
);
|
||||
|
||||
/**
|
||||
* Construct a TurtlePlayer which exists in the world
|
||||
*
|
||||
* @param world The world the player exists in
|
||||
* @deprecated This is required by {@link Entity}.
|
||||
*/
|
||||
@Deprecated
|
||||
public TurtlePlayer( World world )
|
||||
{
|
||||
super( (WorldServer) world, DEFAULT_PROFILE );
|
||||
}
|
||||
|
||||
public TurtlePlayer( ITurtleAccess turtle )
|
||||
{
|
||||
super( (WorldServer) turtle.getWorld(), getProfile( turtle.getOwningPlayer() ));
|
||||
|
||||
private TurtlePlayer( ITurtleAccess turtle )
|
||||
{
|
||||
super( (WorldServer) turtle.getWorld(), getProfile( turtle.getOwningPlayer() ) );
|
||||
setState( turtle );
|
||||
}
|
||||
|
||||
private static GameProfile getProfile( @Nullable GameProfile profile )
|
||||
{
|
||||
return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE;
|
||||
}
|
||||
|
||||
private void setState( ITurtleAccess turtle )
|
||||
{
|
||||
BlockPos position = turtle.getPosition();
|
||||
posX = position.getX() + 0.5;
|
||||
posY = position.getY() + 0.5;
|
||||
@@ -53,11 +68,27 @@ public class TurtlePlayer extends FakePlayer
|
||||
|
||||
rotationYaw = turtle.getDirection().getHorizontalAngle();
|
||||
rotationPitch = 0.0f;
|
||||
|
||||
inventory.clear();
|
||||
}
|
||||
|
||||
private static GameProfile getProfile( @Nullable GameProfile profile )
|
||||
public static TurtlePlayer get( ITurtleAccess access )
|
||||
{
|
||||
return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE;
|
||||
if( !(access instanceof TurtleBrain) ) return new TurtlePlayer( access );
|
||||
|
||||
TurtleBrain brain = (TurtleBrain) access;
|
||||
TurtlePlayer player = brain.m_cachedPlayer;
|
||||
if( player == null || player.getGameProfile() != getProfile( access.getOwningPlayer() )
|
||||
|| player.getEntityWorld() != access.getWorld() )
|
||||
{
|
||||
player = brain.m_cachedPlayer = new TurtlePlayer( brain );
|
||||
}
|
||||
else
|
||||
{
|
||||
player.setState( access );
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ItemStack currentStack )
|
||||
@@ -76,7 +107,7 @@ public class TurtlePlayer extends FakePlayer
|
||||
// Store (or drop) anything else we found
|
||||
BlockPos dropPosition = turtle.getPosition();
|
||||
EnumFacing dropDirection = turtle.getDirection().getOpposite();
|
||||
for( int i=0; i<inventory.getSizeInventory(); ++i )
|
||||
for( int i = 0; i < inventory.getSizeInventory(); ++i )
|
||||
{
|
||||
ItemStack stack = inventory.getStackInSlot( i );
|
||||
if( !stack.isEmpty() )
|
||||
|
@@ -15,7 +15,7 @@ public class ItemTurtleAdvanced extends ItemTurtleNormal
|
||||
public ItemTurtleAdvanced( Block block )
|
||||
{
|
||||
super( block );
|
||||
setUnlocalizedName( "computercraft:advanced_turtle" );
|
||||
setTranslationKey( "computercraft:advanced_turtle" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -114,7 +114,7 @@ public abstract class ItemTurtleBase extends ItemComputerBase implements ITurtle
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
||||
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||
{
|
||||
ComputerFamily family = getFamily( stack );
|
||||
switch( family )
|
||||
@@ -139,7 +139,7 @@ public abstract class ItemTurtleBase extends ItemComputerBase implements ITurtle
|
||||
@Override
|
||||
public String getItemStackDisplayName( @Nonnull ItemStack stack )
|
||||
{
|
||||
String baseString = getUnlocalizedName( stack );
|
||||
String baseString = getTranslationKey( stack );
|
||||
ITurtleUpgrade left = getUpgrade( stack, TurtleSide.Left );
|
||||
ITurtleUpgrade right = getUpgrade( stack, TurtleSide.Right );
|
||||
if( left != null && right != null )
|
||||
|
@@ -23,7 +23,7 @@ public class ItemTurtleLegacy extends ItemTurtleBase
|
||||
public ItemTurtleLegacy( Block block )
|
||||
{
|
||||
super( block );
|
||||
setUnlocalizedName( "computercraft:turtle" );
|
||||
setTranslationKey( "computercraft:turtle" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,7 @@ public class ItemTurtleNormal extends ItemTurtleBase
|
||||
public ItemTurtleNormal( Block block )
|
||||
{
|
||||
super( block );
|
||||
setUnlocalizedName( "computercraft:turtle" );
|
||||
setTranslationKey( "computercraft:turtle" );
|
||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ public class ItemTurtleNormal extends ItemTurtleBase
|
||||
}
|
||||
if( overlay != null )
|
||||
{
|
||||
nbt.setString( "overlay_mod", overlay.getResourceDomain() );
|
||||
nbt.setString( "overlay_path", overlay.getResourcePath() );
|
||||
nbt.setString( "overlay_mod", overlay.getNamespace() );
|
||||
nbt.setString( "overlay_path", overlay.getPath() );
|
||||
}
|
||||
stack.setTagCompound( nbt );
|
||||
|
||||
|
@@ -11,13 +11,14 @@ import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleVerb;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -30,20 +31,16 @@ public class TurtleHoe extends TurtleTool
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBreakBlock( World world, BlockPos pos )
|
||||
protected boolean canBreakBlock( IBlockState state, World world, BlockPos pos, TurtlePlayer player )
|
||||
{
|
||||
if( super.canBreakBlock( world, pos ) )
|
||||
{
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
Material material = state.getMaterial( );
|
||||
return
|
||||
material == Material.PLANTS ||
|
||||
material == Material.CACTUS ||
|
||||
material == Material.GOURD ||
|
||||
material == Material.LEAVES ||
|
||||
material == Material.VINE;
|
||||
}
|
||||
return false;
|
||||
if( !super.canBreakBlock( state, world, pos, player ) ) return false;
|
||||
|
||||
Material material = state.getMaterial();
|
||||
return material == Material.PLANTS ||
|
||||
material == Material.CACTUS ||
|
||||
material == Material.GOURD ||
|
||||
material == Material.LEAVES ||
|
||||
material == Material.VINE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -118,7 +118,7 @@ public class TurtleInventoryCrafting extends InventoryCrafting
|
||||
}
|
||||
|
||||
// Do post-pickup stuff
|
||||
TurtlePlayer turtlePlayer = new TurtlePlayer( m_turtle );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.get( m_turtle );
|
||||
result.onCrafting( world, turtlePlayer, numToCraft );
|
||||
results.add( result );
|
||||
|
||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleVerb;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.item.Item;
|
||||
@@ -30,26 +31,22 @@ public class TurtleShovel extends TurtleTool
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBreakBlock( World world, BlockPos pos )
|
||||
protected boolean canBreakBlock( IBlockState state, World world, BlockPos pos, TurtlePlayer player )
|
||||
{
|
||||
if( super.canBreakBlock( world, pos ) )
|
||||
{
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
Material material = state.getMaterial( );
|
||||
return
|
||||
material == Material.GROUND ||
|
||||
material == Material.SAND ||
|
||||
material == Material.SNOW ||
|
||||
material == Material.CLAY ||
|
||||
material == Material.CRAFTED_SNOW ||
|
||||
material == Material.GRASS ||
|
||||
material == Material.PLANTS ||
|
||||
material == Material.CACTUS ||
|
||||
material == Material.GOURD ||
|
||||
material == Material.LEAVES ||
|
||||
material == Material.VINE;
|
||||
}
|
||||
return false;
|
||||
if( !super.canBreakBlock( state, world, pos, player ) ) return false;
|
||||
|
||||
Material material = state.getMaterial();
|
||||
return material == Material.GROUND ||
|
||||
material == Material.SAND ||
|
||||
material == Material.SNOW ||
|
||||
material == Material.CLAY ||
|
||||
material == Material.CRAFTED_SNOW ||
|
||||
material == Material.GRASS ||
|
||||
material == Material.PLANTS ||
|
||||
material == Material.CACTUS ||
|
||||
material == Material.GOURD ||
|
||||
material == Material.LEAVES ||
|
||||
material == Material.VINE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -5,11 +5,13 @@
|
||||
*/
|
||||
|
||||
package dan200.computercraft.shared.turtle.upgrades;
|
||||
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class TurtleSword extends TurtleTool
|
||||
@@ -20,20 +22,16 @@ public class TurtleSword extends TurtleTool
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBreakBlock( World world, BlockPos pos )
|
||||
protected boolean canBreakBlock( IBlockState state, World world, BlockPos pos, TurtlePlayer player )
|
||||
{
|
||||
if( super.canBreakBlock( world, pos ) )
|
||||
{
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
Material material = state.getMaterial( );
|
||||
return
|
||||
material == Material.PLANTS ||
|
||||
material == Material.LEAVES ||
|
||||
material == Material.VINE ||
|
||||
material == Material.CLOTH ||
|
||||
material == Material.WEB;
|
||||
}
|
||||
return false;
|
||||
if( !super.canBreakBlock( state, world, pos, player ) ) return false;
|
||||
|
||||
Material material = state.getMaterial();
|
||||
return material == Material.PLANTS ||
|
||||
material == Material.LEAVES ||
|
||||
material == Material.VINE ||
|
||||
material == Material.CLOTH ||
|
||||
material == Material.WEB;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -23,20 +23,17 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.item.EntityArmorStand;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
@@ -46,6 +43,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TurtleTool implements ITurtleUpgrade
|
||||
{
|
||||
@@ -142,27 +140,20 @@ public class TurtleTool implements ITurtleUpgrade
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean canBreakBlock( World world, BlockPos pos )
|
||||
protected boolean canBreakBlock( IBlockState state, World world, BlockPos pos, TurtlePlayer player )
|
||||
{
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
Block block = state.getBlock();
|
||||
return !block.isAir( state, world, pos ) && block != Blocks.BEDROCK && state.getBlockHardness( world, pos ) > -1.0F;
|
||||
return !block.isAir( state, world, pos )
|
||||
&& block != Blocks.BEDROCK
|
||||
&& state.getPlayerRelativeBlockHardness( player, world, pos ) > 0
|
||||
&& block.canEntityDestroy( state, world, pos, player );
|
||||
}
|
||||
|
||||
protected boolean canHarvestBlock( ITurtleAccess turtleAccess, BlockPos pos )
|
||||
{
|
||||
World world = turtleAccess.getWorld();
|
||||
Block block = world.getBlockState( pos ).getBlock();
|
||||
TurtlePlayer turtlePlayer = new TurtlePlayer( turtleAccess );
|
||||
turtlePlayer.loadInventory( m_item.copy() );
|
||||
return ForgeHooks.canHarvestBlock( block, turtlePlayer, world, pos );
|
||||
}
|
||||
|
||||
|
||||
protected float getDamageMultiplier()
|
||||
{
|
||||
return 3.0f;
|
||||
}
|
||||
|
||||
|
||||
private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
|
||||
{
|
||||
// Create a fake player, and orient it appropriately
|
||||
@@ -193,22 +184,15 @@ public class TurtleTool implements ITurtleUpgrade
|
||||
{
|
||||
return TurtleCommandResult.failure( attackEvent.getFailureMessage() );
|
||||
}
|
||||
|
||||
|
||||
// Start claiming entity drops
|
||||
ComputerCraft.setEntityDropConsumer( hitEntity, ( entity, drop ) ->
|
||||
{
|
||||
ItemStack remainder = InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
WorldUtil.dropItemStack( remainder, world, position, turtle.getDirection().getOpposite() );
|
||||
}
|
||||
} );
|
||||
ComputerCraft.setDropConsumer( hitEntity, turtleDropConsumer( turtle ) );
|
||||
|
||||
// Attack the entity
|
||||
boolean attacked = false;
|
||||
if( !hitEntity.hitByEntity( turtlePlayer ) )
|
||||
{
|
||||
float damage = (float)turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue();
|
||||
float damage = (float) turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue();
|
||||
damage *= getDamageMultiplier();
|
||||
if( damage > 0.0f )
|
||||
{
|
||||
@@ -234,7 +218,7 @@ public class TurtleTool implements ITurtleUpgrade
|
||||
}
|
||||
|
||||
// Stop claiming drops
|
||||
ComputerCraft.clearEntityDropConsumer( hitEntity );
|
||||
stopConsuming( turtle );
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
if( attacked )
|
||||
@@ -246,75 +230,77 @@ public class TurtleTool implements ITurtleUpgrade
|
||||
|
||||
return TurtleCommandResult.failure( "Nothing to attack here" );
|
||||
}
|
||||
|
||||
|
||||
private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
|
||||
{
|
||||
// Get ready to dig
|
||||
World world = turtle.getWorld();
|
||||
BlockPos position = turtle.getPosition();
|
||||
BlockPos newPosition = WorldUtil.moveCoords( position, direction );
|
||||
BlockPos turtlePosition = turtle.getPosition();
|
||||
BlockPos blockPosition = WorldUtil.moveCoords( turtlePosition, direction );
|
||||
|
||||
if( WorldUtil.isBlockInWorld( world, newPosition ) &&
|
||||
!world.isAirBlock( newPosition ) &&
|
||||
!WorldUtil.isLiquidBlock( world, newPosition ) )
|
||||
if( WorldUtil.isBlockInWorld( world, blockPosition ) &&
|
||||
!world.isAirBlock( blockPosition ) &&
|
||||
!WorldUtil.isLiquidBlock( world, blockPosition ) )
|
||||
{
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction );
|
||||
IBlockState state = world.getBlockState( blockPosition );
|
||||
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, turtlePosition, direction );
|
||||
turtlePlayer.loadInventory( m_item.copy() );
|
||||
|
||||
if( ComputerCraft.turtlesObeyBlockProtection )
|
||||
{
|
||||
// Check spawn protection
|
||||
|
||||
if( MinecraftForge.EVENT_BUS.post( new BlockEvent.BreakEvent( world, newPosition, world.getBlockState( newPosition ), turtlePlayer ) ) )
|
||||
if( MinecraftForge.EVENT_BUS.post( new BlockEvent.BreakEvent( world, blockPosition, state, turtlePlayer ) ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( "Cannot break protected block" );
|
||||
}
|
||||
|
||||
if( !ComputerCraft.isBlockEditable( world, newPosition, turtlePlayer ) )
|
||||
|
||||
if( !ComputerCraft.isBlockEditable( world, blockPosition, turtlePlayer ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( "Cannot break protected block" );
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we can break the block
|
||||
if( !canBreakBlock( world, newPosition ) )
|
||||
if( !canBreakBlock( state, world, blockPosition, turtlePlayer ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( "Unbreakable block detected" );
|
||||
}
|
||||
|
||||
// Fire the dig event, checking whether it was cancelled.
|
||||
TurtleBlockEvent.Dig digEvent = new TurtleBlockEvent.Dig( turtle, turtlePlayer, world, newPosition, world.getBlockState( newPosition ), this, side );
|
||||
TurtleBlockEvent.Dig digEvent = new TurtleBlockEvent.Dig( turtle, turtlePlayer, world, blockPosition, state, this, side );
|
||||
if( MinecraftForge.EVENT_BUS.post( digEvent ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( digEvent.getFailureMessage() );
|
||||
}
|
||||
|
||||
// Consume the items the block drops
|
||||
if( canHarvestBlock( turtle, newPosition ) )
|
||||
{
|
||||
List<ItemStack> items = getBlockDropped( world, newPosition, turtlePlayer );
|
||||
if( items != null && items.size() > 0 )
|
||||
{
|
||||
for( ItemStack stack : items )
|
||||
{
|
||||
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
// If there's no room for the items, drop them
|
||||
WorldUtil.dropItemStack( remainder, world, position, direction );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ComputerCraft.setDropConsumer( world, blockPosition, turtleDropConsumer( turtle ) );
|
||||
|
||||
TileEntity tile = world.getTileEntity( blockPosition );
|
||||
|
||||
// Much of this logic comes from PlayerInteractionManager#tryHarvestBlock, so it's a good idea
|
||||
// to consult there before making any changes.
|
||||
|
||||
// Play the destruction sound
|
||||
world.playEvent( 2001, blockPosition, Block.getStateId( state ) );
|
||||
|
||||
// Destroy the block
|
||||
IBlockState previousState = world.getBlockState( newPosition );
|
||||
world.playEvent(2001, newPosition, Block.getStateId(previousState));
|
||||
world.setBlockToAir( newPosition );
|
||||
boolean canHarvest = state.getBlock().canHarvestBlock( world, blockPosition, turtlePlayer );
|
||||
boolean canBreak = state.getBlock().removedByPlayer( state, world, blockPosition, turtlePlayer, canHarvest );
|
||||
if( canBreak ) state.getBlock().onPlayerDestroy( world, blockPosition, state );
|
||||
if( canHarvest )
|
||||
{
|
||||
state.getBlock().harvestBlock( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getHeldItemMainhand() );
|
||||
}
|
||||
|
||||
stopConsuming( turtle );
|
||||
|
||||
// Remember the previous block
|
||||
if( turtle instanceof TurtleBrain )
|
||||
{
|
||||
TurtleBrain brain = (TurtleBrain)turtle;
|
||||
brain.saveBlockChange( newPosition, previousState );
|
||||
TurtleBrain brain = (TurtleBrain) turtle;
|
||||
brain.saveBlockChange( blockPosition, state );
|
||||
}
|
||||
|
||||
return TurtleCommandResult.success();
|
||||
@@ -323,22 +309,17 @@ public class TurtleTool implements ITurtleUpgrade
|
||||
return TurtleCommandResult.failure( "Nothing to dig here" );
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private List<ItemStack> getBlockDropped( World world, BlockPos pos, EntityPlayer player )
|
||||
private Function<ItemStack, ItemStack> turtleDropConsumer( ITurtleAccess turtle )
|
||||
{
|
||||
IBlockState state = world.getBlockState( pos );
|
||||
Block block = state.getBlock();
|
||||
// Note, we use the deprecated version as some mods override that instead. Those mods are wrong (TM).
|
||||
List<ItemStack> drops = block.getDrops( world, pos, world.getBlockState( pos ), 0 );
|
||||
double chance = ForgeEventFactory.fireBlockHarvesting( drops, world, pos, state, 0, 1, false, player );
|
||||
return drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
}
|
||||
|
||||
for( int i = drops.size() - 1; i >= 0; i-- )
|
||||
private void stopConsuming( ITurtleAccess turtle )
|
||||
{
|
||||
List<ItemStack> extra = ComputerCraft.clearDropConsumer();
|
||||
for( ItemStack remainder : extra )
|
||||
{
|
||||
if( world.rand.nextFloat() > chance )
|
||||
{
|
||||
drops.remove( i );
|
||||
}
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), turtle.getDirection().getOpposite() );
|
||||
}
|
||||
return drops;
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ public class CreativeTabMain extends CreativeTabs
|
||||
@Nonnull
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public ItemStack getTabIconItem()
|
||||
public ItemStack createIcon()
|
||||
{
|
||||
return new ItemStack( ComputerCraft.Blocks.computer );
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class CreativeTabMain extends CreativeTabs
|
||||
@Nonnull
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public String getTranslatedTabLabel()
|
||||
public String getTranslationKey()
|
||||
{
|
||||
return getTabLabel();
|
||||
}
|
||||
|
@@ -21,14 +21,14 @@ public class CreativeTabTreasure extends CreativeTabs
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack getTabIconItem()
|
||||
public ItemStack createIcon()
|
||||
{
|
||||
return new ItemStack( ComputerCraft.Items.treasureDisk );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getTranslatedTabLabel()
|
||||
public String getTranslationKey()
|
||||
{
|
||||
return getTabLabel();
|
||||
}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.shared.util;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface IEntityDropConsumer
|
||||
{
|
||||
void consumeDrop( Entity dropper, @Nonnull ItemStack drop );
|
||||
}
|
@@ -73,13 +73,13 @@ public class InventoryUtil
|
||||
|
||||
// Look for entity with inventory
|
||||
Vec3d vecStart = new Vec3d(
|
||||
pos.getX() + 0.5 + 0.6 * side.getFrontOffsetX(),
|
||||
pos.getY() + 0.5 + 0.6 * side.getFrontOffsetY(),
|
||||
pos.getZ() + 0.5 + 0.6 * side.getFrontOffsetZ()
|
||||
pos.getX() + 0.5 + 0.6 * side.getXOffset(),
|
||||
pos.getY() + 0.5 + 0.6 * side.getYOffset(),
|
||||
pos.getZ() + 0.5 + 0.6 * side.getZOffset()
|
||||
);
|
||||
EnumFacing dir = side.getOpposite();
|
||||
Vec3d vecDir = new Vec3d(
|
||||
dir.getFrontOffsetX(), dir.getFrontOffsetY(), dir.getFrontOffsetZ()
|
||||
dir.getXOffset(), dir.getYOffset(), dir.getZOffset()
|
||||
);
|
||||
Pair<Entity, Vec3d> hit = WorldUtil.rayTraceEntities( world, vecStart, vecDir, 1.1 );
|
||||
if( hit != null )
|
||||
|
@@ -43,14 +43,14 @@ public class WorldUtil
|
||||
|
||||
public static Pair<Entity, Vec3d> rayTraceEntities( World world, Vec3d vecStart, Vec3d vecDir, double distance )
|
||||
{
|
||||
Vec3d vecEnd = vecStart.addVector( vecDir.x * distance, vecDir.y * distance, vecDir.z * distance );
|
||||
Vec3d vecEnd = vecStart.add( vecDir.x * distance, vecDir.y * distance, vecDir.z * distance );
|
||||
|
||||
// Raycast for blocks
|
||||
RayTraceResult result = world.rayTraceBlocks( vecStart.addVector(0.0,0.0,0.0), vecEnd.addVector(0.0,0.0,0.0) );
|
||||
RayTraceResult result = world.rayTraceBlocks( vecStart, vecEnd );
|
||||
if( result != null && result.typeOfHit == RayTraceResult.Type.BLOCK )
|
||||
{
|
||||
distance = vecStart.distanceTo( result.hitVec );
|
||||
vecEnd = vecStart.addVector( vecDir.x * distance, vecDir.y * distance, vecDir.z * distance );
|
||||
vecEnd = vecStart.add( vecDir.x * distance, vecDir.y * distance, vecDir.z * distance );
|
||||
}
|
||||
|
||||
// Check for entities
|
||||
@@ -114,7 +114,7 @@ public class WorldUtil
|
||||
}
|
||||
if( closest != null && closestDist <= distance )
|
||||
{
|
||||
Vec3d closestPos = vecStart.addVector( vecDir.x * closestDist, vecDir.y * closestDist, vecDir.z * closestDist );
|
||||
Vec3d closestPos = vecStart.add( vecDir.x * closestDist, vecDir.y * closestDist, vecDir.z * closestDist );
|
||||
return Pair.of( closest, closestPos );
|
||||
}
|
||||
return null;
|
||||
@@ -128,7 +128,7 @@ public class WorldUtil
|
||||
public static Vec3d getRayEnd( EntityPlayer player) {
|
||||
double reach = player.getEntityAttribute(EntityPlayer.REACH_DISTANCE).getAttributeValue();
|
||||
Vec3d look = player.getLookVec();
|
||||
return getRayStart( player ).addVector( look.x * reach, look.y * reach, look.z * reach );
|
||||
return getRayStart( player ).add( look.x * reach, look.y * reach, look.z * reach );
|
||||
}
|
||||
|
||||
public static boolean isVecInsideInclusive(AxisAlignedBB bb , Vec3d vec) {
|
||||
@@ -147,9 +147,9 @@ public class WorldUtil
|
||||
double zDir;
|
||||
if( direction != null )
|
||||
{
|
||||
xDir = direction.getFrontOffsetX();
|
||||
yDir = direction.getFrontOffsetY();
|
||||
zDir = direction.getFrontOffsetZ();
|
||||
xDir = direction.getXOffset();
|
||||
yDir = direction.getYOffset();
|
||||
zDir = direction.getZOffset();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# RecordMedia (and related methods)
|
||||
public net.minecraft.item.ItemRecord field_185076_b # sound
|
||||
public net.minecraft.item.ItemRecord field_185077_c # displayName
|
||||
# ItemPocketRenderer
|
||||
# ItemPocketRenderer/ItemPrintoutRenderer
|
||||
public net.minecraft.client.renderer.ItemRenderer func_187466_c()V # renderArms
|
||||
public net.minecraft.client.renderer.ItemRenderer func_178100_c(F)F # getMapAngleFromPitch
|
||||
public net.minecraft.client.renderer.ItemRenderer func_187456_a(FFLnet/minecraft/util/EnumHandSide;)V # renderArmFirstPerson
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user