mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-15 22:17:39 +00:00
Compare commits
43 Commits
v1.80pr1.7
...
v1.80pr1.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8ae65d1bdd | ||
![]() |
0829506176 | ||
![]() |
71ee692da0 | ||
![]() |
acd0092ed5 | ||
![]() |
1160ffbf9e | ||
![]() |
93cb6547bd | ||
![]() |
f9c91f288f | ||
![]() |
62cf921cc6 | ||
![]() |
4bd7381827 | ||
![]() |
43459ec825 | ||
![]() |
5fa01f8b96 | ||
![]() |
67d5693d2a | ||
![]() |
c2a782afa4 | ||
![]() |
14c9558ee6 | ||
![]() |
d53a73e7e7 | ||
![]() |
7e334bd4a5 | ||
![]() |
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 |
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.
|
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,12 +1,12 @@
|
|||||||
build
|
/build
|
||||||
out
|
/out
|
||||||
run
|
/run
|
||||||
deploy
|
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
.idea
|
.idea
|
||||||
.gradle
|
.gradle
|
||||||
luaj-2.0.3/lib
|
/luaj-2.0.3/lib
|
||||||
luaj-2.0.3/*.jar
|
/luaj-2.0.3/*.jar
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
/test-files
|
||||||
|
@@ -28,6 +28,8 @@ to see the full changes, but here's a couple of the more interesting changes:
|
|||||||
computers remotely.
|
computers remotely.
|
||||||
- Add full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is
|
- Add full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is
|
||||||
installed).
|
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?
|
## Relation to CCTweaks?
|
||||||
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said,
|
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said,
|
||||||
@@ -36,7 +38,7 @@ computers.
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you do wish to contribute
|
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:T, 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:
|
||||||
|
|
||||||
|
15
build.gradle
15
build.gradle
@@ -23,12 +23,12 @@ apply plugin: 'org.ajoberstar.grgit'
|
|||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
apply plugin: 'maven'
|
apply plugin: 'maven'
|
||||||
|
|
||||||
version = "1.80pr1.7"
|
version = "1.80pr1.11"
|
||||||
group = "org.squiddev"
|
group = "org.squiddev"
|
||||||
archivesBaseName = "cc-tweaked"
|
archivesBaseName = "cc-tweaked"
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
version = "1.12.2-14.23.2.2634"
|
version = "1.12.2-14.23.4.2749"
|
||||||
runDir = "run"
|
runDir = "run"
|
||||||
replace '${version}', project.version
|
replace '${version}', project.version
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ minecraft {
|
|||||||
// stable_# stables are built at the discretion of the MCP team.
|
// stable_# stables are built at the discretion of the MCP team.
|
||||||
// Use non-default mappings at your own risk. they may not allways work.
|
// 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.
|
// 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.
|
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +50,8 @@ repositories {
|
|||||||
name = "squiddev"
|
name = "squiddev"
|
||||||
url = "https://dl.bintray.com/squiddev/maven"
|
url = "https://dl.bintray.com/squiddev/maven"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ivy { artifactPattern "https://asie.pl/files/mods/Charset/LibOnly/[module]-[revision](-[classifier]).[ext]" }
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
@@ -60,8 +62,11 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
deobfProvided "mezz.jei:jei_1.12.2:4.8.5.159:api"
|
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"
|
runtime "mezz.jei:jei_1.12.2:4.8.5.159"
|
||||||
shade 'org.squiddev:Cobalt:0.3.1'
|
|
||||||
|
shade 'org.squiddev:Cobalt:0.4.0'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
|
|
||||||
@@ -126,7 +131,7 @@ curseforge {
|
|||||||
project {
|
project {
|
||||||
id = '282001'
|
id = '282001'
|
||||||
releaseType = 'beta'
|
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})."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
cd luaj-2.0.3
|
|
||||||
echo "Building LuaJ..."
|
|
||||||
ant clean
|
|
||||||
ant
|
|
||||||
|
|
||||||
echo "Copying output to libs..."
|
|
||||||
rm ../libs/luaj-jse-2.0.3.jar
|
|
||||||
cp luaj-jse-2.0.3.jar ../libs
|
|
||||||
|
|
||||||
echo "Done."
|
|
||||||
cd ..
|
|
10
codesize.sh
10
codesize.sh
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
echo "Java code:"
|
|
||||||
cat `find src | grep \\.java$` | wc
|
|
||||||
|
|
||||||
echo "Lua code:"
|
|
||||||
cat `find src/main/resources/assets/computercraft/lua | grep \\.lua$` | wc
|
|
||||||
|
|
||||||
echo "JSON:"
|
|
||||||
cat `find src/main/resources/assets/computercraft | grep \\.json$` | wc
|
|
||||||
|
|
21
deploy.sh
21
deploy.sh
@@ -1,21 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "Building with gradle..."
|
|
||||||
rm -rf build/libs
|
|
||||||
rm -rf build/resources
|
|
||||||
rm -rf build/classes
|
|
||||||
chmod -R +rw src/main/resources
|
|
||||||
chmod +x gradlew
|
|
||||||
./gradlew build
|
|
||||||
|
|
||||||
echo "Deleting old deployment..."
|
|
||||||
rm -rf deploy
|
|
||||||
mkdir deploy
|
|
||||||
|
|
||||||
echo "Making new deployment..."
|
|
||||||
INPUTJAR=`ls -1 build/libs | grep -v sources`
|
|
||||||
OUTPUTJAR=`ls -1 build/libs | grep -v sources | sed s/\-//g`
|
|
||||||
FRIENDLYNAME=`ls -1 build/libs | grep -v sources | sed s/\-/\ /g | sed s/\.jar//g`
|
|
||||||
cp build/libs/$INPUTJAR deploy/$OUTPUTJAR
|
|
||||||
|
|
||||||
echo "Done."
|
|
Binary file not shown.
@@ -1,7 +0,0 @@
|
|||||||
echo "Setting up IntelliJ development environment with gradle..."
|
|
||||||
rmdir /s /q .\build
|
|
||||||
call gradlew.bat --stacktrace setupDecompWorkspace --refresh-dependencies
|
|
||||||
call gradlew.bat --stacktrace cleanIdea idea
|
|
||||||
|
|
||||||
echo "Done."
|
|
||||||
pause
|
|
14
setup.sh
14
setup.sh
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "Setting permissions..."
|
|
||||||
chmod +x codesize.sh
|
|
||||||
chmod +x build_luaj.sh
|
|
||||||
chmod +x deploy.sh
|
|
||||||
chmod +x gradlew
|
|
||||||
|
|
||||||
echo "Setting up IntelliJ development environment with gradle..."
|
|
||||||
rm -rf build
|
|
||||||
./gradlew --stacktrace setupDecompWorkspace --refresh-dependencies
|
|
||||||
./gradlew --stacktrace cleanIdea idea
|
|
||||||
|
|
||||||
echo "Done."
|
|
@@ -26,11 +26,9 @@ import dan200.computercraft.api.turtle.event.TurtleAction;
|
|||||||
import dan200.computercraft.core.apis.AddressPredicate;
|
import dan200.computercraft.core.apis.AddressPredicate;
|
||||||
import dan200.computercraft.core.filesystem.ComboMount;
|
import dan200.computercraft.core.filesystem.ComboMount;
|
||||||
import dan200.computercraft.core.filesystem.FileMount;
|
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.terminal.Terminal;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.core.tracking.Tracking;
|
||||||
import dan200.computercraft.shared.command.CommandComputer;
|
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
|
||||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||||
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
|
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
|
||||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||||
@@ -60,7 +58,10 @@ import dan200.computercraft.shared.proxy.IComputerCraftProxy;
|
|||||||
import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
|
import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
|
||||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||||
import dan200.computercraft.shared.util.*;
|
import dan200.computercraft.shared.util.CreativeTabMain;
|
||||||
|
import dan200.computercraft.shared.util.IDAssigner;
|
||||||
|
import dan200.computercraft.shared.util.InventoryUtil;
|
||||||
|
import dan200.computercraft.shared.util.WorldUtil;
|
||||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||||
import dan200.computercraft.shared.wired.WiredNode;
|
import dan200.computercraft.shared.wired.WiredNode;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
@@ -97,8 +98,10 @@ import java.io.*;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Function;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
@@ -108,12 +111,12 @@ import java.util.zip.ZipFile;
|
|||||||
|
|
||||||
@Mod(
|
@Mod(
|
||||||
modid = ComputerCraft.MOD_ID, name = "CC: Tweaked", version = "${version}",
|
modid = ComputerCraft.MOD_ID, name = "CC: Tweaked", version = "${version}",
|
||||||
guiFactory = "dan200.computercraft.client.gui.GuiConfigCC$Factory"
|
guiFactory = "dan200.computercraft.client.gui.GuiConfigCC$Factory",
|
||||||
|
dependencies = "required:forge@[14.23.4.2746,)"
|
||||||
)
|
)
|
||||||
public class ComputerCraft
|
public class ComputerCraft
|
||||||
{
|
{
|
||||||
public static final String MOD_ID = "computercraft";
|
public static final String MOD_ID = "computercraft";
|
||||||
public static final String LOWER_ID = "computercraft";
|
|
||||||
|
|
||||||
// GUI IDs
|
// GUI IDs
|
||||||
public static final int diskDriveGUIID = 100;
|
public static final int diskDriveGUIID = 100;
|
||||||
@@ -567,7 +570,7 @@ public class ComputerCraft
|
|||||||
width = terminal.getWidth();
|
width = terminal.getWidth();
|
||||||
height = terminal.getHeight();
|
height = terminal.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack useful terminal information into the various coordinate bits.
|
// Pack useful terminal information into the various coordinate bits.
|
||||||
// These are extracted in ComputerCraftProxyCommon.getClientGuiElement
|
// These are extracted in ComputerCraftProxyCommon.getClientGuiElement
|
||||||
player.openGui( ComputerCraft.instance, ComputerCraft.viewComputerGUIID, player.getEntityWorld(),
|
player.openGui( ComputerCraft.instance, ComputerCraft.viewComputerGUIID, player.getEntityWorld(),
|
||||||
@@ -922,11 +925,12 @@ public class ComputerCraft
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IMount jarMount = new JarMount( modJar, subPath );
|
FileSystem fs = FileSystems.newFileSystem( modJar.toPath(), ComputerCraft.class.getClassLoader() );
|
||||||
mounts.add( jarMount );
|
mounts.add( new FileSystemMount( fs, subPath ) );
|
||||||
}
|
}
|
||||||
catch( IOException e )
|
catch( IOException | RuntimeException | ServiceConfigurationError e )
|
||||||
{
|
{
|
||||||
|
ComputerCraft.log.error( "Could not load mount from mod jar", e );
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -936,16 +940,16 @@ public class ComputerCraft
|
|||||||
if( resourcePackDir.exists() && resourcePackDir.isDirectory() )
|
if( resourcePackDir.exists() && resourcePackDir.isDirectory() )
|
||||||
{
|
{
|
||||||
String[] resourcePacks = resourcePackDir.list();
|
String[] resourcePacks = resourcePackDir.list();
|
||||||
for( String resourcePack1 : resourcePacks )
|
for( String resourcePackName : resourcePacks )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File resourcePack = new File( resourcePackDir, resourcePack1 );
|
File resourcePack = new File( resourcePackDir, resourcePackName );
|
||||||
if( !resourcePack.isDirectory() )
|
if( !resourcePack.isDirectory() )
|
||||||
{
|
{
|
||||||
// Mount a resource pack from a jar
|
// Mount a resource pack from a jar
|
||||||
IMount resourcePackMount = new JarMount( resourcePack, subPath );
|
FileSystem fs = FileSystems.newFileSystem( resourcePack.toPath(), ComputerCraft.class.getClassLoader() );
|
||||||
mounts.add( resourcePackMount );
|
mounts.add( new FileSystemMount( fs, subPath ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -958,9 +962,9 @@ public class ComputerCraft
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( IOException e )
|
catch( IOException | RuntimeException | ServiceConfigurationError e )
|
||||||
{
|
{
|
||||||
// Ignore
|
ComputerCraft.log.error( "Could not load resource pack '" + resourcePackName + "'", e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1125,18 +1129,18 @@ public class ComputerCraft
|
|||||||
turtleProxy.addAllUpgradedTurtles( list );
|
turtleProxy.addAllUpgradedTurtles( list );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setDropConsumer( Entity entity, Consumer<ItemStack> consumer )
|
public static void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer )
|
||||||
{
|
{
|
||||||
turtleProxy.setDropConsumer( entity, consumer );
|
turtleProxy.setDropConsumer( entity, consumer );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setDropConsumer( World world, BlockPos pos, Consumer<ItemStack> consumer )
|
public static void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer )
|
||||||
{
|
{
|
||||||
turtleProxy.setDropConsumer( world, pos, consumer );
|
turtleProxy.setDropConsumer( world, pos, consumer );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearDropConsumer( )
|
public static List<ItemStack> clearDropConsumer( )
|
||||||
{
|
{
|
||||||
turtleProxy.clearDropConsumer();
|
return turtleProxy.clearDropConsumer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,8 @@ import net.minecraft.world.World;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.util.List;
|
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".
|
* @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.
|
* @return A stream representing the contents of the file.
|
||||||
* @throws IOException If the file does not exist, or could not be opened.
|
* @throws IOException If the file does not exist, or could not be opened.
|
||||||
|
* @deprecated Use {@link #openChannelForRead(String)} instead
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@Deprecated
|
||||||
InputStream openForRead( @Nonnull String path ) throws IOException;
|
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 javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
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)}
|
* 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".
|
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
|
||||||
* @return A stream for writing to
|
* @return A stream for writing to
|
||||||
* @throws IOException If the file could not be opened for writing.
|
* @throws IOException If the file could not be opened for writing.
|
||||||
|
* @deprecated Use {@link #openChannelForWrite(String)} instead.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@Deprecated
|
||||||
OutputStream openForWrite( @Nonnull String path ) throws IOException;
|
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.
|
* 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".
|
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
|
||||||
* @return A stream for writing to.
|
* @return A stream for writing to.
|
||||||
* @throws IOException If the file could not be opened for writing.
|
* @throws IOException If the file could not be opened for writing.
|
||||||
|
* @deprecated Use {@link #openChannelForAppend(String)} instead.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@Deprecated
|
||||||
OutputStream openForAppend( @Nonnull String path ) throws IOException;
|
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
|
* 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.
|
* mount, and write operations should fail once it reaches zero.
|
||||||
|
@@ -8,11 +8,7 @@ package dan200.computercraft.client.proxy;
|
|||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.client.gui.*;
|
import dan200.computercraft.client.gui.*;
|
||||||
import dan200.computercraft.client.render.ItemPocketRenderer;
|
import dan200.computercraft.client.render.*;
|
||||||
import dan200.computercraft.client.render.ItemPrintoutRenderer;
|
|
||||||
import dan200.computercraft.client.render.RenderOverlayCable;
|
|
||||||
import dan200.computercraft.client.render.TileEntityCableRenderer;
|
|
||||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
|
||||||
import dan200.computercraft.shared.command.ContainerViewComputer;
|
import dan200.computercraft.shared.command.ContainerViewComputer;
|
||||||
import dan200.computercraft.shared.computer.blocks.ComputerState;
|
import dan200.computercraft.shared.computer.blocks.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||||
@@ -35,7 +31,7 @@ import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
|
|||||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||||
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
|
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
|
||||||
import dan200.computercraft.shared.util.Colour;
|
import dan200.computercraft.shared.util.Colour;
|
||||||
import gnu.trove.map.hash.TIntIntHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiNewChat;
|
import net.minecraft.client.gui.GuiNewChat;
|
||||||
@@ -75,7 +71,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||||
{
|
{
|
||||||
private static TIntIntHashMap lastCounts = new TIntIntHashMap();
|
private static Int2IntOpenHashMap lastCounts = new Int2IntOpenHashMap();
|
||||||
|
|
||||||
private long m_tick;
|
private long m_tick;
|
||||||
private long m_renderFrame;
|
private long m_renderFrame;
|
||||||
|
@@ -65,6 +65,10 @@ public class PrintoutRenderer
|
|||||||
|
|
||||||
public static void drawText( int x, int y, int start, String[] text, String[] colours )
|
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();
|
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||||
|
|
||||||
for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line )
|
for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line )
|
||||||
@@ -76,6 +80,9 @@ public class PrintoutRenderer
|
|||||||
public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook )
|
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.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||||
|
GlStateManager.enableBlend();
|
||||||
|
GlStateManager.enableTexture2D();
|
||||||
|
|
||||||
Minecraft.getMinecraft().getTextureManager().bindTexture( BG );
|
Minecraft.getMinecraft().getTextureManager().bindTexture( BG );
|
||||||
|
|
||||||
Tessellator tessellator = Tessellator.getInstance();
|
Tessellator tessellator = Tessellator.getInstance();
|
||||||
|
@@ -71,7 +71,7 @@ public class TileEntityCableRenderer extends TileEntitySpecialRenderer<TileCable
|
|||||||
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
|
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
|
||||||
buffer.noColor();
|
buffer.noColor();
|
||||||
|
|
||||||
ForgeHooksClient.setRenderLayer( block.getBlockLayer() );
|
ForgeHooksClient.setRenderLayer( block.getRenderLayer() );
|
||||||
|
|
||||||
// See BlockRendererDispatcher#renderBlockDamage
|
// See BlockRendererDispatcher#renderBlockDamage
|
||||||
TextureAtlasSprite breakingTexture = mc.getTextureMapBlocks().getAtlasSprite( "minecraft:blocks/destroy_stage_" + destroyStage );
|
TextureAtlasSprite breakingTexture = mc.getTextureMapBlocks().getAtlasSprite( "minecraft:blocks/destroy_stage_" + destroyStage );
|
||||||
|
@@ -18,12 +18,14 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.client.renderer.block.model.*;
|
import net.minecraft.client.renderer.block.model.*;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
import net.minecraft.client.resources.IResourceManager;
|
import net.minecraft.client.resources.IResourceManager;
|
||||||
import net.minecraft.client.resources.IResourceManagerReloadListener;
|
|
||||||
import net.minecraft.entity.EntityLivingBase;
|
import net.minecraft.entity.EntityLivingBase;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.EnumFacing;
|
import net.minecraft.util.EnumFacing;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.client.resource.IResourceType;
|
||||||
|
import net.minecraftforge.client.resource.ISelectiveResourceReloadListener;
|
||||||
|
import net.minecraftforge.client.resource.VanillaResourceType;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@@ -32,8 +34,9 @@ import javax.vecmath.Matrix4f;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReloadListener
|
public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceReloadListener
|
||||||
{
|
{
|
||||||
private static final Matrix4f s_identity, s_flip;
|
private static final Matrix4f s_identity, s_flip;
|
||||||
|
|
||||||
@@ -155,9 +158,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResourceManagerReload( @Nonnull IResourceManager resourceManager )
|
public void onResourceManagerReload( @Nonnull IResourceManager resourceManager, @Nonnull Predicate<IResourceType> resourcePredicate )
|
||||||
{
|
{
|
||||||
m_cachedModels.clear();
|
if( resourcePredicate.test( VanillaResourceType.MODELS ) ) m_cachedModels.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBakedModel buildModel( TurtleModelCombination combo )
|
private IBakedModel buildModel( TurtleModelCombination combo )
|
||||||
|
@@ -9,19 +9,23 @@ package dan200.computercraft.core.apis;
|
|||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||||
import dan200.computercraft.core.apis.handles.BinaryOutputHandle;
|
import dan200.computercraft.core.apis.handles.BinaryWritableHandle;
|
||||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
||||||
import dan200.computercraft.core.apis.handles.EncodedOutputHandle;
|
import dan200.computercraft.core.apis.handles.EncodedWritableHandle;
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
|
import dan200.computercraft.core.filesystem.FileSystemWrapper;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.InputStream;
|
import java.io.BufferedReader;
|
||||||
import java.io.OutputStream;
|
import java.io.BufferedWriter;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||||
|
|
||||||
@@ -221,38 +225,38 @@ public class FSAPI implements ILuaAPI
|
|||||||
case "r":
|
case "r":
|
||||||
{
|
{
|
||||||
// Open the file for reading, then create a wrapper around the reader
|
// Open the file for reading, then create a wrapper around the reader
|
||||||
InputStream reader = m_fileSystem.openForRead( path );
|
FileSystemWrapper<BufferedReader> reader = m_fileSystem.openForRead( path, EncodedReadableHandle::openUtf8 );
|
||||||
return new Object[] { new EncodedInputHandle( reader ) };
|
return new Object[] { new EncodedReadableHandle( reader.get(), reader ) };
|
||||||
}
|
}
|
||||||
case "w":
|
case "w":
|
||||||
{
|
{
|
||||||
// Open the file for writing, then create a wrapper around the writer
|
// Open the file for writing, then create a wrapper around the writer
|
||||||
OutputStream writer = m_fileSystem.openForWrite( path, false );
|
FileSystemWrapper<BufferedWriter> writer = m_fileSystem.openForWrite( path, false, EncodedWritableHandle::openUtf8 );
|
||||||
return new Object[] { new EncodedOutputHandle( writer ) };
|
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||||
}
|
}
|
||||||
case "a":
|
case "a":
|
||||||
{
|
{
|
||||||
// Open the file for appending, then create a wrapper around the writer
|
// Open the file for appending, then create a wrapper around the writer
|
||||||
OutputStream writer = m_fileSystem.openForWrite( path, true );
|
FileSystemWrapper<BufferedWriter> writer = m_fileSystem.openForWrite( path, true, EncodedWritableHandle::openUtf8 );
|
||||||
return new Object[] { new EncodedOutputHandle( writer ) };
|
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||||
}
|
}
|
||||||
case "rb":
|
case "rb":
|
||||||
{
|
{
|
||||||
// Open the file for binary reading, then create a wrapper around the reader
|
// Open the file for binary reading, then create a wrapper around the reader
|
||||||
InputStream reader = m_fileSystem.openForRead( path );
|
FileSystemWrapper<ReadableByteChannel> reader = m_fileSystem.openForRead( path, Function.identity() );
|
||||||
return new Object[] { new BinaryInputHandle( reader ) };
|
return new Object[] { new BinaryReadableHandle( reader.get(), reader ) };
|
||||||
}
|
}
|
||||||
case "wb":
|
case "wb":
|
||||||
{
|
{
|
||||||
// Open the file for binary writing, then create a wrapper around the writer
|
// Open the file for binary writing, then create a wrapper around the writer
|
||||||
OutputStream writer = m_fileSystem.openForWrite( path, false );
|
FileSystemWrapper<WritableByteChannel> writer = m_fileSystem.openForWrite( path, false, Function.identity() );
|
||||||
return new Object[] { new BinaryOutputHandle( writer ) };
|
return new Object[] { new BinaryWritableHandle( writer.get(), writer ) };
|
||||||
}
|
}
|
||||||
case "ab":
|
case "ab":
|
||||||
{
|
{
|
||||||
// Open the file for binary appending, then create a wrapper around the reader
|
// Open the file for binary appending, then create a wrapper around the reader
|
||||||
OutputStream writer = m_fileSystem.openForWrite( path, true );
|
FileSystemWrapper<WritableByteChannel> writer = m_fileSystem.openForWrite( path, true, Function.identity() );
|
||||||
return new Object[] { new BinaryOutputHandle( writer ) };
|
return new Object[] { new BinaryWritableHandle( writer.get(), writer ) };
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new LuaException( "Unsupported mode" );
|
throw new LuaException( "Unsupported mode" );
|
||||||
|
@@ -64,7 +64,8 @@ public class TermAPI implements ILuaAPI
|
|||||||
"setPaletteColour",
|
"setPaletteColour",
|
||||||
"setPaletteColor",
|
"setPaletteColor",
|
||||||
"getPaletteColour",
|
"getPaletteColour",
|
||||||
"getPaletteColor"
|
"getPaletteColor",
|
||||||
|
"getCursorBlink",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +285,9 @@ public class TermAPI implements ILuaAPI
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case 23:
|
||||||
|
// getCursorBlink
|
||||||
|
return new Object[] { m_terminal.getCursorBlink() };
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return null;
|
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 = asSeekable( channel );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) & 0xFF };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
package dan200.computercraft.core.apis.handles;
|
||||||
|
|
||||||
|
import com.google.common.collect.ObjectArrays;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.core.apis.ArgumentHelper;
|
import dan200.computercraft.core.apis.ArgumentHelper;
|
||||||
import dan200.computercraft.shared.util.StringUtil;
|
import dan200.computercraft.shared.util.StringUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
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 );
|
super( closeable );
|
||||||
this.m_writer = writer;
|
this.m_writer = channel;
|
||||||
|
this.m_seekable = asSeekable( channel );
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryWritableHandle( WritableByteChannel channel )
|
||||||
|
{
|
||||||
|
this( channel, channel );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String[] getMethodNames()
|
public String[] getMethodNames()
|
||||||
{
|
{
|
||||||
return new String[] {
|
return m_seekable == null ? METHOD_NAMES : METHOD_SEEK_NAMES;
|
||||||
"write",
|
|
||||||
"flush",
|
|
||||||
"close",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,12 +55,16 @@ public class BinaryOutputHandle extends HandleGeneric
|
|||||||
if( args.length > 0 && args[ 0 ] instanceof Number )
|
if( args.length > 0 && args[ 0 ] instanceof Number )
|
||||||
{
|
{
|
||||||
int number = ((Number) args[ 0 ]).intValue();
|
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 )
|
else if( args.length > 0 && args[ 0 ] instanceof String )
|
||||||
{
|
{
|
||||||
String value = (String) args[ 0 ];
|
String value = (String) args[ 0 ];
|
||||||
m_writer.write( StringUtil.encodeString( value ) );
|
m_writer.write( ByteBuffer.wrap( StringUtil.encodeString( value ) ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -65,7 +81,9 @@ public class BinaryOutputHandle extends HandleGeneric
|
|||||||
checkOpen();
|
checkOpen();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_writer.flush();
|
// Technically this is not needed
|
||||||
|
if( m_writer instanceof FileChannel ) ((FileChannel) m_writer).force( false );
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch( IOException e )
|
catch( IOException e )
|
||||||
@@ -76,6 +94,10 @@ public class BinaryOutputHandle extends HandleGeneric
|
|||||||
//close
|
//close
|
||||||
close();
|
close();
|
||||||
return null;
|
return null;
|
||||||
|
case 3:
|
||||||
|
// seek
|
||||||
|
checkOpen();
|
||||||
|
return handleSeek( m_seekable, args );
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
@@ -4,51 +4,40 @@ import dan200.computercraft.api.lua.ILuaContext;
|
|||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
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.CharsetDecoder;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
|
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 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;
|
this.m_reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EncodedInputHandle( InputStream stream )
|
public EncodedReadableHandle( @Nonnull BufferedReader reader )
|
||||||
{
|
{
|
||||||
this( stream, "UTF-8" );
|
this( reader, reader );
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String[] getMethodNames()
|
public String[] getMethodNames()
|
||||||
{
|
{
|
||||||
return new String[] {
|
return new String[] {
|
||||||
"readLine",
|
"readLine",
|
||||||
"readAll",
|
"readAll",
|
||||||
@@ -63,13 +52,17 @@ public class EncodedInputHandle extends HandleGeneric
|
|||||||
switch( method )
|
switch( method )
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
{
|
||||||
// readLine
|
// readLine
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
boolean withTrailing = optBoolean( args, 0, false );
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String line = m_reader.readLine();
|
String line = m_reader.readLine();
|
||||||
if( line != null )
|
if( line != null )
|
||||||
{
|
{
|
||||||
|
// While this is technically inaccurate, it's better than nothing
|
||||||
|
if( withTrailing ) line += "\n";
|
||||||
return new Object[] { line };
|
return new Object[] { line };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -81,6 +74,7 @@ public class EncodedInputHandle extends HandleGeneric
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case 1:
|
case 1:
|
||||||
// readAll
|
// readAll
|
||||||
checkOpen();
|
checkOpen();
|
||||||
@@ -108,7 +102,6 @@ public class EncodedInputHandle extends HandleGeneric
|
|||||||
close();
|
close();
|
||||||
return null;
|
return null;
|
||||||
case 3:
|
case 3:
|
||||||
// read
|
|
||||||
checkOpen();
|
checkOpen();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -161,4 +154,19 @@ public class EncodedInputHandle extends HandleGeneric
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BufferedReader openUtf8( ReadableByteChannel channel )
|
||||||
|
{
|
||||||
|
return open( channel, StandardCharsets.UTF_8 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedReader open( ReadableByteChannel channel, Charset charset )
|
||||||
|
{
|
||||||
|
// Create a charset decoder with the same properties as StreamDecoder does for
|
||||||
|
// InputStreams: namely, replace everything instead of erroring.
|
||||||
|
CharsetDecoder decoder = charset.newDecoder()
|
||||||
|
.onMalformedInput( CodingErrorAction.REPLACE )
|
||||||
|
.onUnmappableCharacter( CodingErrorAction.REPLACE );
|
||||||
|
return new BufferedReader( Channels.newReader( channel, decoder, -1 ) );
|
||||||
|
}
|
||||||
}
|
}
|
@@ -4,41 +4,29 @@ import dan200.computercraft.api.lua.ILuaContext;
|
|||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
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.CharsetEncoder;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
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;
|
this.m_writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EncodedOutputHandle( OutputStream stream )
|
public EncodedWritableHandle( @Nonnull BufferedWriter writer )
|
||||||
{
|
{
|
||||||
this( stream, "UTF-8" );
|
this( writer, writer );
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@@ -125,4 +113,20 @@ public class EncodedOutputHandle extends HandleGeneric
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static BufferedWriter openUtf8( WritableByteChannel channel )
|
||||||
|
{
|
||||||
|
return open( channel, StandardCharsets.UTF_8 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedWriter open( WritableByteChannel channel, Charset charset )
|
||||||
|
{
|
||||||
|
// Create a charset encoder with the same properties as StreamEncoder does for
|
||||||
|
// OutputStreams: namely, replace everything instead of erroring.
|
||||||
|
CharsetEncoder encoder = charset.newEncoder()
|
||||||
|
.onMalformedInput( CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
return new BufferedWriter( Channels.newWriter( channel, encoder, -1 ) );
|
||||||
|
}
|
||||||
}
|
}
|
@@ -3,17 +3,23 @@ package dan200.computercraft.core.apis.handles;
|
|||||||
import dan200.computercraft.api.lua.ILuaObject;
|
import dan200.computercraft.api.lua.ILuaObject;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.Channel;
|
||||||
|
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
|
public abstract class HandleGeneric implements ILuaObject
|
||||||
{
|
{
|
||||||
protected final Closeable m_closable;
|
private Closeable m_closable;
|
||||||
protected boolean m_open = true;
|
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
|
protected void checkOpen() throws LuaException
|
||||||
@@ -21,7 +27,7 @@ public abstract class HandleGeneric implements ILuaObject
|
|||||||
if( !m_open ) throw new LuaException( "attempt to use a closed file" );
|
if( !m_open ) throw new LuaException( "attempt to use a closed file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void close()
|
protected final void close()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -31,5 +37,64 @@ public abstract class HandleGeneric implements ILuaObject
|
|||||||
catch( IOException ignored )
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static SeekableByteChannel asSeekable( Channel channel )
|
||||||
|
{
|
||||||
|
if( !(channel instanceof SeekableByteChannel) ) return null;
|
||||||
|
|
||||||
|
SeekableByteChannel seekable = (SeekableByteChannel) channel;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
seekable.position( seekable.position() );
|
||||||
|
return seekable;
|
||||||
|
}
|
||||||
|
catch( IOException | UnsupportedOperationException e )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ package dan200.computercraft.core.apis.http;
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import dan200.computercraft.shared.util.ThreadUtils;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
|
||||||
@@ -24,18 +24,14 @@ public final class HTTPExecutor
|
|||||||
public static final ListeningExecutorService EXECUTOR = MoreExecutors.listeningDecorator( new ThreadPoolExecutor(
|
public static final ListeningExecutorService EXECUTOR = MoreExecutors.listeningDecorator( new ThreadPoolExecutor(
|
||||||
4, Integer.MAX_VALUE,
|
4, Integer.MAX_VALUE,
|
||||||
60L, TimeUnit.SECONDS,
|
60L, TimeUnit.SECONDS,
|
||||||
new SynchronousQueue<Runnable>(),
|
new SynchronousQueue<>(),
|
||||||
new ThreadFactoryBuilder()
|
ThreadUtils.builder( "HTTP" )
|
||||||
.setDaemon( true )
|
|
||||||
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
||||||
.setNameFormat( "ComputerCraft-HTTP-%d" )
|
|
||||||
.build()
|
.build()
|
||||||
) );
|
) );
|
||||||
|
|
||||||
public static final EventLoopGroup LOOP_GROUP = new NioEventLoopGroup( 4, new ThreadFactoryBuilder()
|
public static final EventLoopGroup LOOP_GROUP = new NioEventLoopGroup( 4, ThreadUtils.builder( "Netty" )
|
||||||
.setDaemon( true )
|
|
||||||
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
|
||||||
.setNameFormat( "ComputerCraft-Netty-%d" )
|
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -13,13 +13,17 @@ import dan200.computercraft.api.lua.ILuaContext;
|
|||||||
import dan200.computercraft.api.lua.ILuaObject;
|
import dan200.computercraft.api.lua.ILuaObject;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
||||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||||
|
import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
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.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -182,6 +186,10 @@ public class HTTPRequest implements Runnable
|
|||||||
byte[] result = ByteStreams.toByteArray( is );
|
byte[] result = ByteStreams.toByteArray( is );
|
||||||
is.close();
|
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.
|
// We've got some sort of response, so let's build a resulting object.
|
||||||
Joiner joiner = Joiner.on( ',' );
|
Joiner joiner = Joiner.on( ',' );
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
@@ -193,9 +201,11 @@ public class HTTPRequest implements Runnable
|
|||||||
m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD,
|
m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD,
|
||||||
getHeaderSize( connection.getHeaderFields() ) + result.length );
|
getHeaderSize( connection.getHeaderFields() ) + result.length );
|
||||||
|
|
||||||
InputStream contents = new ByteArrayInputStream( result );
|
SeekableByteChannel contents = new ArrayByteChannel( result );
|
||||||
ILuaObject stream = wrapStream(
|
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
|
connection.getResponseCode(), headers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -13,7 +13,9 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.core.apis.HTTPAPI;
|
import dan200.computercraft.core.apis.HTTPAPI;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
|
import dan200.computercraft.shared.util.StringUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
@@ -26,6 +28,8 @@ import javax.annotation.Nullable;
|
|||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
||||||
|
|
||||||
public class WebsocketConnection extends SimpleChannelInboundHandler<Object> implements ILuaObject, Closeable
|
public class WebsocketConnection extends SimpleChannelInboundHandler<Object> implements ILuaObject, Closeable
|
||||||
{
|
{
|
||||||
public static final String SUCCESS_EVENT = "websocket_success";
|
public static final String SUCCESS_EVENT = "websocket_success";
|
||||||
@@ -174,8 +178,11 @@ public class WebsocketConnection extends SimpleChannelInboundHandler<Object> imp
|
|||||||
{
|
{
|
||||||
checkOpen();
|
checkOpen();
|
||||||
String text = arguments.length > 0 && arguments[0] != null ? arguments[0].toString() : "";
|
String text = arguments.length > 0 && arguments[0] != null ? arguments[0].toString() : "";
|
||||||
|
boolean binary = optBoolean(arguments, 1, false);
|
||||||
computer.addTrackingChange( TrackingField.WEBSOCKET_OUTGOING, text.length() );
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
|
@@ -8,13 +8,14 @@ package dan200.computercraft.core.computer;
|
|||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.core.tracking.Tracking;
|
||||||
|
import dan200.computercraft.shared.util.ThreadUtils;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
public class ComputerThread
|
public class ComputerThread
|
||||||
{
|
{
|
||||||
@@ -56,8 +57,8 @@ public class ComputerThread
|
|||||||
*/
|
*/
|
||||||
private static Thread[] s_threads = null;
|
private static Thread[] s_threads = null;
|
||||||
|
|
||||||
private static final AtomicInteger s_ManagerCounter = new AtomicInteger( 1 );
|
private static final ThreadFactory s_ManagerFactory = ThreadUtils.factory( "Computer-Manager" );
|
||||||
private static final AtomicInteger s_DelegateCounter = new AtomicInteger( 1 );
|
private static final ThreadFactory s_RunnerFactory = ThreadUtils.factory( "Computer-Runner" );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the computer thread
|
* Start the computer thread
|
||||||
@@ -72,16 +73,12 @@ public class ComputerThread
|
|||||||
s_threads = new Thread[ComputerCraft.computer_threads];
|
s_threads = new Thread[ComputerCraft.computer_threads];
|
||||||
}
|
}
|
||||||
|
|
||||||
SecurityManager manager = System.getSecurityManager();
|
|
||||||
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
|
|
||||||
for( int i = 0; i < s_threads.length; i++ )
|
for( int i = 0; i < s_threads.length; i++ )
|
||||||
{
|
{
|
||||||
Thread thread = s_threads[i];
|
Thread thread = s_threads[i];
|
||||||
if( thread == null || !thread.isAlive() )
|
if( thread == null || !thread.isAlive() )
|
||||||
{
|
{
|
||||||
thread = s_threads[i] = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager-" + s_ManagerCounter.getAndIncrement() );
|
(s_threads[i] = s_ManagerFactory.newThread( new TaskExecutor() )).start();
|
||||||
thread.setDaemon( true );
|
|
||||||
thread.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,12 +185,7 @@ public class ComputerThread
|
|||||||
if( thread == null || !thread.isAlive() )
|
if( thread == null || !thread.isAlive() )
|
||||||
{
|
{
|
||||||
runner = new TaskRunner();
|
runner = new TaskRunner();
|
||||||
|
(thread = s_RunnerFactory.newThread( runner )).start();
|
||||||
SecurityManager manager = System.getSecurityManager();
|
|
||||||
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
|
|
||||||
Thread thread = this.thread = new Thread( group, runner, "ComputerCraft-Computer-Runner" + s_DelegateCounter.getAndIncrement() );
|
|
||||||
thread.setDaemon( true );
|
|
||||||
thread.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.api.filesystem.IMount;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -114,6 +115,7 @@ public class ComboMount implements IMount
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
for( int i=m_parts.length-1; i>=0; --i )
|
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" );
|
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 dan200.computercraft.api.filesystem.IMount;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class EmptyMount implements IMount
|
public class EmptyMount implements IMount
|
||||||
{
|
{
|
||||||
public EmptyMount()
|
public EmptyMount()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMount implementation
|
// IMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists( @Nonnull String path )
|
public boolean exists( @Nonnull String path )
|
||||||
{
|
{
|
||||||
return path.isEmpty();
|
return path.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory( @Nonnull String path )
|
public boolean isDirectory( @Nonnull String path )
|
||||||
{
|
{
|
||||||
return path.isEmpty();
|
return path.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list( @Nonnull String path, @Nonnull List<String> contents )
|
public void list( @Nonnull String path, @Nonnull List<String> contents )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize( @Nonnull String path )
|
public long getSize( @Nonnull String path )
|
||||||
{
|
{
|
||||||
@@ -45,8 +47,17 @@ public class EmptyMount implements IMount
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@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" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,68 +6,54 @@
|
|||||||
|
|
||||||
package dan200.computercraft.core.filesystem;
|
package dan200.computercraft.core.filesystem;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.OpenOption;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class FileMount implements IWritableMount
|
public class FileMount implements IWritableMount
|
||||||
{
|
{
|
||||||
private static int MINIMUM_FILE_SIZE = 500;
|
private static final int MINIMUM_FILE_SIZE = 500;
|
||||||
|
private static final Set<OpenOption> READ_OPTIONS = Collections.singleton( StandardOpenOption.READ );
|
||||||
private class CountingOutputStream extends OutputStream
|
private static final Set<OpenOption> WRITE_OPTIONS = Sets.newHashSet( StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING );
|
||||||
|
private static final Set<OpenOption> APPEND_OPTIONS = Sets.newHashSet( StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND );
|
||||||
|
|
||||||
|
private class WritableCountingChannel implements WritableByteChannel
|
||||||
{
|
{
|
||||||
private OutputStream m_innerStream;
|
|
||||||
private long m_ignoredBytesLeft;
|
private final WritableByteChannel m_inner;
|
||||||
|
long m_ignoredBytesLeft;
|
||||||
public CountingOutputStream( OutputStream innerStream, long bytesToIgnore )
|
|
||||||
|
WritableCountingChannel( WritableByteChannel inner, long bytesToIgnore )
|
||||||
{
|
{
|
||||||
m_innerStream = innerStream;
|
m_inner = inner;
|
||||||
m_ignoredBytesLeft = bytesToIgnore;
|
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
|
@Override
|
||||||
public void write( int b ) throws IOException
|
public int write( @Nonnull ByteBuffer b ) throws IOException
|
||||||
{
|
{
|
||||||
count( 1 );
|
count( b.remaining() );
|
||||||
m_innerStream.write( b );
|
return m_inner.write( b );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void count( long n ) throws IOException
|
void count( long n ) throws IOException
|
||||||
{
|
{
|
||||||
m_ignoredBytesLeft -= n;
|
m_ignoredBytesLeft -= n;
|
||||||
if( m_ignoredBytesLeft < 0 )
|
if( m_ignoredBytesLeft < 0 )
|
||||||
{
|
{
|
||||||
long newBytes = -m_ignoredBytesLeft;
|
long newBytes = -m_ignoredBytesLeft;
|
||||||
m_ignoredBytesLeft = 0;
|
m_ignoredBytesLeft = 0;
|
||||||
|
|
||||||
long bytesLeft = m_capacity - m_usedSpace;
|
long bytesLeft = m_capacity - m_usedSpace;
|
||||||
if( newBytes > bytesLeft )
|
if( newBytes > bytesLeft )
|
||||||
{
|
{
|
||||||
@@ -79,12 +65,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 File m_rootPath;
|
||||||
private long m_capacity;
|
private long m_capacity;
|
||||||
private long m_usedSpace;
|
private long m_usedSpace;
|
||||||
|
|
||||||
public FileMount( File rootPath, long capacity )
|
public FileMount( File rootPath, long capacity )
|
||||||
{
|
{
|
||||||
m_rootPath = rootPath;
|
m_rootPath = rootPath;
|
||||||
@@ -93,7 +146,7 @@ public class FileMount implements IWritableMount
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IMount implementation
|
// IMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists( @Nonnull String path )
|
public boolean exists( @Nonnull String path )
|
||||||
{
|
{
|
||||||
@@ -107,7 +160,7 @@ public class FileMount implements IWritableMount
|
|||||||
return file.exists();
|
return file.exists();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory( @Nonnull String path )
|
public boolean isDirectory( @Nonnull String path )
|
||||||
{
|
{
|
||||||
@@ -121,7 +174,7 @@ public class FileMount implements IWritableMount
|
|||||||
return file.exists() && file.isDirectory();
|
return file.exists() && file.isDirectory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||||
{
|
{
|
||||||
@@ -150,7 +203,7 @@ public class FileMount implements IWritableMount
|
|||||||
{
|
{
|
||||||
throw new IOException( "/" + path + ": Not a directory" );
|
throw new IOException( "/" + path + ": Not a directory" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -180,9 +233,10 @@ public class FileMount implements IWritableMount
|
|||||||
}
|
}
|
||||||
throw new IOException( "/" + path + ": No such file" );
|
throw new IOException( "/" + path + ": No such file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
if( created() )
|
if( created() )
|
||||||
@@ -193,11 +247,26 @@ public class FileMount implements IWritableMount
|
|||||||
return new FileInputStream( file );
|
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(), READ_OPTIONS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException( "/" + path + ": No such file" );
|
||||||
|
}
|
||||||
|
|
||||||
// IWritableMount implementation
|
// IWritableMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void makeDirectory( @Nonnull String path ) throws IOException
|
public void makeDirectory( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
@@ -224,7 +293,7 @@ public class FileMount implements IWritableMount
|
|||||||
{
|
{
|
||||||
throw new IOException( "/" + path + ": Out of space" );
|
throw new IOException( "/" + path + ": Out of space" );
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean success = file.mkdirs();
|
boolean success = file.mkdirs();
|
||||||
if( success )
|
if( success )
|
||||||
{
|
{
|
||||||
@@ -236,7 +305,7 @@ public class FileMount implements IWritableMount
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete( @Nonnull String path ) throws IOException
|
public void delete( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
@@ -244,7 +313,7 @@ public class FileMount implements IWritableMount
|
|||||||
{
|
{
|
||||||
throw new IOException( "/" + path + ": Access denied" );
|
throw new IOException( "/" + path + ": Access denied" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( created() )
|
if( created() )
|
||||||
{
|
{
|
||||||
File file = getRealPath( path );
|
File file = getRealPath( path );
|
||||||
@@ -254,7 +323,7 @@ public class FileMount implements IWritableMount
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteRecursively( File file ) throws IOException
|
private void deleteRecursively( File file ) throws IOException
|
||||||
{
|
{
|
||||||
// Empty directories first
|
// Empty directories first
|
||||||
@@ -266,7 +335,7 @@ public class FileMount implements IWritableMount
|
|||||||
deleteRecursively( new File( file, aChildren ) );
|
deleteRecursively( new File( file, aChildren ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then delete
|
// Then delete
|
||||||
long fileSize = file.isDirectory() ? 0 : file.length();
|
long fileSize = file.isDirectory() ? 0 : file.length();
|
||||||
boolean success = file.delete();
|
boolean success = file.delete();
|
||||||
@@ -279,10 +348,26 @@ public class FileMount implements IWritableMount
|
|||||||
throw new IOException( "Access denied" );
|
throw new IOException( "Access denied" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public OutputStream openForWrite( @Nonnull String path ) throws IOException
|
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();
|
create();
|
||||||
File file = getRealPath( path );
|
File file = getRealPath( path );
|
||||||
@@ -308,13 +393,13 @@ public class FileMount implements IWritableMount
|
|||||||
m_usedSpace -= Math.max( file.length(), MINIMUM_FILE_SIZE );
|
m_usedSpace -= Math.max( file.length(), MINIMUM_FILE_SIZE );
|
||||||
m_usedSpace += 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(), WRITE_OPTIONS ), MINIMUM_FILE_SIZE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openForAppend( @Nonnull String path ) throws IOException
|
public WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
if( created() )
|
if( created() )
|
||||||
{
|
{
|
||||||
@@ -329,7 +414,11 @@ public class FileMount implements IWritableMount
|
|||||||
}
|
}
|
||||||
else
|
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(), APPEND_OPTIONS ),
|
||||||
|
Math.max( MINIMUM_FILE_SIZE - file.length(), 0 )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -337,23 +426,23 @@ public class FileMount implements IWritableMount
|
|||||||
throw new IOException( "/" + path + ": No such file" );
|
throw new IOException( "/" + path + ": No such file" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getRemainingSpace()
|
public long getRemainingSpace()
|
||||||
{
|
{
|
||||||
return Math.max( m_capacity - m_usedSpace, 0 );
|
return Math.max( m_capacity - m_usedSpace, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getRealPath( String path )
|
public File getRealPath( String path )
|
||||||
{
|
{
|
||||||
return new File( m_rootPath, path );
|
return new File( m_rootPath, path );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean created()
|
private boolean created()
|
||||||
{
|
{
|
||||||
return m_rootPath.exists();
|
return m_rootPath.exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void create() throws IOException
|
private void create() throws IOException
|
||||||
{
|
{
|
||||||
if( !m_rootPath.exists() )
|
if( !m_rootPath.exists() )
|
||||||
@@ -365,7 +454,7 @@ public class FileMount implements IWritableMount
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long measureUsedSpace( File file )
|
private long measureUsedSpace( File file )
|
||||||
{
|
{
|
||||||
if( !file.exists() )
|
if( !file.exists() )
|
||||||
|
@@ -6,13 +6,23 @@
|
|||||||
|
|
||||||
package dan200.computercraft.core.filesystem;
|
package dan200.computercraft.core.filesystem;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
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.*;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class FileSystem
|
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 );
|
path = toLocal( path );
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if( m_mount.exists( path ) && !m_mount.isDirectory( path ) )
|
if( m_mount.exists( path ) && !m_mount.isDirectory( path ) )
|
||||||
{
|
{
|
||||||
return m_mount.openForRead( path );
|
return m_mount.openChannelForRead( path );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -209,13 +219,17 @@ public class FileSystem
|
|||||||
m_writableMount.delete( path );
|
m_writableMount.delete( path );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch( AccessDeniedException e )
|
||||||
|
{
|
||||||
|
throw new FileSystemException( "Access denied" );
|
||||||
|
}
|
||||||
catch( IOException e )
|
catch( IOException e )
|
||||||
{
|
{
|
||||||
throw new FileSystemException( e.getMessage() );
|
throw new FileSystemException( e.getMessage() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream openForWrite( String path ) throws FileSystemException
|
public WritableByteChannel openForWrite( String path ) throws FileSystemException
|
||||||
{
|
{
|
||||||
if( m_writableMount == null )
|
if( m_writableMount == null )
|
||||||
{
|
{
|
||||||
@@ -238,16 +252,20 @@ public class FileSystem
|
|||||||
m_writableMount.makeDirectory( dir );
|
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 )
|
catch( IOException e )
|
||||||
{
|
{
|
||||||
throw new FileSystemException( e.getMessage() );
|
throw new FileSystemException( e.getMessage() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream openForAppend( String path ) throws FileSystemException
|
public WritableByteChannel openForAppend( String path ) throws FileSystemException
|
||||||
{
|
{
|
||||||
if( m_writableMount == null )
|
if( m_writableMount == null )
|
||||||
{
|
{
|
||||||
@@ -266,7 +284,7 @@ public class FileSystem
|
|||||||
m_writableMount.makeDirectory( dir );
|
m_writableMount.makeDirectory( dir );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m_writableMount.openForWrite( path );
|
return m_writableMount.openChannelForWrite( path );
|
||||||
}
|
}
|
||||||
else if( m_mount.isDirectory( path ) )
|
else if( m_mount.isDirectory( path ) )
|
||||||
{
|
{
|
||||||
@@ -274,9 +292,13 @@ public class FileSystem
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return m_writableMount.openForAppend( path );
|
return m_writableMount.openChannelForAppend( path );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch( AccessDeniedException e )
|
||||||
|
{
|
||||||
|
throw new FileSystemException( "Access denied" );
|
||||||
|
}
|
||||||
catch( IOException e )
|
catch( IOException e )
|
||||||
{
|
{
|
||||||
throw new FileSystemException( e.getMessage() );
|
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 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
|
public FileSystem( String rootLabel, IMount rootMount ) throws FileSystemException
|
||||||
{
|
{
|
||||||
mount( rootLabel, "", rootMount );
|
mount( rootLabel, "", rootMount );
|
||||||
@@ -310,24 +334,15 @@ public class FileSystem
|
|||||||
// Close all dangling open files
|
// Close all dangling open files
|
||||||
synchronized( m_openFiles )
|
synchronized( m_openFiles )
|
||||||
{
|
{
|
||||||
for( Closeable file : m_openFiles )
|
for( Closeable file : m_openFiles.values() ) closeQuietly( file );
|
||||||
{
|
|
||||||
try {
|
|
||||||
file.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_openFiles.clear();
|
m_openFiles.clear();
|
||||||
|
while( m_openFileQueue.poll() != null ) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void mount( String label, String location, IMount mount ) throws FileSystemException
|
public synchronized void mount( String label, String location, IMount mount ) throws FileSystemException
|
||||||
{
|
{
|
||||||
if( mount == null )
|
if( mount == null ) throw new NullPointerException();
|
||||||
{
|
|
||||||
throw new NullPointerException();
|
|
||||||
}
|
|
||||||
location = sanitizePath( location );
|
location = sanitizePath( location );
|
||||||
if( location.contains( ".." ) ) {
|
if( location.contains( ".." ) ) {
|
||||||
throw new FileSystemException( "Cannot mount below the root" );
|
throw new FileSystemException( "Cannot mount below the root" );
|
||||||
@@ -348,24 +363,18 @@ public class FileSystem
|
|||||||
}
|
}
|
||||||
mount( new MountWrapper( label, location, mount ) );
|
mount( new MountWrapper( label, location, mount ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void mount( MountWrapper wrapper )
|
private synchronized void mount( MountWrapper wrapper )
|
||||||
{
|
{
|
||||||
String location = wrapper.getLocation();
|
String location = wrapper.getLocation();
|
||||||
if( m_mounts.containsKey( location ) )
|
m_mounts.remove( location );
|
||||||
{
|
|
||||||
m_mounts.remove( location );
|
|
||||||
}
|
|
||||||
m_mounts.put( location, wrapper );
|
m_mounts.put( location, wrapper );
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unmount( String path )
|
public synchronized void unmount( String path )
|
||||||
{
|
{
|
||||||
path = sanitizePath( 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 )
|
public synchronized String combine( String path, String childPath )
|
||||||
@@ -599,108 +608,85 @@ public class FileSystem
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Copy a file:
|
// Copy a file:
|
||||||
InputStream source = null;
|
try( ReadableByteChannel source = sourceMount.openForRead( sourcePath );
|
||||||
OutputStream destination = null;
|
WritableByteChannel destination = destinationMount.openForWrite( destinationPath ) )
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// Open both files
|
|
||||||
source = sourceMount.openForRead( sourcePath );
|
|
||||||
destination = destinationMount.openForWrite( destinationPath );
|
|
||||||
|
|
||||||
// Copy bytes as fast as we can
|
// Copy bytes as fast as we can
|
||||||
byte[] buffer = new byte[1024];
|
ByteStreams.copy( source, destination );
|
||||||
while( true )
|
}
|
||||||
{
|
catch( AccessDeniedException e )
|
||||||
int bytesRead = source.read( buffer );
|
{
|
||||||
if( bytesRead >= 0 )
|
throw new FileSystemException( "Access denied" );
|
||||||
{
|
|
||||||
destination.write( buffer, 0, bytesRead );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch( IOException e )
|
catch( IOException e )
|
||||||
{
|
{
|
||||||
throw new FileSystemException( e.getMessage() );
|
throw new FileSystemException( e.getMessage() );
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanup()
|
||||||
|
{
|
||||||
|
synchronized( m_openFiles )
|
||||||
|
{
|
||||||
|
Reference<?> ref;
|
||||||
|
while( (ref = m_openFileQueue.poll()) != null )
|
||||||
{
|
{
|
||||||
// Close both files
|
Closeable file = m_openFiles.remove( ref );
|
||||||
if( source != null )
|
if( file != null ) closeQuietly( file );
|
||||||
{
|
|
||||||
try {
|
|
||||||
source.close();
|
|
||||||
} catch( IOException e ) {
|
|
||||||
// nobody cares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( destination != null )
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
destination.close();
|
|
||||||
} catch( IOException e ) {
|
|
||||||
// nobody cares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 )
|
synchronized( m_openFiles )
|
||||||
{
|
{
|
||||||
if( ComputerCraft.maximumFilesOpen > 0 &&
|
if( ComputerCraft.maximumFilesOpen > 0 &&
|
||||||
m_openFiles.size() >= ComputerCraft.maximumFilesOpen )
|
m_openFiles.size() >= ComputerCraft.maximumFilesOpen )
|
||||||
{
|
{
|
||||||
if( handle != null )
|
closeQuietly( file );
|
||||||
{
|
throw new FileSystemException( "Too many files already open" );
|
||||||
try {
|
|
||||||
handle.close();
|
|
||||||
} catch ( IOException ignored ) {
|
|
||||||
// shrug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new FileSystemException("Too many files already open");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_openFiles.add( handle );
|
FileSystemWrapper<T> wrapper = new FileSystemWrapper<>( this, file, m_openFileQueue );
|
||||||
return file;
|
m_openFiles.put( wrapper.self, file );
|
||||||
|
return wrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void closeFile( Closeable handle ) throws IOException
|
synchronized void removeFile( FileSystemWrapper<?> handle )
|
||||||
{
|
{
|
||||||
synchronized( m_openFiles )
|
synchronized( m_openFiles )
|
||||||
{
|
{
|
||||||
m_openFiles.remove( handle );
|
m_openFiles.remove( handle.self );
|
||||||
handle.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 );
|
MountWrapper mount = getMount( path );
|
||||||
InputStream stream = mount.openForRead( path );
|
ReadableByteChannel channel = mount.openForRead( path );
|
||||||
if( stream != null )
|
if( channel != null )
|
||||||
{
|
{
|
||||||
return openFile( new ClosingInputStream( stream ), stream );
|
return openFile( open.apply( channel ) );
|
||||||
}
|
}
|
||||||
return null;
|
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 );
|
MountWrapper mount = getMount( path );
|
||||||
OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path );
|
WritableByteChannel channel = append ? mount.openForAppend( path ) : mount.openForWrite( path );
|
||||||
if( stream != null )
|
if( channel != null )
|
||||||
{
|
{
|
||||||
return openFile( new ClosingOutputStream( stream ), stream );
|
return openFile( open.apply( channel ) );
|
||||||
}
|
}
|
||||||
return null;
|
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();
|
||||||
}
|
}
|
||||||
|
catch( IOException ignored )
|
||||||
@Override
|
|
||||||
public void close() throws IOException
|
|
||||||
{
|
{
|
||||||
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;
|
package dan200.computercraft.core.filesystem;
|
||||||
|
|
||||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.util.Collections;
|
import java.nio.file.FileSystem;
|
||||||
import java.util.List;
|
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
|
Queue<Entry> entries = new ArrayDeque<>();
|
||||||
public void makeDirectory( @Nonnull String path ) throws IOException
|
entries.add( rootEntry );
|
||||||
{
|
while( !entries.isEmpty() )
|
||||||
try
|
|
||||||
{
|
{
|
||||||
m_filesystem.makeDir( path );
|
Entry entry = entries.remove();
|
||||||
}
|
try( Stream<Path> childStream = Files.list( entry.path ) )
|
||||||
catch( FileSystemException e )
|
{
|
||||||
{
|
Iterator<Path> children = childStream.iterator();
|
||||||
throw new IOException( e.getMessage() );
|
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
|
@Override
|
||||||
public void delete( @Nonnull String path ) throws IOException
|
public boolean exists( @Nonnull String path )
|
||||||
{
|
{
|
||||||
try
|
return getFile( path ) != null;
|
||||||
{
|
|
||||||
m_filesystem.delete( path );
|
|
||||||
}
|
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public OutputStream openForWrite( @Nonnull String path ) throws IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return m_filesystem.openForWrite( path, false );
|
|
||||||
}
|
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public OutputStream openForAppend( @Nonnull String path ) throws IOException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return m_filesystem.openForWrite( path, true );
|
|
||||||
}
|
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getRemainingSpace() throws IOException
|
public boolean isDirectory( @Nonnull String path )
|
||||||
{
|
{
|
||||||
try
|
Entry entry = getFile( path );
|
||||||
{
|
return entry != null && entry.directory;
|
||||||
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
|
@Override
|
||||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||||
{
|
{
|
||||||
try
|
Entry entry = getFile( path );
|
||||||
{
|
if( entry == null || !entry.directory ) throw new IOException( "/" + path + ": Not a directory" );
|
||||||
Collections.addAll( contents, m_filesystem.list( path ) );
|
|
||||||
}
|
contents.addAll( entry.children.keySet() );
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize( @Nonnull String path ) throws IOException
|
public long getSize( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
try
|
Entry file = getFile( path );
|
||||||
{
|
if( file == null ) throw new IOException( "/" + path + ": No such file" );
|
||||||
return m_filesystem.getSize( path );
|
return file.size;
|
||||||
}
|
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
try
|
Entry file = getFile( path );
|
||||||
{
|
if( file == null || file.directory ) throw new IOException( "/" + path + ": No such file" );
|
||||||
return m_filesystem.openForRead( path );
|
|
||||||
}
|
return Files.newInputStream( file.path, StandardOpenOption.READ );
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
@Override
|
@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
|
private Entry getFile( String path )
|
||||||
public void copy( String from, String to ) throws IOException
|
|
||||||
{
|
{
|
||||||
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 );
|
if( entry == null || entry.children == null ) return null;
|
||||||
}
|
entry = entry.children.get( component );
|
||||||
catch( FileSystemException e )
|
|
||||||
{
|
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static class Entry
|
||||||
public void move( String from, String to ) throws IOException
|
|
||||||
{
|
{
|
||||||
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 );
|
if( name.endsWith( "/" ) || name.endsWith( "\\" ) ) name = name.substring( 0, name.length() - 1 );
|
||||||
}
|
|
||||||
catch( FileSystemException e )
|
this.name = name;
|
||||||
{
|
this.path = path;
|
||||||
throw new IOException( e.getMessage() );
|
|
||||||
|
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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -20,15 +20,16 @@ import java.util.Map;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class JarMount implements IMount
|
public class JarMount implements IMount
|
||||||
{
|
{
|
||||||
private class FileInZip
|
private static class FileInZip
|
||||||
{
|
{
|
||||||
private String m_path;
|
private String m_path;
|
||||||
private boolean m_directory;
|
private boolean m_directory;
|
||||||
private long m_size;
|
private long m_size;
|
||||||
private Map<String, FileInZip> m_children;
|
private Map<String, FileInZip> m_children;
|
||||||
|
|
||||||
public FileInZip( String path, boolean directory, long size )
|
public FileInZip( String path, boolean directory, long size )
|
||||||
{
|
{
|
||||||
m_path = path;
|
m_path = path;
|
||||||
@@ -36,44 +37,44 @@ public class JarMount implements IMount
|
|||||||
m_size = m_directory ? 0 : size;
|
m_size = m_directory ? 0 : size;
|
||||||
m_children = new LinkedHashMap<>();
|
m_children = new LinkedHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPath()
|
public String getPath()
|
||||||
{
|
{
|
||||||
return m_path;
|
return m_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDirectory()
|
public boolean isDirectory()
|
||||||
{
|
{
|
||||||
return m_directory;
|
return m_directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSize()
|
public long getSize()
|
||||||
{
|
{
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void list( List<String> contents )
|
public void list( List<String> contents )
|
||||||
{
|
{
|
||||||
contents.addAll( m_children.keySet() );
|
contents.addAll( m_children.keySet() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertChild( FileInZip child )
|
public void insertChild( FileInZip child )
|
||||||
{
|
{
|
||||||
String localPath = FileSystem.toLocal( child.getPath(), m_path );
|
String localPath = FileSystem.toLocal( child.getPath(), m_path );
|
||||||
m_children.put( localPath, child );
|
m_children.put( localPath, child );
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileInZip getFile( String path )
|
public FileInZip getFile( String path )
|
||||||
{
|
{
|
||||||
// If we've reached the target, return this
|
// If we've reached the target, return this
|
||||||
if( path.equals( m_path ) )
|
if( path.equals( m_path ) )
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, get the next component of the path
|
// Otherwise, get the next component of the path
|
||||||
String localPath = FileSystem.toLocal( path, m_path );
|
String localPath = FileSystem.toLocal( path, m_path );
|
||||||
int slash = localPath.indexOf("/");
|
int slash = localPath.indexOf( "/" );
|
||||||
if( slash >= 0 )
|
if( slash >= 0 )
|
||||||
{
|
{
|
||||||
localPath = localPath.substring( 0, slash );
|
localPath = localPath.substring( 0, slash );
|
||||||
@@ -85,17 +86,17 @@ public class JarMount implements IMount
|
|||||||
{
|
{
|
||||||
return subFile.getFile( path );
|
return subFile.getFile( path );
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileInZip getParent( String path )
|
public FileInZip getParent( String path )
|
||||||
{
|
{
|
||||||
if( path.length() == 0 )
|
if( path.length() == 0 )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileInZip file = getFile( FileSystem.getDirectory( path ) );
|
FileInZip file = getFile( FileSystem.getDirectory( path ) );
|
||||||
if( file.isDirectory() )
|
if( file.isDirectory() )
|
||||||
{
|
{
|
||||||
@@ -104,18 +105,19 @@ public class JarMount implements IMount
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ZipFile m_zipFile;
|
private ZipFile m_zipFile;
|
||||||
private FileInZip m_root;
|
private FileInZip m_root;
|
||||||
private String m_rootPath;
|
private String m_rootPath;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public JarMount( File jarFile, String subPath ) throws IOException
|
public JarMount( File jarFile, String subPath ) throws IOException
|
||||||
{
|
{
|
||||||
if( !jarFile.exists() || jarFile.isDirectory() )
|
if( !jarFile.exists() || jarFile.isDirectory() )
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException();
|
throw new FileNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the zip file
|
// Open the zip file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -125,13 +127,13 @@ public class JarMount implements IMount
|
|||||||
{
|
{
|
||||||
throw new IOException( "Error loading zip file" );
|
throw new IOException( "Error loading zip file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_zipFile.getEntry( subPath ) == null )
|
if( m_zipFile.getEntry( subPath ) == null )
|
||||||
{
|
{
|
||||||
m_zipFile.close();
|
m_zipFile.close();
|
||||||
throw new IOException( "Zip does not contain path" );
|
throw new IOException( "Zip does not contain path" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read in all the entries
|
// Read in all the entries
|
||||||
Enumeration<? extends ZipEntry> zipEntries = m_zipFile.entries();
|
Enumeration<? extends ZipEntry> zipEntries = m_zipFile.entries();
|
||||||
while( zipEntries.hasMoreElements() )
|
while( zipEntries.hasMoreElements() )
|
||||||
@@ -139,7 +141,7 @@ public class JarMount implements IMount
|
|||||||
ZipEntry entry = zipEntries.nextElement();
|
ZipEntry entry = zipEntries.nextElement();
|
||||||
String entryName = entry.getName();
|
String entryName = entry.getName();
|
||||||
if( entryName.startsWith( subPath ) )
|
if( entryName.startsWith( subPath ) )
|
||||||
{
|
{
|
||||||
entryName = FileSystem.toLocal( entryName, subPath );
|
entryName = FileSystem.toLocal( entryName, subPath );
|
||||||
if( m_root == null )
|
if( m_root == null )
|
||||||
{
|
{
|
||||||
@@ -169,19 +171,19 @@ public class JarMount implements IMount
|
|||||||
// TODO: handle this case. The code currently assumes we find folders before their contents
|
// TODO: handle this case. The code currently assumes we find folders before their contents
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMount implementation
|
// IMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists( @Nonnull String path )
|
public boolean exists( @Nonnull String path )
|
||||||
{
|
{
|
||||||
FileInZip file = m_root.getFile( path );
|
FileInZip file = m_root.getFile( path );
|
||||||
return file != null;
|
return file != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory( @Nonnull String path )
|
public boolean isDirectory( @Nonnull String path )
|
||||||
{
|
{
|
||||||
@@ -192,7 +194,7 @@ public class JarMount implements IMount
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
|
||||||
{
|
{
|
||||||
@@ -203,10 +205,10 @@ public class JarMount implements IMount
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new IOException( "/" + path + ": Not a directory" );
|
throw new IOException( "/" + path + ": Not a directory" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize( @Nonnull String path ) throws IOException
|
public long getSize( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
@@ -215,7 +217,7 @@ public class JarMount implements IMount
|
|||||||
{
|
{
|
||||||
return file.getSize();
|
return file.getSize();
|
||||||
}
|
}
|
||||||
throw new IOException( "/" + path + ": No such file" );
|
throw new IOException( "/" + path + ": No such file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@@ -243,6 +245,6 @@ public class JarMount implements IMount
|
|||||||
// treat errors as non-existance of file
|
// treat errors as non-existance of file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IOException( "/" + path + ": No such file" );
|
throw new IOException( "/" + path + ": No such file" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.api.filesystem.IMount;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SubMount implements IMount
|
public class SubMount implements IMount
|
||||||
@@ -52,11 +53,19 @@ public class SubMount implements IMount
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public InputStream openForRead( @Nonnull String path ) throws IOException
|
public InputStream openForRead( @Nonnull String path ) throws IOException
|
||||||
{
|
{
|
||||||
return m_parent.openForRead( getFullPath( path ) );
|
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 )
|
private String getFullPath( String path )
|
||||||
{
|
{
|
||||||
if( path.length() == 0 )
|
if( path.length() == 0 )
|
||||||
|
@@ -7,14 +7,13 @@
|
|||||||
package dan200.computercraft.core.lua;
|
package dan200.computercraft.core.lua;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.*;
|
||||||
import dan200.computercraft.api.lua.ILuaObject;
|
|
||||||
import dan200.computercraft.api.lua.ILuaTask;
|
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import dan200.computercraft.core.computer.ITask;
|
import dan200.computercraft.core.computer.ITask;
|
||||||
import dan200.computercraft.core.computer.MainThread;
|
import dan200.computercraft.core.computer.MainThread;
|
||||||
|
import dan200.computercraft.core.tracking.Tracking;
|
||||||
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
|
import dan200.computercraft.shared.util.ThreadUtils;
|
||||||
import org.squiddev.cobalt.*;
|
import org.squiddev.cobalt.*;
|
||||||
import org.squiddev.cobalt.compiler.CompileException;
|
import org.squiddev.cobalt.compiler.CompileException;
|
||||||
import org.squiddev.cobalt.compiler.LoadState;
|
import org.squiddev.cobalt.compiler.LoadState;
|
||||||
@@ -25,7 +24,7 @@ import org.squiddev.cobalt.function.LibFunction;
|
|||||||
import org.squiddev.cobalt.function.LuaFunction;
|
import org.squiddev.cobalt.function.LuaFunction;
|
||||||
import org.squiddev.cobalt.function.VarArgFunction;
|
import org.squiddev.cobalt.function.VarArgFunction;
|
||||||
import org.squiddev.cobalt.lib.*;
|
import org.squiddev.cobalt.lib.*;
|
||||||
import org.squiddev.cobalt.lib.platform.AbstractResourceManipulator;
|
import org.squiddev.cobalt.lib.platform.VoidResourceManipulator;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -35,6 +34,9 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.squiddev.cobalt.Constants.NONE;
|
import static org.squiddev.cobalt.Constants.NONE;
|
||||||
import static org.squiddev.cobalt.ValueFactory.valueOf;
|
import static org.squiddev.cobalt.ValueFactory.valueOf;
|
||||||
@@ -42,12 +44,19 @@ import static org.squiddev.cobalt.ValueFactory.varargsOf;
|
|||||||
|
|
||||||
public class CobaltLuaMachine implements ILuaMachine
|
public class CobaltLuaMachine implements ILuaMachine
|
||||||
{
|
{
|
||||||
|
private static final ThreadPoolExecutor coroutines = new ThreadPoolExecutor(
|
||||||
|
0, Integer.MAX_VALUE,
|
||||||
|
60L, TimeUnit.SECONDS,
|
||||||
|
new SynchronousQueue<>(),
|
||||||
|
ThreadUtils.factory( "Coroutine" )
|
||||||
|
);
|
||||||
|
|
||||||
private final Computer m_computer;
|
private final Computer m_computer;
|
||||||
|
|
||||||
private final LuaState m_state;
|
private LuaState m_state;
|
||||||
private final LuaTable m_globals;
|
private LuaTable m_globals;
|
||||||
|
|
||||||
private LuaThread m_mainRoutine;
|
private LuaThread m_mainRoutine;
|
||||||
|
|
||||||
private String m_eventFilter;
|
private String m_eventFilter;
|
||||||
private String m_softAbortMessage;
|
private String m_softAbortMessage;
|
||||||
private String m_hardAbortMessage;
|
private String m_hardAbortMessage;
|
||||||
@@ -57,60 +66,71 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
m_computer = computer;
|
m_computer = computer;
|
||||||
|
|
||||||
// Create an environment to run in
|
// Create an environment to run in
|
||||||
final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator()
|
LuaState state = this.m_state = LuaState.builder()
|
||||||
{
|
.resourceManipulator( new VoidResourceManipulator() )
|
||||||
@Override
|
.debug( new DebugHandler()
|
||||||
public InputStream findResource( String filename )
|
|
||||||
{
|
{
|
||||||
return null;
|
private int count = 0;
|
||||||
}
|
private boolean hasSoftAbort;
|
||||||
} );
|
|
||||||
state.debug = new DebugHandler( state )
|
|
||||||
{
|
|
||||||
private int count = 0;
|
|
||||||
private boolean hasSoftAbort;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError
|
public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError
|
||||||
{
|
|
||||||
int count = ++this.count;
|
|
||||||
if( count > 100000 )
|
|
||||||
{
|
{
|
||||||
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
|
int count = ++this.count;
|
||||||
this.count = 0;
|
if( count > 100000 )
|
||||||
|
{
|
||||||
|
if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE );
|
||||||
|
this.count = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handleSoftAbort();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onInstruction( ds, di, pc, extras, top );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
@Override
|
||||||
|
public void poll() throws LuaError
|
||||||
{
|
{
|
||||||
|
if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE );
|
||||||
handleSoftAbort();
|
handleSoftAbort();
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onInstruction( ds, di, pc, extras, top );
|
private void handleSoftAbort() throws LuaError
|
||||||
}
|
{
|
||||||
|
// If the soft abort has been cleared then we can reset our flags and continue.
|
||||||
|
String message = m_softAbortMessage;
|
||||||
|
if( message == null )
|
||||||
|
{
|
||||||
|
hasSoftAbort = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
if( hasSoftAbort && m_hardAbortMessage == null )
|
||||||
public void poll() throws LuaError
|
{
|
||||||
{
|
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
|
||||||
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
|
return;
|
||||||
handleSoftAbort();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSoftAbort() throws LuaError {
|
hasSoftAbort = true;
|
||||||
// If the soft abort has been cleared then we can reset our flags and continue.
|
throw new LuaError( message );
|
||||||
String message = m_softAbortMessage;
|
|
||||||
if (message == null) {
|
|
||||||
hasSoftAbort = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} )
|
||||||
if (hasSoftAbort && m_hardAbortMessage == null) {
|
.coroutineFactory( command -> {
|
||||||
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
|
Tracking.addValue( m_computer, TrackingField.COROUTINES_CREATED, 1 );
|
||||||
return;
|
coroutines.execute( () -> {
|
||||||
}
|
try
|
||||||
|
{
|
||||||
hasSoftAbort = true;
|
command.run();
|
||||||
throw new LuaError(message);
|
}
|
||||||
}
|
finally
|
||||||
};
|
{
|
||||||
|
Tracking.addValue( m_computer, TrackingField.COROUTINES_DISPOSED, 1 );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} )
|
||||||
|
.build();
|
||||||
|
|
||||||
m_globals = new LuaTable();
|
m_globals = new LuaTable();
|
||||||
state.setupThread( m_globals );
|
state.setupThread( m_globals );
|
||||||
@@ -128,7 +148,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
|
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
|
||||||
|
|
||||||
// Remove globals we don't want to expose
|
// 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( "dofile", Constants.NIL );
|
||||||
m_globals.rawset( "loadfile", Constants.NIL );
|
m_globals.rawset( "loadfile", Constants.NIL );
|
||||||
m_globals.rawset( "print", Constants.NIL );
|
m_globals.rawset( "print", Constants.NIL );
|
||||||
@@ -166,10 +186,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
public void loadBios( InputStream bios )
|
public void loadBios( InputStream bios )
|
||||||
{
|
{
|
||||||
// Begin executing a file (ie, the bios)
|
// Begin executing a file (ie, the bios)
|
||||||
if( m_mainRoutine != null )
|
if( m_mainRoutine != null ) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -178,30 +195,19 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
}
|
}
|
||||||
catch( CompileException e )
|
catch( CompileException e )
|
||||||
{
|
{
|
||||||
if( m_mainRoutine != null )
|
unload();
|
||||||
{
|
|
||||||
m_mainRoutine.abandon();
|
|
||||||
m_mainRoutine = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch( IOException e )
|
catch( IOException e )
|
||||||
{
|
{
|
||||||
ComputerCraft.log.warn( "Could not load bios.lua ", e );
|
ComputerCraft.log.warn( "Could not load bios.lua ", e );
|
||||||
if( m_mainRoutine != null )
|
unload();
|
||||||
{
|
|
||||||
m_mainRoutine.abandon();
|
|
||||||
m_mainRoutine = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent( String eventName, Object[] arguments )
|
public void handleEvent( String eventName, Object[] arguments )
|
||||||
{
|
{
|
||||||
if( m_mainRoutine == null )
|
if( m_mainRoutine == null ) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_eventFilter != null && eventName != null && !eventName.equals( m_eventFilter ) && !eventName.equals( "terminate" ) )
|
if( m_eventFilter != null && eventName != null && !eventName.equals( m_eventFilter ) && !eventName.equals( "terminate" ) )
|
||||||
{
|
{
|
||||||
@@ -228,26 +234,14 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
LuaValue filter = results.arg( 2 );
|
LuaValue filter = results.arg( 2 );
|
||||||
if( filter.isString() )
|
m_eventFilter = filter.isString() ? filter.toString() : null;
|
||||||
{
|
|
||||||
m_eventFilter = filter.toString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_eventFilter = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaThread mainThread = m_mainRoutine;
|
if( m_mainRoutine.getStatus().equals( "dead" ) ) unload();
|
||||||
if( mainThread.getStatus().equals( "dead" ) )
|
|
||||||
{
|
|
||||||
m_mainRoutine = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch( LuaError e )
|
catch( LuaError e )
|
||||||
{
|
{
|
||||||
m_mainRoutine.abandon();
|
unload();
|
||||||
m_mainRoutine = null;
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -284,18 +278,18 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
@Override
|
@Override
|
||||||
public boolean isFinished()
|
public boolean isFinished()
|
||||||
{
|
{
|
||||||
return (m_mainRoutine == null);
|
return m_mainRoutine == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unload()
|
public void unload()
|
||||||
{
|
{
|
||||||
if( m_mainRoutine != null )
|
if( m_state == null ) return;
|
||||||
{
|
|
||||||
LuaThread mainThread = m_mainRoutine;
|
m_state.abandon();
|
||||||
mainThread.abandon();
|
m_mainRoutine = null;
|
||||||
m_mainRoutine = null;
|
m_state = null;
|
||||||
}
|
m_globals = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LuaTable wrapLuaObject( ILuaObject object )
|
private LuaTable wrapLuaObject( ILuaObject object )
|
||||||
@@ -691,7 +685,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
private int offset, remaining = 0;
|
private int offset, remaining = 0;
|
||||||
|
|
||||||
public StringInputStream( LuaState state, LuaValue func )
|
StringInputStream( LuaState state, LuaValue func )
|
||||||
{
|
{
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package dan200.computercraft.core.tracking;
|
package dan200.computercraft.core.tracking;
|
||||||
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import gnu.trove.map.hash.TObjectLongHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@@ -18,13 +18,13 @@ public class ComputerTracker
|
|||||||
private long serverCount;
|
private long serverCount;
|
||||||
private long serverTime;
|
private long serverTime;
|
||||||
|
|
||||||
private final TObjectLongHashMap<TrackingField> fields;
|
private final Object2LongOpenHashMap<TrackingField> fields;
|
||||||
|
|
||||||
public ComputerTracker( Computer computer )
|
public ComputerTracker( Computer computer )
|
||||||
{
|
{
|
||||||
this.computer = new WeakReference<>( computer );
|
this.computer = new WeakReference<>( computer );
|
||||||
this.computerId = computer.getID();
|
this.computerId = computer.getID();
|
||||||
this.fields = new TObjectLongHashMap<>();
|
this.fields = new Object2LongOpenHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputerTracker( ComputerTracker timings )
|
ComputerTracker( ComputerTracker timings )
|
||||||
@@ -39,7 +39,7 @@ public class ComputerTracker
|
|||||||
this.serverCount = timings.serverCount;
|
this.serverCount = timings.serverCount;
|
||||||
this.serverTime = timings.serverTime;
|
this.serverTime = timings.serverTime;
|
||||||
|
|
||||||
this.fields = new TObjectLongHashMap<>( timings.fields );
|
this.fields = new Object2LongOpenHashMap<>( timings.fields );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -90,7 +90,7 @@ public class ComputerTracker
|
|||||||
{
|
{
|
||||||
synchronized( fields )
|
synchronized( fields )
|
||||||
{
|
{
|
||||||
fields.adjustOrPutValue( field, change, change );
|
fields.addTo( field, change );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public class ComputerTracker
|
|||||||
|
|
||||||
synchronized( fields )
|
synchronized( fields )
|
||||||
{
|
{
|
||||||
return fields.get( field );
|
return fields.getLong( field );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,6 +28,9 @@ public class TrackingField
|
|||||||
public static final TrackingField WEBSOCKET_INCOMING = TrackingField.of( "websocket_incoming", "Websocket incoming", TrackingField::formatBytes );
|
public static final TrackingField WEBSOCKET_INCOMING = TrackingField.of( "websocket_incoming", "Websocket incoming", TrackingField::formatBytes );
|
||||||
public static final TrackingField WEBSOCKET_OUTGOING = TrackingField.of( "websocket_outgoing", "Websocket outgoing", TrackingField::formatBytes );
|
public static final TrackingField WEBSOCKET_OUTGOING = TrackingField.of( "websocket_outgoing", "Websocket outgoing", TrackingField::formatBytes );
|
||||||
|
|
||||||
|
public static final TrackingField COROUTINES_CREATED = TrackingField.of( "coroutines_created", "Coroutines created", x -> String.format( "%4d", x ) );
|
||||||
|
public static final TrackingField COROUTINES_DISPOSED = TrackingField.of( "coroutines_dead", "Coroutines disposed", x -> String.format( "%4d", x ) );
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final LongFunction<String> format;
|
private final LongFunction<String> format;
|
||||||
|
@@ -23,6 +23,7 @@ import net.minecraft.world.World;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static dan200.computercraft.shared.command.framework.ChatHelpers.*;
|
import static dan200.computercraft.shared.command.framework.ChatHelpers.*;
|
||||||
|
|
||||||
@@ -147,26 +148,15 @@ public final class CommandComputerCraft extends CommandDelegate
|
|||||||
@Override
|
@Override
|
||||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||||
{
|
{
|
||||||
Set<ServerComputer> computers = Sets.newHashSet();
|
withComputers( arguments, computers -> {
|
||||||
if( arguments.size() > 0 )
|
int shutdown = 0;
|
||||||
{
|
for( ServerComputer computer : computers )
|
||||||
for( String arg : arguments )
|
|
||||||
{
|
{
|
||||||
computers.addAll( ComputerSelector.getComputers( arg ) );
|
if( computer.isOn() ) shutdown++;
|
||||||
|
computer.unload();
|
||||||
}
|
}
|
||||||
}
|
context.getSender().sendMessage( text( "Shutdown " + shutdown + " / " + computers.size() + " computers" ) );
|
||||||
else
|
} );
|
||||||
{
|
|
||||||
computers.addAll( ComputerCraft.serverComputerRegistry.getComputers() );
|
|
||||||
}
|
|
||||||
|
|
||||||
int shutdown = 0;
|
|
||||||
for( ServerComputer computer : computers )
|
|
||||||
{
|
|
||||||
if( computer.isOn() ) shutdown++;
|
|
||||||
computer.unload();
|
|
||||||
}
|
|
||||||
context.getSender().sendMessage( text( "Shutdown " + shutdown + " / " + computers.size() + " computers" ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@@ -188,26 +178,15 @@ public final class CommandComputerCraft extends CommandDelegate
|
|||||||
@Override
|
@Override
|
||||||
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
public void execute( @Nonnull CommandContext context, @Nonnull List<String> arguments ) throws CommandException
|
||||||
{
|
{
|
||||||
Set<ServerComputer> computers = Sets.newHashSet();
|
withComputers( arguments, computers -> {
|
||||||
if( arguments.size() > 0 )
|
int on = 0;
|
||||||
{
|
for( ServerComputer computer : computers )
|
||||||
for( String arg : arguments )
|
|
||||||
{
|
{
|
||||||
computers.addAll( ComputerSelector.getComputers( arg ) );
|
if( !computer.isOn() ) on++;
|
||||||
|
computer.turnOn();
|
||||||
}
|
}
|
||||||
}
|
context.getSender().sendMessage( text( "Turned on " + on + " / " + computers.size() + " computers" ) );
|
||||||
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" ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@@ -489,8 +468,6 @@ public final class CommandComputerCraft extends CommandDelegate
|
|||||||
if( server.getID() > maxId ) maxId = server.getID();
|
if( server.getID() > maxId ) maxId = server.getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
ICommandSender sender = context.getSender();
|
|
||||||
|
|
||||||
timings.sort( Comparator.<ComputerTracker, Long>comparing( x -> x.get( field ) ).reversed() );
|
timings.sort( Comparator.<ComputerTracker, Long>comparing( x -> x.get( field ) ).reversed() );
|
||||||
|
|
||||||
boolean defaultLayout = field == TrackingField.TASKS || field == TrackingField.TOTAL_TIME
|
boolean defaultLayout = field == TrackingField.TASKS || field == TrackingField.TOTAL_TIME
|
||||||
@@ -526,4 +503,30 @@ public final class CommandComputerCraft extends CommandDelegate
|
|||||||
|
|
||||||
table.displayTo( context.getSender() );
|
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
|
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.
|
// We copy it to prevent concurrent modifications.
|
||||||
List<ServerComputer> computers = Lists.newArrayList( ComputerCraft.serverComputerRegistry.getComputers() );
|
ArrayList<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() );
|
||||||
List<ServerComputer> candidates = Lists.newArrayList();
|
computers.removeIf( predicate.negate() );
|
||||||
for( ServerComputer searchComputer : computers )
|
return computers;
|
||||||
{
|
|
||||||
if( predicate.test( searchComputer ) ) candidates.add( searchComputer );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( candidates.isEmpty() )
|
|
||||||
{
|
|
||||||
throw new CommandException( "No computer matching " + selector );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return candidates;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServerComputer getComputer( String selector ) throws CommandException
|
public static ServerComputer getComputer( String selector ) throws CommandException
|
||||||
{
|
{
|
||||||
List<ServerComputer> computers = getComputers( selector );
|
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 );
|
return computers.get( 0 );
|
||||||
}
|
}
|
||||||
@@ -71,17 +63,17 @@ public final class ComputerSelector
|
|||||||
throw new CommandException( "'" + selector + "' is not a valid number" );
|
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 ) == '@' )
|
else if( selector.length() > 0 && selector.charAt( 0 ) == '@' )
|
||||||
{
|
{
|
||||||
String label = selector.substring( 1 );
|
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 ) == '~' )
|
else if( selector.length() > 0 && selector.charAt( 0 ) == '~' )
|
||||||
{
|
{
|
||||||
String familyName = selector.substring( 1 );
|
String familyName = selector.substring( 1 );
|
||||||
return getComputers( x -> x.getFamily().name().equalsIgnoreCase( familyName ), selector );
|
return getComputers( x -> x.getFamily().name().equalsIgnoreCase( familyName ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -96,14 +88,7 @@ public final class ComputerSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance );
|
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance );
|
||||||
if( computer == null )
|
return computer == null ? Collections.emptyList() : Collections.singletonList( computer );
|
||||||
{
|
|
||||||
throw new CommandException( "No such computer for instance id " + instance );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Collections.singletonList( computer );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,7 +42,7 @@ public class BlockCommandComputer extends BlockComputerBase
|
|||||||
super( Material.IRON );
|
super( Material.IRON );
|
||||||
setBlockUnbreakable();
|
setBlockUnbreakable();
|
||||||
setResistance( 6000000.0F );
|
setResistance( 6000000.0F );
|
||||||
setUnlocalizedName( "computercraft:command_computer" );
|
setTranslationKey( "computercraft:command_computer" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( this.blockState.getBaseState()
|
setDefaultState( this.blockState.getBaseState()
|
||||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||||
@@ -62,7 +62,7 @@ public class BlockCommandComputer extends BlockComputerBase
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public IBlockState getStateFromMeta( int meta )
|
public IBlockState getStateFromMeta( int meta )
|
||||||
{
|
{
|
||||||
EnumFacing dir = EnumFacing.getFront( meta & 0x7 );
|
EnumFacing dir = EnumFacing.byIndex( meta & 0x7 );
|
||||||
if( dir.getAxis() == EnumFacing.Axis.Y )
|
if( dir.getAxis() == EnumFacing.Axis.Y )
|
||||||
{
|
{
|
||||||
dir = EnumFacing.NORTH;
|
dir = EnumFacing.NORTH;
|
||||||
|
@@ -44,7 +44,7 @@ public class BlockComputer extends BlockComputerBase
|
|||||||
{
|
{
|
||||||
super( Material.ROCK );
|
super( Material.ROCK );
|
||||||
setHardness( 2.0f );
|
setHardness( 2.0f );
|
||||||
setUnlocalizedName( "computercraft:computer" );
|
setTranslationKey( "computercraft:computer" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( this.blockState.getBaseState()
|
setDefaultState( this.blockState.getBaseState()
|
||||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||||
@@ -65,7 +65,7 @@ public class BlockComputer extends BlockComputerBase
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public IBlockState getStateFromMeta( int meta )
|
public IBlockState getStateFromMeta( int meta )
|
||||||
{
|
{
|
||||||
EnumFacing dir = EnumFacing.getFront( meta & 0x7 );
|
EnumFacing dir = EnumFacing.byIndex( meta & 0x7 );
|
||||||
if( dir.getAxis() == EnumFacing.Axis.Y )
|
if( dir.getAxis() == EnumFacing.Axis.Y )
|
||||||
{
|
{
|
||||||
dir = EnumFacing.NORTH;
|
dir = EnumFacing.NORTH;
|
||||||
|
@@ -24,7 +24,7 @@ public class ItemCommandComputer extends ItemComputer
|
|||||||
super( block );
|
super( block );
|
||||||
setMaxStackSize( 64 );
|
setMaxStackSize( 64 );
|
||||||
setHasSubtypes( true );
|
setHasSubtypes( true );
|
||||||
setUnlocalizedName( "computercraft:command_computer" );
|
setTranslationKey( "computercraft:command_computer" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ public class ItemComputer extends ItemComputerBase
|
|||||||
super( block );
|
super( block );
|
||||||
setMaxStackSize( 64 );
|
setMaxStackSize( 64 );
|
||||||
setHasSubtypes( true );
|
setHasSubtypes( true );
|
||||||
setUnlocalizedName( "computercraft:computer" );
|
setTranslationKey( "computercraft:computer" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ public class ItemComputer extends ItemComputerBase
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
switch( getFamily( stack ) )
|
switch( getFamily( stack ) )
|
||||||
{
|
{
|
||||||
|
23
src/main/java/dan200/computercraft/shared/datafix/Fixes.java
Normal file
23
src/main/java/dan200/computercraft/shared/datafix/Fixes.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.datafix;
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import net.minecraft.util.datafix.FixTypes;
|
||||||
|
import net.minecraftforge.common.util.CompoundDataFixer;
|
||||||
|
import net.minecraftforge.common.util.ModFixs;
|
||||||
|
|
||||||
|
public class Fixes
|
||||||
|
{
|
||||||
|
public static final int VERSION = 1;
|
||||||
|
|
||||||
|
public static void register( CompoundDataFixer fixer )
|
||||||
|
{
|
||||||
|
ModFixs fixes = fixer.init( ComputerCraft.MOD_ID, VERSION );
|
||||||
|
fixes.registerFix( FixTypes.BLOCK_ENTITY, new TileEntityDataFixer() );
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.datafix;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
import net.minecraft.util.datafix.IFixableData;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import static dan200.computercraft.ComputerCraft.MOD_ID;
|
||||||
|
import static dan200.computercraft.shared.datafix.Fixes.VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes up the botched tile entity IDs from the 1.11 port.
|
||||||
|
*/
|
||||||
|
public class TileEntityDataFixer implements IFixableData
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int getFixVersion()
|
||||||
|
{
|
||||||
|
return VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public NBTTagCompound fixTagCompound( @Nonnull NBTTagCompound tag )
|
||||||
|
{
|
||||||
|
String id = tag.getString( "id" );
|
||||||
|
if( id.startsWith( MOD_ID + " : " ) )
|
||||||
|
{
|
||||||
|
tag.setString( "id", id.replaceFirst( MOD_ID + " : ", MOD_ID + ":" ) );
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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 );
|
setMaxStackSize( 1 );
|
||||||
setHasSubtypes( true );
|
setHasSubtypes( true );
|
||||||
setUnlocalizedName( "computercraft:disk" );
|
setTranslationKey( "computercraft:disk" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,7 +39,7 @@ public class ItemPrintout extends Item
|
|||||||
{
|
{
|
||||||
setMaxStackSize( 1 );
|
setMaxStackSize( 1 );
|
||||||
setHasSubtypes( true );
|
setHasSubtypes( true );
|
||||||
setUnlocalizedName( "computercraft:page" );
|
setTranslationKey( "computercraft:page" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class ItemPrintout extends Item
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
Type type = getType( stack );
|
Type type = getType( stack );
|
||||||
switch( type )
|
switch( type )
|
||||||
|
@@ -34,7 +34,7 @@ public class ItemTreasureDisk extends Item
|
|||||||
{
|
{
|
||||||
setMaxStackSize( 1 );
|
setMaxStackSize( 1 );
|
||||||
setHasSubtypes( true );
|
setHasSubtypes( true );
|
||||||
setUnlocalizedName( "computercraft:treasure_disk" );
|
setTranslationKey( "computercraft:treasure_disk" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -55,7 +55,7 @@ public class BlockCable extends BlockPeripheralBase
|
|||||||
public BlockCable()
|
public BlockCable()
|
||||||
{
|
{
|
||||||
setHardness( 1.5f );
|
setHardness( 1.5f );
|
||||||
setUnlocalizedName( "computercraft:cable" );
|
setTranslationKey( "computercraft:cable" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( this.blockState.getBaseState()
|
setDefaultState( this.blockState.getBaseState()
|
||||||
.withProperty( Properties.MODEM, BlockCableModemVariant.None )
|
.withProperty( Properties.MODEM, BlockCableModemVariant.None )
|
||||||
@@ -94,12 +94,12 @@ public class BlockCable extends BlockPeripheralBase
|
|||||||
if( meta < 6 )
|
if( meta < 6 )
|
||||||
{
|
{
|
||||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.NONE );
|
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 )
|
else if( meta < 12 )
|
||||||
{
|
{
|
||||||
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.ANY );
|
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 )
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@@ -44,7 +44,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
|||||||
public BlockPeripheral()
|
public BlockPeripheral()
|
||||||
{
|
{
|
||||||
setHardness( 2.0f );
|
setHardness( 2.0f );
|
||||||
setUnlocalizedName( "computercraft:peripheral" );
|
setTranslationKey( "computercraft:peripheral" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( this.blockState.getBaseState()
|
setDefaultState( this.blockState.getBaseState()
|
||||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||||
@@ -55,7 +55,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
|||||||
@Override
|
@Override
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@SideOnly( Side.CLIENT)
|
@SideOnly( Side.CLIENT)
|
||||||
public BlockRenderLayer getBlockLayer()
|
public BlockRenderLayer getRenderLayer()
|
||||||
{
|
{
|
||||||
return BlockRenderLayer.CUTOUT;
|
return BlockRenderLayer.CUTOUT;
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
|||||||
if( meta >= 2 && meta <= 5 )
|
if( meta >= 2 && meta <= 5 )
|
||||||
{
|
{
|
||||||
state = state.withProperty( Properties.VARIANT, BlockPeripheralVariant.DiskDriveEmpty );
|
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 )
|
else if( meta <= 9 )
|
||||||
{
|
{
|
||||||
@@ -93,7 +93,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
state = state.withProperty( Properties.VARIANT, BlockPeripheralVariant.WirelessModemOff );
|
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 )
|
else if( meta == 10 )
|
||||||
@@ -651,7 +651,7 @@ public class BlockPeripheral extends BlockPeripheralBase
|
|||||||
public boolean causesSuffocation(IBlockState state)
|
public boolean causesSuffocation(IBlockState state)
|
||||||
{
|
{
|
||||||
// This normally uses the default state
|
// This normally uses the default state
|
||||||
return blockMaterial.blocksMovement() && state.isOpaqueCube();
|
return material.blocksMovement() && state.isOpaqueCube();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -34,7 +34,7 @@ public class BlockWiredModemFull extends BlockPeripheralBase
|
|||||||
public BlockWiredModemFull()
|
public BlockWiredModemFull()
|
||||||
{
|
{
|
||||||
setHardness( 1.5f );
|
setHardness( 1.5f );
|
||||||
setUnlocalizedName( "computercraft:wired_modem_full" );
|
setTranslationKey( "computercraft:wired_modem_full" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( blockState.getBaseState()
|
setDefaultState( blockState.getBaseState()
|
||||||
.withProperty( Properties.MODEM_ON, false )
|
.withProperty( Properties.MODEM_ON, false )
|
||||||
|
@@ -21,7 +21,7 @@ public class ItemAdvancedModem extends ItemPeripheralBase
|
|||||||
public ItemAdvancedModem( Block block )
|
public ItemAdvancedModem( Block block )
|
||||||
{
|
{
|
||||||
super( block );
|
super( block );
|
||||||
setUnlocalizedName( "computercraft:advanced_modem" );
|
setTranslationKey( "computercraft:advanced_modem" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,7 +28,7 @@ public class ItemCable extends ItemPeripheralBase
|
|||||||
public ItemCable( Block block )
|
public ItemCable( Block block )
|
||||||
{
|
{
|
||||||
super( block );
|
super( block );
|
||||||
setUnlocalizedName( "computercraft:cable" );
|
setTranslationKey( "computercraft:cable" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ public class ItemPeripheral extends ItemPeripheralBase
|
|||||||
public ItemPeripheral( Block block )
|
public ItemPeripheral( Block block )
|
||||||
{
|
{
|
||||||
super( block );
|
super( block );
|
||||||
setUnlocalizedName( "computercraft:peripheral" );
|
setTranslationKey( "computercraft:peripheral" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -59,7 +59,7 @@ public abstract class ItemPeripheralBase extends ItemBlock implements IPeriphera
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
PeripheralType type = getPeripheralType( stack );
|
PeripheralType type = getPeripheralType( stack );
|
||||||
switch( type )
|
switch( type )
|
||||||
|
@@ -112,12 +112,12 @@ public abstract class TilePeripheralBase extends TileGeneric
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getAnim()
|
public int getAnim()
|
||||||
{
|
{
|
||||||
return m_anim;
|
return m_anim;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setAnim( int anim )
|
public void setAnim( int anim )
|
||||||
{
|
{
|
||||||
if( anim != m_anim )
|
if( anim != m_anim )
|
||||||
{
|
{
|
||||||
@@ -127,12 +127,12 @@ public abstract class TilePeripheralBase extends TileGeneric
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void update()
|
public void update()
|
||||||
{
|
{
|
||||||
if( m_changed )
|
if( m_changed )
|
||||||
{
|
{
|
||||||
updateBlock();
|
|
||||||
m_changed = false;
|
m_changed = false;
|
||||||
|
updateBlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ public abstract class TilePeripheralBase extends TileGeneric
|
|||||||
super.readFromNBT(nbttagcompound);
|
super.readFromNBT(nbttagcompound);
|
||||||
if( nbttagcompound.hasKey( "dir" ) )
|
if( nbttagcompound.hasKey( "dir" ) )
|
||||||
{
|
{
|
||||||
m_dir = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
m_dir = EnumFacing.byIndex( nbttagcompound.getInteger( "dir" ) );
|
||||||
}
|
}
|
||||||
if( nbttagcompound.hasKey( "anim" ) )
|
if( nbttagcompound.hasKey( "anim" ) )
|
||||||
{
|
{
|
||||||
@@ -174,7 +174,7 @@ public abstract class TilePeripheralBase extends TileGeneric
|
|||||||
public void readDescription( @Nonnull NBTTagCompound nbttagcompound )
|
public void readDescription( @Nonnull NBTTagCompound nbttagcompound )
|
||||||
{
|
{
|
||||||
super.readDescription( nbttagcompound );
|
super.readDescription( nbttagcompound );
|
||||||
m_dir = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
m_dir = EnumFacing.byIndex( nbttagcompound.getInteger( "dir" ) );
|
||||||
m_anim = nbttagcompound.getInteger( "anim" );
|
m_anim = nbttagcompound.getInteger( "anim" );
|
||||||
if( nbttagcompound.hasKey( "label" ) )
|
if( nbttagcompound.hasKey( "label" ) )
|
||||||
{
|
{
|
||||||
|
@@ -80,12 +80,9 @@ public class TileDiskDrive extends TilePeripheralBase
|
|||||||
public void destroy()
|
public void destroy()
|
||||||
{
|
{
|
||||||
ejectContents( true );
|
ejectContents( true );
|
||||||
synchronized( this )
|
if( m_recordPlaying )
|
||||||
{
|
{
|
||||||
if( m_recordPlaying )
|
stopRecord();
|
||||||
{
|
|
||||||
stopRecord();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,13 +167,10 @@ public class TileDiskDrive extends TilePeripheralBase
|
|||||||
super.update();
|
super.update();
|
||||||
|
|
||||||
// Ejection
|
// Ejection
|
||||||
synchronized( this )
|
if( m_ejectQueued )
|
||||||
{
|
{
|
||||||
if( m_ejectQueued )
|
ejectContents( false );
|
||||||
{
|
m_ejectQueued = false;
|
||||||
ejectContents( false );
|
|
||||||
m_ejectQueued = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
@@ -391,10 +385,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
|||||||
@Override
|
@Override
|
||||||
public void clear()
|
public void clear()
|
||||||
{
|
{
|
||||||
synchronized( this )
|
setInventorySlotContents( 0, ItemStack.EMPTY );
|
||||||
{
|
|
||||||
setInventorySlotContents( 0, ItemStack.EMPTY );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -425,18 +416,12 @@ public class TileDiskDrive extends TilePeripheralBase
|
|||||||
@Nonnull
|
@Nonnull
|
||||||
public ItemStack getDiskStack()
|
public ItemStack getDiskStack()
|
||||||
{
|
{
|
||||||
synchronized( this )
|
return getStackInSlot( 0 );
|
||||||
{
|
|
||||||
return getStackInSlot( 0 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDiskStack( @Nonnull ItemStack stack )
|
public void setDiskStack( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
synchronized( this )
|
setInventorySlotContents( 0, stack );
|
||||||
{
|
|
||||||
setInventorySlotContents( 0, stack );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMedia getDiskMedia()
|
public IMedia getDiskMedia()
|
||||||
@@ -569,7 +554,7 @@ public class TileDiskDrive extends TilePeripheralBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateAnim()
|
private void updateAnim()
|
||||||
{
|
{
|
||||||
if( !m_diskStack.isEmpty() )
|
if( !m_diskStack.isEmpty() )
|
||||||
{
|
{
|
||||||
@@ -605,8 +590,8 @@ public class TileDiskDrive extends TilePeripheralBase
|
|||||||
if( !destroyed )
|
if( !destroyed )
|
||||||
{
|
{
|
||||||
EnumFacing dir = getDirection();
|
EnumFacing dir = getDirection();
|
||||||
xOff = dir.getFrontOffsetX();
|
xOff = dir.getXOffset();
|
||||||
zOff = dir.getFrontOffsetZ();
|
zOff = dir.getZOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockPos pos = getPos();
|
BlockPos pos = getPos();
|
||||||
|
@@ -33,7 +33,7 @@ public class BlockAdvancedModem extends BlockPeripheralBase
|
|||||||
public BlockAdvancedModem()
|
public BlockAdvancedModem()
|
||||||
{
|
{
|
||||||
setHardness( 2.0f );
|
setHardness( 2.0f );
|
||||||
setUnlocalizedName( "computercraft:advanced_modem" );
|
setTranslationKey( "computercraft:advanced_modem" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( this.blockState.getBaseState()
|
setDefaultState( this.blockState.getBaseState()
|
||||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||||
@@ -54,7 +54,7 @@ public class BlockAdvancedModem extends BlockPeripheralBase
|
|||||||
public IBlockState getStateFromMeta( int meta )
|
public IBlockState getStateFromMeta( int meta )
|
||||||
{
|
{
|
||||||
IBlockState state = getDefaultState();
|
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 );
|
state = state.withProperty( Properties.ON, false );
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@@ -14,53 +14,43 @@ import dan200.computercraft.api.network.IPacketSender;
|
|||||||
import dan200.computercraft.api.network.Packet;
|
import dan200.computercraft.api.network.Packet;
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import gnu.trove.set.TIntSet;
|
|
||||||
import gnu.trove.set.hash.TIntHashSet;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
||||||
|
|
||||||
public abstract class ModemPeripheral
|
public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPacketReceiver
|
||||||
implements IPeripheral, IPacketSender, IPacketReceiver
|
|
||||||
{
|
{
|
||||||
private IPacketNetwork m_network;
|
private IPacketNetwork m_network;
|
||||||
private IComputerAccess m_computer;
|
private final Set<IComputerAccess> m_computers = new HashSet<>( 1 );
|
||||||
private final TIntSet m_channels;
|
private final ModemState m_state;
|
||||||
|
|
||||||
private boolean m_open;
|
protected ModemPeripheral( ModemState state )
|
||||||
private boolean m_changed;
|
|
||||||
|
|
||||||
public ModemPeripheral()
|
|
||||||
{
|
{
|
||||||
m_network = null;
|
this.m_state = state;
|
||||||
m_computer = null;
|
}
|
||||||
m_channels = new TIntHashSet();
|
|
||||||
m_open = false;
|
public ModemState getModemState()
|
||||||
m_changed = true;
|
{
|
||||||
|
return m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void setNetwork( IPacketNetwork network )
|
private synchronized void setNetwork( IPacketNetwork network )
|
||||||
{
|
{
|
||||||
if( m_network != network )
|
if( m_network == network ) return;
|
||||||
{
|
|
||||||
// Leave old network
|
|
||||||
if( m_network != null )
|
|
||||||
{
|
|
||||||
m_network.removeReceiver( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new network
|
// Leave old network
|
||||||
m_network = network;
|
if( m_network != null ) m_network.removeReceiver( this );
|
||||||
|
|
||||||
// Join new network
|
// Set new network
|
||||||
if( m_network != null )
|
m_network = network;
|
||||||
{
|
|
||||||
m_network.addReceiver( this );
|
// Join new network
|
||||||
}
|
if( m_network != null ) m_network.addReceiver( this );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void switchNetwork()
|
protected void switchNetwork()
|
||||||
@@ -68,39 +58,22 @@ public abstract class ModemPeripheral
|
|||||||
setNetwork( getNetwork() );
|
setNetwork( getNetwork() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void destroy()
|
public void destroy()
|
||||||
{
|
{
|
||||||
setNetwork( null );
|
setNetwork( null );
|
||||||
m_channels.clear();
|
|
||||||
m_open = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean pollChanged()
|
|
||||||
{
|
|
||||||
if( m_changed )
|
|
||||||
{
|
|
||||||
m_changed = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean isActive()
|
|
||||||
{
|
|
||||||
return (m_computer != null) && m_open;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receiveSameDimension( @Nonnull Packet packet, double distance )
|
public void receiveSameDimension( @Nonnull Packet packet, double distance )
|
||||||
{
|
{
|
||||||
if( packet.getSender() == this ) return;
|
if( packet.getSender() == this || !m_state.isOpen( packet.getChannel() ) ) return;
|
||||||
|
|
||||||
synchronized (m_channels)
|
synchronized( m_computers )
|
||||||
{
|
{
|
||||||
if( m_computer != null && m_channels.contains( packet.getChannel() ) )
|
for( IComputerAccess computer : m_computers )
|
||||||
{
|
{
|
||||||
m_computer.queueEvent( "modem_message", new Object[] {
|
computer.queueEvent( "modem_message", new Object[]{
|
||||||
m_computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance
|
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,21 +82,21 @@ public abstract class ModemPeripheral
|
|||||||
@Override
|
@Override
|
||||||
public void receiveDifferentDimension( @Nonnull Packet packet )
|
public void receiveDifferentDimension( @Nonnull Packet packet )
|
||||||
{
|
{
|
||||||
if( packet.getSender() == this ) return;
|
if( packet.getSender() == this || !m_state.isOpen( packet.getChannel() ) ) return;
|
||||||
|
|
||||||
synchronized (m_channels)
|
synchronized( m_computers )
|
||||||
{
|
{
|
||||||
if( m_computer != null && m_channels.contains( packet.getChannel() ) )
|
for( IComputerAccess computer : m_computers )
|
||||||
{
|
{
|
||||||
m_computer.queueEvent( "modem_message", new Object[] {
|
computer.queueEvent( "modem_message", new Object[]{
|
||||||
m_computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload()
|
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload()
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract IPacketNetwork getNetwork();
|
protected abstract IPacketNetwork getNetwork();
|
||||||
|
|
||||||
// IPeripheral implementation
|
// IPeripheral implementation
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@@ -132,12 +105,12 @@ public abstract class ModemPeripheral
|
|||||||
{
|
{
|
||||||
return "modem";
|
return "modem";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String[] getMethodNames()
|
public String[] getMethodNames()
|
||||||
{
|
{
|
||||||
return new String[] {
|
return new String[]{
|
||||||
"open",
|
"open",
|
||||||
"isOpen",
|
"isOpen",
|
||||||
"close",
|
"close",
|
||||||
@@ -146,7 +119,7 @@ public abstract class ModemPeripheral
|
|||||||
"isWireless",
|
"isWireless",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int parseChannel( Object[] arguments, int index ) throws LuaException
|
private static int parseChannel( Object[] arguments, int index ) throws LuaException
|
||||||
{
|
{
|
||||||
int channel = getInt( arguments, index );
|
int channel = getInt( arguments, index );
|
||||||
@@ -156,7 +129,7 @@ public abstract class ModemPeripheral
|
|||||||
}
|
}
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||||
{
|
{
|
||||||
@@ -166,68 +139,26 @@ public abstract class ModemPeripheral
|
|||||||
{
|
{
|
||||||
// open
|
// open
|
||||||
int channel = parseChannel( arguments, 0 );
|
int channel = parseChannel( arguments, 0 );
|
||||||
synchronized( this )
|
m_state.open( channel );
|
||||||
{
|
|
||||||
if( !m_channels.contains( channel ) )
|
|
||||||
{
|
|
||||||
if( m_channels.size() >= 128 )
|
|
||||||
{
|
|
||||||
throw new LuaException( "Too many open channels" );
|
|
||||||
}
|
|
||||||
|
|
||||||
m_channels.add( channel );
|
|
||||||
if( !m_open )
|
|
||||||
{
|
|
||||||
m_open = true;
|
|
||||||
m_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
// isOpen
|
// isOpen
|
||||||
int channel = parseChannel( arguments, 0 );
|
int channel = parseChannel( arguments, 0 );
|
||||||
synchronized( this )
|
return new Object[]{ m_state.isOpen( channel ) };
|
||||||
{
|
|
||||||
boolean open = m_channels.contains( channel );
|
|
||||||
return new Object[] { open };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
// close
|
// close
|
||||||
int channel = parseChannel( arguments, 0 );
|
int channel = parseChannel( arguments, 0 );
|
||||||
synchronized( this )
|
m_state.close( channel );
|
||||||
{
|
|
||||||
if( m_channels.remove( channel ) )
|
|
||||||
{
|
|
||||||
if( m_channels.size() == 0 )
|
|
||||||
{
|
|
||||||
m_open = false;
|
|
||||||
m_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
// closeAll
|
// closeAll
|
||||||
synchronized( this )
|
m_state.closeAll();
|
||||||
{
|
|
||||||
if( m_channels.size() > 0 )
|
|
||||||
{
|
|
||||||
m_channels.clear();
|
|
||||||
|
|
||||||
if( m_open )
|
|
||||||
{
|
|
||||||
m_open = false;
|
|
||||||
m_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
@@ -235,12 +166,12 @@ public abstract class ModemPeripheral
|
|||||||
// transmit
|
// transmit
|
||||||
int channel = parseChannel( arguments, 0 );
|
int channel = parseChannel( arguments, 0 );
|
||||||
int replyChannel = parseChannel( arguments, 1 );
|
int replyChannel = parseChannel( arguments, 1 );
|
||||||
Object payload = (arguments.length >= 3) ? arguments[2] : null;
|
Object payload = arguments.length > 2 ? arguments[2] : null;
|
||||||
synchronized( this )
|
synchronized( this )
|
||||||
{
|
{
|
||||||
World world = getWorld();
|
World world = getWorld();
|
||||||
Vec3d position = getPosition();
|
Vec3d position = getPosition();
|
||||||
if( world != null && position != null && m_network != null)
|
if( world != null && position != null && m_network != null )
|
||||||
{
|
{
|
||||||
Packet packet = new Packet( channel, replyChannel, payload, this );
|
Packet packet = new Packet( channel, replyChannel, payload, this );
|
||||||
if( isInterdimensional() )
|
if( isInterdimensional() )
|
||||||
@@ -258,14 +189,8 @@ public abstract class ModemPeripheral
|
|||||||
case 5:
|
case 5:
|
||||||
{
|
{
|
||||||
// isWireless
|
// isWireless
|
||||||
synchronized( this )
|
IPacketNetwork network = m_network;
|
||||||
{
|
return new Object[]{ network != null && network.isWireless() };
|
||||||
if( m_network != null )
|
|
||||||
{
|
|
||||||
return new Object[] { m_network.isWireless() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Object[] { false };
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@@ -273,50 +198,46 @@ public abstract class ModemPeripheral
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void attach( @Nonnull IComputerAccess computer )
|
public synchronized void attach( @Nonnull IComputerAccess computer )
|
||||||
{
|
{
|
||||||
m_computer = computer;
|
synchronized( m_computers )
|
||||||
|
{
|
||||||
|
m_computers.add( computer );
|
||||||
|
}
|
||||||
|
|
||||||
setNetwork( getNetwork() );
|
setNetwork( getNetwork() );
|
||||||
m_open = !m_channels.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void detach( @Nonnull IComputerAccess computer )
|
public synchronized void detach( @Nonnull IComputerAccess computer )
|
||||||
{
|
{
|
||||||
if( m_network != null )
|
boolean empty;
|
||||||
|
synchronized( m_computers )
|
||||||
{
|
{
|
||||||
m_network.removeReceiver( this );
|
m_computers.remove( computer );
|
||||||
m_channels.clear();
|
empty = m_computers.isEmpty();
|
||||||
m_network = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_computer = null;
|
if( empty ) setNetwork( null );
|
||||||
|
|
||||||
if( m_open )
|
|
||||||
{
|
|
||||||
m_open = false;
|
|
||||||
m_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IComputerAccess getComputer()
|
|
||||||
{
|
|
||||||
return m_computer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getSenderID()
|
public String getSenderID()
|
||||||
{
|
{
|
||||||
if( m_computer == null )
|
synchronized( m_computers )
|
||||||
{
|
{
|
||||||
return "unknown";
|
if( m_computers.size() != 1 )
|
||||||
}
|
{
|
||||||
else
|
return "unknown";
|
||||||
{
|
}
|
||||||
return m_computer.getID() + "_" + m_computer.getAttachmentName();
|
else
|
||||||
|
{
|
||||||
|
IComputerAccess computer = m_computers.iterator().next();
|
||||||
|
return computer.getID() + "_" + computer.getAttachmentName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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.peripheral.modem;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class ModemState
|
||||||
|
{
|
||||||
|
private boolean open = false;
|
||||||
|
private AtomicBoolean changed = new AtomicBoolean( true );
|
||||||
|
|
||||||
|
private final IntSet channels = new IntOpenHashSet();
|
||||||
|
|
||||||
|
private void setOpen( boolean open )
|
||||||
|
{
|
||||||
|
if( this.open == open ) return;
|
||||||
|
this.open = open;
|
||||||
|
this.changed.set( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean pollChanged()
|
||||||
|
{
|
||||||
|
return changed.getAndSet( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpen()
|
||||||
|
{
|
||||||
|
return open;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpen( int channel )
|
||||||
|
{
|
||||||
|
synchronized( channels )
|
||||||
|
{
|
||||||
|
return channels.contains( channel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open( int channel ) throws LuaException
|
||||||
|
{
|
||||||
|
synchronized( channels )
|
||||||
|
{
|
||||||
|
if( !channels.contains( channel ) )
|
||||||
|
{
|
||||||
|
if( channels.size() >= 128 ) throw new LuaException( "Too many open channels" );
|
||||||
|
channels.add( channel );
|
||||||
|
setOpen( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close( int channel )
|
||||||
|
{
|
||||||
|
synchronized( channels )
|
||||||
|
{
|
||||||
|
channels.remove( channel );
|
||||||
|
if( channels.isEmpty() ) setOpen( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeAll()
|
||||||
|
{
|
||||||
|
synchronized( channels )
|
||||||
|
{
|
||||||
|
channels.clear();
|
||||||
|
setOpen( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,7 +25,7 @@ public class TileAdvancedModem extends TileModemBase
|
|||||||
|
|
||||||
public Peripheral( TileModemBase entity )
|
public Peripheral( TileModemBase entity )
|
||||||
{
|
{
|
||||||
super( true );
|
super( new ModemState(), true );
|
||||||
m_entity = entity;
|
m_entity = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -97,7 +97,7 @@ public class TileCable extends TileModemBase
|
|||||||
|
|
||||||
private boolean m_hasDirection = false;
|
private boolean m_hasDirection = false;
|
||||||
private boolean m_connectionsFormed = false;
|
private boolean m_connectionsFormed = false;
|
||||||
|
|
||||||
private WiredModemElement m_cable;
|
private WiredModemElement m_cable;
|
||||||
private IWiredNode m_node;
|
private IWiredNode m_node;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public class TileCable extends TileModemBase
|
|||||||
{
|
{
|
||||||
m_cable = new CableElement( this );
|
m_cable = new CableElement( this );
|
||||||
m_node = m_cable.getNode();
|
m_node = m_cable.getNode();
|
||||||
return new WiredModemPeripheral( m_cable )
|
return new WiredModemPeripheral( new ModemState(), m_cable )
|
||||||
{
|
{
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
@@ -453,14 +453,8 @@ public class TileCable extends TileModemBase
|
|||||||
protected void updateAnim()
|
protected void updateAnim()
|
||||||
{
|
{
|
||||||
int anim = 0;
|
int anim = 0;
|
||||||
if( m_modem.isActive() )
|
if( m_modem.getModemState().isOpen() ) anim |= 1;
|
||||||
{
|
if( m_peripheralAccessAllowed ) anim |= 2;
|
||||||
anim += 1;
|
|
||||||
}
|
|
||||||
if( m_peripheralAccessAllowed )
|
|
||||||
{
|
|
||||||
anim += 2;
|
|
||||||
}
|
|
||||||
setAnim( anim );
|
setAnim( anim );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ public abstract class TileModemBase extends TilePeripheralBase
|
|||||||
protected abstract ModemPeripheral createPeripheral();
|
protected abstract ModemPeripheral createPeripheral();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void destroy()
|
public void destroy()
|
||||||
{
|
{
|
||||||
if( m_modem != null )
|
if( m_modem != null )
|
||||||
{
|
{
|
||||||
@@ -56,10 +56,7 @@ public abstract class TileModemBase extends TilePeripheralBase
|
|||||||
public void onNeighbourChange()
|
public void onNeighbourChange()
|
||||||
{
|
{
|
||||||
EnumFacing dir = getDirection();
|
EnumFacing dir = getDirection();
|
||||||
if( !getWorld().isSideSolid(
|
if( !getWorld().isSideSolid( getPos().offset( dir ), dir.getOpposite() ) )
|
||||||
getPos().offset( dir ),
|
|
||||||
dir.getOpposite()
|
|
||||||
) )
|
|
||||||
{
|
{
|
||||||
// Drop everything and remove block
|
// Drop everything and remove block
|
||||||
((BlockGeneric)getBlockType()).dropAllItems( getWorld(), getPos(), false );
|
((BlockGeneric)getBlockType()).dropAllItems( getWorld(), getPos(), false );
|
||||||
@@ -79,22 +76,15 @@ public abstract class TileModemBase extends TilePeripheralBase
|
|||||||
public void update()
|
public void update()
|
||||||
{
|
{
|
||||||
super.update();
|
super.update();
|
||||||
if( !getWorld().isRemote && m_modem.pollChanged() )
|
if( !getWorld().isRemote && m_modem.getModemState().pollChanged() )
|
||||||
{
|
{
|
||||||
updateAnim();
|
updateAnim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateAnim()
|
protected void updateAnim()
|
||||||
{
|
{
|
||||||
if( m_modem.isActive() )
|
setAnim( m_modem.getModemState().isOpen() ? 1 : 0 );
|
||||||
{
|
|
||||||
setAnim(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setAnim(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,15 +99,6 @@ public abstract class TileModemBase extends TilePeripheralBase
|
|||||||
@Override
|
@Override
|
||||||
public IPeripheral getPeripheral( EnumFacing side )
|
public IPeripheral getPeripheral( EnumFacing side )
|
||||||
{
|
{
|
||||||
if( side == getDirection() )
|
return side == getDirection() ? m_modem : null;
|
||||||
{
|
|
||||||
return m_modem;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isAttached()
|
|
||||||
{
|
|
||||||
return (m_modem != null) && (m_modem.getComputer() != null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -83,6 +83,7 @@ public class TileWiredModemFull extends TilePeripheralBase
|
|||||||
private boolean m_destroyed = false;
|
private boolean m_destroyed = false;
|
||||||
private boolean m_connectionsFormed = false;
|
private boolean m_connectionsFormed = false;
|
||||||
|
|
||||||
|
private final ModemState m_modemState = new ModemState();
|
||||||
private final WiredModemElement m_element = new FullElement( this );
|
private final WiredModemElement m_element = new FullElement( this );
|
||||||
private final IWiredNode m_node = m_element.getNode();
|
private final IWiredNode m_node = m_element.getNode();
|
||||||
|
|
||||||
@@ -236,19 +237,8 @@ public class TileWiredModemFull extends TilePeripheralBase
|
|||||||
protected void updateAnim()
|
protected void updateAnim()
|
||||||
{
|
{
|
||||||
int anim = 0;
|
int anim = 0;
|
||||||
for( WiredModemPeripheral modem : m_modems )
|
if( m_modemState.isOpen() ) anim |= 1;
|
||||||
{
|
if( m_peripheralAccessAllowed ) anim |= 2;
|
||||||
if( modem != null && modem.isActive() )
|
|
||||||
{
|
|
||||||
anim += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_peripheralAccessAllowed )
|
|
||||||
{
|
|
||||||
anim += 2;
|
|
||||||
}
|
|
||||||
setAnim( anim );
|
setAnim( anim );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,12 +254,7 @@ public class TileWiredModemFull extends TilePeripheralBase
|
|||||||
{
|
{
|
||||||
if( !getWorld().isRemote )
|
if( !getWorld().isRemote )
|
||||||
{
|
{
|
||||||
boolean changed = false;
|
if( m_modemState.pollChanged() ) updateAnim();
|
||||||
for( WiredModemPeripheral peripheral : m_modems )
|
|
||||||
{
|
|
||||||
if( peripheral != null && peripheral.pollChanged() ) changed = true;
|
|
||||||
}
|
|
||||||
if( changed ) updateAnim();
|
|
||||||
|
|
||||||
if( !m_connectionsFormed )
|
if( !m_connectionsFormed )
|
||||||
{
|
{
|
||||||
@@ -402,7 +387,7 @@ public class TileWiredModemFull extends TilePeripheralBase
|
|||||||
if( peripheral == null )
|
if( peripheral == null )
|
||||||
{
|
{
|
||||||
WiredModemLocalPeripheral localPeripheral = m_peripherals[side.ordinal()];
|
WiredModemLocalPeripheral localPeripheral = m_peripherals[side.ordinal()];
|
||||||
peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_element )
|
peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_modemState, m_element )
|
||||||
{
|
{
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
|
@@ -29,7 +29,7 @@ public class TileWirelessModem extends TileModemBase
|
|||||||
|
|
||||||
public Peripheral( TileModemBase entity )
|
public Peripheral( TileModemBase entity )
|
||||||
{
|
{
|
||||||
super( false );
|
super( new ModemState(), false );
|
||||||
m_entity = entity;
|
m_entity = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,6 +18,8 @@ import javax.annotation.Nonnull;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||||
|
|
||||||
@@ -25,10 +27,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
{
|
{
|
||||||
private final WiredModemElement modem;
|
private final WiredModemElement modem;
|
||||||
|
|
||||||
private final Map<String, RemotePeripheralWrapper> peripheralWrappers = new HashMap<>();
|
private final Map<IComputerAccess, ConcurrentMap<String, RemotePeripheralWrapper>> peripheralWrappers = new HashMap<>( 1 );
|
||||||
|
|
||||||
public WiredModemPeripheral( WiredModemElement modem )
|
public WiredModemPeripheral( ModemState state, WiredModemElement modem )
|
||||||
{
|
{
|
||||||
|
super( state );
|
||||||
this.modem = modem;
|
this.modem = modem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,56 +91,54 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
// getNamesRemote
|
// getNamesRemote
|
||||||
synchronized( peripheralWrappers )
|
Map<String, RemotePeripheralWrapper> wrappers = getWrappers( computer );
|
||||||
|
Map<Object, Object> table = new HashMap<>();
|
||||||
|
if( wrappers != null )
|
||||||
{
|
{
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
Map<Object, Object> table = new HashMap<>();
|
for( String name : wrappers.keySet() ) table.put( idx++, name );
|
||||||
for( String name : peripheralWrappers.keySet() )
|
|
||||||
{
|
|
||||||
table.put( idx++, name );
|
|
||||||
}
|
|
||||||
return new Object[]{ table };
|
|
||||||
}
|
}
|
||||||
|
return new Object[]{ table };
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
// isPresentRemote
|
// isPresentRemote
|
||||||
String type = getTypeRemote( getString( arguments, 0 ) );
|
String name = getString( arguments, 0 );
|
||||||
return new Object[]{ type != null };
|
return new Object[]{ getWrapper( computer, name ) != null };
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
// getTypeRemote
|
// getTypeRemote
|
||||||
String type = getTypeRemote( getString( arguments, 0 ) );
|
String name = getString( arguments, 0 );
|
||||||
if( type != null )
|
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
|
||||||
{
|
return wrapper != null ? new Object[]{ wrapper.getType() } : null;
|
||||||
return new Object[]{ type };
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
// getMethodsRemote
|
// getMethodsRemote
|
||||||
String[] methodNames = getMethodNamesRemote( getString( arguments, 0 ) );
|
String name = getString( arguments, 0 );
|
||||||
if( methodNames != null )
|
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
|
||||||
|
if( wrapper == null ) return null;
|
||||||
|
|
||||||
|
String[] methodNames = wrapper.getMethodNames();
|
||||||
|
Map<Object, Object> table = new HashMap<>();
|
||||||
|
for( int i = 0; i < methodNames.length; ++i )
|
||||||
{
|
{
|
||||||
Map<Object, Object> table = new HashMap<>();
|
table.put( i + 1, methodNames[i] );
|
||||||
for( int i = 0; i < methodNames.length; ++i )
|
|
||||||
{
|
|
||||||
table.put( i + 1, methodNames[i] );
|
|
||||||
}
|
|
||||||
return new Object[]{ table };
|
|
||||||
}
|
}
|
||||||
return null;
|
return new Object[]{ table };
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
// callRemote
|
// callRemote
|
||||||
String remoteName = getString( arguments, 0 );
|
String remoteName = getString( arguments, 0 );
|
||||||
String methodName = getString( arguments, 1 );
|
String methodName = getString( arguments, 1 );
|
||||||
|
RemotePeripheralWrapper wrapper = getWrapper( computer, remoteName );
|
||||||
|
if( wrapper == null ) throw new LuaException( "No peripheral: " + remoteName );
|
||||||
|
|
||||||
Object[] methodArgs = new Object[arguments.length - 2];
|
Object[] methodArgs = new Object[arguments.length - 2];
|
||||||
System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 );
|
System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 );
|
||||||
return callMethodRemote( remoteName, context, methodName, methodArgs );
|
return wrapper.callMethod( context, methodName, methodArgs );
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
{
|
{
|
||||||
@@ -157,29 +158,37 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
public void attach( @Nonnull IComputerAccess computer )
|
public void attach( @Nonnull IComputerAccess computer )
|
||||||
{
|
{
|
||||||
super.attach( computer );
|
super.attach( computer );
|
||||||
|
|
||||||
|
ConcurrentMap<String, RemotePeripheralWrapper> wrappers;
|
||||||
|
synchronized( peripheralWrappers )
|
||||||
|
{
|
||||||
|
wrappers = peripheralWrappers.get( computer );
|
||||||
|
if( wrappers == null ) peripheralWrappers.put( computer, wrappers = new ConcurrentHashMap<>() );
|
||||||
|
}
|
||||||
|
|
||||||
synchronized( modem.getRemotePeripherals() )
|
synchronized( modem.getRemotePeripherals() )
|
||||||
{
|
{
|
||||||
synchronized( peripheralWrappers )
|
for( Map.Entry<String, IPeripheral> entry : modem.getRemotePeripherals().entrySet() )
|
||||||
{
|
{
|
||||||
for( Map.Entry<String, IPeripheral> entry : modem.getRemotePeripherals().entrySet() )
|
attachPeripheralImpl( computer, wrappers, entry.getKey(), entry.getValue() );
|
||||||
{
|
|
||||||
attachPeripheralImpl( entry.getKey(), entry.getValue() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void detach( @Nonnull IComputerAccess computer )
|
public void detach( @Nonnull IComputerAccess computer )
|
||||||
{
|
{
|
||||||
|
Map<String, RemotePeripheralWrapper> wrappers;
|
||||||
synchronized( peripheralWrappers )
|
synchronized( peripheralWrappers )
|
||||||
{
|
{
|
||||||
for( RemotePeripheralWrapper wrapper : peripheralWrappers.values() )
|
wrappers = peripheralWrappers.remove( computer );
|
||||||
{
|
|
||||||
wrapper.detach();
|
|
||||||
}
|
|
||||||
peripheralWrappers.clear();
|
|
||||||
}
|
}
|
||||||
|
if( wrappers != null )
|
||||||
|
{
|
||||||
|
for( RemotePeripheralWrapper wrapper : wrappers.values() ) wrapper.detach();
|
||||||
|
wrappers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
super.detach( computer );
|
super.detach( computer );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,11 +213,12 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
|
|
||||||
public void attachPeripheral( String name, IPeripheral peripheral )
|
public void attachPeripheral( String name, IPeripheral peripheral )
|
||||||
{
|
{
|
||||||
if( getComputer() == null ) return;
|
|
||||||
|
|
||||||
synchronized( peripheralWrappers )
|
synchronized( peripheralWrappers )
|
||||||
{
|
{
|
||||||
attachPeripheralImpl( name, peripheral );
|
for( Map.Entry<IComputerAccess, ConcurrentMap<String, RemotePeripheralWrapper>> entry : peripheralWrappers.entrySet() )
|
||||||
|
{
|
||||||
|
attachPeripheralImpl( entry.getKey(), entry.getValue(), name, peripheral );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,63 +226,35 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
{
|
{
|
||||||
synchronized( peripheralWrappers )
|
synchronized( peripheralWrappers )
|
||||||
{
|
{
|
||||||
RemotePeripheralWrapper wrapper = peripheralWrappers.get( name );
|
for(ConcurrentMap<String, RemotePeripheralWrapper> wrappers : peripheralWrappers.values()) {
|
||||||
if( wrapper != null )
|
RemotePeripheralWrapper wrapper = wrappers.remove( name );
|
||||||
{
|
if( wrapper != null ) wrapper.detach();
|
||||||
peripheralWrappers.remove( name );
|
|
||||||
wrapper.detach();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void attachPeripheralImpl( String periphName, IPeripheral peripheral )
|
private void attachPeripheralImpl( IComputerAccess computer, ConcurrentMap<String, RemotePeripheralWrapper> peripherals, String periphName, IPeripheral peripheral )
|
||||||
{
|
{
|
||||||
if( !peripheralWrappers.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) )
|
if( !peripherals.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) )
|
||||||
{
|
{
|
||||||
RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, getComputer(), periphName );
|
RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, computer, periphName );
|
||||||
peripheralWrappers.put( periphName, wrapper );
|
peripherals.put( periphName, wrapper );
|
||||||
wrapper.attach();
|
wrapper.attach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTypeRemote( String remoteName )
|
private ConcurrentMap<String, RemotePeripheralWrapper> getWrappers( IComputerAccess computer ) {
|
||||||
{
|
|
||||||
synchronized( peripheralWrappers )
|
synchronized( peripheralWrappers )
|
||||||
{
|
{
|
||||||
RemotePeripheralWrapper wrapper = peripheralWrappers.get( remoteName );
|
return peripheralWrappers.get( computer );
|
||||||
if( wrapper != null )
|
|
||||||
{
|
|
||||||
return wrapper.getType();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] getMethodNamesRemote( String remoteName )
|
private RemotePeripheralWrapper getWrapper( IComputerAccess computer, String remoteName )
|
||||||
{
|
{
|
||||||
synchronized( peripheralWrappers )
|
ConcurrentMap<String, RemotePeripheralWrapper> wrappers = getWrappers( computer );
|
||||||
{
|
return wrappers == null ? null : wrappers.get( remoteName );
|
||||||
RemotePeripheralWrapper wrapper = peripheralWrappers.get( remoteName );
|
|
||||||
if( wrapper != null )
|
|
||||||
{
|
|
||||||
return wrapper.getMethodNames();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object[] callMethodRemote( String remoteName, ILuaContext context, String method, Object[] arguments ) throws LuaException, InterruptedException
|
|
||||||
{
|
|
||||||
RemotePeripheralWrapper wrapper;
|
|
||||||
synchronized( peripheralWrappers )
|
|
||||||
{
|
|
||||||
wrapper = peripheralWrappers.get( remoteName );
|
|
||||||
}
|
|
||||||
if( wrapper != null )
|
|
||||||
{
|
|
||||||
return wrapper.callMethod( context, method, arguments );
|
|
||||||
}
|
|
||||||
throw new LuaException( "No peripheral: " + remoteName );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RemotePeripheralWrapper implements IComputerAccess, IComputerOwned
|
private static class RemotePeripheralWrapper implements IComputerAccess, IComputerOwned
|
||||||
|
@@ -15,9 +15,16 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral
|
|||||||
{
|
{
|
||||||
private boolean m_advanced;
|
private boolean m_advanced;
|
||||||
|
|
||||||
|
public WirelessModemPeripheral( ModemState state, boolean advanced )
|
||||||
|
{
|
||||||
|
super( state );
|
||||||
|
m_advanced = advanced;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public WirelessModemPeripheral( boolean advanced )
|
public WirelessModemPeripheral( boolean advanced )
|
||||||
{
|
{
|
||||||
m_advanced = advanced;
|
this( new ModemState(), advanced );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -13,8 +13,9 @@ import dan200.computercraft.api.network.IPacketSender;
|
|||||||
import dan200.computercraft.api.network.Packet;
|
import dan200.computercraft.api.network.Packet;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.HashSet;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class WirelessNetwork implements IPacketNetwork
|
public class WirelessNetwork implements IPacketNetwork
|
||||||
{
|
{
|
||||||
@@ -34,45 +35,34 @@ public class WirelessNetwork implements IPacketNetwork
|
|||||||
s_universalNetwork = null;
|
s_universalNetwork = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Set<IPacketReceiver> m_receivers;
|
private final Set<IPacketReceiver> m_receivers = Collections.newSetFromMap( new ConcurrentHashMap<>() );
|
||||||
|
|
||||||
private WirelessNetwork()
|
|
||||||
{
|
|
||||||
m_receivers = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void addReceiver( @Nonnull IPacketReceiver receiver )
|
public void addReceiver( @Nonnull IPacketReceiver receiver )
|
||||||
{
|
{
|
||||||
Preconditions.checkNotNull( receiver, "device cannot be null" );
|
Preconditions.checkNotNull( receiver, "device cannot be null" );
|
||||||
m_receivers.add( receiver );
|
m_receivers.add( receiver );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void removeReceiver( @Nonnull IPacketReceiver receiver )
|
public void removeReceiver( @Nonnull IPacketReceiver receiver )
|
||||||
{
|
{
|
||||||
Preconditions.checkNotNull( receiver, "device cannot be null" );
|
Preconditions.checkNotNull( receiver, "device cannot be null" );
|
||||||
m_receivers.remove( receiver );
|
m_receivers.remove( receiver );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void transmitSameDimension( @Nonnull Packet packet, double range )
|
public void transmitSameDimension( @Nonnull Packet packet, double range )
|
||||||
{
|
{
|
||||||
Preconditions.checkNotNull( packet, "packet cannot be null" );
|
Preconditions.checkNotNull( packet, "packet cannot be null" );
|
||||||
for( IPacketReceiver device : m_receivers )
|
for( IPacketReceiver device : m_receivers ) tryTransmit( device, packet, range, false );
|
||||||
{
|
|
||||||
tryTransmit( device, packet, range, false );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void transmitInterdimensional( @Nonnull Packet packet )
|
public void transmitInterdimensional( @Nonnull Packet packet )
|
||||||
{
|
{
|
||||||
Preconditions.checkNotNull( packet, "packet cannot be null" );
|
Preconditions.checkNotNull( packet, "packet cannot be null" );
|
||||||
for (IPacketReceiver device : m_receivers)
|
for( IPacketReceiver device : m_receivers ) tryTransmit( device, packet, 0, true );
|
||||||
{
|
|
||||||
tryTransmit( device, packet, 0, true );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryTransmit( IPacketReceiver receiver, Packet packet, double range, boolean interdimensional )
|
private void tryTransmit( IPacketReceiver receiver, Packet packet, double range, boolean interdimensional )
|
||||||
|
@@ -66,7 +66,8 @@ public class MonitorPeripheral implements IPeripheral
|
|||||||
"setPaletteColor",
|
"setPaletteColor",
|
||||||
"getPaletteColour",
|
"getPaletteColour",
|
||||||
"getPaletteColor",
|
"getPaletteColor",
|
||||||
"getTextScale"
|
"getTextScale",
|
||||||
|
"getCursorBlink",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,8 +246,12 @@ public class MonitorPeripheral implements IPeripheral
|
|||||||
// getTextScale
|
// getTextScale
|
||||||
return new Object[] { monitor.getTextScale() / 2.0 };
|
return new Object[] { monitor.getTextScale() / 2.0 };
|
||||||
}
|
}
|
||||||
|
case 25:
|
||||||
|
// getCursorBlink
|
||||||
|
return new Object[] { terminal.getCursorBlink() };
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -344,7 +344,7 @@ public class TileMonitor extends TilePeripheralBase
|
|||||||
|
|
||||||
public EnumFacing getFront()
|
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()
|
public EnumFacing getRight()
|
||||||
|
@@ -190,10 +190,7 @@ public class TilePrinter extends TilePeripheralBase
|
|||||||
@Override
|
@Override
|
||||||
public ItemStack getStackInSlot(int i)
|
public ItemStack getStackInSlot(int i)
|
||||||
{
|
{
|
||||||
synchronized( m_inventory )
|
return m_inventory.get( i );
|
||||||
{
|
|
||||||
return m_inventory.get( i );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@@ -8,10 +8,11 @@ package dan200.computercraft.shared.peripheral.speaker;
|
|||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.ILuaTask;
|
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
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.ResourceLocation;
|
||||||
import net.minecraft.util.SoundCategory;
|
import net.minecraft.util.SoundCategory;
|
||||||
import net.minecraft.util.SoundEvent;
|
import net.minecraft.util.SoundEvent;
|
||||||
@@ -20,33 +21,35 @@ import net.minecraft.world.World;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
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.getString;
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.optReal;
|
import static dan200.computercraft.core.apis.ArgumentHelper.optReal;
|
||||||
|
|
||||||
public class SpeakerPeripheral implements IPeripheral {
|
public class SpeakerPeripheral implements IPeripheral
|
||||||
private TileSpeaker m_speaker;
|
{
|
||||||
|
private final TileSpeaker m_speaker;
|
||||||
private long m_clock;
|
private long m_clock;
|
||||||
private long m_lastPlayTime;
|
private long m_lastPlayTime;
|
||||||
private int m_notesThisTick;
|
private final AtomicInteger m_notesThisTick;
|
||||||
|
|
||||||
public SpeakerPeripheral()
|
public SpeakerPeripheral()
|
||||||
{
|
{
|
||||||
m_clock = 0;
|
this( null );
|
||||||
m_lastPlayTime = 0;
|
|
||||||
m_notesThisTick = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SpeakerPeripheral(TileSpeaker speaker)
|
SpeakerPeripheral( TileSpeaker speaker )
|
||||||
{
|
{
|
||||||
this();
|
m_clock = 0;
|
||||||
|
m_lastPlayTime = 0;
|
||||||
|
m_notesThisTick = new AtomicInteger( );
|
||||||
m_speaker = speaker;
|
m_speaker = speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void update()
|
public void update()
|
||||||
{
|
{
|
||||||
m_clock++;
|
m_clock++;
|
||||||
m_notesThisTick = 0;
|
m_notesThisTick.set( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public World getWorld()
|
public World getWorld()
|
||||||
@@ -59,9 +62,9 @@ public class SpeakerPeripheral implements IPeripheral {
|
|||||||
return m_speaker.getPos();
|
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 */
|
/* IPeripheral implementation */
|
||||||
@@ -69,15 +72,9 @@ public class SpeakerPeripheral implements IPeripheral {
|
|||||||
@Override
|
@Override
|
||||||
public boolean equals( IPeripheral other )
|
public boolean equals( IPeripheral other )
|
||||||
{
|
{
|
||||||
if( other != null && other instanceof SpeakerPeripheral )
|
if( other == this ) return true;
|
||||||
{
|
if( !(other instanceof SpeakerPeripheral) ) return false;
|
||||||
SpeakerPeripheral otherSpeaker = (SpeakerPeripheral) other;
|
return m_speaker == ((SpeakerPeripheral) other).m_speaker;
|
||||||
return otherSpeaker.m_speaker == m_speaker;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@@ -92,8 +89,8 @@ public class SpeakerPeripheral implements IPeripheral {
|
|||||||
public String[] getMethodNames()
|
public String[] getMethodNames()
|
||||||
{
|
{
|
||||||
return new String[] {
|
return new String[] {
|
||||||
"playSound", // Plays sound at resourceLocator
|
"playSound", // Plays sound at resourceLocator
|
||||||
"playNote" // Plays note
|
"playNote" // Plays note
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,18 +102,22 @@ public class SpeakerPeripheral implements IPeripheral {
|
|||||||
// playSound
|
// playSound
|
||||||
case 0:
|
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
|
// playNote
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
return playNote(args, context);
|
return playNote( args, context );
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
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
|
@Nonnull
|
||||||
private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException
|
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 volume = (float) optReal( arguments, 1, 1.0 );
|
||||||
float pitch = (float) optReal( arguments, 2, 1.0 );
|
float pitch = (float) optReal( arguments, 2, 1.0 );
|
||||||
|
|
||||||
|
String noteName = "block.note." + name;
|
||||||
|
|
||||||
// Check if sound exists
|
// Check if the note exists
|
||||||
if ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + name ) ) )
|
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
|
// If the resource location for note block notes changes, this method call will need to be updated
|
||||||
Object[] returnValue = playSound(
|
boolean success = playSound( context, noteName, volume, (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true );
|
||||||
new Object[] {
|
|
||||||
"block.note." + name,
|
|
||||||
(double)Math.min( volume, 3f ),
|
|
||||||
Math.pow( 2.0f, ( pitch - 12.0f ) / 12.0f)
|
|
||||||
}, context, true
|
|
||||||
);
|
|
||||||
|
|
||||||
if( returnValue[0] instanceof Boolean && (Boolean) returnValue[0] )
|
if( success ) m_notesThisTick.incrementAndGet();
|
||||||
{
|
return new Object[] { success };
|
||||||
m_notesThisTick++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
private synchronized boolean playSound( ILuaContext context, String name, float volume, float pitch, boolean isNote ) throws LuaException
|
||||||
private synchronized Object[] playSound( Object[] arguments, ILuaContext context, boolean isNote ) throws LuaException
|
|
||||||
{
|
{
|
||||||
String name = getString(arguments, 0);
|
if( m_clock - m_lastPlayTime < TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS &&
|
||||||
float volume = (float) optReal( arguments, 1, 1.0 );
|
(!isNote || m_clock - m_lastPlayTime != 0 || m_notesThisTick.get() >= ComputerCraft.maxNotesPerTick) )
|
||||||
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( SoundEvent.REGISTRY.containsKey(resourceName) )
|
// 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.
|
||||||
final World world = getWorld();
|
return false;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public synchronized void update()
|
public void update()
|
||||||
{
|
{
|
||||||
m_peripheral.update();
|
m_peripheral.update();
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
{
|
{
|
||||||
setMaxStackSize( 1 );
|
setMaxStackSize( 1 );
|
||||||
setHasSubtypes( true );
|
setHasSubtypes( true );
|
||||||
setUnlocalizedName( "computercraft:pocket_computer" );
|
setTranslationKey( "computercraft:pocket_computer" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
switch( getFamily( stack ) )
|
switch( getFamily( stack ) )
|
||||||
{
|
{
|
||||||
@@ -209,7 +209,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
|||||||
@Override
|
@Override
|
||||||
public String getItemStackDisplayName( @Nonnull ItemStack stack )
|
public String getItemStackDisplayName( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
String baseString = getUnlocalizedName( stack );
|
String baseString = getTranslationKey( stack );
|
||||||
IPocketUpgrade upgrade = getUpgrade( stack );
|
IPocketUpgrade upgrade = getUpgrade( stack );
|
||||||
if( upgrade != null )
|
if( upgrade != null )
|
||||||
{
|
{
|
||||||
|
@@ -5,6 +5,7 @@ import dan200.computercraft.api.pocket.IPocketAccess;
|
|||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||||
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
|
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityLivingBase;
|
import net.minecraft.entity.EntityLivingBase;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
@@ -75,7 +76,8 @@ public class PocketModem implements IPocketUpgrade
|
|||||||
modem.setLocation( entity.getEntityWorld(), entity.posX, entity.posY, entity.posZ );
|
modem.setLocation( entity.getEntityWorld(), entity.posX, entity.posY, entity.posZ );
|
||||||
}
|
}
|
||||||
|
|
||||||
access.setLight( modem.isActive() ? 0xBA0000 : -1 );
|
ModemState state = modem.getModemState();
|
||||||
|
if( state.pollChanged() ) access.setLight( state.isOpen() ? 0xBA0000 : -1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
package dan200.computercraft.shared.pocket.peripherals;
|
package dan200.computercraft.shared.pocket.peripherals;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||||
import dan200.computercraft.shared.peripheral.modem.WirelessModemPeripheral;
|
import dan200.computercraft.shared.peripheral.modem.WirelessModemPeripheral;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
@@ -19,7 +20,7 @@ public class PocketModemPeripheral extends WirelessModemPeripheral
|
|||||||
|
|
||||||
public PocketModemPeripheral( boolean advanced )
|
public PocketModemPeripheral( boolean advanced )
|
||||||
{
|
{
|
||||||
super( advanced );
|
super( new ModemState(), advanced );
|
||||||
m_world = null;
|
m_world = null;
|
||||||
m_position = new Vec3d( 0.0, 0.0, 0.0 );
|
m_position = new Vec3d( 0.0, 0.0, 0.0 );
|
||||||
}
|
}
|
||||||
|
@@ -48,17 +48,19 @@ import net.minecraftforge.registries.IForgeRegistry;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
||||||
{
|
{
|
||||||
private Map<Integer, ITurtleUpgrade> m_legacyTurtleUpgrades;
|
private Map<Integer, ITurtleUpgrade> m_legacyTurtleUpgrades;
|
||||||
private Map<String, ITurtleUpgrade> m_turtleUpgrades;
|
private Map<String, ITurtleUpgrade> m_turtleUpgrades;
|
||||||
|
|
||||||
private Consumer<ItemStack> dropConsumer;
|
private Function<ItemStack, ItemStack> dropConsumer;
|
||||||
|
private List<ItemStack> remainingDrops;
|
||||||
private WeakReference<World> dropWorld;
|
private WeakReference<World> dropWorld;
|
||||||
private BlockPos dropPos;
|
private BlockPos dropPos;
|
||||||
private AxisAlignedBB dropBounds;
|
private AxisAlignedBB dropBounds;
|
||||||
@@ -200,9 +202,10 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDropConsumer( Entity entity, Consumer<ItemStack> consumer )
|
public void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer )
|
||||||
{
|
{
|
||||||
dropConsumer = consumer;
|
dropConsumer = consumer;
|
||||||
|
remainingDrops = new ArrayList<>();
|
||||||
dropEntity = new WeakReference<>( entity );
|
dropEntity = new WeakReference<>( entity );
|
||||||
dropWorld = new WeakReference<>( entity.world );
|
dropWorld = new WeakReference<>( entity.world );
|
||||||
dropPos = null;
|
dropPos = null;
|
||||||
@@ -212,9 +215,10 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDropConsumer( World world, BlockPos pos, Consumer<ItemStack> consumer )
|
public void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer )
|
||||||
{
|
{
|
||||||
dropConsumer = consumer;
|
dropConsumer = consumer;
|
||||||
|
remainingDrops = new ArrayList<>();
|
||||||
dropEntity = null;
|
dropEntity = null;
|
||||||
dropWorld = new WeakReference<>( world );
|
dropWorld = new WeakReference<>( world );
|
||||||
dropPos = pos;
|
dropPos = pos;
|
||||||
@@ -222,7 +226,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearDropConsumer()
|
public List<ItemStack> clearDropConsumer()
|
||||||
{
|
{
|
||||||
if( dropEntity != null )
|
if( dropEntity != null )
|
||||||
{
|
{
|
||||||
@@ -232,17 +236,22 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
entity.captureDrops = false;
|
entity.captureDrops = false;
|
||||||
if( entity.capturedDrops != null )
|
if( entity.capturedDrops != null )
|
||||||
{
|
{
|
||||||
for( EntityItem entityItem : entity.capturedDrops ) dropConsumer.accept( entityItem.getItem() );
|
for( EntityItem entityItem : entity.capturedDrops ) handleDrops( entityItem.getItem() );
|
||||||
entity.capturedDrops.clear();
|
entity.capturedDrops.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ItemStack> remainingStacks = remainingDrops;
|
||||||
|
|
||||||
dropConsumer = null;
|
dropConsumer = null;
|
||||||
|
remainingDrops = null;
|
||||||
dropEntity = null;
|
dropEntity = null;
|
||||||
dropWorld = null;
|
dropWorld = null;
|
||||||
dropPos = null;
|
dropPos = null;
|
||||||
dropBounds = null;
|
dropBounds = null;
|
||||||
|
|
||||||
|
return remainingStacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerTurtleUpgradeInternal( ITurtleUpgrade upgrade )
|
private void registerTurtleUpgradeInternal( ITurtleUpgrade upgrade )
|
||||||
@@ -413,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.
|
// 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() )
|
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;
|
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||||
|
|
||||||
String key = mapping.key.getResourcePath();
|
String key = mapping.key.getPath();
|
||||||
if( key.equalsIgnoreCase( "CC-Turtle" ) )
|
if( key.equalsIgnoreCase( "CC-Turtle" ) )
|
||||||
{
|
{
|
||||||
mapping.remap( Item.getItemFromBlock( ComputerCraft.Blocks.turtle ) );
|
mapping.remap( Item.getItemFromBlock( ComputerCraft.Blocks.turtle ) );
|
||||||
@@ -438,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.
|
// 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() )
|
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;
|
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||||
|
|
||||||
String key = mapping.key.getResourcePath();
|
String key = mapping.key.getPath();
|
||||||
if( key.equalsIgnoreCase( "CC-Turtle" ) )
|
if( key.equalsIgnoreCase( "CC-Turtle" ) )
|
||||||
{
|
{
|
||||||
mapping.remap( ComputerCraft.Blocks.turtle );
|
mapping.remap( ComputerCraft.Blocks.turtle );
|
||||||
@@ -460,9 +469,9 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
private void registerTileEntities()
|
private void registerTileEntities()
|
||||||
{
|
{
|
||||||
// TileEntities
|
// TileEntities
|
||||||
GameRegistry.registerTileEntity( TileTurtle.class, ComputerCraft.LOWER_ID + " : " + "turtle" );
|
GameRegistry.registerTileEntity( TileTurtle.class, new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ) );
|
||||||
GameRegistry.registerTileEntity( TileTurtleExpanded.class, ComputerCraft.LOWER_ID + " : " + "turtleex" );
|
GameRegistry.registerTileEntity( TileTurtleExpanded.class, new ResourceLocation( ComputerCraft.MOD_ID, "turtleex" ) );
|
||||||
GameRegistry.registerTileEntity( TileTurtleAdvanced.class, ComputerCraft.LOWER_ID + " : " + "turtleadv" );
|
GameRegistry.registerTileEntity( TileTurtleAdvanced.class, new ResourceLocation( ComputerCraft.MOD_ID, "turtleadv" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerForgeHandlers()
|
private void registerForgeHandlers()
|
||||||
@@ -471,6 +480,12 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
MinecraftForge.EVENT_BUS.register( handlers );
|
MinecraftForge.EVENT_BUS.register( handlers );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleDrops(ItemStack stack)
|
||||||
|
{
|
||||||
|
ItemStack remaining = dropConsumer.apply(stack);
|
||||||
|
if (!remaining.isEmpty()) remainingDrops.add(remaining);
|
||||||
|
}
|
||||||
|
|
||||||
private class ForgeHandlers
|
private class ForgeHandlers
|
||||||
{
|
{
|
||||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||||
@@ -480,7 +495,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
if( dropEntity != null && event.getEntity() == dropEntity.get() )
|
if( dropEntity != null && event.getEntity() == dropEntity.get() )
|
||||||
{
|
{
|
||||||
List<EntityItem> drops = event.getDrops();
|
List<EntityItem> drops = event.getDrops();
|
||||||
for( EntityItem entityItem : drops ) dropConsumer.accept( entityItem.getItem() );
|
for( EntityItem entityItem : drops ) handleDrops( entityItem.getItem() );
|
||||||
drops.clear();
|
drops.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -494,7 +509,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
{
|
{
|
||||||
for( ItemStack item : event.getDrops() )
|
for( ItemStack item : event.getDrops() )
|
||||||
{
|
{
|
||||||
if( event.getWorld().rand.nextFloat() < event.getDropChance() ) dropConsumer.accept( item );
|
if( event.getWorld().rand.nextFloat() < event.getDropChance() ) handleDrops( item );
|
||||||
}
|
}
|
||||||
event.getDrops().clear();
|
event.getDrops().clear();
|
||||||
}
|
}
|
||||||
@@ -507,7 +522,7 @@ public abstract class CCTurtleProxyCommon implements ICCTurtleProxy
|
|||||||
if( dropWorld != null && dropWorld.get() == event.getWorld() && event.getEntity() instanceof EntityItem
|
if( dropWorld != null && dropWorld.get() == event.getWorld() && event.getEntity() instanceof EntityItem
|
||||||
&& dropBounds.contains( event.getEntity().getPositionVector() ) )
|
&& dropBounds.contains( event.getEntity().getPositionVector() ) )
|
||||||
{
|
{
|
||||||
dropConsumer.accept( ((EntityItem) event.getEntity()).getItem() );
|
handleDrops( ((EntityItem) event.getEntity()).getItem() );
|
||||||
event.setCanceled( true );
|
event.setCanceled( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,8 @@ import dan200.computercraft.shared.computer.core.*;
|
|||||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||||
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
|
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
|
||||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||||
|
import dan200.computercraft.shared.datafix.Fixes;
|
||||||
|
import dan200.computercraft.shared.integration.charset.IntegrationCharset;
|
||||||
import dan200.computercraft.shared.media.common.DefaultMediaProvider;
|
import dan200.computercraft.shared.media.common.DefaultMediaProvider;
|
||||||
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
||||||
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
|
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
|
||||||
@@ -76,6 +78,8 @@ import net.minecraftforge.event.RegistryEvent;
|
|||||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||||
import net.minecraftforge.event.world.WorldEvent;
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
|
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
|
||||||
|
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||||
|
import net.minecraftforge.fml.common.Loader;
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||||
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
|
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
|
||||||
@@ -83,6 +87,7 @@ import net.minecraftforge.fml.common.network.IGuiHandler;
|
|||||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||||
import net.minecraftforge.registries.IForgeRegistry;
|
import net.minecraftforge.registries.IForgeRegistry;
|
||||||
|
import pl.asie.charset.ModCharset;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -117,6 +122,9 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
|||||||
{
|
{
|
||||||
registerTileEntities();
|
registerTileEntities();
|
||||||
registerForgeHandlers();
|
registerForgeHandlers();
|
||||||
|
|
||||||
|
Fixes.register( FMLCommonHandler.instance().getDataFixer() );
|
||||||
|
if( Loader.isModLoaded( ModCharset.MODID ) ) IntegrationCharset.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -414,10 +422,10 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
|||||||
// We have to use mappings.getAllMappings() as the mod ID is upper case but the domain lower.
|
// 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() )
|
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;
|
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||||
|
|
||||||
String key = mapping.key.getResourcePath();
|
String key = mapping.key.getPath();
|
||||||
if( key.equalsIgnoreCase( "CC-Computer" ) )
|
if( key.equalsIgnoreCase( "CC-Computer" ) )
|
||||||
{
|
{
|
||||||
mapping.remap( Item.getItemFromBlock( ComputerCraft.Blocks.computer ) );
|
mapping.remap( Item.getItemFromBlock( ComputerCraft.Blocks.computer ) );
|
||||||
@@ -451,10 +459,10 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
|||||||
// We have to use mappings.getAllMappings() as the mod ID is upper case but the domain lower.
|
// 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() )
|
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;
|
if( !domain.equalsIgnoreCase( ComputerCraft.MOD_ID ) ) continue;
|
||||||
|
|
||||||
String key = mapping.key.getResourcePath();
|
String key = mapping.key.getPath();
|
||||||
if( key.equalsIgnoreCase( "CC-Computer" ) )
|
if( key.equalsIgnoreCase( "CC-Computer" ) )
|
||||||
{
|
{
|
||||||
mapping.remap( ComputerCraft.Blocks.computer );
|
mapping.remap( ComputerCraft.Blocks.computer );
|
||||||
@@ -473,16 +481,16 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
|
|||||||
private void registerTileEntities()
|
private void registerTileEntities()
|
||||||
{
|
{
|
||||||
// Tile Entities
|
// Tile Entities
|
||||||
GameRegistry.registerTileEntity( TileComputer.class, ComputerCraft.LOWER_ID + " : " + "computer" );
|
GameRegistry.registerTileEntity( TileComputer.class, new ResourceLocation( ComputerCraft.MOD_ID, "computer" ) );
|
||||||
GameRegistry.registerTileEntity( TileDiskDrive.class, ComputerCraft.LOWER_ID + " : " + "diskdrive" );
|
GameRegistry.registerTileEntity( TileDiskDrive.class, new ResourceLocation( ComputerCraft.MOD_ID, "diskdrive" ) );
|
||||||
GameRegistry.registerTileEntity( TileWirelessModem.class, ComputerCraft.LOWER_ID + " : " + "wirelessmodem" );
|
GameRegistry.registerTileEntity( TileWirelessModem.class, new ResourceLocation( ComputerCraft.MOD_ID, "wirelessmodem" ) );
|
||||||
GameRegistry.registerTileEntity( TileMonitor.class, ComputerCraft.LOWER_ID + " : " + "monitor" );
|
GameRegistry.registerTileEntity( TileMonitor.class, new ResourceLocation( ComputerCraft.MOD_ID, "monitor" ) );
|
||||||
GameRegistry.registerTileEntity( TilePrinter.class, ComputerCraft.LOWER_ID + " : " + "ccprinter" );
|
GameRegistry.registerTileEntity( TilePrinter.class, new ResourceLocation( ComputerCraft.MOD_ID, "ccprinter" ) );
|
||||||
GameRegistry.registerTileEntity( TileCable.class, ComputerCraft.LOWER_ID + " : " + "wiredmodem" );
|
GameRegistry.registerTileEntity( TileCable.class, new ResourceLocation( ComputerCraft.MOD_ID, "wiredmodem" ) );
|
||||||
GameRegistry.registerTileEntity( TileCommandComputer.class, ComputerCraft.LOWER_ID + " : " + "command_computer" );
|
GameRegistry.registerTileEntity( TileCommandComputer.class, new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ) );
|
||||||
GameRegistry.registerTileEntity( TileAdvancedModem.class, ComputerCraft.LOWER_ID + " : " + "advanced_modem" );
|
GameRegistry.registerTileEntity( TileAdvancedModem.class, new ResourceLocation( ComputerCraft.MOD_ID, "advanced_modem" ) );
|
||||||
GameRegistry.registerTileEntity( TileSpeaker.class, ComputerCraft.LOWER_ID + " : " + "speaker" );
|
GameRegistry.registerTileEntity( TileSpeaker.class, new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ) );
|
||||||
GameRegistry.registerTileEntity( TileWiredModemFull.class, ComputerCraft.LOWER_ID + " : " + "wired_modem_full" );
|
GameRegistry.registerTileEntity( TileWiredModemFull.class, new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full" ) );
|
||||||
|
|
||||||
// Register peripheral providers
|
// Register peripheral providers
|
||||||
ComputerCraftAPI.registerPeripheralProvider( new DefaultPeripheralProvider() );
|
ComputerCraftAPI.registerPeripheralProvider( new DefaultPeripheralProvider() );
|
||||||
|
@@ -14,7 +14,9 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public interface ICCTurtleProxy
|
public interface ICCTurtleProxy
|
||||||
{
|
{
|
||||||
@@ -27,7 +29,7 @@ public interface ICCTurtleProxy
|
|||||||
ITurtleUpgrade getTurtleUpgrade( @Nonnull ItemStack item );
|
ITurtleUpgrade getTurtleUpgrade( @Nonnull ItemStack item );
|
||||||
void addAllUpgradedTurtles( NonNullList<ItemStack> list );
|
void addAllUpgradedTurtles( NonNullList<ItemStack> list );
|
||||||
|
|
||||||
void setDropConsumer( Entity entity, Consumer<ItemStack> consumer );
|
void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer );
|
||||||
void setDropConsumer( World world, BlockPos pos, Consumer<ItemStack> consumer );
|
void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer );
|
||||||
void clearDropConsumer();
|
List<ItemStack> clearDropConsumer();
|
||||||
}
|
}
|
||||||
|
@@ -48,7 +48,7 @@ public class BlockTurtle extends BlockComputerBase
|
|||||||
{
|
{
|
||||||
super( Material.IRON );
|
super( Material.IRON );
|
||||||
setHardness( 2.5f );
|
setHardness( 2.5f );
|
||||||
setUnlocalizedName( "computercraft:turtle" );
|
setTranslationKey( "computercraft:turtle" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
setDefaultState( this.blockState.getBaseState()
|
setDefaultState( this.blockState.getBaseState()
|
||||||
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
.withProperty( Properties.FACING, EnumFacing.NORTH )
|
||||||
|
@@ -226,7 +226,7 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
public void readFromNBT( NBTTagCompound nbttagcompound )
|
public void readFromNBT( NBTTagCompound nbttagcompound )
|
||||||
{
|
{
|
||||||
// Read state
|
// Read state
|
||||||
m_direction = EnumFacing.getFront( nbttagcompound.getInteger( "dir" ) );
|
m_direction = EnumFacing.byIndex( nbttagcompound.getInteger( "dir" ) );
|
||||||
m_selectedSlot = nbttagcompound.getInteger( "selectedSlot" );
|
m_selectedSlot = nbttagcompound.getInteger( "selectedSlot" );
|
||||||
if( nbttagcompound.hasKey( "fuelLevel" ) )
|
if( nbttagcompound.hasKey( "fuelLevel" ) )
|
||||||
{
|
{
|
||||||
@@ -370,8 +370,8 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
// Write overlay
|
// Write overlay
|
||||||
if( m_overlay != null )
|
if( m_overlay != null )
|
||||||
{
|
{
|
||||||
nbttagcompound.setString( "overlay_mod", m_overlay.getResourceDomain() );
|
nbttagcompound.setString( "overlay_mod", m_overlay.getNamespace() );
|
||||||
nbttagcompound.setString( "overlay_path", m_overlay.getResourcePath() );
|
nbttagcompound.setString( "overlay_path", m_overlay.getPath() );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write NBT
|
// Write NBT
|
||||||
@@ -429,8 +429,8 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
// Overlay
|
// Overlay
|
||||||
if( m_overlay != null )
|
if( m_overlay != null )
|
||||||
{
|
{
|
||||||
nbttagcompound.setString( "overlay_mod", m_overlay.getResourceDomain() );
|
nbttagcompound.setString( "overlay_mod", m_overlay.getNamespace() );
|
||||||
nbttagcompound.setString( "overlay_path", m_overlay.getResourcePath() );
|
nbttagcompound.setString( "overlay_path", m_overlay.getPath() );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animation
|
// Animation
|
||||||
@@ -505,7 +505,7 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
m_lastAnimationProgress = 0;
|
m_lastAnimationProgress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_direction = EnumFacing.getFront( nbttagcompound.getInteger( "direction" ) );
|
m_direction = EnumFacing.byIndex( nbttagcompound.getInteger( "direction" ) );
|
||||||
m_fuelLevel = nbttagcompound.getInteger( "fuelLevel" );
|
m_fuelLevel = nbttagcompound.getInteger( "fuelLevel" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,9 +998,9 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
|
|
||||||
double distance = -1.0 + getAnimationFraction( f );
|
double distance = -1.0 + getAnimationFraction( f );
|
||||||
return new Vec3d(
|
return new Vec3d(
|
||||||
distance * dir.getFrontOffsetX(),
|
distance * dir.getXOffset(),
|
||||||
distance * dir.getFrontOffsetY(),
|
distance * dir.getYOffset(),
|
||||||
distance * dir.getFrontOffsetZ()
|
distance * dir.getZOffset()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1184,31 +1184,31 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
|
|
||||||
float pushFrac = 1.0f - ((float)(m_animationProgress + 1) / (float)ANIM_DURATION);
|
float pushFrac = 1.0f - ((float)(m_animationProgress + 1) / (float)ANIM_DURATION);
|
||||||
float push = Math.max( pushFrac + 0.0125f, 0.0f );
|
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
|
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
|
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
|
else
|
||||||
{
|
{
|
||||||
maxZ -= moveDir.getFrontOffsetZ() * push;
|
maxZ -= moveDir.getZOffset() * push;
|
||||||
}
|
}
|
||||||
|
|
||||||
AxisAlignedBB aabb = new AxisAlignedBB( minX, minY, minZ, maxX, maxY, maxZ );
|
AxisAlignedBB aabb = new AxisAlignedBB( minX, minY, minZ, maxX, maxY, maxZ );
|
||||||
@@ -1216,9 +1216,9 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
if( !list.isEmpty() )
|
if( !list.isEmpty() )
|
||||||
{
|
{
|
||||||
double pushStep = 1.0f / ANIM_DURATION;
|
double pushStep = 1.0f / ANIM_DURATION;
|
||||||
double pushStepX = moveDir.getFrontOffsetX() * pushStep;
|
double pushStepX = moveDir.getXOffset() * pushStep;
|
||||||
double pushStepY = moveDir.getFrontOffsetY() * pushStep;
|
double pushStepY = moveDir.getYOffset() * pushStep;
|
||||||
double pushStepZ = moveDir.getFrontOffsetZ() * pushStep;
|
double pushStepZ = moveDir.getZOffset() * pushStep;
|
||||||
for (Entity entity : list)
|
for (Entity entity : list)
|
||||||
{
|
{
|
||||||
entity.move( MoverType.PISTON, pushStepX, pushStepY, pushStepZ );
|
entity.move( MoverType.PISTON, pushStepX, pushStepY, pushStepZ );
|
||||||
|
@@ -126,7 +126,7 @@ public class TurtleCompareCommand implements ITurtleCommand
|
|||||||
{
|
{
|
||||||
return TurtleCommandResult.success();
|
return TurtleCommandResult.success();
|
||||||
}
|
}
|
||||||
else if( selectedStack.getUnlocalizedName().equals( lookAtStack.getUnlocalizedName() ) )
|
else if( selectedStack.getTranslationKey().equals( lookAtStack.getTranslationKey() ) )
|
||||||
{
|
{
|
||||||
return TurtleCommandResult.success();
|
return TurtleCommandResult.success();
|
||||||
}
|
}
|
||||||
|
@@ -87,9 +87,9 @@ public class TurtleMoveCommand implements ITurtleCommand
|
|||||||
if( entityBB != null )
|
if( entityBB != null )
|
||||||
{
|
{
|
||||||
AxisAlignedBB pushedBB = entityBB.offset(
|
AxisAlignedBB pushedBB = entityBB.offset(
|
||||||
direction.getFrontOffsetX(),
|
direction.getXOffset(),
|
||||||
direction.getFrontOffsetY(),
|
direction.getYOffset(),
|
||||||
direction.getFrontOffsetZ()
|
direction.getZOffset()
|
||||||
);
|
);
|
||||||
if( !oldWorld.getCollisionBoxes( null, pushedBB ).isEmpty() )
|
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.math.Vec3d;
|
||||||
import net.minecraft.util.text.TextComponentString;
|
import net.minecraft.util.text.TextComponentString;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldServer;
|
|
||||||
import net.minecraftforge.common.ForgeHooks;
|
import net.minecraftforge.common.ForgeHooks;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
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 org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class TurtlePlaceCommand implements ITurtleCommand
|
public class TurtlePlaceCommand implements ITurtleCommand
|
||||||
{
|
{
|
||||||
@@ -194,9 +194,9 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
|||||||
// Stop intersection with the turtle itself
|
// Stop intersection with the turtle itself
|
||||||
if( turtle.getPosition().equals( position ) )
|
if( turtle.getPosition().equals( position ) )
|
||||||
{
|
{
|
||||||
turtlePlayer.posX += 0.48 * direction.getFrontOffsetX();
|
turtlePlayer.posX += 0.48 * direction.getXOffset();
|
||||||
turtlePlayer.posY += 0.48 * direction.getFrontOffsetY();
|
turtlePlayer.posY += 0.48 * direction.getYOffset();
|
||||||
turtlePlayer.posZ += 0.48 * direction.getFrontOffsetZ();
|
turtlePlayer.posZ += 0.48 * direction.getZOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( direction.getAxis() != EnumFacing.Axis.Y )
|
if( direction.getAxis() != EnumFacing.Axis.Y )
|
||||||
@@ -241,14 +241,10 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
|||||||
// Start claiming entity drops
|
// Start claiming entity drops
|
||||||
Entity hitEntity = hit.getKey();
|
Entity hitEntity = hit.getKey();
|
||||||
Vec3d hitPos = hit.getValue();
|
Vec3d hitPos = hit.getValue();
|
||||||
ComputerCraft.setDropConsumer( hitEntity, ( drop ) ->
|
ComputerCraft.setDropConsumer(
|
||||||
{
|
hitEntity,
|
||||||
ItemStack remainder = InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() )
|
||||||
if( !remainder.isEmpty() )
|
);
|
||||||
{
|
|
||||||
WorldUtil.dropItemStack( remainder, world, position, turtle.getDirection().getOpposite() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Place on the entity
|
// Place on the entity
|
||||||
boolean placed = false;
|
boolean placed = false;
|
||||||
@@ -285,7 +281,11 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop claiming drops
|
// Stop claiming drops
|
||||||
ComputerCraft.clearDropConsumer();
|
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
|
// Put everything we collected into the turtles inventory, then return
|
||||||
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
||||||
@@ -367,9 +367,9 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
|||||||
orientPlayer( turtle, turtlePlayer, playerPosition, playerDir );
|
orientPlayer( turtle, turtlePlayer, playerPosition, playerDir );
|
||||||
|
|
||||||
// Calculate where the turtle would hit the block
|
// Calculate where the turtle would hit the block
|
||||||
float hitX = 0.5f + side.getFrontOffsetX() * 0.5f;
|
float hitX = 0.5f + side.getXOffset() * 0.5f;
|
||||||
float hitY = 0.5f + side.getFrontOffsetY() * 0.5f;
|
float hitY = 0.5f + side.getYOffset() * 0.5f;
|
||||||
float hitZ = 0.5f + side.getFrontOffsetZ() * 0.5f;
|
float hitZ = 0.5f + side.getZOffset() * 0.5f;
|
||||||
if( Math.abs( hitY - 0.5f ) < 0.01f )
|
if( Math.abs( hitY - 0.5f ) < 0.01f )
|
||||||
{
|
{
|
||||||
hitY = 0.45f;
|
hitY = 0.45f;
|
||||||
|
@@ -15,7 +15,7 @@ public class ItemTurtleAdvanced extends ItemTurtleNormal
|
|||||||
public ItemTurtleAdvanced( Block block )
|
public ItemTurtleAdvanced( Block block )
|
||||||
{
|
{
|
||||||
super( block );
|
super( block );
|
||||||
setUnlocalizedName( "computercraft:advanced_turtle" );
|
setTranslationKey( "computercraft:advanced_turtle" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -114,7 +114,7 @@ public abstract class ItemTurtleBase extends ItemComputerBase implements ITurtle
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getUnlocalizedName( @Nonnull ItemStack stack )
|
public String getTranslationKey( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
ComputerFamily family = getFamily( stack );
|
ComputerFamily family = getFamily( stack );
|
||||||
switch( family )
|
switch( family )
|
||||||
@@ -139,7 +139,7 @@ public abstract class ItemTurtleBase extends ItemComputerBase implements ITurtle
|
|||||||
@Override
|
@Override
|
||||||
public String getItemStackDisplayName( @Nonnull ItemStack stack )
|
public String getItemStackDisplayName( @Nonnull ItemStack stack )
|
||||||
{
|
{
|
||||||
String baseString = getUnlocalizedName( stack );
|
String baseString = getTranslationKey( stack );
|
||||||
ITurtleUpgrade left = getUpgrade( stack, TurtleSide.Left );
|
ITurtleUpgrade left = getUpgrade( stack, TurtleSide.Left );
|
||||||
ITurtleUpgrade right = getUpgrade( stack, TurtleSide.Right );
|
ITurtleUpgrade right = getUpgrade( stack, TurtleSide.Right );
|
||||||
if( left != null && right != null )
|
if( left != null && right != null )
|
||||||
|
@@ -23,7 +23,7 @@ public class ItemTurtleLegacy extends ItemTurtleBase
|
|||||||
public ItemTurtleLegacy( Block block )
|
public ItemTurtleLegacy( Block block )
|
||||||
{
|
{
|
||||||
super( block );
|
super( block );
|
||||||
setUnlocalizedName( "computercraft:turtle" );
|
setTranslationKey( "computercraft:turtle" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ public class ItemTurtleNormal extends ItemTurtleBase
|
|||||||
public ItemTurtleNormal( Block block )
|
public ItemTurtleNormal( Block block )
|
||||||
{
|
{
|
||||||
super( block );
|
super( block );
|
||||||
setUnlocalizedName( "computercraft:turtle" );
|
setTranslationKey( "computercraft:turtle" );
|
||||||
setCreativeTab( ComputerCraft.mainCreativeTab );
|
setCreativeTab( ComputerCraft.mainCreativeTab );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,8 +72,8 @@ public class ItemTurtleNormal extends ItemTurtleBase
|
|||||||
}
|
}
|
||||||
if( overlay != null )
|
if( overlay != null )
|
||||||
{
|
{
|
||||||
nbt.setString( "overlay_mod", overlay.getResourceDomain() );
|
nbt.setString( "overlay_mod", overlay.getNamespace() );
|
||||||
nbt.setString( "overlay_path", overlay.getResourcePath() );
|
nbt.setString( "overlay_path", overlay.getPath() );
|
||||||
}
|
}
|
||||||
stack.setTagCompound( nbt );
|
stack.setTagCompound( nbt );
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|||||||
import dan200.computercraft.api.turtle.*;
|
import dan200.computercraft.api.turtle.*;
|
||||||
import dan200.computercraft.shared.peripheral.PeripheralType;
|
import dan200.computercraft.shared.peripheral.PeripheralType;
|
||||||
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
|
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||||
import dan200.computercraft.shared.peripheral.modem.WirelessModemPeripheral;
|
import dan200.computercraft.shared.peripheral.modem.WirelessModemPeripheral;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||||
@@ -17,9 +18,9 @@ import net.minecraft.client.renderer.block.model.ModelManager;
|
|||||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NBTTagCompound;
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.EnumFacing;
|
import net.minecraft.util.EnumFacing;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraftforge.fml.relauncher.Side;
|
import net.minecraftforge.fml.relauncher.Side;
|
||||||
@@ -37,7 +38,7 @@ public class TurtleModem implements ITurtleUpgrade
|
|||||||
|
|
||||||
public Peripheral( ITurtleAccess turtle, boolean advanced )
|
public Peripheral( ITurtleAccess turtle, boolean advanced )
|
||||||
{
|
{
|
||||||
super( advanced );
|
super( new ModemState(), advanced );
|
||||||
m_turtle = turtle;
|
m_turtle = turtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,12 +222,12 @@ public class TurtleModem implements ITurtleUpgrade
|
|||||||
if( !turtle.getWorld().isRemote )
|
if( !turtle.getWorld().isRemote )
|
||||||
{
|
{
|
||||||
IPeripheral peripheral = turtle.getPeripheral( side );
|
IPeripheral peripheral = turtle.getPeripheral( side );
|
||||||
if( peripheral != null && peripheral instanceof Peripheral )
|
if( peripheral instanceof Peripheral )
|
||||||
{
|
{
|
||||||
Peripheral modemPeripheral = (Peripheral)peripheral;
|
ModemState state = ((Peripheral) peripheral).getModemState();
|
||||||
if( modemPeripheral.pollChanged() )
|
if( state.pollChanged() )
|
||||||
{
|
{
|
||||||
turtle.getUpgradeNBTData( side ).setBoolean( "active", modemPeripheral.isActive() );
|
turtle.getUpgradeNBTData( side ).setBoolean( "active", state.isOpen() );
|
||||||
turtle.updateUpgradeNBTData( side );
|
turtle.updateUpgradeNBTData( side );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,9 +42,8 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.vecmath.Matrix4f;
|
import javax.vecmath.Matrix4f;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class TurtleTool implements ITurtleUpgrade
|
public class TurtleTool implements ITurtleUpgrade
|
||||||
{
|
{
|
||||||
@@ -103,7 +102,7 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
@SideOnly(Side.CLIENT)
|
@SideOnly( Side.CLIENT )
|
||||||
public Pair<IBakedModel, Matrix4f> getModel( ITurtleAccess turtle, @Nonnull TurtleSide side )
|
public Pair<IBakedModel, Matrix4f> getModel( ITurtleAccess turtle, @Nonnull TurtleSide side )
|
||||||
{
|
{
|
||||||
float xOffset = (side == TurtleSide.Left) ? -0.40625f : 0.40625f;
|
float xOffset = (side == TurtleSide.Left) ? -0.40625f : 0.40625f;
|
||||||
@@ -154,7 +153,7 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
{
|
{
|
||||||
return 3.0f;
|
return 3.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
|
private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
|
||||||
{
|
{
|
||||||
// Create a fake player, and orient it appropriately
|
// Create a fake player, and orient it appropriately
|
||||||
@@ -185,10 +184,9 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
{
|
{
|
||||||
return TurtleCommandResult.failure( attackEvent.getFailureMessage() );
|
return TurtleCommandResult.failure( attackEvent.getFailureMessage() );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start claiming entity drops
|
// Start claiming entity drops
|
||||||
List<ItemStack> extra = new ArrayList<>( );
|
ComputerCraft.setDropConsumer( hitEntity, turtleDropConsumer( turtle ) );
|
||||||
ComputerCraft.setDropConsumer( hitEntity, turtleDropConsumer( turtle, extra ) );
|
|
||||||
|
|
||||||
// Attack the entity
|
// Attack the entity
|
||||||
boolean attacked = false;
|
boolean attacked = false;
|
||||||
@@ -220,7 +218,7 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop claiming drops
|
// Stop claiming drops
|
||||||
stopConsuming( turtle, extra );
|
stopConsuming( turtle );
|
||||||
|
|
||||||
// Put everything we collected into the turtles inventory, then return
|
// Put everything we collected into the turtles inventory, then return
|
||||||
if( attacked )
|
if( attacked )
|
||||||
@@ -232,7 +230,7 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
|
|
||||||
return TurtleCommandResult.failure( "Nothing to attack here" );
|
return TurtleCommandResult.failure( "Nothing to attack here" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
|
private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
|
||||||
{
|
{
|
||||||
// Get ready to dig
|
// Get ready to dig
|
||||||
@@ -277,8 +275,7 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Consume the items the block drops
|
// Consume the items the block drops
|
||||||
List<ItemStack> extra = new ArrayList<>( );
|
ComputerCraft.setDropConsumer( world, blockPosition, turtleDropConsumer( turtle ) );
|
||||||
ComputerCraft.setDropConsumer( world, blockPosition, turtleDropConsumer( turtle, extra ) );
|
|
||||||
|
|
||||||
TileEntity tile = world.getTileEntity( blockPosition );
|
TileEntity tile = world.getTileEntity( blockPosition );
|
||||||
|
|
||||||
@@ -291,13 +288,13 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
// Destroy the block
|
// Destroy the block
|
||||||
boolean canHarvest = state.getBlock().canHarvestBlock( world, blockPosition, turtlePlayer );
|
boolean canHarvest = state.getBlock().canHarvestBlock( world, blockPosition, turtlePlayer );
|
||||||
boolean canBreak = state.getBlock().removedByPlayer( state, world, blockPosition, turtlePlayer, canHarvest );
|
boolean canBreak = state.getBlock().removedByPlayer( state, world, blockPosition, turtlePlayer, canHarvest );
|
||||||
if( canBreak ) state.getBlock().onBlockDestroyedByPlayer( world, blockPosition, state );
|
if( canBreak ) state.getBlock().onPlayerDestroy( world, blockPosition, state );
|
||||||
if( canHarvest )
|
if( canHarvest )
|
||||||
{
|
{
|
||||||
state.getBlock().harvestBlock( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getHeldItemMainhand() );
|
state.getBlock().harvestBlock( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getHeldItemMainhand() );
|
||||||
}
|
}
|
||||||
|
|
||||||
stopConsuming( turtle, extra );
|
stopConsuming( turtle );
|
||||||
|
|
||||||
// Remember the previous block
|
// Remember the previous block
|
||||||
if( turtle instanceof TurtleBrain )
|
if( turtle instanceof TurtleBrain )
|
||||||
@@ -312,18 +309,14 @@ public class TurtleTool implements ITurtleUpgrade
|
|||||||
return TurtleCommandResult.failure( "Nothing to dig here" );
|
return TurtleCommandResult.failure( "Nothing to dig here" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<ItemStack> turtleDropConsumer( ITurtleAccess turtle, List<ItemStack> extra )
|
private Function<ItemStack, ItemStack> turtleDropConsumer( ITurtleAccess turtle )
|
||||||
{
|
{
|
||||||
return ( drop ) ->
|
return drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||||
{
|
|
||||||
ItemStack remainder = InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
|
||||||
if( !remainder.isEmpty() ) extra.add( remainder );
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopConsuming( ITurtleAccess turtle, List<ItemStack> extra )
|
private void stopConsuming( ITurtleAccess turtle )
|
||||||
{
|
{
|
||||||
ComputerCraft.clearDropConsumer();
|
List<ItemStack> extra = ComputerCraft.clearDropConsumer();
|
||||||
for( ItemStack remainder : extra )
|
for( ItemStack remainder : extra )
|
||||||
{
|
{
|
||||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), turtle.getDirection().getOpposite() );
|
WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), turtle.getDirection().getOpposite() );
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user