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

Compare commits

...

89 Commits

Author SHA1 Message Date
SquidDev
d7301ff15e Merge pull request #412 from Wilma456/ComputerCraft-1/textfix
Add Check to textutils.tabulate/pagedTabulate
2017-11-15 16:58:13 +00:00
SquidDev
1cf10c5c47 Merge pull request #490 from zardyh/ComputerCraft/master
Propagate errors arising from API loading
2017-11-15 16:51:26 +00:00
SquidDev
6691ec8e3a Merge pull request #390 from Wilma456/ComputerCraft-1/errormsg
Show fs error in paint and edit
2017-11-15 16:39:37 +00:00
SquidDev
a9f77221ff Merge pull request #469 from Wilma456/ComputerCraft-1/newrecipe
Add more Recipes to Recipebook
2017-11-15 16:35:27 +00:00
SquidDev
dd3b69a633 Rebranding!
I feel kinda guilty about this, but it's probably a good idea to make it
clear that this isn't "actual, proper, stable" ComputerCraft.
2017-11-15 16:25:10 +00:00
hydraz
d766f8b34e Propagate errors arising from API loading 2017-11-15 14:22:36 -02:00
SquidDev
2ae6fb47e7 Move CommandComputer into a child package
Means we can be a little more organised where we put the additional
commands.
2017-11-15 15:57:10 +00:00
SquidDev
dd5698241b Add support for running multiple computers at the same time
- ComputerThread constructs multiple threads instead of just one,
   depending on a config options.
 - The synchronized blocks of PeripheralAPI.PeripheralWrapper have been
   shifted a little to ensure no deadlocks occur.
2017-11-15 13:30:40 +00:00
SquidDev
ed8e9d7817 Add support for enabling Lua's debug library
Whilst I'm pretty sure this is safe for general use, I'm disabling this
by default for now. I may consider enabling it in the future if no
issues are found.
2017-11-15 12:18:10 +00:00
SquidDev
6c29b44c3c Merge pull request #440 from Wilma456/ComputerCraft-1/iomulti
Make io.write() accept multiple args
2017-11-15 11:47:33 +00:00
SquidDev
0caa133089 Merge pull request #454 from SquidDev-CC/ComputerCraft/hotfix/lazy-computer-peripheral
[WIP] Only instantiate ServerComputer on tile ticks
2017-11-15 11:42:54 +00:00
SquidDev
a8b08bd971 Remove apis.HTTPRequest
I evidently duplicated this during some rebase, more fool me.
2017-11-15 11:39:48 +00:00
SquidDev
c9181a121f Merge pull request #395 from SquidDev-CC/ComputerCraft/feature/websocket
Websocket support
2017-11-15 11:39:02 +00:00
SquidDev
30f4e0829f Add websocket support to HTTP API
This uses Netty's websocket functionality, meaning we do not have to
depend on another library.

As websockets do not fit neatly into the standard polling socket model,
the API is significantly more event based than CCTweaks's. One uses
http.websocket to connect, which will wait until a connection is
established and then returns the connection object (an async variant is
available).

Once you have a websocket object, you can use .send(msg) to transmit a
message. Incoming messages will fire a "websocket_message" event, with
the URL and content as arguments. A convenience method (.receive())
exists to aid waiting for valid messages.
2017-11-15 11:32:17 +00:00
SquidDev
2155fce036 Merge pull request #486 from Wilma456/ComputerCraft-1/extensionfix
Fix Bug in Paint and Edit
2017-11-14 23:55:14 +00:00
SquidDev
27602ec8fc Merge pull request #485 from Luca0208/ComputerCraft/patch-1
Removed the "the" that was too much(In /rom/help/cd.txt)
2017-11-14 23:54:54 +00:00
SquidDev
66f683d9c9 Merge pull request #475 from Wilma456/ComputerCraft-1/ioline
Fix io.lines()
2017-11-14 23:53:50 +00:00
SquidDev
ac08a52323 Merge pull request #480 from Wilma456/ComputerCraft-1/monitorscale
Add getTextScale() to Monitor
2017-11-14 23:53:04 +00:00
SquidDev
fe0f998c27 Merge pull request #448 from Wilma456/ComputerCraft-1/writecheck
Fix check of write()
2017-11-14 23:50:12 +00:00
Wilma456
bcf79165f9 Merge pull request #455 from Wilma456/ComputerCraft-1/fileread
Add read() to Filehandle
2017-11-14 23:48:38 +00:00
Steven Dirth
9b2a50cdfc Merge pull request #362 from KingofGamesYami/ComputerCraft/featurecommand-event
Command Event
2017-11-14 23:27:11 +00:00
Wilma456
c5d99db654 Merge pull request #411 from Wilma456/ComputerCraft-1/copyfixup
Fix Bug in copy.lua, mkdir.lua and rename.lua (updated)
2017-11-14 23:24:11 +00:00
SquidDev
5253ab3e58 Merge pull request #463 from josephcsible/ComputerCraft/notnull
Remove some unnecessary null checks
2017-11-14 22:58:36 +00:00
SquidDev
11d8253d9c Merge pull request #464 from josephcsible/ComputerCraft/unnecessary
Remove unnecessary code
2017-11-14 22:57:36 +00:00
SquidDev
bc2b481918 Merge pull request #289 from Wojbie/ComputerCraft/Speaker-pocket-computer-light
Add pocket computer light support to Speaker.
2017-11-14 22:46:01 +00:00
SquidDev
b26564ccb9 Update README to explain what this project is
Also add a .editorconfig file to ensure some level of consistent
formatting.
2017-11-14 22:42:07 +00:00
SquidDev
28e3ffe978 Update Gradle and build system
- Bundle Javadoc and APIs using gradle instead of deploy.sh
 - Bump Gradle to 4.3, significantly improving compile times.
2017-11-14 22:42:03 +00:00
SquidDev
540e2e25aa Merge pull request #163 from SquidDev-CC/ComputerCraft/feature/cobalt
Replace LuaJ with Cobalt
2017-11-14 21:48:47 +00:00
SquidDev
845118e9e2 Merge pull request #218 from SquidDev-CC/ComputerCraft/feature/new-computer-thread
Rewrite the computer thread system
2017-11-14 21:33:20 +00:00
SquidDev
b2b8753ee7 Merge pull request #227 from SquidDev-CC/ComputerCraft/feature/improved-cable
Improving cable/wired modem interactions
2017-11-14 21:32:53 +00:00
SquidDev
060fb21bdb Merge pull request #298 from SquidDev-CC/ComputerCraft/feature/luaj-bit32
Replace BitAPI with a LuaJ implementation of bit32
2017-11-14 21:32:19 +00:00
SquidDev
ef008709c7 Merge pull request #402 from SquidDev-CC/ComputerCraft/feature/shell-resolution
Tweak shell program resolution slightly
2017-11-14 21:31:10 +00:00
SquidDev
09da119f27 Merge pull request #451 from SquidDev-CC/ComputerCraft/hotfix/disk-drive-stop
Use custom packet to play records, instead of using block events
2017-11-14 21:30:52 +00:00
SquidDev
f8487d1e1c Merge pull request #453 from SquidDev-CC/ComputerCraft/hotfix/eager-remove-te
Remove tile before calling destroy
2017-11-14 21:15:47 +00:00
SquidDev
b6f773ffce Merge pull request #457 from SquidDev-CC/ComputerCraft/hotfix/computer-reload
Turn on ServerComputer instances if they have timed out
2017-11-14 21:14:49 +00:00
SquidDev
c8673473ef Merge pull request #476 from SquidDev-CC/ComputerCraft/hotfix/printer-clear
Fix the printer overwriting the current page
2017-11-14 21:14:10 +00:00
SquidDev
3829815756 Merge pull request #479 from SquidDev-CC/ComputerCraft/feature/network-optimisations
Only send terminal state to interacting players
2017-11-14 21:13:17 +00:00
SquidDev
7eac8faf0d Merge pull request #482 from SquidDev-CC/ComputerCraft/feature/jei-integration
Add simple JEI integration
2017-11-14 21:06:02 +00:00
SquidDev
0bd0f4d313 Prefix all loaded strings with "="
Whilst this is not consistent with normal Lua, this is required in order
to remain compatible with LuaJ.
2017-11-14 18:41:01 +00:00
SquidDev
73873eb8cb Change timeout system to occur on instructions instead of API calls
This means loops which do not touch CC specific methods will still
produce an error, rather than terminating the computer.
2017-11-14 18:41:00 +00:00
SquidDev
0420b6c831 Remove string metatable protection
The string metatable and environment are no longer shared, so this
sandboxing is no longer required.
2017-11-14 18:41:00 +00:00
SquidDev
bb741975b7 Migrate LuaJLuaMachine to use Cobalt 2017-11-14 18:41:00 +00:00
SquidDev
8bffec6964 Add dependency on Cobalt 2017-11-14 18:40:57 +00:00
Wilma456
9e19dd7070 Fix Bug in Paint and Edit 2017-11-02 20:14:34 +01:00
Luca S
aba0e3d2d4 Removed the "the" that was too much 2017-10-29 19:06:34 +01:00
SquidDev
d9d025e33b Add simple JEI integration
- Ensure pocket computers and turtles are distinguished by upgrades and
   computer family.
 - Ensure disks are distinguished by colour.
 - Hide treasure disks from the list
2017-10-25 13:40:35 +01:00
Wilma456 (Jakob0815)
1fe29ab098 Add getTextScale() to Monitor 2017-10-13 12:37:55 +02:00
SquidDev
53f16782ab Only send terminal state to interacting players
This splits the computer state (blinking, label, etc...) and terminal
state into two separate packets. When a computer changes, the computer
state is sent to all players and the terminal state is sent to players
who are curerntly using the computer.

This reduces network usage by a substantial amount.
2017-10-12 10:45:38 +01:00
SquidDev
bfb4f88304 Fix the printer clearing the previous page
When printing on top of an already printed page, the previous contents
should be preserved. However, this did not occur as the stack had been
shrunk and so the item was no longer considered a printout.

Closes SquidDev-CC/ComputerCraft#2
2017-10-06 12:04:49 +01:00
SquidDev
fb6d65ec23 Invalidate the network when the peripheral is removed
Fixes #83
2017-10-04 21:49:41 +01:00
SquidDev
6b364052c7 Only render breaking animation on the part being hit 2017-10-04 21:49:40 +01:00
SquidDev
7169abcd7b Ensure the modem's peripheral is incorrectly invalidated when changed 2017-10-04 21:49:40 +01:00
SquidDev
75ccfbdb3d Migrate cable core block state to an enum
This allows us to render the cable "core", as was done pre-1.8.
2017-10-04 21:49:40 +01:00
SquidDev
728644c104 Initial attempt at improving cable/wired modem interactions
- Cable and modem can be broken individually
 - Ray tracing will go through "holes" in the cable.
 - Pick block will determine which part you are looking at.
 - Selection box will only highlight the region you are looking at:
   modem or cable.
2017-10-04 21:49:37 +01:00
Wilma456 (Jakob0815)
999351e667 Fix io.lines() 2017-10-04 18:51:48 +02:00
Wilma456
11e879db41 Removce Bow Upgrade Recipe 2017-09-29 18:56:52 +02:00
Wilma456
a4a774fcdf Add more Recipes to Recipebook 2017-09-27 20:08:48 +02:00
Joseph C. Sible
80ec54eaf6 Remove unnecessary code
- Remove unnecessary casts
- Use the diamond operator where possible
- Remove "throws" declarations that aren't actually thrown
- Remove unused local variables
- Remove unused imports
- Remove redundant superinterfaces
2017-09-24 01:23:29 -04:00
Joseph C. Sible
9e4ae3a494 Remove some unnecessary null checks
We know turtle can't be null in any of these places, since in preceding code,
we called methods on it, so we would have gotten a NullPointerException then
and never gotten here if it were null.
2017-09-24 01:00:55 -04:00
Daniel Ratcliffe
19e4c03d3a Merge pull request #456 from Wilma456/ioerror
Set errorlevel for "Unsupported format" to 2
2017-09-22 13:20:34 +01:00
Daniel Ratcliffe
c6b8cb1fab Merge pull request #458 from SquidDev-CC/hotfix/block-shapes
Fix BlockFaceShape not being overridden for turtles and peripherals
2017-09-22 13:20:08 +01:00
SquidDev
01f5d006fc Fix BlockFaceShape not being overridden for turtles and peripherals
This meant one could perform various illogical actions to
non-full-blocks, such as connecting fences and placing paitings.

We also modify the behaviour of isOpaqueCube and isFullCube for
peripherals, only returning false for the case of modems and cables.
2017-09-18 08:33:40 +01:00
SquidDev
cd6b076efe Turn on ServerComputer instances if they have timed out 2017-09-16 20:09:51 +01:00
Wilma456 (Jakob0815)
f8193a4d23 Set errorlevel for "Unsupported format" to 2 2017-09-16 16:11:36 +02:00
SquidDev
fbbfe33e21 Do not instantiate ServerComputer instances in the peripheral provider
Instead we create a ComputerProxy, which delegates methods to the
ServerComputer or TileComputerBase, depending on which one exists.
2017-09-15 18:58:13 +01:00
SquidDev
7a916ed8c2 Do not instantiate a ServerComputer for pocket computers's mount 2017-09-15 18:48:57 +01:00
SquidDev
60305cd106 Remove tile before calling destroy
This ensures that the tile will updating neighbouring blocks, and so
the destroyed tile will not be wrapped as a peripheral.
2017-09-15 17:40:53 +01:00
Daniel Ratcliffe
1c8480a329 Merge pull request #441 from SquidDev-CC/hotfix/paintutils-read
Fix a non-existent method being used in paintutils
2017-09-13 17:10:07 +01:00
Daniel Ratcliffe
5219648128 Merge pull request #445 from SquidDev-CC/hotfix/container-backgrounds
Fix background and tooltips not rendering within containers
2017-09-13 00:43:10 +01:00
Wilma456 (Jakob0815)
282aa804f8 Changes sugested by dan200 2017-09-12 19:42:08 +02:00
Daniel Ratcliffe
e959051239 Merge pull request #447 from BombBloke/patch-1
Correct minor typo in rednet.receive
2017-09-12 18:14:34 +01:00
Wilma456 (Jakob0815)
373b7ba293 Fix check of write()
if you call write(nil), you will get the error "bios.lua:229: bad argument: string expected, got nil", so nil is not a valid argument for write() and should be removed.
2017-09-12 17:17:58 +02:00
Bomb Bloke
70c6f3498b Correct minor typo in rednet.receive
Caused attempts to set a time-out value to throw "expected number, got number".
2017-09-13 01:06:59 +10:00
SquidDev
afec3743f3 Use custom packet to play records, instead of using block events
Breaking a disk drive was not stopping the record being played as the
block event never reached the client. Instead, we send a custom packet
which starts/stops music at a given location.

We also remove all the plumbing for eventReceived/sendBlockEvent from
the generic block/tile classes, as they are no longer used.

Closes #443
2017-09-12 15:46:46 +01:00
SquidDev
baa8993999 Fix background and tooltips not rendering within containers
The methods to draw these now have to be explicitly called, hence not
showing up.
2017-09-12 15:05:32 +01:00
Wilma456 (Jakob0815)
92f5860de6 Use select() 2017-09-12 15:27:09 +02:00
SquidDev
12abd4292e Fixes a non-existent method being used in paintutils 2017-09-12 09:52:21 +01:00
SquidDev
4bd5b0d236 Remove HTTPTask, queueing the event when it has finished executing
This means we don't have to have lots of shared state between the run
and whenFinished method, and allows for easier chaining of futures later
on.
2017-09-11 22:13:00 +01:00
Wilma456 (Jakob0815)
0115bc8dca Make io.write() accept multiple args
This is just to bring the io API from CC close the the io API from normal lua, which accept multiple args for io.write().
2017-09-11 15:47:30 +02:00
SquidDev
5f323a85a7 Rethrow/retrigger interrupted status where appropriate 2017-09-10 22:08:08 +01:00
SquidDev
85c556d324 Rewrite the computer thread system
This makes a couple of significant changes to the original system, to
reduce the number of threads created and allow for multiple threads in
the future. There are several notable changes from the original
implementation:

 - A blocking queue is used for the main task queue queue. This removes
   the need for the "monitor" variable and allows for multiple threads
   polling this queue in the future.
 - The thread used to execute tasks is "cached" between tasks,
   significantly reducing the number of threads which need to be
   created. If a task needs to be stopped then the thread is then
   terminated and a new one constructed, though this rarely happens.
2017-09-10 22:08:08 +01:00
Wilma456
0c1114edbc Update to new Style 2017-08-30 17:29:06 +02:00
Wilma456 (Jakob0815)
2c264728d9 Add Check to textutils.tabulate/pagedTabulate 2017-08-10 13:40:35 +02:00
SquidDev
5df97e5133 Tweak shell program resolution slightly
- Path containing '/' or '\' are resolved relative to the current
   directory, rather than using the path. Paths starting with '/' still
   resolve relative to the root directory.
 - Shell completion will also include sub-directories of the current
   directory.

Closes #219
2017-08-03 07:24:21 +01:00
Wojbie
acb5f65e16 Functional change Lignum suggested 2017-07-29 01:14:36 +02:00
Wilma456
c9e7b45509 Show fs error in paint and edit 2017-07-28 15:36:33 +02:00
SquidDev
6fca136327 Move Bit32 library to LuaJ sources 2017-06-28 23:05:48 +01:00
SquidDev
084bbe8480 Replace BitAPI with a LuaJ implementation of bit32 2017-06-17 21:39:12 +01:00
Wojbie
225ec594e7 Adds pocket computer light support to Speaker
This makes use of new pocket computer light access peripherals have and adds said functionality to speaker. If noisy pocket has made sound the pocket computer light will turn dark blue for a second.
2017-06-12 20:54:38 +02:00
150 changed files with 4129 additions and 1631 deletions

15
.editorconfig Normal file
View File

@@ -0,0 +1,15 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.properties]
insert_final_newline = false

View File

@@ -1,26 +1,31 @@
ComputerCraft
=============
[![Build Status](https://travis-ci.org/dan200/ComputerCraft.svg?branch=master)](https://travis-ci.org/dan200/ComputerCraft)
# CC: Tweaked
[![Build Status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked)
ComputerCraft is a Minecraft modification which adds programmable Robots and Computers to the world of Minecraft.
If you're not familiar with ComputerCraft, visit the [Website](http://www.computercraft.info/download) or the [Wiki](http://www.computercraft.info/wiki) to find out more.
CC: Tweaked is a fork of ComputerCraft which aims to provide more earlier access to the more experimental and
in-development features of the mod. For a more stable experience, I recommend checking out the
[original mod](https://github.com/dan200/ComputerCraft).
About this Repository
=====================
## What?
CC: Tweaked does not aim to create a competing fork of ComputerCraft, nor am I planning to take it in in a vastly
different direction to the original mod. In fact, CC: Tweaked aims to be a nurturing ground for various features, with
a pull request against the original mod being the end goal.
ComputerCraft was originally released in late 2011 by [Daniel Ratcliffe](https://twitter.com/DanTwoHundred). In early 2017, after working on the mod solo for five years, it was decided to release the source code publicly to allow Dan to devote time to other projects. This repository marks the first public release of this source code.
CC: Tweaked also includes many pull requests from the community which have not yet been merged, offering a large number
of additional bug fixes and features over the original mod.
The code in this repository will always represent the "bleeding edge" of the ComputerCraft codebase, but stable builds back to 1.79 will be marked on the [Releases](https://github.com/dan200/ComputerCraft/releases) page.
## Relation to CCTweaks?
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. However, I do plan
to migrate some features of CCTweaks into CC: Tweaked.
Contributing
============
## Contributing
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you do wish to contribute
code, do consider submitting it to the ComputerCraft repository instead.
While ComputerCraft will no longer be actively developed by Daniel Ratcliffe, you may still contribute pull requests which will be reviewed and incorporated into releases periodically. A pull request is more likely to be accepted if it meets the following criteria:
That being said, in order to start helping develop CC: Tweaked, you'll need to follow these steps:
* It does not add any new dependencies for compiling, running or using the mod.
* It does not break compatibility with world saves or programs created with previous versions of the mod.
* It does not add unneccessary complexity for users of the mod, and maintains the accessibility for which the mod is known.
* It does not add unneccessary complexity or stylistic changes to the code, especially where functionality is not being changed.
* It does not create bugs!
The pull requests most likely to be accepted are those which fix bugs, simplify code, or make the mod compatible with newer versions of Minecraft.
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
- **Setup Forge:** `./gradlew setupDecompWorkspace`
- **Test your changes:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
If you want to run CC: Tweaked in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from
`build/libs`.

View File

@@ -23,9 +23,9 @@ plugins {
}
*/
version = "1.80pr1"
group = "dan200.computercraft"
archivesBaseName = "ComputerCraft"
version = "1.80pr1.1"
group = "org.squiddev"
archivesBaseName = "cc-tweaked"
minecraft {
version = "1.12-14.21.1.2387"
@@ -41,34 +41,46 @@ minecraft {
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
}
repositories {
maven {
name = "JEI"
url = "http://dvs1.progwml6.com/files/maven"
}
maven {
name = "squiddev"
url = "https://dl.bintray.com/squiddev/maven"
}
}
configurations {
shade
compile.extendsFrom shade
}
dependencies {
// you may put jars on which you depend on in ./libs
// or you may define them like so..
//compile "some.group:artifact:version:classifier"
//compile "some.group:artifact:version"
// real examples
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
//compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
// the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
//provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
// except that these dependencies get remapped to your current MCP mappings
//deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
//deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// for more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api"
runtime "mezz.jei:jei_1.12:4.7.5.86"
shade 'org.squiddev:Cobalt:0.3.0'
}
javadoc {
include "dan200/computercraft/api/**/*.java"
}
jar {
dependsOn javadoc
manifest {
attributes('FMLAT': 'computercraft_at.cfg')
}
into("docs", { from (javadoc.destinationDir) })
into("api", { from (sourceSets.main.allSource) {
include "dan200/computercraft/api/**/*.java"
}})
from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
}
import org.ajoberstar.grgit.Grgit
@@ -109,3 +121,5 @@ gradle.projectsEvaluated {
}
}
runClient.outputs.upToDateWhen { false }
runServer.outputs.upToDateWhen { false }

View File

@@ -18,29 +18,4 @@ 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 "Creating API..."
mkdir -p deploy/api/src/dan200/computercraft
cp -r build/sources/main/java/dan200/computercraft/api deploy/api/src/dan200/computercraft/api
echo "Creating API Javadocs..."
mkdir -p deploy/api/doc
cd src/main/java/dan200/computercraft/api
find . -type f -name "*.java" | xargs javadoc -d ../../../../../../deploy/api/doc -windowtitle "$FRIENDLYNAME"
cd ../../../../../..
echo "Adding API and Javadocs to deployment..."
cd deploy
zip -r $OUTPUTJAR api/doc > /dev/null
zip -r $OUTPUTJAR api/src/dan200/computercraft > /dev/null
cd ..
rm -rf deploy/api
echo "Adding LuaJ to deployment..."
mkdir deploy/luaj
cd deploy/luaj
jar xf ../../libs/luaj-jse-2.0.3.jar
zip -r ../$OUTPUTJAR org > /dev/null
cd ../..
rm -rf deploy/luaj
echo "Done."

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Mon Sep 14 12:28:28 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip

110
gradlew vendored
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@@ -6,47 +6,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="-Xmx2048m"
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

14
gradlew.bat vendored
View File

@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-d64 -Xmx2048m"
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

Binary file not shown.

View File

@@ -0,0 +1,181 @@
/*******************************************************************************
* Copyright (c) 2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of LibFunction that implements the Lua standard {@code bit32} library.
*/
public class Bit32Lib extends ZeroArgFunction
{
public LuaValue call( )
{
LuaTable t = new LuaTable();
bind( t, Bit32LibV.class, new String[] {
"band", "bnot", "bor", "btest", "bxor", "extract", "replace"
} );
bind( t, Bit32Lib2.class, new String[] {
"arshift", "lrotate", "lshift", "rrotate", "rshift"
} );
env.set( "bit32", t );
return t;
}
public static final class Bit32LibV extends VarArgFunction
{
public Varargs invoke( Varargs args )
{
switch( opcode )
{
case 0: // band
{
int result = -1;
for( int i = 1; i <= args.narg(); i++ )
{
result &= args.checkint( i );
}
return bitsToValue( result );
}
case 1: // bnot
return bitsToValue( ~args.checkint( 1 ) );
case 2: // bot
{
int result = 0;
for( int i = 1; i <= args.narg(); i++ )
{
result |= args.checkint( i );
}
return bitsToValue( result );
}
case 3: // btest
{
int bits = -1;
for( int i = 1; i <= args.narg(); i++ )
{
bits &= args.checkint( i );
}
return valueOf( bits != 0 );
}
case 4: // bxor
{
int result = 0;
for( int i = 1; i <= args.narg(); i++ )
{
result ^= args.checkint( i );
}
return bitsToValue( result );
}
case 5: // extract
{
int field = args.checkint( 2 );
int width = args.optint( 3, 1 );
if( field < 0 ) argerror( 2, "field cannot be negative" );
if( width <= 0 ) argerror( 3, "width must be postive" );
if( field + width > 32 ) error( "trying to access non-existent bits" );
return bitsToValue( (args.checkint( 1 ) >>> field) & (-1 >>> (32 - width)) );
}
case 6: // replace
{
int n = args.checkint( 1 );
int v = args.checkint( 2 );
int field = args.checkint( 3 );
int width = args.optint( 4, 1 );
if( field < 0 ) argerror( 3, "field cannot be negative" );
if( width <= 0 ) argerror( 4, "width must be postive" );
if( field + width > 32 ) error( "trying to access non-existent bits" );
int mask = (-1 >>> (32 - width)) << field;
n = (n & ~mask) | ((v << field) & mask);
return bitsToValue( n );
}
}
return NIL;
}
}
public static final class Bit32Lib2 extends TwoArgFunction
{
public LuaValue call( LuaValue arg1, LuaValue arg2 )
{
switch( opcode )
{
case 0: // arshift
{
int x = arg1.checkint();
int disp = arg2.checkint();
return disp >= 0 ? bitsToValue( x >> disp ) : bitsToValue( x << -disp );
}
case 1: // lrotate
return rotate( arg1.checkint(), arg2.checkint() );
case 2: // lshift
return shift( arg1.checkint(), arg2.checkint() );
case 3: // rrotate
return rotate( arg1.checkint(), -arg2.checkint() );
case 4: // rshift
return shift( arg1.checkint(), -arg2.checkint() );
}
return NIL;
}
}
static LuaValue rotate( int x, int disp )
{
if( disp < 0 )
{
disp = -disp & 31;
return bitsToValue( (x >>> disp) | (x << (32 - disp)) );
}
else
{
disp = disp & 31;
return bitsToValue( (x << disp) | (x >>> (32 - disp)) );
}
}
static LuaValue shift( int x, int disp )
{
if( disp >= 32 || disp <= -32 )
{
return ZERO;
}
else if( disp >= 0 )
{
return bitsToValue( x << disp );
}
else
{
return bitsToValue( x >>> -disp );
}
}
private static LuaValue bitsToValue( int x )
{
return x < 0 ? LuaValue.valueOf( (long) x & 0xFFFFFFFFL ) : LuaInteger.valueOf( x );
}
}

View File

@@ -22,6 +22,7 @@ import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.filesystem.ComboMount;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.JarMount;
import dan200.computercraft.shared.command.CommandComputer;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
@@ -93,7 +94,7 @@ import java.util.zip.ZipFile;
///////////////
@Mod(
modid = ComputerCraft.MOD_ID, name = "ComputerCraft", version = "${version}",
modid = ComputerCraft.MOD_ID, name = "CC: Tweaked", version = "${version}",
guiFactory = "dan200.computercraft.client.gui.GuiConfigCC$Factory"
)
public class ComputerCraft
@@ -119,12 +120,15 @@ public class ComputerCraft
"192.168.0.0/16",
"fd00::/8",
};
public static boolean http_enable = true;
public static boolean http_websocket_enable = true;
public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
public static AddressPredicate http_blacklist = new AddressPredicate( DEFAULT_HTTP_BLACKLIST );
public static boolean disable_lua51_features = false;
public static String default_computer_settings = "";
public static boolean debug_enable = false;
public static int computer_threads = 1;
public static boolean logPeripheralErrors = false;
public static boolean enableCommandBlock = false;
@@ -200,10 +204,13 @@ public class ComputerCraft
public static Configuration config;
public static Property http_enable;
public static Property http_websocket_enable;
public static Property http_whitelist;
public static Property http_blacklist;
public static Property disable_lua51_features;
public static Property default_computer_settings;
public static Property debug_enable;
public static Property computer_threads;
public static Property logPeripheralErrors;
public static Property enableCommandBlock;
@@ -271,6 +278,9 @@ public class ComputerCraft
Config.http_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "http_enable", http_enable );
Config.http_enable.setComment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for more fine grained control than this)" );
Config.http_websocket_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "http_websocket_enable", http_websocket_enable );
Config.http_websocket_enable.setComment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." );
{
ConfigCategory category = Config.config.getCategory( Configuration.CATEGORY_GENERAL );
Property currentProperty = category.get( "http_whitelist" );
@@ -298,10 +308,20 @@ public class ComputerCraft
Config.default_computer_settings = Config.config.get( Configuration.CATEGORY_GENERAL, "default_computer_settings", default_computer_settings );
Config.default_computer_settings.setComment( "A comma seperated list of default system settings to set on new computers. Example: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\" will disable all autocompletion" );
Config.debug_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "debug_enable", debug_enable );
Config.debug_enable.setComment( "Enable Lua's debug library. Whilst this should be safe for general use, it may allow players to interact with other computers. Enable at your own risk." );
Config.computer_threads = Config.config.get( Configuration.CATEGORY_GENERAL, "computer_threads", computer_threads );
Config.computer_threads
.setMinValue( 1 )
.setRequiresWorldRestart( true )
.setComment( "Set the number of threads computers can run on. A higher number means more computers can run at once, but may induce lag.\n" +
"Please note that some mods may not work with a thread count higher than 1. Use with caution." );
Config.logPeripheralErrors = Config.config.get( Configuration.CATEGORY_GENERAL, "logPeripheralErrors", logPeripheralErrors );
Config.logPeripheralErrors.setComment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." );
Config.enableCommandBlock = Config.config.get( Configuration.CATEGORY_GENERAL, "enableCommandBlock", enableCommandBlock );
Config.enableCommandBlock.setComment( "Enable Command Block peripheral support" );
@@ -362,10 +382,13 @@ public class ComputerCraft
public static void syncConfig() {
http_enable = Config.http_enable.getBoolean();
http_websocket_enable = Config.http_websocket_enable.getBoolean();
http_whitelist = new AddressPredicate( Config.http_whitelist.getStringList() );
http_blacklist = new AddressPredicate( Config.http_blacklist.getStringList() );
disable_lua51_features = Config.disable_lua51_features.getBoolean();
default_computer_settings = Config.default_computer_settings.getString();
debug_enable = Config.debug_enable.getBoolean();
computer_threads = Config.computer_threads.getInt();
logPeripheralErrors = Config.logPeripheralErrors.getBoolean();
enableCommandBlock = Config.enableCommandBlock.getBoolean();
@@ -400,6 +423,7 @@ public class ComputerCraft
@Mod.EventHandler
public void onServerStarting( FMLServerStartingEvent event )
{
event.registerServerCommand( new CommandComputer() );
}
@Mod.EventHandler
@@ -533,6 +557,11 @@ public class ComputerCraft
networkEventChannel.sendToServer( encode( packet ) );
}
public static void sendToAllAround( ComputerCraftPacket packet, NetworkRegistry.TargetPoint point )
{
networkEventChannel.sendToAllAround( encode( packet ), point );
}
public static void handlePacket( ComputerCraftPacket packet, EntityPlayer player )
{
proxy.handlePacket( packet, player );

View File

@@ -141,10 +141,10 @@ public class FixedWidthFontRenderer
}
// Draw char
int index = (int)s.charAt( i );
int index = s.charAt( i );
if( index < 0 || index > 255 )
{
index = (int)'?';
index = '?';
}
drawChar( renderer, x + i * FONT_WIDTH, y, index, colour, p, greyScale );
}

View File

@@ -18,7 +18,7 @@ public class GuiConfigCC extends GuiConfig
{
public GuiConfigCC( GuiScreen parentScreen )
{
super( parentScreen, getConfigElements(), ComputerCraft.MOD_ID, false, false, "ComputerCraft" );
super( parentScreen, getConfigElements(), ComputerCraft.MOD_ID, false, false, "CC: Tweaked" );
}
private static List<IConfigElement> getConfigElements()

View File

@@ -43,4 +43,12 @@ public class GuiDiskDrive extends GuiContainer
int i1 = (height - ySize) / 2;
drawTexturedModalRect(l, i1, 0, 0, xSize, ySize);
}
@Override
public void drawScreen( int mouseX, int mouseY, float partialTicks)
{
drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
renderHoveredToolTip(mouseX, mouseY);
}
}

View File

@@ -51,4 +51,12 @@ public class GuiPrinter extends GuiContainer
drawTexturedModalRect(startX + 34, startY + 21, 176, 0, 25, 45);
}
}
@Override
public void drawScreen( int mouseX, int mouseY, float partialTicks)
{
drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
renderHoveredToolTip(mouseX, mouseY);
}
}

View File

@@ -155,4 +155,12 @@ public class GuiTurtle extends GuiContainer
drawSelectionSlot( advanced );
}
@Override
public void drawScreen( int mouseX, int mouseY, float partialTicks)
{
drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
renderHoveredToolTip(mouseX, mouseY);
}
}

View File

@@ -175,10 +175,10 @@ public abstract class Widget extends Gui
{
Tessellator tessellator = Tessellator.getInstance();
tessellator.getBuffer().begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
tessellator.getBuffer().pos( (double) ( x + 0 ), (double) ( y + h ), (double) this.zLevel ).tex( 0.0, 1.0 ).endVertex();
tessellator.getBuffer().pos( (double) ( x + w ), (double) ( y + h ), (double) this.zLevel ).tex( 1.0, 1.0 ).endVertex();
tessellator.getBuffer().pos( (double) ( x + w ), (double) ( y + 0 ), (double) this.zLevel ).tex( 1.0, 0.0 ).endVertex();
tessellator.getBuffer().pos( (double) ( x + 0 ), (double) ( y + 0 ), (double) this.zLevel ).tex( 0.0, 0.0 ).endVertex();
tessellator.getBuffer().pos( x + 0, y + h, this.zLevel ).tex( 0.0, 1.0 ).endVertex();
tessellator.getBuffer().pos( x + w, y + h, this.zLevel ).tex( 1.0, 1.0 ).endVertex();
tessellator.getBuffer().pos( x + w, y + 0, this.zLevel ).tex( 1.0, 0.0 ).endVertex();
tessellator.getBuffer().pos( x + 0, y + 0, this.zLevel ).tex( 0.0, 0.0 ).endVertex();
tessellator.draw();
}

View File

@@ -8,6 +8,8 @@ package dan200.computercraft.client.proxy;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.RenderOverlayCable;
import dan200.computercraft.client.render.TileEntityCableRenderer;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.shared.computer.blocks.ComputerState;
import dan200.computercraft.shared.computer.blocks.TileComputer;
@@ -19,6 +21,7 @@ import dan200.computercraft.shared.media.items.ItemDiskLegacy;
import dan200.computercraft.shared.media.items.ItemPrintout;
import dan200.computercraft.shared.network.ComputerCraftPacket;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.modem.TileCable;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
@@ -51,7 +54,6 @@ import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -222,6 +224,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
// Setup renderers
ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() );
ClientRegistry.bindTileEntitySpecialRenderer( TileCable.class, new TileEntityCableRenderer() );
}
private void registerItemModel( Block block, int damage, String name )
@@ -314,17 +317,6 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
}
}
@Override
public void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos )
{
Minecraft mc = FMLClientHandler.instance().getClient();
world.playRecord( pos, record );
if( record != null )
{
mc.ingameGUI.setRecordPlayingMessage( recordInfo );
}
}
@Override
public Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive )
{
@@ -383,7 +375,9 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
switch( packet.m_packetType )
{
case ComputerCraftPacket.ComputerChanged:
case ComputerCraftPacket.ComputerTerminalChanged:
case ComputerCraftPacket.ComputerDeleted:
case ComputerCraftPacket.PlayRecord:
{
// Packet from Server to Client
IThreadListener listener = Minecraft.getMinecraft();
@@ -417,6 +411,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
// Packets from Server to Client //
///////////////////////////////////
case ComputerCraftPacket.ComputerChanged:
case ComputerCraftPacket.ComputerTerminalChanged:
{
int instanceID = packet.m_dataInt[ 0 ];
if( !ComputerCraft.clientComputerRegistry.contains( instanceID ) )
@@ -435,6 +430,22 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
}
break;
}
case ComputerCraftPacket.PlayRecord:
{
BlockPos pos = new BlockPos( packet.m_dataInt[ 0 ], packet.m_dataInt[ 1 ], packet.m_dataInt[ 2 ] );
Minecraft mc = Minecraft.getMinecraft();
if( packet.m_dataInt.length > 3 )
{
SoundEvent sound = SoundEvent.REGISTRY.getObjectById( packet.m_dataInt[ 3 ] );
mc.world.playRecord( pos, sound );
mc.ingameGUI.setRecordPlayingMessage( packet.m_dataString[ 0 ] );
}
else
{
mc.world.playRecord( pos, null );
}
}
}
}
@@ -442,6 +453,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
{
ForgeHandlers handlers = new ForgeHandlers();
MinecraftForge.EVENT_BUS.register( handlers );
MinecraftForge.EVENT_BUS.register( new RenderOverlayCable() );
}
public class ForgeHandlers

View File

@@ -0,0 +1,215 @@
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.common.BlockCable;
import dan200.computercraft.shared.peripheral.modem.TileCable;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.opengl.GL11;
public class RenderOverlayCable
{
private static final float EXPAND = 0.002f;
private static final double MIN = TileCable.MIN - EXPAND;
private static final double MAX = TileCable.MAX + EXPAND;
@SubscribeEvent
public void drawHighlight( DrawBlockHighlightEvent event )
{
if( event.getTarget().typeOfHit != RayTraceResult.Type.BLOCK ) return;
BlockPos pos = event.getTarget().getBlockPos();
World world = event.getPlayer().getEntityWorld();
IBlockState state = world.getBlockState( pos );
if( state.getBlock() != ComputerCraft.Blocks.cable ) return;
TileEntity tile = world.getTileEntity( pos );
if( tile == null || !(tile instanceof TileCable) ) return;
event.setCanceled( true );
TileCable cable = (TileCable) tile;
PeripheralType type = cable.getPeripheralType();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0 );
GlStateManager.color( 0.0f, 0.0f, 0.0f, 0.4f );
GL11.glLineWidth( 2.0F );
GlStateManager.disableTexture2D();
GlStateManager.depthMask( false );
GlStateManager.pushMatrix();
EnumFacing direction = type != PeripheralType.Cable ? cable.getDirection() : null;
{
EntityPlayer player = event.getPlayer();
double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks();
double y = player.lastTickPosY + (player.posY - player.lastTickPosY) * event.getPartialTicks();
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * event.getPartialTicks();
GlStateManager.translate( -x + pos.getX(), -y + pos.getY(), -z + pos.getZ() );
}
if( type != PeripheralType.Cable && WorldUtil.isVecInsideInclusive( cable.getModemBounds(), event.getTarget().hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
{
RenderGlobal.drawSelectionBoundingBox( cable.getModemBounds(), 0, 0, 0, 0.4f );
}
else
{
int flags = 0;
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
for( EnumFacing facing : EnumFacing.VALUES )
{
if( direction == facing || BlockCable.isCable( world, pos.offset( facing ) ) )
{
flags |= 1 << facing.ordinal();
switch( facing.getAxis() )
{
case X:
{
double offset = facing == EnumFacing.WEST ? -EXPAND : 1 + EXPAND;
double centre = facing == EnumFacing.WEST ? MIN : MAX;
buffer.begin( GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION );
buffer.pos( offset, MIN, MIN ).endVertex();
buffer.pos( offset, MAX, MIN ).endVertex();
buffer.pos( offset, MAX, MAX ).endVertex();
buffer.pos( offset, MIN, MAX ).endVertex();
buffer.pos( offset, MIN, MIN ).endVertex();
tessellator.draw();
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
buffer.pos( offset, MIN, MIN ).endVertex();
buffer.pos( centre, MIN, MIN ).endVertex();
buffer.pos( offset, MAX, MIN ).endVertex();
buffer.pos( centre, MAX, MIN ).endVertex();
buffer.pos( offset, MAX, MAX ).endVertex();
buffer.pos( centre, MAX, MAX ).endVertex();
buffer.pos( offset, MIN, MAX ).endVertex();
buffer.pos( centre, MIN, MAX ).endVertex();
tessellator.draw();
break;
}
case Y:
{
double offset = facing == EnumFacing.DOWN ? -EXPAND : 1 + EXPAND;
double centre = facing == EnumFacing.DOWN ? MIN : MAX;
buffer.begin( GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION );
buffer.pos( MIN, offset, MIN ).endVertex();
buffer.pos( MAX, offset, MIN ).endVertex();
buffer.pos( MAX, offset, MAX ).endVertex();
buffer.pos( MIN, offset, MAX ).endVertex();
buffer.pos( MIN, offset, MIN ).endVertex();
tessellator.draw();
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
buffer.pos( MIN, offset, MIN ).endVertex();
buffer.pos( MIN, centre, MIN ).endVertex();
buffer.pos( MAX, offset, MIN ).endVertex();
buffer.pos( MAX, centre, MIN ).endVertex();
buffer.pos( MAX, offset, MAX ).endVertex();
buffer.pos( MAX, centre, MAX ).endVertex();
buffer.pos( MIN, offset, MAX ).endVertex();
buffer.pos( MIN, centre, MAX ).endVertex();
tessellator.draw();
break;
}
case Z:
{
double offset = facing == EnumFacing.NORTH ? -EXPAND : 1 + EXPAND;
double centre = facing == EnumFacing.NORTH ? MIN : MAX;
buffer.begin( GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION );
buffer.pos( MIN, MIN, offset ).endVertex();
buffer.pos( MAX, MIN, offset ).endVertex();
buffer.pos( MAX, MAX, offset ).endVertex();
buffer.pos( MIN, MAX, offset ).endVertex();
buffer.pos( MIN, MIN, offset ).endVertex();
tessellator.draw();
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
buffer.pos( MIN, MIN, offset ).endVertex();
buffer.pos( MIN, MIN, centre ).endVertex();
buffer.pos( MAX, MIN, offset ).endVertex();
buffer.pos( MAX, MIN, centre ).endVertex();
buffer.pos( MAX, MAX, offset ).endVertex();
buffer.pos( MAX, MAX, centre ).endVertex();
buffer.pos( MIN, MAX, offset ).endVertex();
buffer.pos( MIN, MAX, centre ).endVertex();
tessellator.draw();
break;
}
}
}
}
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION );
draw( buffer, flags, EnumFacing.WEST, EnumFacing.DOWN, EnumFacing.Axis.Z );
draw( buffer, flags, EnumFacing.WEST, EnumFacing.UP, EnumFacing.Axis.Z );
draw( buffer, flags, EnumFacing.EAST, EnumFacing.DOWN, EnumFacing.Axis.Z );
draw( buffer, flags, EnumFacing.EAST, EnumFacing.UP, EnumFacing.Axis.Z );
draw( buffer, flags, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.Axis.Y );
draw( buffer, flags, EnumFacing.WEST, EnumFacing.SOUTH, EnumFacing.Axis.Y );
draw( buffer, flags, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.Axis.Y );
draw( buffer, flags, EnumFacing.EAST, EnumFacing.SOUTH, EnumFacing.Axis.Y );
draw( buffer, flags, EnumFacing.DOWN, EnumFacing.NORTH, EnumFacing.Axis.X );
draw( buffer, flags, EnumFacing.DOWN, EnumFacing.SOUTH, EnumFacing.Axis.X );
draw( buffer, flags, EnumFacing.UP, EnumFacing.NORTH, EnumFacing.Axis.X );
draw( buffer, flags, EnumFacing.UP, EnumFacing.SOUTH, EnumFacing.Axis.X );
tessellator.draw();
}
GlStateManager.popMatrix();
GlStateManager.depthMask( true );
GlStateManager.enableTexture2D();
GlStateManager.disableBlend();
}
private static void draw( BufferBuilder buffer, int flags, EnumFacing a, EnumFacing b, EnumFacing.Axis other )
{
if( ((flags >> a.ordinal()) & 1) != ((flags >> b.ordinal()) & 1) ) return;
double offA = a.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? MIN : MAX;
double offB = b.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? MIN : MAX;
switch( other )
{
case X:
buffer.pos( MIN, offA, offB ).endVertex();
buffer.pos( MAX, offA, offB ).endVertex();
break;
case Y:
buffer.pos( offA, MIN, offB ).endVertex();
buffer.pos( offA, MAX, offB ).endVertex();
break;
case Z:
buffer.pos( offA, offB, MIN ).endVertex();
buffer.pos( offA, offB, MAX ).endVertex();
break;
}
}
}

View File

@@ -0,0 +1,122 @@
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.common.BlockCable;
import dan200.computercraft.shared.peripheral.common.BlockCableModemVariant;
import dan200.computercraft.shared.peripheral.modem.TileCable;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.MinecraftForgeClient;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
/**
* Render breaking animation only over part of a {@link TileCable}.
*/
public class TileEntityCableRenderer extends TileEntitySpecialRenderer<TileCable>
{
@Override
public void render( @Nonnull TileCable te, double x, double y, double z, float partialTicks, int destroyStage, float alpha )
{
if( destroyStage < 0 ) return;
BlockPos pos = te.getPos();
Minecraft mc = Minecraft.getMinecraft();
RayTraceResult hit = mc.objectMouseOver;
if( hit == null || !hit.getBlockPos().equals( pos ) ) return;
if( MinecraftForgeClient.getRenderPass() != 0 ) return;
World world = te.getWorld();
IBlockState state = world.getBlockState( pos );
Block block = state.getBlock();
if( block != ComputerCraft.Blocks.cable ) return;
state = state.getActualState( world, pos );
if( te.getPeripheralType() != PeripheralType.Cable && WorldUtil.isVecInsideInclusive( te.getModemBounds(), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
{
state = block.getDefaultState().withProperty( BlockCable.Properties.MODEM, state.getValue( BlockCable.Properties.MODEM ) );
}
else
{
state = state.withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None );
}
IBakedModel model = mc.getBlockRendererDispatcher().getModelForState( state );
if( model == null ) return;
preRenderDamagedBlocks();
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.BLOCK );
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
buffer.noColor();
ForgeHooksClient.setRenderLayer( block.getBlockLayer() );
// See BlockRendererDispatcher#renderBlockDamage
TextureAtlasSprite breakingTexture = mc.getTextureMapBlocks().getAtlasSprite( "minecraft:blocks/destroy_stage_" + destroyStage );
Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelRenderer().renderModel(
world,
ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos ),
state, pos, buffer, true
);
ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID );
buffer.setTranslation( 0, 0, 0 );
Tessellator.getInstance().draw();
postRenderDamagedBlocks();
}
/**
* @see RenderGlobal#preRenderDamagedBlocks()
*/
private void preRenderDamagedBlocks()
{
GlStateManager.disableLighting();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate( GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.enableBlend();
GlStateManager.color( 1.0F, 1.0F, 1.0F, 0.5F );
GlStateManager.doPolygonOffset( -3.0F, -3.0F );
GlStateManager.enablePolygonOffset();
GlStateManager.alphaFunc( 516, 0.1F );
GlStateManager.enableAlpha();
GlStateManager.pushMatrix();
}
/**
* @see RenderGlobal#postRenderDamagedBlocks()
*/
private void postRenderDamagedBlocks()
{
GlStateManager.disableAlpha();
GlStateManager.doPolygonOffset( 0.0F, 0.0F );
GlStateManager.disablePolygonOffset();
GlStateManager.disablePolygonOffset();
GlStateManager.depthMask( true );
GlStateManager.popMatrix();
}
}

View File

@@ -82,11 +82,11 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
GlStateManager.rotate( pitch, 1.0f, 0.0f, 0.0f );
GlStateManager.translate(
-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
((double)origin.getHeight() - 0.5) - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN),
(origin.getHeight() - 0.5) - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN),
0.5
);
double xSize = (double)origin.getWidth() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
double ySize = (double)origin.getHeight() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
double xSize = origin.getWidth() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
double ySize = origin.getHeight() - 2.0 * ( TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER );
// Get renderers
Minecraft mc = Minecraft.getMinecraft();
@@ -126,8 +126,8 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
GlStateManager.pushMatrix();
try
{
double xScale = xSize / (double) ( width * FixedWidthFontRenderer.FONT_WIDTH );
double yScale = ySize / (double) ( height * FixedWidthFontRenderer.FONT_HEIGHT );
double xScale = xSize / ( width * FixedWidthFontRenderer.FONT_WIDTH );
double yScale = ySize / ( height * FixedWidthFontRenderer.FONT_HEIGHT );
GlStateManager.scale( xScale, -yScale, 1.0 );
// Draw background
@@ -140,7 +140,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
{
double marginXSize = TileMonitor.RENDER_MARGIN / xScale;
double marginYSize = TileMonitor.RENDER_MARGIN / yScale;
double marginSquash = marginYSize / (double) FixedWidthFontRenderer.FONT_HEIGHT;
double marginSquash = marginYSize / FixedWidthFontRenderer.FONT_HEIGHT;
// Top and bottom margins
GlStateManager.pushMatrix();

View File

@@ -116,20 +116,12 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
// Setup the transform
Vec3d offset;
float yaw;
if( turtle != null )
{
offset = turtle.getRenderOffset( f );
yaw = turtle.getRenderYaw( f );
}
else
{
offset = new Vec3d( 0.0, 0.0, 0.0 );
yaw = 0.0f;
}
offset = turtle.getRenderOffset( f );
yaw = turtle.getRenderYaw( f );
GlStateManager.translate( posX + offset.x, posY + offset.y, posZ + offset.z );
// Render the label
IComputer computer = (turtle != null) ? turtle.getComputer() : null;
IComputer computer = turtle.getComputer();
String label = (computer != null) ? computer.getLabel() : null;
if( label != null )
{
@@ -145,18 +137,9 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
int colour;
ComputerFamily family;
ResourceLocation overlay;
if( turtle != null )
{
colour = turtle.getColour();
family = turtle.getFamily();
overlay = turtle.getOverlay();
}
else
{
colour = -1;
family = ComputerFamily.Normal;
overlay = null;
}
colour = turtle.getColour();
family = turtle.getFamily();
overlay = turtle.getOverlay();
renderModel( state, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
@@ -183,11 +166,8 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
}
// Render the upgrades
if( turtle != null )
{
renderUpgrade( state, turtle, TurtleSide.Left, f );
renderUpgrade( state, turtle, TurtleSide.Right, f );
}
renderUpgrade( state, turtle, TurtleSide.Left, f );
renderUpgrade( state, turtle, TurtleSide.Right, f );
}
finally
{
@@ -310,10 +290,10 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder renderer = tessellator.getBuffer();
renderer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
renderer.pos( (double) ( -xOffset - 1 ), (double) ( -1 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( (double) ( -xOffset - 1 ), (double) ( 8 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( (double) ( xOffset + 1 ), (double) ( 8 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( (double) ( xOffset + 1 ), (double) ( -1 + yOffset ), 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( -xOffset - 1, -1 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( -xOffset - 1, 8 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( xOffset + 1, 8 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
renderer.pos( xOffset + 1, -1 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex();
tessellator.draw();
}
finally

View File

@@ -45,8 +45,8 @@ public class AddressPredicate
public AddressPredicate( String... filters )
{
List<Pattern> wildcards = this.wildcards = new ArrayList<Pattern>();
List<HostRange> ranges = this.ranges = new ArrayList<HostRange>();
List<Pattern> wildcards = this.wildcards = new ArrayList<>();
List<HostRange> ranges = this.ranges = new ArrayList<>();
for( String filter : filters )
{

View File

@@ -1,95 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
// Contributed by Nia
// Based on LuaBit (http://luaforge.net/projects/bit)
public class BitAPI implements ILuaAPI
{
private static final int BNOT = 0;
private static final int BAND = 1;
private static final int BOR = 2;
private static final int BXOR = 3;
private static final int BRSHIFT = 4;
private static final int BLSHIFT = 5;
private static final int BLOGIC_RSHIFT = 6;
public BitAPI( IAPIEnvironment _environment )
{
}
@Override
public String[] getNames()
{
return new String[] {
"bit"
};
}
@Override
public void startup( )
{
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{
}
@Nonnull
@Override
public String[] getMethodNames() {
return new String[] {
"bnot", "band", "bor", "bxor",
"brshift", "blshift", "blogic_rshift"
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
int ret = 0;
switch(method) {
case BNOT:
ret = ~getInt( args, 0 );
break;
case BAND:
ret = getInt( args, 0 ) & getInt( args, 1 );
break;
case BOR:
ret = getInt( args, 0 ) | getInt( args, 1 );
break;
case BXOR:
ret = getInt( args, 0 ) ^ getInt( args, 1 );
break;
case BRSHIFT:
ret = getInt( args, 0 ) >> getInt( args, 1 );
break;
case BLSHIFT:
ret = getInt( args, 0 ) << getInt( args, 1 );
break;
case BLOGIC_RSHIFT:
ret = getInt( args, 0 ) >>> getInt( args, 1 );
break;
}
return new Object[]{ ret&0xFFFFFFFFL };
}
}

View File

@@ -6,29 +6,34 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.http.HTTPCheck;
import dan200.computercraft.core.apis.http.HTTPRequest;
import dan200.computercraft.core.apis.http.HTTPTask;
import dan200.computercraft.core.apis.http.*;
import javax.annotation.Nonnull;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Future;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class HTTPAPI implements ILuaAPI
{
private final IAPIEnvironment m_apiEnvironment;
private final List<HTTPTask> m_httpTasks;
private final List<Future<?>> m_httpTasks;
private final Set<Closeable> m_closeables;
public HTTPAPI( IAPIEnvironment environment )
{
m_apiEnvironment = environment;
m_httpTasks = new ArrayList<>();
m_closeables = new HashSet<>();
}
@Override
public String[] getNames()
{
@@ -45,42 +50,53 @@ public class HTTPAPI implements ILuaAPI
@Override
public void advance( double _dt )
{
// Wait for all of our http requests
// Wait for all of our http requests
synchronized( m_httpTasks )
{
Iterator<HTTPTask> it = m_httpTasks.iterator();
Iterator<Future<?>> it = m_httpTasks.iterator();
while( it.hasNext() )
{
final HTTPTask h = it.next();
if( h.isFinished() )
{
h.whenFinished( m_apiEnvironment );
it.remove();
}
final Future<?> h = it.next();
if( h.isDone() ) it.remove();
}
}
}
@Override
public void shutdown( )
{
synchronized( m_httpTasks )
{
for( HTTPTask r : m_httpTasks )
for( Future<?> r : m_httpTasks )
{
r.cancel();
r.cancel( false );
}
m_httpTasks.clear();
}
synchronized( m_closeables )
{
for( Closeable x : m_closeables )
{
try
{
x.close();
}
catch( IOException ignored )
{
}
}
m_closeables.clear();
}
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
return new String[] {
"request",
"checkURL"
"checkURL",
"websocket",
};
}
@@ -113,7 +129,7 @@ public class HTTPAPI implements ILuaAPI
}
}
}
// Get binary
boolean binary = false;
if( args.length >= 4 )
@@ -125,10 +141,10 @@ public class HTTPAPI implements ILuaAPI
try
{
URL url = HTTPRequest.checkURL( urlString );
HTTPRequest request = new HTTPRequest( urlString, url, postString, headers, binary );
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary );
synchronized( m_httpTasks )
{
m_httpTasks.add( HTTPTask.submit( request ) );
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
}
return new Object[] { true };
}
@@ -147,9 +163,47 @@ public class HTTPAPI implements ILuaAPI
try
{
URL url = HTTPRequest.checkURL( urlString );
HTTPCheck check = new HTTPCheck( urlString, url );
synchronized( m_httpTasks ) {
m_httpTasks.add( HTTPTask.submit( check ) );
HTTPCheck check = new HTTPCheck( m_apiEnvironment, urlString, url );
synchronized( m_httpTasks )
{
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( check ) );
}
return new Object[] { true };
}
catch( HTTPRequestException e )
{
return new Object[] { false, e.getMessage() };
}
}
case 2: // websocket
{
String address = getString( args, 0 );
Map<Object, Object> headerTbl = optTable( args, 1, Collections.emptyMap() );
HashMap<String, String> headers = new HashMap<String, String>( headerTbl.size() );
for( Object key : headerTbl.keySet() )
{
Object value = headerTbl.get( key );
if( key instanceof String && value instanceof String )
{
headers.put( (String) key, (String) value );
}
}
if( !ComputerCraft.http_websocket_enable )
{
throw new LuaException( "Websocket connections are disabled" );
}
try
{
URI uri = WebsocketConnector.checkURI( address );
int port = WebsocketConnector.getPort( uri );
Future<?> connector = WebsocketConnector.createConnector( m_apiEnvironment, this, uri, address, port, headers );
synchronized( m_httpTasks )
{
m_httpTasks.add( connector );
}
return new Object[] { true };
}
@@ -164,4 +218,20 @@ public class HTTPAPI implements ILuaAPI
}
}
}
public void addCloseable( Closeable closeable )
{
synchronized( m_closeables )
{
m_closeables.add( closeable );
}
}
public void removeCloseable( Closeable closeable )
{
synchronized( m_closeables )
{
m_closeables.remove( closeable );
}
}
}

View File

@@ -1,245 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import com.google.common.base.Joiner;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HTTPRequest
{
public static URL checkURL( String urlString ) throws HTTPRequestException
{
URL url;
try
{
url = new URL( urlString );
}
catch( MalformedURLException e )
{
throw new HTTPRequestException( "URL malformed" );
}
// Validate the URL
String protocol = url.getProtocol().toLowerCase();
if( !protocol.equals("http") && !protocol.equals("https") )
{
throw new HTTPRequestException( "URL not http" );
}
// Compare the URL to the whitelist
if( !ComputerCraft.http_whitelist.matches( url.getHost() ) || ComputerCraft.http_blacklist.matches( url.getHost() ) )
{
throw new HTTPRequestException( "Domain not permitted" );
}
return url;
}
public HTTPRequest( String url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
{
// Parse the URL
m_urlString = url;
m_url = checkURL( m_urlString );
m_binary = binary;
// Start the thread
m_cancelled = false;
m_complete = false;
m_success = false;
m_result = null;
m_responseCode = -1;
Thread thread = new Thread( () ->
{
try
{
// Connect to the URL
HttpURLConnection connection = (HttpURLConnection)m_url.openConnection();
if( postText != null )
{
connection.setRequestMethod( "POST" );
connection.setDoOutput( true );
}
else
{
connection.setRequestMethod( "GET" );
}
// Set headers
connection.setRequestProperty( "accept-charset", "UTF-8" );
if( postText != null )
{
connection.setRequestProperty( "content-type", "application/x-www-form-urlencoded; charset=utf-8" );
connection.setRequestProperty( "content-encoding", "UTF-8" );
}
if( headers != null )
{
for( Map.Entry<String, String> header : headers.entrySet() )
{
connection.setRequestProperty( header.getKey(), header.getValue() );
}
}
// Send POST text
if( postText != null )
{
OutputStream os = connection.getOutputStream();
OutputStreamWriter osw;
try
{
osw = new OutputStreamWriter( os, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
osw = new OutputStreamWriter( os );
}
BufferedWriter writer = new BufferedWriter( osw );
writer.write( postText, 0, postText.length() );
writer.close();
}
// Read response
InputStream is;
int code = connection.getResponseCode();
boolean responseSuccess;
if (code >= 200 && code < 400) {
is = connection.getInputStream();
responseSuccess = true;
} else {
is = connection.getErrorStream();
responseSuccess = false;
}
byte[] result = ByteStreams.toByteArray( is );
is.close();
synchronized( m_lock )
{
if( m_cancelled )
{
// We cancelled
m_complete = true;
m_success = false;
m_result = null;
}
else
{
// We completed
m_complete = true;
m_success = responseSuccess;
m_result = result;
m_responseCode = connection.getResponseCode();
m_encoding = connection.getContentEncoding();
Joiner joiner = Joiner.on( ',' );
Map<String, String> headers1 = m_responseHeaders = new HashMap<>();
for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
headers1.put(header.getKey(), joiner.join( header.getValue() ));
}
}
}
connection.disconnect(); // disconnect
}
catch( IOException e )
{
synchronized( m_lock )
{
// There was an error
m_complete = true;
m_success = false;
m_result = null;
}
}
} );
thread.setDaemon(true);
thread.start();
}
public String getURL() {
return m_urlString;
}
public void cancel()
{
synchronized(m_lock) {
m_cancelled = true;
}
}
public boolean isComplete()
{
synchronized(m_lock) {
return m_complete;
}
}
public int getResponseCode() {
synchronized(m_lock) {
return m_responseCode;
}
}
public Map<String, String> getResponseHeaders() {
synchronized (m_lock) {
return m_responseHeaders;
}
}
public boolean wasSuccessful()
{
synchronized(m_lock) {
return m_success;
}
}
public boolean isBinary()
{
return m_binary;
}
public InputStream getContents()
{
byte[] result;
synchronized(m_lock) {
result = m_result;
}
if( result != null ) {
return new ByteArrayInputStream( result );
}
return null;
}
public String getEncoding() {
return m_encoding;
}
private final Object m_lock = new Object();
private final URL m_url;
private final String m_urlString;
private boolean m_complete;
private boolean m_cancelled;
private boolean m_success;
private String m_encoding;
private byte[] m_result;
private boolean m_binary;
private int m_responseCode;
private Map<String, String> m_responseHeaders;
}

View File

@@ -52,8 +52,8 @@ public class OSAPI implements ILuaAPI
@Override
public int compareTo( @Nonnull Alarm o )
{
double t = (double)m_day * 24.0 + m_time;
double ot = (double)m_day * 24.0 + m_time;
double t = m_day * 24.0 + m_time;
double ot = m_day * 24.0 + m_time;
if( t < ot ) {
return -1;
} else if( t > ot ) {
@@ -135,13 +135,13 @@ public class OSAPI implements ILuaAPI
if( time > previousTime || day > previousDay )
{
double now = (double)m_day * 24.0 + m_time;
double now = m_day * 24.0 + m_time;
Iterator<Map.Entry<Integer, Alarm>> it = m_alarms.entrySet().iterator();
while( it.hasNext() )
{
Map.Entry<Integer, Alarm> entry = it.next();
Alarm alarm = entry.getValue();
double t = (double)alarm.m_day * 24.0 + alarm.m_time;
double t = alarm.m_day * 24.0 + alarm.m_time;
if( now >= t )
{
queueLuaEvent( "alarm", new Object[]{ entry.getKey() } );
@@ -196,8 +196,8 @@ public class OSAPI implements ILuaAPI
private float getTimeForCalendar(Calendar c)
{
float time = c.get(Calendar.HOUR_OF_DAY);
time += (float)c.get(Calendar.MINUTE) / 60.0f;
time += (float)c.get(Calendar.SECOND) / (60.0f * 60.0f);
time += c.get(Calendar.MINUTE) / 60.0f;
time += c.get(Calendar.SECOND) / (60.0f * 60.0f);
return time;
}
@@ -296,7 +296,7 @@ public class OSAPI implements ILuaAPI
// clock
synchronized( m_timers )
{
return new Object[] { (double)m_clock * 0.05 };
return new Object[] { m_clock * 0.05 };
}
}
case 11:

View File

@@ -34,74 +34,77 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private String[] m_methods;
private Map<String, Integer> m_methodMap;
private boolean m_attached;
private Set<String> m_mounts;
public PeripheralWrapper( IPeripheral peripheral, String side )
{
m_side = side;
m_peripheral = peripheral;
m_attached = false;
m_type = peripheral.getType();
m_methods = peripheral.getMethodNames();
assert( m_type != null );
assert( m_methods != null );
m_methodMap = new HashMap<>();
for(int i=0; i<m_methods.length; ++i ) {
if( m_methods[i] != null ) {
m_methodMap.put( m_methods[i], i );
}
}
m_mounts = new HashSet<>();
}
public IPeripheral getPeripheral()
{
return m_peripheral;
}
public String getType()
{
return m_type;
}
public String[] getMethods()
{
return m_methods;
}
public synchronized boolean isAttached()
{
return m_attached;
}
public synchronized void attach()
{
m_attached = true;
m_peripheral.attach( this );
}
public synchronized void detach()
public void detach()
{
// Call detach
m_peripheral.detach( this );
m_attached = false;
// Unmount everything the detach function forgot to do
for( String m_mount : m_mounts )
synchronized( this )
{
m_fileSystem.unmount( m_mount );
m_attached = false;
// Unmount everything the detach function forgot to do
for( String m_mount : m_mounts )
{
m_fileSystem.unmount( m_mount );
}
m_mounts.clear();
}
m_mounts.clear();
}
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
{
int method = -1;
synchronized( this )
synchronized( this )
{
if( m_methodMap.containsKey( methodName ) )
{
@@ -133,7 +136,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{
throw new RuntimeException( "You are not attached to this Computer" );
}
// Mount the location
String location;
synchronized( m_fileSystem )
@@ -151,7 +154,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
if( location != null )
{
m_mounts.add( location );
}
}
return location;
}
@@ -168,7 +171,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{
throw new RuntimeException( "You are not attached to this Computer" );
}
// Mount the location
String location;
synchronized( m_fileSystem )
@@ -186,49 +189,49 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
if( location != null )
{
m_mounts.add( location );
}
}
return location;
}
@Override
public synchronized void unmount( String location )
{
if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" );
}
if( location != null )
{
if( !m_mounts.contains( location ) ) {
throw new RuntimeException( "You didn't mount this location" );
}
m_fileSystem.unmount( location );
m_mounts.remove( location );
}
}
@Override
public synchronized int getID()
public int getID()
{
if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" );
}
return m_environment.getComputerID();
}
@Override
public synchronized void queueEvent( @Nonnull final String event, final Object[] arguments )
public void queueEvent( @Nonnull final String event, final Object[] arguments )
{
if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" );
}
}
m_environment.queueEvent( event, arguments );
}
@Nonnull
@Override
public synchronized String getAttachmentName()
public String getAttachmentName()
{
if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" );
@@ -236,7 +239,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
return m_side;
}
}
private final IAPIEnvironment m_environment;
private FileSystem m_fileSystem;
private final PeripheralWrapper[] m_peripherals;
@@ -246,16 +249,16 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{
m_environment = _environment;
m_environment.setPeripheralChangeListener( this );
m_peripherals = new PeripheralWrapper[6];
for(int i=0; i<6; ++i)
{
m_peripherals[i] = null;
}
m_running = false;
}
// IPeripheralChangeListener
@Override
@@ -282,11 +285,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
}
}, null);
// Queue a detachment event
m_environment.queueEvent( "peripheral_detach", new Object[] { Computer.s_sideNames[side] } );
}
// Assign the new peripheral
if( newPeripheral != null )
{
@@ -296,7 +299,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{
m_peripherals[side] = null;
}
if( m_peripherals[side] != null )
{
// Queue an attachment
@@ -318,7 +321,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
}
}, null );
// Queue an attachment event
m_environment.queueEvent( "peripheral", new Object[] { Computer.s_sideNames[side] } );
}
@@ -326,7 +329,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
// ILuaAPI implementation
@Override
public String[] getNames()
{
@@ -352,12 +355,12 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
}
}
@Override
public void advance( double _dt )
{
}
@Override
public void shutdown( )
{
@@ -464,8 +467,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
// call
int side = parseSide( args );
String methodName = getString( args, 1 );
Object[] methodArgs = trimArray( args, 2 );
Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length );
if( side >= 0 )
{
PeripheralWrapper p;
@@ -486,13 +489,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
}
}
// Privates
private Object[] trimArray( Object[] array, int skip )
{
return Arrays.copyOfRange( array, skip, array.length );
}
// Privates
private int parseSide( Object[] args ) throws LuaException
{
@@ -506,7 +504,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
}
return -1;
}
private String findFreeLocation( String desiredLoc )
{
try

View File

@@ -6,6 +6,8 @@ import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import java.io.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class EncodedInputHandle extends HandleGeneric
{
private final BufferedReader m_reader;
@@ -49,6 +51,7 @@ public class EncodedInputHandle extends HandleGeneric
"readLine",
"readAll",
"close",
"read",
};
}
@@ -102,6 +105,26 @@ public class EncodedInputHandle extends HandleGeneric
// close
close();
return null;
case 3:
// read
checkOpen();
try
{
int count = optInt( args, 0, 1 );
if( count <= 0 || count >= 1024 * 16 )
{
throw new LuaException( "Count out of range" );
}
char[] bytes = new char[ count ];
count = m_reader.read( bytes );
if( count < 0 ) return null;
String str = new String( bytes, 0, count );
return new Object[] { str };
}
catch( IOException e )
{
return null;
}
default:
return null;
}

View File

@@ -1,18 +1,18 @@
package dan200.computercraft.core.apis.http;
import dan200.computercraft.core.apis.HTTPRequestException;
import dan200.computercraft.core.apis.IAPIEnvironment;
import java.net.URL;
public class HTTPCheck implements HTTPTask.IHTTPTask
public class HTTPCheck implements Runnable
{
private final IAPIEnvironment environment;
private final String urlString;
private final URL url;
private String error;
public HTTPCheck( String urlString, URL url )
public HTTPCheck( IAPIEnvironment environment, String urlString, URL url )
{
this.environment = environment;
this.urlString = urlString;
this.url = url;
}
@@ -22,24 +22,12 @@ public class HTTPCheck implements HTTPTask.IHTTPTask
{
try
{
HTTPRequest.checkHost( url );
HTTPRequest.checkHost( url.getHost() );
environment.queueEvent( "http_check", new Object[] { urlString, true } );
}
catch( HTTPRequestException e )
{
error = e.getMessage();
}
}
@Override
public void whenFinished( IAPIEnvironment environment )
{
if( error == null )
{
environment.queueEvent( "http_check", new Object[] { urlString, true } );
}
else
{
environment.queueEvent( "http_check", new Object[] { urlString, false, error } );
environment.queueEvent( "http_check", new Object[] { urlString, false, e.getMessage() } );
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Just a shared object for executing simple HTTP related tasks.
*/
public final class HTTPExecutor
{
public static final ListeningExecutorService EXECUTOR = MoreExecutors.listeningDecorator( new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setDaemon( true )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-HTTP-%d" )
.build()
) );
public static final EventLoopGroup LOOP_GROUP = new NioEventLoopGroup( 4, new ThreadFactoryBuilder()
.setDaemon( true )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-Netty-%d" )
.build()
);
private HTTPExecutor()
{
}
}

View File

@@ -12,7 +12,6 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.HTTPRequestException;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
@@ -25,7 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HTTPRequest implements HTTPTask.IHTTPTask
public class HTTPRequest implements Runnable
{
public static URL checkURL( String urlString ) throws HTTPRequestException
{
@@ -55,11 +54,11 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
return url;
}
public static InetAddress checkHost( URL url ) throws HTTPRequestException
public static InetAddress checkHost( String host ) throws HTTPRequestException
{
try
{
InetAddress resolved = InetAddress.getByName( url.getHost() );
InetAddress resolved = InetAddress.getByName( host );
if( !ComputerCraft.http_whitelist.matches( resolved ) || ComputerCraft.http_blacklist.matches( resolved ) )
{
throw new HTTPRequestException( "Domain not permitted" );
@@ -73,22 +72,16 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
}
}
private final IAPIEnvironment m_environment;
private final URL m_url;
private final String m_urlString;
private final String m_postText;
private final Map<String, String> m_headers;
private boolean m_success = false;
private String m_encoding;
private byte[] m_result;
private boolean m_binary;
private int m_responseCode = -1;
private Map<String, String> m_responseHeaders;
private String m_errorMessage;
public HTTPRequest( String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
public HTTPRequest( IAPIEnvironment environment, String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
{
// Parse the URL
m_environment = environment;
m_urlString = urlString;
m_url = url;
m_binary = binary;
@@ -96,28 +89,19 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
m_headers = headers;
}
public InputStream getContents()
{
byte[] result = m_result;
if( result != null )
{
return new ByteArrayInputStream( result );
}
return null;
}
@Override
public void run()
{
// First verify the address is allowed.
try
{
checkHost( m_url );
checkHost( m_url.getHost() );
}
catch( HTTPRequestException e )
{
m_success = false;
m_errorMessage = e.getMessage();
// Queue the failure event if not.
String error = e.getMessage();
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, error == null ? "Could not connect" : error, null } );
return;
}
@@ -186,58 +170,36 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
byte[] result = ByteStreams.toByteArray( is );
is.close();
// We completed
m_success = responseSuccess;
m_result = result;
m_responseCode = connection.getResponseCode();
m_encoding = connection.getContentEncoding();
// We've got some sort of response, so let's build a resulting object.
Joiner joiner = Joiner.on( ',' );
Map<String, String> headers = m_responseHeaders = new HashMap<String, String>();
Map<String, String> headers = new HashMap<>();
for( Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet() )
{
headers.put( header.getKey(), joiner.join( header.getValue() ) );
}
InputStream contents = new ByteArrayInputStream( result );
ILuaObject stream = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ),
connection.getResponseCode(), headers
);
connection.disconnect(); // disconnect
// Queue the appropriate event.
if( responseSuccess )
{
m_environment.queueEvent( "http_success", new Object[] { m_urlString, stream } );
}
else
{
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", stream } );
}
}
catch( IOException e )
{
// There was an error
m_success = false;
}
}
@Override
public void whenFinished( IAPIEnvironment environment )
{
final String url = m_urlString;
if( m_success )
{
// Queue the "http_success" event
InputStream contents = getContents();
Object result = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
m_responseCode, m_responseHeaders
);
environment.queueEvent( "http_success", new Object[] { url, result } );
}
else
{
// Queue the "http_failure" event
String error = "Could not connect";
if( m_errorMessage != null ) error = m_errorMessage;
InputStream contents = getContents();
Object result = null;
if( contents != null )
{
result = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
m_responseCode, m_responseHeaders
);
}
environment.queueEvent( "http_failure", new Object[] { url, error, result } );
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", null } );
}
}

View File

@@ -1,4 +1,4 @@
package dan200.computercraft.core.apis;
package dan200.computercraft.core.apis.http;
public class HTTPRequestException extends Exception
{

View File

@@ -1,61 +0,0 @@
package dan200.computercraft.core.apis.http;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dan200.computercraft.core.apis.IAPIEnvironment;
import java.util.concurrent.*;
/**
* A task which executes asynchronously on a new thread.
*
* This functions very similarly to a {@link Future}, but with an additional
* method which is called on the main thread when the task is completed.
*/
public class HTTPTask
{
public interface IHTTPTask extends Runnable
{
void whenFinished( IAPIEnvironment environment );
}
private static final ExecutorService httpThreads = new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setDaemon( true )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-HTTP-%d" )
.build()
);
private final Future<?> future;
private final IHTTPTask task;
private HTTPTask( Future<?> future, IHTTPTask task )
{
this.future = future;
this.task = task;
}
public static HTTPTask submit( IHTTPTask task )
{
Future<?> future = httpThreads.submit( task );
return new HTTPTask( future, task );
}
public void cancel()
{
future.cancel( false );
}
public boolean isFinished()
{
return future.isDone();
}
public void whenFinished( IAPIEnvironment environment )
{
task.whenFinished( environment );
}
}

View File

@@ -0,0 +1,186 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import com.google.common.base.Objects;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.HTTPAPI;
import dan200.computercraft.core.apis.IAPIEnvironment;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
public class WebsocketConnection extends SimpleChannelInboundHandler<Object> implements ILuaObject, Closeable
{
public static final String SUCCESS_EVENT = "websocket_success";
public static final String FAILURE_EVENT = "websocket_failure";
public static final String CLOSE_EVENT = "websocket_closed";
public static final String MESSAGE_EVENT = "websocket_message";
private final String url;
private final IAPIEnvironment computer;
private final HTTPAPI api;
private boolean open = true;
private Channel channel;
private final WebSocketClientHandshaker handshaker;
public WebsocketConnection( IAPIEnvironment computer, HTTPAPI api, WebSocketClientHandshaker handshaker, String url )
{
this.computer = computer;
this.api = api;
this.handshaker = handshaker;
this.url = url;
api.addCloseable( this );
}
private void close( boolean remove )
{
open = false;
if( remove ) api.removeCloseable( this );
if( channel != null )
{
channel.close();
channel = null;
}
}
@Override
public void close() throws IOException
{
close( false );
}
private void onClosed()
{
close( true );
computer.queueEvent( CLOSE_EVENT, new Object[] { url } );
}
@Override
public void handlerAdded( ChannelHandlerContext ctx ) throws Exception
{
channel = ctx.channel();
super.handlerAdded( ctx );
}
@Override
public void channelActive( ChannelHandlerContext ctx ) throws Exception
{
handshaker.handshake( ctx.channel() );
super.channelActive( ctx );
}
@Override
public void channelInactive( ChannelHandlerContext ctx ) throws Exception
{
onClosed();
super.channelInactive( ctx );
}
@Override
public void channelRead0( ChannelHandlerContext ctx, Object msg ) throws Exception
{
Channel ch = ctx.channel();
if( !handshaker.isHandshakeComplete() )
{
handshaker.finishHandshake( ch, (FullHttpResponse) msg );
computer.queueEvent( SUCCESS_EVENT, new Object[] { url, this } );
return;
}
if( msg instanceof FullHttpResponse )
{
FullHttpResponse response = (FullHttpResponse) msg;
throw new IllegalStateException( "Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString( CharsetUtil.UTF_8 ) + ')' );
}
WebSocketFrame frame = (WebSocketFrame) msg;
if( frame instanceof TextWebSocketFrame )
{
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, ((TextWebSocketFrame) frame).text() } );
}
else if( frame instanceof BinaryWebSocketFrame )
{
ByteBuf data = frame.content();
byte[] converted = new byte[ data.readableBytes() ];
data.readBytes( converted );
computer.queueEvent( MESSAGE_EVENT, new Object[] { url, data } );
}
else if( frame instanceof CloseWebSocketFrame )
{
ch.close();
onClosed();
}
}
@Override
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
{
ctx.close();
computer.queueEvent( FAILURE_EVENT, new Object[] {
url,
cause instanceof WebSocketHandshakeException ? cause.getMessage() : "Could not connect"
} );
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] { "receive", "send", "close" };
}
@Nullable
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{
switch( method )
{
case 0:
while( true )
{
checkOpen();
Object[] event = context.pullEvent( MESSAGE_EVENT );
if( event.length >= 3 && Objects.equal( event[ 1 ], url ) )
{
return new Object[] { event[ 2 ] };
}
}
case 1:
{
checkOpen();
String text = arguments.length > 0 && arguments[ 0 ] != null ? arguments[ 0 ].toString() : "";
channel.writeAndFlush( new TextWebSocketFrame( text ) );
return null;
}
case 2:
close( true );
return null;
default:
return null;
}
}
private void checkOpen() throws LuaException
{
if( !open ) throw new LuaException( "attempt to use a closed file" );
}
}

View File

@@ -0,0 +1,200 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.apis.HTTPAPI;
import dan200.computercraft.core.apis.IAPIEnvironment;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.Map;
import java.util.concurrent.Future;
/*
* Provides functionality to verify and connect to a remote websocket.
*/
public final class WebsocketConnector
{
private static final Object lock = new Object();
private static TrustManagerFactory trustManager;
private WebsocketConnector()
{
}
private static TrustManagerFactory getTrustManager()
{
if( trustManager != null ) return trustManager;
synchronized( lock )
{
if( trustManager != null ) return trustManager;
TrustManagerFactory tmf = null;
try
{
tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
tmf.init( (KeyStore) null );
}
catch( Exception e )
{
ComputerCraft.log.error( "Cannot setup trust manager", e );
}
return trustManager = tmf;
}
}
public static URI checkURI( String address ) throws HTTPRequestException
{
URI uri = null;
try
{
uri = new URI( address );
}
catch( URISyntaxException ignored )
{
}
if( uri == null || uri.getHost() == null )
{
try
{
uri = new URI( "ws://" + address );
}
catch( URISyntaxException ignored )
{
}
}
if( uri == null || uri.getHost() == null ) throw new HTTPRequestException( "URL malformed" );
String scheme = uri.getScheme();
if( scheme == null )
{
try
{
uri = new URI( "ws://" + uri.toString() );
}
catch( URISyntaxException e )
{
throw new HTTPRequestException( "URL malformed" );
}
}
else if( !scheme.equalsIgnoreCase( "wss" ) && !scheme.equalsIgnoreCase( "ws" ) )
{
throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" );
}
if( !ComputerCraft.http_whitelist.matches( uri.getHost() ) || ComputerCraft.http_blacklist.matches( uri.getHost() ) )
{
throw new HTTPRequestException( "Domain not permitted" );
}
return uri;
}
public static int getPort( URI uri ) throws HTTPRequestException
{
int port = uri.getPort();
if( port >= 0 ) return port;
String scheme = uri.getScheme();
if( scheme.equalsIgnoreCase( "ws" ) )
{
return 80;
}
else if( scheme.equalsIgnoreCase( "wss" ) )
{
return 443;
}
else
{
throw new HTTPRequestException( "Invalid scheme '" + scheme + "'" );
}
}
public static Future<?> createConnector( final IAPIEnvironment environment, final HTTPAPI api, final URI uri, final String address, final int port, final Map<String, String> headers )
{
return HTTPExecutor.EXECUTOR.submit( () -> {
InetAddress resolved;
try
{
resolved = HTTPRequest.checkHost( uri.getHost() );
}
catch( HTTPRequestException e )
{
environment.queueEvent( WebsocketConnection.FAILURE_EVENT, new Object[] { address, e.getMessage() } );
return;
}
InetSocketAddress socketAddress = new InetSocketAddress( resolved, uri.getPort() == -1 ? port : uri.getPort() );
final SslContext ssl;
if( uri.getScheme().equalsIgnoreCase( "wss" ) )
{
try
{
ssl = SslContextBuilder.forClient().trustManager( getTrustManager() ).build();
}
catch( SSLException e )
{
environment.queueEvent( WebsocketConnection.FAILURE_EVENT, new Object[] { address, "Cannot create secure socket" } );
return;
}
}
else
{
ssl = null;
}
HttpHeaders httpHeaders = new DefaultHttpHeaders();
for( Map.Entry<String, String> header : headers.entrySet() )
{
httpHeaders.add( header.getKey(), header.getValue() );
}
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, null, false, httpHeaders );
final WebsocketConnection connection = new WebsocketConnection( environment, api, handshaker, address );
new Bootstrap()
.group( HTTPExecutor.LOOP_GROUP )
.channel( NioSocketChannel.class )
.handler( new ChannelInitializer<SocketChannel>()
{
@Override
protected void initChannel( SocketChannel ch ) throws Exception
{
ChannelPipeline p = ch.pipeline();
if( ssl != null ) p.addLast( ssl.newHandler( ch.alloc(), uri.getHost(), port ) );
p.addLast( new HttpClientCodec(), new HttpObjectAggregator( 8192 ), connection );
}
} )
.remoteAddress( socketAddress )
.connect();
} );
}
}

View File

@@ -15,7 +15,7 @@ import dan200.computercraft.core.apis.*;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.lua.ILuaMachine;
import dan200.computercraft.core.lua.LuaJLuaMachine;
import dan200.computercraft.core.lua.CobaltLuaMachine;
import dan200.computercraft.core.terminal.Terminal;
import java.io.IOException;
@@ -452,7 +452,7 @@ public class Computer
private boolean initFileSystem()
{
// Create the file system
int id = assignID();
assignID();
try
{
m_fileSystem = new FileSystem( "hdd", getRootMount() );
@@ -611,7 +611,6 @@ public class Computer
m_apis.add( new FSAPI( m_apiEnvironment ) );
m_apis.add( new PeripheralAPI( m_apiEnvironment ) );
m_apis.add( new OSAPI( m_apiEnvironment ) );
m_apis.add( new BitAPI( m_apiEnvironment ) );
//m_apis.add( new BufferAPI( m_apiEnvironment ) );
if( ComputerCraft.http_enable )
{
@@ -622,7 +621,7 @@ public class Computer
private void initLua()
{
// Create the lua machine
ILuaMachine machine = new LuaJLuaMachine( this );
ILuaMachine machine = new CobaltLuaMachine( this );
// Add the APIs
for(ILuaAPI api : m_apis)

View File

@@ -8,223 +8,322 @@ package dan200.computercraft.core.computer;
import dan200.computercraft.ComputerCraft;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class ComputerThread
{
private static final Object m_lock;
private static Thread m_thread;
private static final WeakHashMap <Object, LinkedBlockingQueue<ITask>> m_computerTasks;
private static final ArrayList <LinkedBlockingQueue<ITask>> m_computerTasksActive;
private static final ArrayList <LinkedBlockingQueue<ITask>> m_computerTasksPending;
private static final Object m_defaultQueue;
private static final Object m_monitor;
private static final int QUEUE_LIMIT = 256;
private static boolean m_running;
private static boolean m_stopped;
static
{
m_lock = new Object();
m_thread = null;
m_computerTasks = new WeakHashMap<>();
m_computerTasksPending = new ArrayList<>();
m_computerTasksActive = new ArrayList<>();
m_defaultQueue = new Object();
m_monitor = new Object();
m_running = false;
m_stopped = false;
}
/**
* Lock used for modifications to the object
*/
private static final Object s_stateLock = new Object();
/**
* Lock for various task operations
*/
private static final Object s_taskLock = new Object();
/**
* Map of objects to task list
*/
private static final WeakHashMap<Object, BlockingQueue<ITask>> s_computerTaskQueues = new WeakHashMap<>();
/**
* Active queues to execute
*/
private static final BlockingQueue<BlockingQueue<ITask>> s_computerTasksActive = new LinkedBlockingQueue<>();
private static final Set<BlockingQueue<ITask>> s_computerTasksActiveSet = new HashSet<>();
/**
* The default object for items which don't have an owner
*/
private static final Object s_defaultOwner = new Object();
/**
* Whether the thread is stopped or should be stopped
*/
private static boolean s_stopped = false;
/**
* The thread tasks execute on
*/
private static Thread[] s_threads = null;
private static final AtomicInteger s_ManagerCounter = new AtomicInteger( 1 );
private static final AtomicInteger s_DelegateCounter = new AtomicInteger( 1 );
/**
* Start the computer thread
*/
public static void start()
{
synchronized( m_lock )
synchronized( s_stateLock )
{
if( m_running )
s_stopped = false;
if( s_threads == null || s_threads.length != ComputerCraft.computer_threads )
{
m_stopped = false;
return;
s_threads = new Thread[ ComputerCraft.computer_threads ];
}
m_thread = new Thread( () ->
SecurityManager manager = System.getSecurityManager();
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
for( int i = 0; i < s_threads.length; i++ )
{
Thread thread = s_threads[ i ];
if( thread == null || !thread.isAlive() )
{
thread = s_threads[ i ] = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager-" + s_ManagerCounter.getAndIncrement() );
thread.setDaemon( true );
thread.start();
}
}
}
}
/**
* Attempt to stop the computer thread
*/
public static void stop()
{
synchronized( s_stateLock )
{
if( s_threads != null )
{
s_stopped = true;
for( Thread thread : s_threads )
{
if( thread != null && thread.isAlive() )
{
thread.interrupt();
}
}
}
}
synchronized( s_taskLock )
{
s_computerTaskQueues.clear();
s_computerTasksActive.clear();
s_computerTasksActiveSet.clear();
}
}
/**
* Queue a task to execute on the thread
*
* @param task The task to execute
* @param computer The computer to execute it on, use {@code null} to execute on the default object.
*/
public static void queueTask( ITask task, Computer computer )
{
Object queueObject = computer == null ? s_defaultOwner : computer;
BlockingQueue<ITask> queue;
synchronized( s_computerTaskQueues )
{
queue = s_computerTaskQueues.get( queueObject );
if( queue == null )
{
s_computerTaskQueues.put( queueObject, queue = new LinkedBlockingQueue<>( QUEUE_LIMIT ) );
}
}
synchronized( s_taskLock )
{
if( queue.offer( task ) && !s_computerTasksActiveSet.contains( queue ) )
{
s_computerTasksActive.add( queue );
s_computerTasksActiveSet.add( queue );
}
}
}
/**
* Responsible for pulling and managing computer tasks. This pulls a task from {@link #s_computerTasksActive},
* creates a new thread using {@link TaskRunner} or reuses a previous one and uses that to execute the task.
*
* If the task times out, then it will attempt to interrupt the {@link TaskRunner} instance.
*/
private static final class TaskExecutor implements Runnable
{
private TaskRunner runner;
private Thread thread;
@Override
public void run()
{
try
{
while( true )
{
synchronized( m_computerTasksPending )
{
if (!m_computerTasksPending.isEmpty())
{
Iterator<LinkedBlockingQueue<ITask>> it = m_computerTasksPending.iterator();
while(it.hasNext())
{
LinkedBlockingQueue<ITask> queue = it.next();
if (!m_computerTasksActive.contains(queue))
{
m_computerTasksActive.add(queue);
}
it.remove();
}
/*
m_computerTasksActive.addAll(m_computerTasksPending); // put any that have been added since
m_computerTasksPending.clear();
*/
}
}
Iterator<LinkedBlockingQueue<ITask>> it = m_computerTasksActive.iterator();
while (it.hasNext())
{
LinkedBlockingQueue<ITask> queue = it.next();
if (queue == null || queue.isEmpty()) // we don't need the blocking part of the queue. Null check to ensure it exists due to a weird NPE I got
{
continue;
}
synchronized( m_lock )
{
if( m_stopped )
{
m_running = false;
m_thread = null;
return;
}
}
try
{
final ITask task = queue.take();
// Wait for an active queue to execute
BlockingQueue<ITask> queue = s_computerTasksActive.take();
// Create the task
Thread worker = new Thread( () ->
{
try {
task.execute();
} catch( Throwable e ) {
ComputerCraft.log.error( "Error running task", e );
}
} );
// Run the task
worker.setDaemon(true);
worker.start();
worker.join( 7000 );
if( worker.isAlive() )
{
// Task ran for too long
// Initiate escape plan
Computer computer = task.getOwner();
if( computer != null )
{
// Step 1: Soft abort
computer.abort( false );
worker.join( 1500 );
if( worker.isAlive() )
{
// Step 2: Hard abort
computer.abort( true );
worker.join( 1500 );
}
}
// Step 3: abandon
if( worker.isAlive() )
{
// ComputerCraft.log.warn( "Failed to abort Computer " + computer.getID() + ". Dangling lua thread could cause errors." );
worker.interrupt();
}
}
}
catch( InterruptedException e )
{
continue;
}
synchronized (queue)
{
if (queue.isEmpty())
{
it.remove();
}
}
}
while (m_computerTasksActive.isEmpty() && m_computerTasksPending.isEmpty())
// If threads should be stopped then return
synchronized( s_stateLock )
{
synchronized (m_monitor)
{
try
{
m_monitor.wait();
}
catch( InterruptedException e )
{
}
}
if( s_stopped ) return;
}
execute( queue );
}
}, "Computer Dispatch Thread" );
m_thread.setDaemon(true);
m_thread.start();
m_running = true;
}
}
public static void stop()
{
synchronized( m_lock )
{
if( m_running )
}
catch( InterruptedException ignored )
{
m_stopped = true;
m_thread.interrupt();
Thread.currentThread().interrupt();
}
}
}
public static void queueTask( ITask _task, Computer computer )
{
Object queueObject = computer;
if (queueObject == null)
{
queueObject = m_defaultQueue;
}
LinkedBlockingQueue<ITask> queue = m_computerTasks.get(queueObject);
if (queue == null)
private void execute( BlockingQueue<ITask> queue ) throws InterruptedException
{
m_computerTasks.put(queueObject, queue = new LinkedBlockingQueue<>( 256 ));
}
synchronized ( m_computerTasksPending )
{
if( queue.offer( _task ) )
ITask task = queue.remove();
if( thread == null || !thread.isAlive() )
{
if( !m_computerTasksPending.contains( queue ) )
runner = new TaskRunner();
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();
}
// Execute the task
runner.submit( task );
try
{
// If we timed out rather than exiting:
boolean done = runner.await( 7000 );
if( !done )
{
m_computerTasksPending.add( queue );
// Attempt to soft then hard abort
Computer computer = task.getOwner();
if( computer != null )
{
computer.abort( false );
done = runner.await( 1500 );
if( !done )
{
computer.abort( true );
done = runner.await( 1500 );
}
}
// Interrupt the thread
if( !done )
{
thread.interrupt();
thread = null;
runner = null;
}
}
}
else
finally
{
//System.out.println( "Event queue overflow" );
// Re-add it back onto the queue or remove it
synchronized( s_taskLock )
{
if( queue.isEmpty() )
{
s_computerTasksActiveSet.remove( queue );
}
else
{
s_computerTasksActive.add( queue );
}
}
}
}
synchronized (m_monitor)
}
/**
* Responsible for the actual running of tasks. It waitin for the {@link TaskRunner#input} semaphore to be
* triggered, consumes a task and then triggers {@link TaskRunner#finished}.
*/
private static final class TaskRunner implements Runnable
{
private final Semaphore input = new Semaphore();
private final Semaphore finished = new Semaphore();
private ITask task;
@Override
public void run()
{
m_monitor.notify();
try
{
while( true )
{
input.await();
try
{
task.execute();
}
catch( RuntimeException e )
{
ComputerCraft.log.error( "Error running task.", e );
}
task = null;
finished.signal();
}
}
catch( InterruptedException e )
{
ComputerCraft.log.error( "Error running task.", e );
Thread.currentThread().interrupt();
}
}
void submit( ITask task )
{
this.task = task;
input.signal();
}
boolean await( long timeout ) throws InterruptedException
{
return finished.await( timeout );
}
}
/**
* A simple method to allow awaiting/providing a signal.
*
* Java does provide similar classes, but I only needed something simple.
*/
private static final class Semaphore
{
private volatile boolean state = false;
synchronized void signal()
{
state = true;
notify();
}
synchronized void await() throws InterruptedException
{
while( !state ) wait();
state = false;
}
synchronized boolean await( long timeout ) throws InterruptedException
{
if( !state )
{
wait( timeout );
if( !state ) return false;
}
state = false;
return true;
}
}
}

View File

@@ -9,7 +9,6 @@ package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@@ -22,31 +21,31 @@ public class EmptyMount implements IMount
// IMount implementation
@Override
public boolean exists( @Nonnull String path ) throws IOException
public boolean exists( @Nonnull String path )
{
return path.isEmpty();
}
@Override
public boolean isDirectory( @Nonnull String path ) throws IOException
public boolean isDirectory( @Nonnull String path )
{
return path.isEmpty();
}
@Override
public void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException
public void list( @Nonnull String path, @Nonnull List<String> contents )
{
}
@Override
public long getSize( @Nonnull String path ) throws IOException
public long getSize( @Nonnull String path )
{
return 0;
}
@Nonnull
@Override
public InputStream openForRead( @Nonnull String path ) throws IOException
public InputStream openForRead( @Nonnull String path )
{
return null;
}

View File

@@ -95,7 +95,7 @@ public class FileMount implements IWritableMount
// IMount implementation
@Override
public boolean exists( @Nonnull String path ) throws IOException
public boolean exists( @Nonnull String path )
{
if( !created() )
{
@@ -109,7 +109,7 @@ public class FileMount implements IWritableMount
}
@Override
public boolean isDirectory( @Nonnull String path ) throws IOException
public boolean isDirectory( @Nonnull String path )
{
if( !created() )
{
@@ -339,7 +339,7 @@ public class FileMount implements IWritableMount
}
@Override
public long getRemainingSpace() throws IOException
public long getRemainingSpace()
{
return Math.max( m_capacity - m_usedSpace, 0 );
}

View File

@@ -65,7 +65,7 @@ public class FileSystem
}
}
public boolean isReadOnly( String path ) throws FileSystemException
public boolean isReadOnly( String path )
{
return (m_writableMount == null);
}
@@ -347,7 +347,7 @@ public class FileSystem
mount( new MountWrapper( label, location, mount ) );
}
private synchronized void mount( MountWrapper wrapper ) throws FileSystemException
private synchronized void mount( MountWrapper wrapper )
{
String location = wrapper.getLocation();
if( m_mounts.containsKey( location ) )

View File

@@ -176,14 +176,14 @@ public class JarMount implements IMount
// IMount implementation
@Override
public boolean exists( @Nonnull String path ) throws IOException
public boolean exists( @Nonnull String path )
{
FileInZip file = m_root.getFile( path );
return file != null;
}
@Override
public boolean isDirectory( @Nonnull String path ) throws IOException
public boolean isDirectory( @Nonnull String path )
{
FileInZip file = m_root.getFile( path );
if( file != null )

View File

@@ -15,105 +15,131 @@ import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ITask;
import dan200.computercraft.core.computer.MainThread;
import org.luaj.vm2.*;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LoadState;
import org.squiddev.cobalt.debug.DebugFrame;
import org.squiddev.cobalt.debug.DebugHandler;
import org.squiddev.cobalt.debug.DebugState;
import org.squiddev.cobalt.function.LibFunction;
import org.squiddev.cobalt.function.LuaFunction;
import org.squiddev.cobalt.function.VarArgFunction;
import org.squiddev.cobalt.lib.*;
import org.squiddev.cobalt.lib.platform.AbstractResourceManipulator;
import javax.annotation.Nonnull;
import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
public class LuaJLuaMachine implements ILuaMachine
{
private Computer m_computer;
import static org.squiddev.cobalt.Constants.NONE;
import static org.squiddev.cobalt.ValueFactory.valueOf;
import static org.squiddev.cobalt.ValueFactory.varargsOf;
private LuaValue m_globals;
private LuaValue m_loadString;
private LuaValue m_assert;
private LuaValue m_coroutine_create;
private LuaValue m_coroutine_resume;
private LuaValue m_coroutine_yield;
private LuaValue m_mainRoutine;
public class CobaltLuaMachine implements ILuaMachine
{
private final Computer m_computer;
private final LuaState m_state;
private final LuaTable m_globals;
private LuaThread m_mainRoutine;
private String m_eventFilter;
private String m_softAbortMessage;
private String m_hardAbortMessage;
private Map<Object, LuaValue> m_valuesInProgress;
private Map<LuaValue, Object> m_objectsInProgress;
public LuaJLuaMachine( Computer computer )
public CobaltLuaMachine( Computer computer )
{
m_computer = computer;
// Create an environment to run in
m_globals = JsePlatform.debugGlobals();
m_loadString = m_globals.get("loadstring");
m_assert = m_globals.get("assert");
LuaValue coroutine = m_globals.get("coroutine");
final LuaValue native_coroutine_create = coroutine.get("create");
LuaValue debug = m_globals.get("debug");
final LuaValue debug_sethook = debug.get("sethook");
coroutine.set("create", new OneArgFunction() {
final LuaState state = this.m_state = new LuaState( new AbstractResourceManipulator()
{
@Override
public LuaValue call( LuaValue value )
public InputStream findResource( String filename )
{
final LuaThread thread = native_coroutine_create.call( value ).checkthread();
debug_sethook.invoke( new LuaValue[] {
thread,
new ZeroArgFunction() {
@Override
public LuaValue call() {
String hardAbortMessage = m_hardAbortMessage;
if( hardAbortMessage != null )
{
LuaThread.yield(LuaValue.NIL);
}
return LuaValue.NIL;
}
},
LuaValue.NIL,
LuaValue.valueOf(100000)
} );
return thread;
return null;
}
});
m_coroutine_create = coroutine.get("create");
m_coroutine_resume = coroutine.get("resume");
m_coroutine_yield = coroutine.get("yield");
} );
state.debug = new DebugHandler( state )
{
private int count = 0;
private boolean hasSoftAbort;
@Override
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 );
this.count = 0;
}
else
{
handleSoftAbort();
}
super.onInstruction( ds, di, pc, extras, top );
}
@Override
public void poll() throws LuaError
{
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE );
handleSoftAbort();
}
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;
}
if (hasSoftAbort && m_hardAbortMessage == null) {
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
return;
}
hasSoftAbort = true;
throw new LuaError(message);
}
};
m_globals = new LuaTable();
state.setupThread( m_globals );
// Add basic libraries
m_globals.load( state, new BaseLib() );
m_globals.load( state, new TableLib() );
m_globals.load( state, new StringLib() );
m_globals.load( state, new MathLib() );
m_globals.load( state, new CoroutineLib() );
m_globals.load( state, new Bit32Lib() );
if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() );
// Register custom load/loadstring provider which automatically adds prefixes.
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
// Remove globals we don't want to expose
m_globals.set( "collectgarbage", LuaValue.NIL );
m_globals.set( "dofile", LuaValue.NIL );
m_globals.set( "loadfile", LuaValue.NIL );
m_globals.set( "module", LuaValue.NIL );
m_globals.set( "require", LuaValue.NIL );
m_globals.set( "package", LuaValue.NIL );
m_globals.set( "io", LuaValue.NIL );
m_globals.set( "os", LuaValue.NIL );
m_globals.set( "print", LuaValue.NIL );
m_globals.set( "luajava", LuaValue.NIL );
m_globals.set( "debug", LuaValue.NIL );
m_globals.set( "newproxy", LuaValue.NIL );
m_globals.set( "__inext", LuaValue.NIL );
m_globals.rawset( "collectgarbage", Constants.NIL );
m_globals.rawset( "dofile", Constants.NIL );
m_globals.rawset( "loadfile", Constants.NIL );
m_globals.rawset( "print", Constants.NIL );
// Add version globals
m_globals.set( "_VERSION", "Lua 5.1" );
m_globals.set( "_HOST", computer.getAPIEnvironment().getComputerEnvironment().getHostString() );
m_globals.set( "_CC_DEFAULT_SETTINGS", toValue( ComputerCraft.default_computer_settings ) );
m_globals.rawset( "_VERSION", valueOf( "Lua 5.1" ) );
m_globals.rawset( "_HOST", valueOf( computer.getAPIEnvironment().getComputerEnvironment().getHostString() ) );
m_globals.rawset( "_CC_DEFAULT_SETTINGS", valueOf( ComputerCraft.default_computer_settings ) );
if( ComputerCraft.disable_lua51_features )
{
m_globals.set( "_CC_DISABLE_LUA51_FEATURES", toValue( true ) );
m_globals.rawset( "_CC_DISABLE_LUA51_FEATURES", Constants.TRUE );
}
// Our main function will go here
@@ -123,7 +149,7 @@ public class LuaJLuaMachine implements ILuaMachine
m_softAbortMessage = null;
m_hardAbortMessage = null;
}
@Override
public void addAPI( ILuaAPI api )
{
@@ -132,10 +158,10 @@ public class LuaJLuaMachine implements ILuaMachine
String[] names = api.getNames();
for( String name : names )
{
m_globals.set( name, table );
m_globals.rawset( name, table );
}
}
@Override
public void loadBios( InputStream bios )
{
@@ -144,56 +170,31 @@ public class LuaJLuaMachine implements ILuaMachine
{
return;
}
try
{
// Read the whole bios into a string
String biosText;
try
{
InputStreamReader isr;
try
{
isr = new InputStreamReader( bios, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
isr = new InputStreamReader( bios );
}
BufferedReader reader = new BufferedReader( isr );
StringBuilder fileText = new StringBuilder( "" );
String line = reader.readLine();
while( line != null ) {
fileText.append( line );
line = reader.readLine();
if( line != null ) {
fileText.append( "\n" );
}
}
biosText = fileText.toString();
}
catch( IOException e )
{
throw new LuaError( "Could not read file" );
}
// Load it
LuaValue program = m_assert.call( m_loadString.call(
toValue( biosText ), toValue( "bios.lua" )
));
m_mainRoutine = m_coroutine_create.call( program );
LuaFunction value = LoadState.load( m_state, bios, "@bios.lua", m_globals );
m_mainRoutine = new LuaThread( m_state, value, m_globals );
}
catch( LuaError e )
catch( CompileException e )
{
if( m_mainRoutine != null )
{
m_mainRoutine.abandon();
m_mainRoutine = null;
}
}
catch( IOException e )
{
ComputerCraft.log.warn( "Could not load bios.lua ", e );
if( m_mainRoutine != null )
{
((LuaThread)m_mainRoutine).abandon();
m_mainRoutine.abandon();
m_mainRoutine = null;
}
}
}
@Override
public void handleEvent( String eventName, Object[] arguments )
{
@@ -206,35 +207,28 @@ public class LuaJLuaMachine implements ILuaMachine
{
return;
}
try
{
LuaValue[] resumeArgs;
{
Varargs resumeArgs = Constants.NONE;
if( eventName != null )
{
resumeArgs = toValues( arguments, 2 );
resumeArgs[0] = m_mainRoutine;
resumeArgs[1] = toValue( eventName );
resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) );
}
else
{
resumeArgs = new LuaValue[1];
resumeArgs[0] = m_mainRoutine;
}
Varargs results = m_coroutine_resume.invoke( LuaValue.varargsOf( resumeArgs ) );
if( m_hardAbortMessage != null )
Varargs results = m_mainRoutine.resume( resumeArgs );
if( m_hardAbortMessage != null )
{
throw new LuaError( m_hardAbortMessage );
}
else if( results.arg1().checkboolean() == false )
else if( !results.first().checkBoolean() )
{
throw new LuaError( results.arg(2).checkstring().toString() );
throw new LuaError( results.arg( 2 ).checkString() );
}
else
{
LuaValue filter = results.arg(2);
if( filter.isstring() )
LuaValue filter = results.arg( 2 );
if( filter.isString() )
{
m_eventFilter = filter.toString();
}
@@ -243,16 +237,16 @@ public class LuaJLuaMachine implements ILuaMachine
m_eventFilter = null;
}
}
LuaThread mainThread = (LuaThread)m_mainRoutine;
if( mainThread.getStatus().equals("dead") )
LuaThread mainThread = m_mainRoutine;
if( mainThread.getStatus().equals( "dead" ) )
{
m_mainRoutine = null;
}
}
catch( LuaError e )
{
((LuaThread)m_mainRoutine).abandon();
m_mainRoutine.abandon();
m_mainRoutine = null;
}
finally
@@ -262,13 +256,13 @@ public class LuaJLuaMachine implements ILuaMachine
}
}
@Override
@Override
public void softAbort( String abortMessage )
{
m_softAbortMessage = abortMessage;
}
@Override
@Override
public void hardAbort( String abortMessage )
{
m_softAbortMessage = abortMessage;
@@ -280,100 +274,88 @@ public class LuaJLuaMachine implements ILuaMachine
{
return false;
}
@Override
public boolean restoreState( InputStream input )
{
return false;
}
@Override
public boolean isFinished()
{
return (m_mainRoutine == null);
}
@Override
public void unload()
{
if( m_mainRoutine != null )
{
LuaThread mainThread = (LuaThread)m_mainRoutine;
LuaThread mainThread = m_mainRoutine;
mainThread.abandon();
m_mainRoutine = null;
}
}
private void tryAbort() throws LuaError
{
// while( m_stopped )
// {
// m_coroutine_yield.call();
// }
String abortMessage = m_softAbortMessage;
if( abortMessage != null )
{
m_softAbortMessage = null;
m_hardAbortMessage = null;
throw new LuaError( abortMessage );
}
}
private LuaTable wrapLuaObject( ILuaObject object )
{
LuaTable table = new LuaTable();
String[] methods = object.getMethodNames();
for(int i=0; i<methods.length; ++i )
for( int i = 0; i < methods.length; ++i )
{
if( methods[i] != null )
if( methods[ i ] != null )
{
final int method = i;
final ILuaObject apiObject = object;
final String methodName = methods[i];
table.set( methodName, new VarArgFunction() {
final String methodName = methods[ i ];
table.rawset( methodName, new VarArgFunction()
{
@Override
public Varargs invoke( Varargs _args )
public Varargs invoke( final LuaState state, Varargs _args ) throws LuaError
{
tryAbort();
Object[] arguments = toObjects( _args, 1 );
Object[] results;
try
{
results = apiObject.callMethod( new ILuaContext() {
results = apiObject.callMethod( new ILuaContext()
{
@Nonnull
@Override
public Object[] pullEvent( String filter ) throws LuaException, InterruptedException
{
Object[] results = pullEventRaw( filter );
if( results.length >= 1 && results[0].equals( "terminate" ) )
if( results.length >= 1 && results[ 0 ].equals( "terminate" ) )
{
throw new LuaException( "Terminated", 0 );
}
return results;
}
@Nonnull
@Override
public Object[] pullEventRaw( String filter ) throws InterruptedException
{
return yield( new Object[] { filter } );
}
@Nonnull
@Override
public Object[] yield( Object[] yieldArgs ) throws InterruptedException
{
try
{
LuaValue[] yieldValues = toValues( yieldArgs, 0 );
Varargs results = m_coroutine_yield.invoke( LuaValue.varargsOf( yieldValues ) );
Varargs results = LuaThread.yield( state, toValues( yieldArgs ) );
return toObjects( results, 1 );
}
catch( OrphanedThread e )
{
throw new InterruptedException();
}
catch( Throwable e )
{
throw new RuntimeException( e );
}
}
@Override
@@ -448,10 +430,10 @@ public class LuaJLuaMachine implements ILuaMachine
Object[] response = pullEvent( "task_complete" );
if( response.length >= 3 && response[ 1 ] instanceof Number && response[ 2 ] instanceof Boolean )
{
if( ( (Number)response[ 1 ] ).intValue() == taskID )
if( ((Number) response[ 1 ]).intValue() == taskID )
{
Object[] returnValues = new Object[ response.length - 3 ];
if( (Boolean)response[ 2 ] )
if( (Boolean) response[ 2 ] )
{
// Extract the return values from the event and return them
System.arraycopy( response, 3, returnValues, 0, returnValues.length );
@@ -460,9 +442,9 @@ public class LuaJLuaMachine implements ILuaMachine
else
{
// Extract the error message from the event and raise it
if( response.length >= 4 && response[3] instanceof String )
if( response.length >= 4 && response[ 3 ] instanceof String )
{
throw new LuaException( (String)response[ 3 ] );
throw new LuaException( (String) response[ 3 ] );
}
else
{
@@ -479,7 +461,7 @@ public class LuaJLuaMachine implements ILuaMachine
catch( InterruptedException e )
{
throw new OrphanedThread();
}
}
catch( LuaException e )
{
throw new LuaError( e.getMessage(), e.getLevel() );
@@ -492,7 +474,7 @@ public class LuaJLuaMachine implements ILuaMachine
}
throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 );
}
return LuaValue.varargsOf( toValues( results, 0 ) );
return toValues( results );
}
} );
}
@@ -500,192 +482,257 @@ public class LuaJLuaMachine implements ILuaMachine
return table;
}
private LuaValue toValue( Object object )
private LuaValue toValue( Object object, Map<Object, LuaValue> values )
{
if( object == null )
{
return LuaValue.NIL;
return Constants.NIL;
}
else if( object instanceof Number )
{
double d = ((Number)object).doubleValue();
return LuaValue.valueOf( d );
double d = ((Number) object).doubleValue();
return valueOf( d );
}
else if( object instanceof Boolean )
{
boolean b = (Boolean) object;
return LuaValue.valueOf( b );
return valueOf( (Boolean) object );
}
else if( object instanceof String )
{
String s = object.toString();
return LuaValue.valueOf( s );
return valueOf( s );
}
else if( object instanceof byte[] )
{
byte[] b = (byte[]) object;
return LuaValue.valueOf( Arrays.copyOf( b, b.length ) );
return valueOf( Arrays.copyOf( b, b.length ) );
}
else if( object instanceof Map )
{
// Table:
// Start remembering stuff
boolean clearWhenDone = false;
try
if( values == null )
{
if( m_valuesInProgress == null )
{
m_valuesInProgress = new IdentityHashMap<>();
clearWhenDone = true;
}
else if( m_valuesInProgress.containsKey( object ) )
{
return m_valuesInProgress.get( object );
}
LuaValue table = new LuaTable();
m_valuesInProgress.put( object, table );
values = new IdentityHashMap<>();
}
else if( values.containsKey( object ) )
{
return values.get( object );
}
LuaTable table = new LuaTable();
values.put( object, table );
// Convert all keys
for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() )
{
LuaValue key = toValue( pair.getKey() );
LuaValue value = toValue( pair.getValue() );
if( !key.isnil() && !value.isnil() )
{
table.set( key, value );
}
}
return table;
}
finally
// Convert all keys
for( Map.Entry<?, ?> pair : ((Map<?, ?>) object).entrySet() )
{
// Clear (if exiting top level)
if( clearWhenDone )
LuaValue key = toValue( pair.getKey(), values );
LuaValue value = toValue( pair.getValue(), values );
if( !key.isNil() && !value.isNil() )
{
m_valuesInProgress = null;
table.rawset( key, value );
}
}
return table;
}
else if( object instanceof ILuaObject )
{
return wrapLuaObject( (ILuaObject)object );
return wrapLuaObject( (ILuaObject) object );
}
else
{
return LuaValue.NIL;
}
return Constants.NIL;
}
}
private LuaValue[] toValues( Object[] objects, int leaveEmpty )
private Varargs toValues( Object[] objects )
{
if( objects == null || objects.length == 0 )
if( objects == null || objects.length == 0 )
{
return new LuaValue[ leaveEmpty ];
return Constants.NONE;
}
LuaValue[] values = new LuaValue[objects.length + leaveEmpty];
for( int i=0; i<values.length; ++i )
LuaValue[] values = new LuaValue[ objects.length ];
for( int i = 0; i < values.length; ++i )
{
if( i < leaveEmpty )
{
values[i] = null;
continue;
}
Object object = objects[i - leaveEmpty];
values[i] = toValue( object );
Object object = objects[ i ];
values[ i ] = toValue( object, null );
}
return values;
return varargsOf( values );
}
private Object toObject( LuaValue value )
private static Object toObject( LuaValue value, Map<LuaValue, Object> objects )
{
switch( value.type() )
{
case LuaValue.TNIL:
case LuaValue.TNONE:
case Constants.TNIL:
case Constants.TNONE:
{
return null;
}
case LuaValue.TINT:
case LuaValue.TNUMBER:
case Constants.TINT:
case Constants.TNUMBER:
{
return value.todouble();
return value.toDouble();
}
case LuaValue.TBOOLEAN:
case Constants.TBOOLEAN:
{
return value.toboolean();
return value.toBoolean();
}
case LuaValue.TSTRING:
case Constants.TSTRING:
{
LuaString str = value.checkstring();
return str.tojstring();
return value.toString();
}
case LuaValue.TTABLE:
case Constants.TTABLE:
{
// Table:
boolean clearWhenDone = false;
try
// Start remembering stuff
if( objects == null )
{
// Start remembering stuff
if( m_objectsInProgress == null )
{
m_objectsInProgress = new IdentityHashMap<>();
clearWhenDone = true;
}
else if( m_objectsInProgress.containsKey( value ) )
{
return m_objectsInProgress.get( value );
}
Map<Object, Object> table = new HashMap<>();
m_objectsInProgress.put( value, table );
// Convert all keys
LuaValue k = LuaValue.NIL;
while( true )
{
Varargs keyValue = value.next( k );
k = keyValue.arg1();
if( k.isnil() )
{
break;
}
LuaValue v = keyValue.arg(2);
Object keyObject = toObject(k);
Object valueObject = toObject(v);
if( keyObject != null && valueObject != null )
{
table.put( keyObject, valueObject );
}
}
return table;
objects = new IdentityHashMap<>();
}
finally
else if( objects.containsKey( value ) )
{
// Clear (if exiting top level)
if( clearWhenDone )
return objects.get( value );
}
Map<Object, Object> table = new HashMap<>();
objects.put( value, table );
LuaTable luaTable = (LuaTable) value;
// Convert all keys
LuaValue k = Constants.NIL;
while( true )
{
Varargs keyValue;
try
{
m_objectsInProgress = null;
keyValue = luaTable.next( k );
}
catch( LuaError luaError )
{
break;
}
k = keyValue.first();
if( k.isNil() )
{
break;
}
LuaValue v = keyValue.arg( 2 );
Object keyObject = toObject( k, objects );
Object valueObject = toObject( v, objects );
if( keyObject != null && valueObject != null )
{
table.put( keyObject, valueObject );
}
}
return table;
}
default:
{
return null;
}
}
}
}
private Object[] toObjects( Varargs values, int startIdx )
private static Object[] toObjects( Varargs values, int startIdx )
{
int count = values.narg();
int count = values.count();
Object[] objects = new Object[ count - startIdx + 1 ];
for( int n=startIdx; n<=count; ++n )
for( int n = startIdx; n <= count; ++n )
{
int i = n - startIdx;
LuaValue value = values.arg(n);
objects[i] = toObject( value );
LuaValue value = values.arg( n );
objects[ i ] = toObject( value, null );
}
return objects;
}
private static class PrefixLoader extends VarArgFunction
{
private static final LuaString FUNCTION_STR = valueOf( "function" );
private static final LuaString EQ_STR = valueOf( "=" );
@Override
public Varargs invoke( LuaState state, Varargs args ) throws LuaError
{
switch (opcode)
{
case 0: // "load", // ( func [,chunkname] ) -> chunk | nil, msg
{
LuaValue func = args.arg( 1 ).checkFunction();
LuaString chunkname = args.arg( 2 ).optLuaString( FUNCTION_STR );
if( !chunkname.startsWith( '@' ) && !chunkname.startsWith( '=' ) )
{
chunkname = OperationHelper.concat( EQ_STR, chunkname );
}
return BaseLib.loadStream( state, new StringInputStream( state, func ), chunkname );
}
case 1: // "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg
{
LuaString script = args.arg( 1 ).checkLuaString();
LuaString chunkname = args.arg( 2 ).optLuaString( script );
if( !chunkname.startsWith( '@' ) && !chunkname.startsWith( '=' ) )
{
chunkname = OperationHelper.concat( EQ_STR, chunkname );
}
return BaseLib.loadStream( state, script.toInputStream(), chunkname );
}
}
return NONE;
}
}
private static class StringInputStream extends InputStream
{
private final LuaState state;
private final LuaValue func;
private byte[] bytes;
private int offset, remaining = 0;
public StringInputStream( LuaState state, LuaValue func )
{
this.state = state;
this.func = func;
}
@Override
public int read() throws IOException
{
if( remaining <= 0 )
{
LuaValue s;
try
{
s = OperationHelper.call( state, func );
} catch (LuaError e)
{
throw new IOException( e );
}
if( s.isNil() )
{
return -1;
}
LuaString ls;
try
{
ls = s.strvalue();
} catch (LuaError e)
{
throw new IOException( e );
}
bytes = ls.bytes;
offset = ls.offset;
remaining = ls.length;
if( remaining <= 0 )
{
return -1;
}
}
--remaining;
return bytes[offset++];
}
}
}

View File

@@ -6,7 +6,9 @@
package dan200.computercraft.server.proxy;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.network.ComputerCraftPacket;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
@@ -64,11 +66,6 @@ public class ComputerCraftProxyServer extends ComputerCraftProxyCommon
{
return null;
}
@Override
public void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos )
{
}
@Override
public Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive )

View File

@@ -0,0 +1,60 @@
package dan200.computercraft.shared.command;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import org.apache.commons.lang3.ArrayUtils;
import javax.annotation.Nonnull;
public class CommandComputer extends CommandBase
{
@Override
@Nonnull
public String getName()
{
return "computer";
}
@Override
@Nonnull
public String getUsage( @Nonnull ICommandSender sender )
{
return "computer <id> <value1> [value2]...";
}
@Override
public void execute( @Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args ) throws CommandException
{
if( args.length < 2 )
{
throw new CommandException( "Usage: /computer <id> <value1> [value2]..." );
}
try
{
ServerComputer computer = ComputerCraft.serverComputerRegistry.lookup( Integer.valueOf( args[ 0 ] ) );
if( computer != null && computer.getFamily() == ComputerFamily.Command )
{
computer.queueEvent( "computer_command", ArrayUtils.remove( args, 0 ) );
}
else
{
throw new CommandException( "Computer #" + args[ 0 ] + " is not a Command Computer" );
}
}
catch( NumberFormatException e )
{
throw new CommandException( "Invalid ID" );
}
}
@Override
public int getRequiredPermissionLevel()
{
return 0;
}
}

View File

@@ -68,7 +68,7 @@ public abstract class BlockGeneric extends Block implements
}
@Override
public final boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest )
public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest )
{
if( !world.isRemote )
{
@@ -111,18 +111,18 @@ public abstract class BlockGeneric extends Block implements
public final void breakBlock( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState newState )
{
TileEntity tile = world.getTileEntity( pos );
super.breakBlock( world, pos, newState );
world.removeTileEntity( pos );
if( tile != null && tile instanceof TileGeneric )
{
TileGeneric generic = (TileGeneric)tile;
generic.destroy();
}
super.breakBlock( world, pos, newState );
world.removeTileEntity( pos );
}
@Nonnull
@Override
public final ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player )
public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player )
{
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileGeneric )
@@ -337,22 +337,6 @@ public abstract class BlockGeneric extends Block implements
return 0;
}
@Override
@Deprecated
public boolean eventReceived( IBlockState state, World world, BlockPos pos, int eventID, int eventParameter )
{
if( world.isRemote )
{
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileGeneric )
{
TileGeneric generic = (TileGeneric)tile;
generic.onBlockEvent( eventID, eventParameter );
}
}
return true;
}
@Nonnull
@Override
public final TileEntity createTileEntity( @Nonnull World world, @Nonnull IBlockState state )

View File

@@ -159,7 +159,7 @@ public abstract class TileGeneric extends TileEntity
double range = getInteractRange( player );
BlockPos pos = getPos();
return player.getEntityWorld() == getWorld() &&
player.getDistanceSq( (double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5 ) <= ( range * range );
player.getDistanceSq( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ) <= ( range * range );
}
return true;
}
@@ -175,20 +175,6 @@ public abstract class TileGeneric extends TileEntity
{
}
public final void sendBlockEvent( int eventID )
{
sendBlockEvent( eventID, 0 );
}
public final void sendBlockEvent( int eventID, int eventParameter )
{
getWorld().addBlockEvent( getPos(), getWorld().getBlockState( getPos() ).getBlock(), eventID, eventParameter );
}
public void onBlockEvent( int eventID, int eventParameter )
{
}
@Override
public boolean shouldRefresh( World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newState )
{

View File

@@ -7,10 +7,8 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.computer.core.ServerComputer;
import javax.annotation.Nonnull;
@@ -18,9 +16,9 @@ public class ComputerPeripheral
implements IPeripheral
{
private final String m_type;
private final ServerComputer m_computer;
private final ComputerProxy m_computer;
public ComputerPeripheral( String type, ServerComputer computer )
public ComputerPeripheral( String type, ComputerProxy computer )
{
m_type = type;
m_computer = computer;
@@ -50,7 +48,7 @@ public class ComputerPeripheral
}
@Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments )
{
switch( method )
{

View File

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

View File

@@ -19,14 +19,14 @@ import net.minecraft.util.EnumFacing;
import net.minecraft.util.NonNullList;
import javax.annotation.Nonnull;
import java.util.List;
public class TileComputer extends TileComputerBase
{
// Statics
// Members
private ComputerProxy m_proxy;
public TileComputer()
{
}
@@ -48,6 +48,23 @@ public class TileComputer extends TileComputerBase
return computer;
}
@Override
public ComputerProxy createProxy()
{
if( m_proxy == null )
{
m_proxy = new ComputerProxy()
{
@Override
protected TileComputerBase getTile()
{
return TileComputer.this;
}
};
}
return m_proxy;
}
@Override
public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative )
{

View File

@@ -37,6 +37,7 @@ public abstract class TileComputerBase extends TileGeneric
protected String m_label;
protected boolean m_on;
protected boolean m_startOn;
protected boolean m_fresh;
protected TileComputerBase()
{
@@ -45,6 +46,7 @@ public abstract class TileComputerBase extends TileGeneric
m_label = null;
m_on = false;
m_startOn = false;
m_fresh = false;
}
@Override
@@ -213,7 +215,7 @@ public abstract class TileComputerBase extends TileGeneric
ServerComputer computer = createServerComputer();
if( computer != null )
{
if( m_startOn )
if( m_startOn || (m_fresh && m_on) )
{
computer.turnOn();
m_startOn = false;
@@ -223,6 +225,7 @@ public abstract class TileComputerBase extends TileGeneric
{
updateOutput();
}
m_fresh = false;
m_computerID = computer.getID();
m_label = computer.getLabel();
m_on = computer.isOn();
@@ -386,6 +389,8 @@ public abstract class TileComputerBase extends TileGeneric
protected abstract ServerComputer createComputer( int instanceID, int id );
public abstract ComputerProxy createProxy();
// ITerminalTile
@Override
@@ -471,6 +476,7 @@ public abstract class TileComputerBase extends TileGeneric
{
ServerComputer computer = createComputer( m_instanceID, m_computerID );
ComputerCraft.serverComputerRegistry.add( m_instanceID, computer );
m_fresh = true;
changed = true;
}
if( changed )

View File

@@ -153,11 +153,8 @@ public class ClientComputer extends ClientTerminal
ComputerCraft.sendToServer( packet );
}
@Override
public void readDescription( NBTTagCompound nbttagcompound )
private void readComputerDescription( NBTTagCompound nbttagcompound )
{
super.readDescription( nbttagcompound );
int oldID = m_computerID;
String oldLabel = m_label;
boolean oldOn = m_on;
@@ -189,10 +186,11 @@ public class ClientComputer extends ClientTerminal
switch( packet.m_packetType )
{
case ComputerCraftPacket.ComputerChanged:
{
readComputerDescription( packet.m_dataNBT );
break;
case ComputerCraftPacket.ComputerTerminalChanged:
readDescription( packet.m_dataNBT );
break;
}
}
}
}

View File

@@ -20,10 +20,13 @@ import dan200.computercraft.shared.network.ComputerCraftPacket;
import dan200.computercraft.shared.network.INetworkedThing;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.Container;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Loader;
import java.io.InputStream;
@@ -36,6 +39,7 @@ public class ServerComputer extends ServerTerminal
private World m_world;
private BlockPos m_position;
private final ComputerFamily m_family;
private final Computer m_computer;
private NBTTagCompound m_userData;
private boolean m_changed;
@@ -51,6 +55,7 @@ public class ServerComputer extends ServerTerminal
m_world = world;
m_position = null;
m_family = family;
m_computer = new Computer( this, getTerminal(), computerID );
m_computer.setLabel( label );
m_userData = null;
@@ -60,6 +65,10 @@ public class ServerComputer extends ServerTerminal
m_ticksSincePing = 0;
}
public ComputerFamily getFamily(){
return m_family;
}
public World getWorld()
{
return m_world;
@@ -131,27 +140,62 @@ public class ServerComputer extends ServerTerminal
{
m_changed = true;
}
public void broadcastState()
{
// Send state to client
private ComputerCraftPacket createComputerPacket() {
ComputerCraftPacket packet = new ComputerCraftPacket();
packet.m_packetType = ComputerCraftPacket.ComputerChanged;
packet.m_dataInt = new int[] { getInstanceID() };
packet.m_dataNBT = new NBTTagCompound();
writeDescription( packet.m_dataNBT );
ComputerCraft.sendToAllPlayers( packet );
writeComputerDescription( packet.m_dataNBT );
return packet;
}
public void sendState( EntityPlayer player )
{
// Send state to client
private ComputerCraftPacket createTerminalPacket() {
ComputerCraftPacket packet = new ComputerCraftPacket();
packet.m_packetType = ComputerCraftPacket.ComputerChanged;
packet.m_packetType = ComputerCraftPacket.ComputerTerminalChanged;
packet.m_dataInt = new int[] { getInstanceID() };
packet.m_dataNBT = new NBTTagCompound();
writeDescription( packet.m_dataNBT );
ComputerCraft.sendToPlayer( player, packet );
return packet;
}
public void broadcastState(boolean force)
{
if(hasOutputChanged() || force)
{
// Send computer state to all clients
ComputerCraft.sendToAllPlayers( createComputerPacket() );
}
if( hasTerminalChanged() || force )
{
// Send terminal state to clients who are currently interacting with the computer.
FMLCommonHandler handler = FMLCommonHandler.instance();
if( handler != null )
{
ComputerCraftPacket packet = createTerminalPacket();
MinecraftServer server = handler.getMinecraftServerInstance();
for( EntityPlayerMP player : server.getPlayerList().getPlayers() )
{
if( isInteracting( player ) )
{
ComputerCraft.sendToPlayer( player, packet );
}
}
}
}
}
public void sendComputerState( EntityPlayer player )
{
// Send state to client
ComputerCraft.sendToPlayer( player, createComputerPacket() );
}
public void sendTerminalState( EntityPlayer player )
{
// Send terminal state to client
ComputerCraft.sendToPlayer( player, createTerminalPacket() );
}
public void broadcastDelete()
@@ -290,7 +334,7 @@ public class ServerComputer extends ServerTerminal
@Override
public double getTimeOfDay()
{
return (double)((m_world.getWorldTime() + 6000) % 24000) / 1000.0;
return (m_world.getWorldTime() + 6000) % 24000 / 1000.0;
}
@Override
@@ -336,12 +380,8 @@ public class ServerComputer extends ServerTerminal
}
// Networking stuff
@Override
public void writeDescription( NBTTagCompound nbttagcompound )
public void writeComputerDescription( NBTTagCompound nbttagcompound )
{
super.writeDescription( nbttagcompound );
nbttagcompound.setInteger( "id", m_computer.getID() );
String label = m_computer.getLabel();
if( label != null )
@@ -362,14 +402,9 @@ public class ServerComputer extends ServerTerminal
public void handlePacket( ComputerCraftPacket packet, EntityPlayer sender )
{
// Allow Computer/Tile updates as they may happen at any time.
if (packet.requiresContainer()) {
if (sender == null) return;
Container container = sender.openContainer;
if (!(container instanceof IContainerComputer)) return;
IComputer computer = ((IContainerComputer) container).getComputer();
if (computer != this) return;
if( packet.requiresContainer() && !isInteracting( sender ) )
{
return;
}
// Receive packets sent from the client to the server
@@ -415,9 +450,20 @@ public class ServerComputer extends ServerTerminal
case ComputerCraftPacket.RequestComputerUpdate:
{
// A player asked for an update on the state of the terminal
sendState( sender );
sendComputerState( sender );
break;
}
}
}
private boolean isInteracting( EntityPlayer player )
{
if( player == null ) return false;
Container container = player.openContainer;
if( !(container instanceof IContainerComputer) ) return false;
IComputer computer = ((IContainerComputer) container).getComputer();
return computer == this;
}
}

View File

@@ -33,7 +33,7 @@ public class ServerComputerRegistry extends ComputerRegistry<ServerComputer>
computer.update();
if( computer.hasTerminalChanged() || computer.hasOutputChanged() )
{
computer.broadcastState();
computer.broadcastState(false);
}
}
}
@@ -44,7 +44,7 @@ public class ServerComputerRegistry extends ComputerRegistry<ServerComputer>
{
//System.out.println( "ADD SERVER COMPUTER " + instanceID );
super.add( instanceID, computer );
computer.broadcastState();
computer.broadcastState(true);
//System.out.println( getComputers().size() + " SERVER COMPUTERS" );
}

View File

@@ -0,0 +1,99 @@
package dan200.computercraft.shared.integration;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.items.ITurtleItem;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.IModRegistry;
import mezz.jei.api.ISubtypeRegistry;
import mezz.jei.api.ISubtypeRegistry.ISubtypeInterpreter;
import mezz.jei.api.JEIPlugin;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
@JEIPlugin
public class JEIComputerCraft implements IModPlugin
{
@Override
public void registerItemSubtypes( ISubtypeRegistry subtypeRegistry )
{
subtypeRegistry.registerSubtypeInterpreter( Item.getItemFromBlock( ComputerCraft.Blocks.turtle ), turtleSubtype );
subtypeRegistry.registerSubtypeInterpreter( Item.getItemFromBlock( ComputerCraft.Blocks.turtleExpanded ), turtleSubtype );
subtypeRegistry.registerSubtypeInterpreter( Item.getItemFromBlock( ComputerCraft.Blocks.turtleAdvanced ), turtleSubtype );
subtypeRegistry.registerSubtypeInterpreter( ComputerCraft.Items.pocketComputer, pocketSubtype );
subtypeRegistry.registerSubtypeInterpreter( ComputerCraft.Items.disk, diskSubtype );
subtypeRegistry.registerSubtypeInterpreter( ComputerCraft.Items.diskExpanded, diskSubtype );
}
@Override
public void register( IModRegistry registry )
{
// Hide treasure disks from the ingredient list
registry.getJeiHelpers().getIngredientBlacklist()
.addIngredientToBlacklist( new ItemStack( ComputerCraft.Items.treasureDisk, OreDictionary.WILDCARD_VALUE ) );
}
/**
* Distinguishes turtles by upgrades and family
*/
private static final ISubtypeInterpreter turtleSubtype = stack -> {
Item item = stack.getItem();
if( !(item instanceof ITurtleItem) ) return "";
ITurtleItem turtle = (ITurtleItem) item;
StringBuilder name = new StringBuilder();
name.append( turtle.getFamily( stack ).toString() );
// Add left and right upgrades to the identifier
ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.Left );
name.append( '|' );
if( left != null ) name.append( left.getUpgradeID() );
ITurtleUpgrade right = turtle.getUpgrade( stack, TurtleSide.Right );
name.append( '|' );
if( right != null ) name.append( '|' ).append( right.getUpgradeID() );
return name.toString();
};
/**
* Distinguishes pocket computers by upgrade and family
*/
private static final ISubtypeInterpreter pocketSubtype = stack -> {
Item item = stack.getItem();
if( !(item instanceof ItemPocketComputer) ) return "";
ItemPocketComputer pocket = (ItemPocketComputer) item;
StringBuilder name = new StringBuilder();
name.append( pocket.getFamily( stack ).toString() );
// Add the upgrade to the identifier
IPocketUpgrade upgrade = pocket.getUpgrade( stack );
name.append( '|' );
if( upgrade != null ) name.append( upgrade.getUpgradeID() );
return name.toString();
};
/**
* Distinguishes disks by colour
*/
private static final ISubtypeInterpreter diskSubtype = stack -> {
Item item = stack.getItem();
if( !(item instanceof ItemDiskLegacy) ) return "";
ItemDiskLegacy disk = (ItemDiskLegacy) item;
int colour = disk.getColour( stack );
return colour == -1 ? "" : String.format( "%06x", colour );
};
}

View File

@@ -30,7 +30,9 @@ public class ComputerCraftPacket
// To client
public static final byte ComputerChanged = 7;
public static final byte ComputerDeleted = 8;
public static final byte ComputerTerminalChanged = 8;
public static final byte ComputerDeleted = 9;
public static final byte PlayRecord = 10;
// Packet class
public byte m_packetType;

View File

@@ -7,20 +7,33 @@
package dan200.computercraft.shared.peripheral.common;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.modem.TileCable;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class BlockCable extends BlockPeripheralBase
{
@@ -29,7 +42,7 @@ public class BlockCable extends BlockPeripheralBase
public static class Properties
{
public static final PropertyEnum<BlockCableModemVariant> MODEM = PropertyEnum.create( "modem", BlockCableModemVariant.class );
public static final PropertyBool CABLE = PropertyBool.create( "cable" );
public static final PropertyEnum<BlockCableCableVariant> CABLE = PropertyEnum.create( "cable", BlockCableCableVariant.class );
public static final PropertyBool NORTH = PropertyBool.create( "north" );
public static final PropertyBool SOUTH = PropertyBool.create( "south" );
public static final PropertyBool EAST = PropertyBool.create( "east" );
@@ -64,7 +77,7 @@ public class BlockCable extends BlockPeripheralBase
setCreativeTab( ComputerCraft.mainCreativeTab );
setDefaultState( this.blockState.getBaseState()
.withProperty( Properties.MODEM, BlockCableModemVariant.None )
.withProperty( Properties.CABLE, true )
.withProperty( Properties.CABLE, BlockCableCableVariant.NONE )
.withProperty( Properties.NORTH, false )
.withProperty( Properties.SOUTH, false )
.withProperty( Properties.EAST, false )
@@ -98,17 +111,17 @@ public class BlockCable extends BlockPeripheralBase
IBlockState state = getDefaultState();
if( meta < 6 )
{
state = state.withProperty( Properties.CABLE, false );
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.NONE );
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.getFront( meta ) ) );
}
else if( meta < 12 )
{
state = state.withProperty( Properties.CABLE, true );
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.ANY );
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( EnumFacing.getFront( meta - 6 ) ) );
}
else if( meta == 13 )
{
state = state.withProperty( Properties.CABLE, true );
state = state.withProperty( Properties.CABLE, BlockCableCableVariant.ANY );
state = state.withProperty( Properties.MODEM, BlockCableModemVariant.None );
}
return state;
@@ -118,7 +131,7 @@ public class BlockCable extends BlockPeripheralBase
public int getMetaFromState( IBlockState state )
{
int meta = 0;
boolean cable = state.getValue( Properties.CABLE );
boolean cable = state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE;
BlockCableModemVariant modem = state.getValue( Properties.MODEM );
if( cable && modem != BlockCableModemVariant.None )
{
@@ -143,20 +156,20 @@ public class BlockCable extends BlockPeripheralBase
case Cable:
{
return getDefaultState()
.withProperty( Properties.CABLE, true )
.withProperty( Properties.CABLE, BlockCableCableVariant.ANY )
.withProperty( Properties.MODEM, BlockCableModemVariant.None );
}
case WiredModem:
default:
{
return getDefaultState()
.withProperty( Properties.CABLE, false )
.withProperty( Properties.CABLE, BlockCableCableVariant.NONE )
.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( placedSide.getOpposite() ) );
}
case WiredModemWithCable:
{
return getDefaultState()
.withProperty( Properties.CABLE, true )
.withProperty( Properties.CABLE, BlockCableCableVariant.ANY )
.withProperty( Properties.MODEM, BlockCableModemVariant.fromFacing( placedSide.getOpposite() ) );
}
}
@@ -164,7 +177,7 @@ public class BlockCable extends BlockPeripheralBase
private boolean doesConnect( IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing dir )
{
if( !state.getValue( Properties.CABLE ) )
if( state.getValue( Properties.CABLE ) == BlockCableCableVariant.NONE )
{
return false;
}
@@ -190,11 +203,30 @@ public class BlockCable extends BlockPeripheralBase
state = state.withProperty( Properties.UP, doesConnect( state, world, pos, EnumFacing.UP ) );
state = state.withProperty( Properties.DOWN, doesConnect( state, world, pos, EnumFacing.DOWN ) );
if( state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE )
{
BlockCableCableVariant direction = null;
if( state.getValue( Properties.WEST ) || state.getValue( Properties.EAST ) )
{
direction = direction == null ? BlockCableCableVariant.X_AXIS : BlockCableCableVariant.ANY;
}
if( state.getValue( Properties.DOWN ) || state.getValue( Properties.UP ) )
{
direction = direction == null ? BlockCableCableVariant.Y_AXIS : BlockCableCableVariant.ANY;
}
if( state.getValue( Properties.NORTH ) || state.getValue( Properties.SOUTH ) )
{
direction = direction == null ? BlockCableCableVariant.Z_AXIS : BlockCableCableVariant.ANY;
}
state = state.withProperty( Properties.CABLE, direction == null ? BlockCableCableVariant.Z_AXIS : direction );
}
int anim;
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TilePeripheralBase )
{
TilePeripheralBase peripheral = (TilePeripheralBase)tile;
TilePeripheralBase peripheral = (TilePeripheralBase) tile;
anim = peripheral.getAnim();
}
else
@@ -207,7 +239,7 @@ public class BlockCable extends BlockPeripheralBase
{
modem = BlockCableModemVariant.values()[
1 + 6 * anim + modem.getFacing().getIndex()
];
];
}
state = state.withProperty( Properties.MODEM, modem );
@@ -230,7 +262,7 @@ public class BlockCable extends BlockPeripheralBase
@Override
public PeripheralType getPeripheralType( IBlockState state )
{
boolean cable = state.getValue( Properties.CABLE );
boolean cable = state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE;
BlockCableModemVariant modem = state.getValue( Properties.MODEM );
if( cable && modem != BlockCableModemVariant.None )
{
@@ -251,4 +283,152 @@ public class BlockCable extends BlockPeripheralBase
{
return new TileCable();
}
@Nullable
@Override
@Deprecated
public RayTraceResult collisionRayTrace( IBlockState blockState, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Vec3d start, @Nonnull Vec3d end )
{
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileGeneric && tile.hasWorld() )
{
TileGeneric generic = (TileGeneric) tile;
double distance = Double.POSITIVE_INFINITY;
RayTraceResult result = null;
List<AxisAlignedBB> bounds = new ArrayList<AxisAlignedBB>( 7 );
generic.getCollisionBounds( bounds );
Vec3d startOff = start.subtract( pos.getX(), pos.getY(), pos.getZ() );
Vec3d endOff = end.subtract( pos.getX(), pos.getY(), pos.getZ() );
for( AxisAlignedBB bb : bounds )
{
RayTraceResult hit = bb.calculateIntercept( startOff, endOff );
if( hit != null )
{
double newDistance = hit.hitVec.squareDistanceTo( startOff );
if( newDistance <= distance )
{
distance = newDistance;
result = hit;
}
}
}
return result == null ? null : new RayTraceResult( result.hitVec.addVector( pos.getX(), pos.getY(), pos.getZ() ), result.sideHit, pos );
}
else
{
return super.collisionRayTrace( blockState, world, pos, start, end );
}
}
@Override
public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest )
{
PeripheralType type = getPeripheralType( world, pos );
if( type == PeripheralType.WiredModemWithCable )
{
RayTraceResult hit = state.collisionRayTrace( world, pos, WorldUtil.getRayStart( player ), WorldUtil.getRayEnd( player ) );
if( hit != null )
{
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileCable && tile.hasWorld() )
{
TileCable cable = (TileCable) tile;
ItemStack item;
AxisAlignedBB bb = cable.getModemBounds();
if( WorldUtil.isVecInsideInclusive( bb, hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
{
world.setBlockState( pos, state.withProperty( Properties.MODEM, BlockCableModemVariant.None ), 3 );
cable.modemChanged();
item = PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 );
}
else
{
world.setBlockState( pos, state.withProperty( Properties.CABLE, BlockCableCableVariant.NONE ), 3 );
item = PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
}
cable.networkChanged();
if( !world.isRemote && !player.capabilities.isCreativeMode ) dropItem( world, pos, item );
return false;
}
}
}
return super.removedByPlayer( state, world, pos, player, willHarvest );
}
@Override
public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult hit, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player )
{
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileCable && tile.hasWorld() )
{
TileCable cable = (TileCable) tile;
PeripheralType type = getPeripheralType( state );
if( type == PeripheralType.WiredModemWithCable )
{
if( hit == null || WorldUtil.isVecInsideInclusive( cable.getModemBounds(), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) )
{
return PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 );
}
else
{
return PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
}
}
else
{
return PeripheralItemFactory.create( type, null, 1 );
}
}
return PeripheralItemFactory.create( PeripheralType.Cable, null, 1 );
}
@Override
public void onBlockPlacedBy( World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack )
{
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileCable )
{
TileCable cable = (TileCable) tile;
if( cable.getPeripheralType() != PeripheralType.WiredModem )
{
cable.networkChanged();
}
}
super.onBlockPlacedBy( world, pos, state, placer, stack );
}
@Override
@Deprecated
public final boolean isOpaqueCube( IBlockState state )
{
return false;
}
@Override
@Deprecated
public final boolean isFullCube( IBlockState state )
{
return false;
}
@Nonnull
@Override
@Deprecated
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
{
return BlockFaceShape.UNDEFINED;
}
}

View File

@@ -0,0 +1,28 @@
package dan200.computercraft.shared.peripheral.common;
import net.minecraft.util.IStringSerializable;
import javax.annotation.Nonnull;
public enum BlockCableCableVariant implements IStringSerializable
{
NONE( "none" ),
ANY( "any" ),
X_AXIS( "x" ),
Y_AXIS( "y" ),
Z_AXIS( "z" ),;
private final String m_name;
BlockCableCableVariant( String name )
{
m_name = name;
}
@Override
@Nonnull
public String getName()
{
return m_name;
}
}

View File

@@ -16,6 +16,7 @@ import dan200.computercraft.shared.peripheral.speaker.TileSpeaker;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
@@ -612,4 +613,29 @@ public class BlockPeripheral extends BlockPeripheralBase
}
}
}
@Override
@Deprecated
public final boolean isOpaqueCube( IBlockState state )
{
PeripheralType type = getPeripheralType( state );
return type == PeripheralType.DiskDrive || type == PeripheralType.Printer
|| type == PeripheralType.Monitor || type == PeripheralType.AdvancedMonitor
|| type == PeripheralType.Speaker;
}
@Override
@Deprecated
public final boolean isFullCube( IBlockState state )
{
return isOpaqueCube( state );
}
@Nonnull
@Override
@Deprecated
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
{
return isOpaqueCube( state ) ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
}

View File

@@ -31,20 +31,6 @@ public abstract class BlockPeripheralBase extends BlockDirectional
protected abstract PeripheralType getPeripheralType( IBlockState state );
protected abstract TilePeripheralBase createTile( PeripheralType type );
@Override
@Deprecated
public final boolean isOpaqueCube( IBlockState state )
{
return false;
}
@Override
@Deprecated
public final boolean isFullCube( IBlockState state )
{
return false;
}
@Override
public final boolean canPlaceBlockOnSide( @Nonnull World world, @Nonnull BlockPos pos, EnumFacing side )
{

View File

@@ -45,12 +45,12 @@ public class DefaultPeripheralProvider implements IPeripheralProvider
{
if( !((TileTurtle)tile).hasMoved() )
{
return new ComputerPeripheral( "turtle", computerTile.createServerComputer() );
return new ComputerPeripheral( "turtle", computerTile.createProxy() );
}
}
else
{
return new ComputerPeripheral( "computer", computerTile.createServerComputer() );
return new ComputerPeripheral( "computer", computerTile.createProxy() );
}
}
}

View File

@@ -89,12 +89,12 @@ public class ItemCable extends ItemPeripheralBase
{
if( !stack.isEmpty() )
{
IBlockState newState = existingState.withProperty( BlockCable.Properties.CABLE, true );
IBlockState newState = existingState.withProperty( BlockCable.Properties.CABLE, BlockCableCableVariant.ANY );
world.setBlockState( pos, newState, 3 );
SoundType soundType = newState.getBlock().getSoundType( newState, world, pos, player );
world.playSound( null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, soundType.getPlaceSound(), SoundCategory.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F );
stack.shrink( 1 );
TileEntity tile = world.getTileEntity( pos );
if( tile != null && tile instanceof TileCable )
{
@@ -143,7 +143,7 @@ public class ItemCable extends ItemPeripheralBase
{
if( !stack.isEmpty() )
{
IBlockState newState = offsetExistingState.withProperty( BlockCable.Properties.CABLE, true );
IBlockState newState = offsetExistingState.withProperty( BlockCable.Properties.CABLE, BlockCableCableVariant.ANY );
world.setBlockState( offset, newState, 3 );
SoundType soundType = newState.getBlock().getSoundType( newState, world, offset, player );
world.playSound( null, offset.getX() + 0.5, offset.getY() + 0.5, offset.getZ() + 0.5, soundType.getPlaceSound(), SoundCategory.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F );

View File

@@ -7,7 +7,6 @@
package dan200.computercraft.shared.peripheral.common;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.common.IDirectionalTile;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.PeripheralType;
import net.minecraft.item.ItemStack;
@@ -20,7 +19,7 @@ import javax.annotation.Nonnull;
import java.util.List;
public abstract class TilePeripheralBase extends TileGeneric
implements IPeripheralTile, IDirectionalTile, ITickable
implements IPeripheralTile, ITickable
{
// Statics

View File

@@ -24,7 +24,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ITickable;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
@@ -44,13 +43,8 @@ import java.util.Set;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
public class TileDiskDrive extends TilePeripheralBase
implements IInventory, ITickable
implements IInventory
{
// Statics
private static final int BLOCKEVENT_PLAY_RECORD = 0;
private static final int BLOCKEVENT_STOP_RECORD = 1;
private static class MountInfo
{
public String mountPath;
@@ -90,7 +84,7 @@ public class TileDiskDrive extends TilePeripheralBase
{
if( m_recordPlaying )
{
sendBlockEvent( BLOCKEVENT_STOP_RECORD );
stopRecord();
}
}
}
@@ -188,7 +182,7 @@ public class TileDiskDrive extends TilePeripheralBase
// Music
synchronized( this )
{
if( m_recordPlaying != m_recordQueued || m_restartRecord )
if( !world.isRemote && m_recordPlaying != m_recordQueued || m_restartRecord )
{
m_restartRecord = false;
if( m_recordQueued )
@@ -198,7 +192,7 @@ public class TileDiskDrive extends TilePeripheralBase
if( record != null )
{
m_recordPlaying = true;
sendBlockEvent( BLOCKEVENT_PLAY_RECORD );
playRecord();
}
else
{
@@ -207,7 +201,7 @@ public class TileDiskDrive extends TilePeripheralBase
}
else
{
sendBlockEvent( BLOCKEVENT_STOP_RECORD );
stopRecord();
m_recordPlaying = false;
}
}
@@ -306,7 +300,7 @@ public class TileDiskDrive extends TilePeripheralBase
// Stop music
if( m_recordPlaying )
{
sendBlockEvent( BLOCKEVENT_STOP_RECORD );
stopRecord();
m_recordPlaying = false;
m_recordQueued = false;
}
@@ -615,13 +609,13 @@ public class TileDiskDrive extends TilePeripheralBase
}
BlockPos pos = getPos();
double x = (double)pos.getX() + 0.5 + ((double)xOff * 0.5);
double y = (double)pos.getY() + 0.75;
double z = (double)pos.getZ() + 0.5 + ((double)zOff * 0.5);
double x = pos.getX() + 0.5 + (xOff * 0.5);
double y = pos.getY() + 0.75;
double z = pos.getZ() + 0.5 + (zOff * 0.5);
EntityItem entityitem = new EntityItem( getWorld(), x, y, z, disks );
entityitem.motionX = (double)xOff * 0.15;
entityitem.motionX = xOff * 0.15;
entityitem.motionY = 0.0;
entityitem.motionZ = (double)zOff * 0.15;
entityitem.motionZ = zOff * 0.15;
getWorld().spawnEntity(entityitem);
if( !destroyed )
@@ -658,25 +652,6 @@ public class TileDiskDrive extends TilePeripheralBase
}
}
@Override
public void onBlockEvent( int eventID, int eventParameter )
{
super.onBlockEvent( eventID, eventParameter );
switch( eventID )
{
case BLOCKEVENT_PLAY_RECORD:
{
playRecord();
break;
}
case BLOCKEVENT_STOP_RECORD:
{
stopRecord();
break;
}
}
}
@Override
public boolean shouldRefresh( World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newState )
{

View File

@@ -12,11 +12,12 @@ import dan200.computercraft.shared.peripheral.common.BlockPeripheralBase;
import dan200.computercraft.shared.peripheral.common.TilePeripheralBase;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import javax.annotation.Nonnull;
@@ -114,4 +115,26 @@ public class BlockAdvancedModem extends BlockPeripheralBase
{
return new TileAdvancedModem();
}
@Override
@Deprecated
public final boolean isOpaqueCube( IBlockState state )
{
return false;
}
@Override
@Deprecated
public final boolean isFullCube( IBlockState state )
{
return false;
}
@Nonnull
@Override
@Deprecated
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
{
return BlockFaceShape.UNDEFINED;
}
}

View File

@@ -41,7 +41,7 @@ public class TileAdvancedModem extends TileModemBase
public Vec3d getPosition()
{
BlockPos pos = m_entity.getPos().offset( m_entity.getDirection() );
return new Vec3d( (double)pos.getX(), (double)pos.getY(), (double)pos.getZ() );
return new Vec3d( pos.getX(), pos.getY(), pos.getZ() );
}
@Override

View File

@@ -46,8 +46,8 @@ import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class TileCable extends TileModemBase
implements IPacketNetwork
{
private static final double MIN = 0.375;
private static final double MAX = 1 - MIN;
public static final double MIN = 0.375;
public static final double MAX = 1 - MIN;
private static final AxisAlignedBB BOX_CENTRE = new AxisAlignedBB( MIN, MIN, MIN, MAX, MAX, MAX );
private static final AxisAlignedBB[] BOXES = new AxisAlignedBB[]{
@@ -101,7 +101,7 @@ public class TileCable extends TileModemBase
{
EnumFacing direction = m_entity.getDirection();
BlockPos pos = m_entity.getPos().offset( direction );
return new Vec3d( (double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5 );
return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
}
@Nonnull
@@ -359,6 +359,7 @@ public class TileCable extends TileModemBase
((BlockGeneric)getBlockType()).dropItem( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, getLabel(), 1 ) );
setLabel( null );
setBlockState( getBlockState().withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None ) );
if( modemChanged() ) networkChanged();
break;
}
}
@@ -682,7 +683,7 @@ public class TileCable extends TileModemBase
{
if( !getWorld().isRemote )
{
if( !m_destroyed )
if( !m_destroyed && getPeripheralType() != PeripheralType.WiredModem)
{
// If this modem is alive, rebuild the network
searchNetwork( ( modem, distance ) ->
@@ -712,6 +713,29 @@ public class TileCable extends TileModemBase
}
}
}
public boolean modemChanged()
{
if( getWorld().isRemote ) return false;
boolean requiresUpdate = false;
PeripheralType type = getPeripheralType();
if( type == PeripheralType.Cable )
{
m_attachedPeripheralID = -1;
}
if( type != PeripheralType.WiredModemWithCable && m_peripheralAccessAllowed )
{
m_peripheralAccessAllowed = false;
requiresUpdate = true;
markDirty();
updateAnim();
}
return requiresUpdate;
}
// private stuff
@@ -1032,13 +1056,19 @@ public class TileCable extends TileModemBase
Queue<SearchLoc> queue = new LinkedList<>();
enqueue( queue, getWorld(), getPos(), 1 );
int visited = 0;
//int visited = 0;
while( queue.peek() != null )
{
SearchLoc loc = queue.remove();
visitBlock( queue, loc, searchID, visitor );
visited++;
//visited++;
}
//System.out.println( "Visited "+visited+" common" );
}
@Override
public boolean canRenderBreaking()
{
return true;
}
}

View File

@@ -45,7 +45,7 @@ public class TileWirelessModem extends TileModemBase
public Vec3d getPosition()
{
BlockPos pos = m_entity.getPos().offset( m_entity.getDirection() );
return new Vec3d( (double)pos.getX(), (double)pos.getY(), (double)pos.getZ() );
return new Vec3d( pos.getX(), pos.getY(), pos.getZ() );
}
@Override

View File

@@ -31,7 +31,7 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral
{
if( m_advanced )
{
return (double)Integer.MAX_VALUE;
return Integer.MAX_VALUE;
}
else
{
@@ -39,12 +39,12 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral
if( world != null )
{
Vec3d position = getPosition();
double minRange = (double) ComputerCraft.modem_range;
double maxRange = (double) ComputerCraft.modem_highAltitudeRange;
double minRange = ComputerCraft.modem_range;
double maxRange = ComputerCraft.modem_highAltitudeRange;
if( world.isRaining() && world.isThundering() )
{
minRange = (double) ComputerCraft.modem_rangeDuringStorm;
maxRange = (double) ComputerCraft.modem_highAltitudeRangeDuringStorm;
minRange = ComputerCraft.modem_rangeDuringStorm;
maxRange = ComputerCraft.modem_highAltitudeRangeDuringStorm;
}
if( position.y > 96.0 && maxRange > minRange )
{

View File

@@ -65,7 +65,8 @@ public class MonitorPeripheral implements IPeripheral
"setPaletteColour",
"setPaletteColor",
"getPaletteColour",
"getPaletteColor"
"getPaletteColor",
"getTextScale"
};
}
@@ -249,6 +250,11 @@ public class MonitorPeripheral implements IPeripheral
}
return null;
}
case 24:
{
// getTextScale
return new Object[] { m_monitor.getTextScale() };
}
}
return null;
}

View File

@@ -290,7 +290,7 @@ public class TileMonitor extends TilePeripheralBase
public double getTextScale()
{
return (double)m_textScale * 0.5;
return m_textScale * 0.5;
}
private void rebuildTerminal()
@@ -301,11 +301,11 @@ public class TileMonitor extends TilePeripheralBase
double textScale = getTextScale();
int termWidth = (int)Math.max(
Math.round( ((double)m_width - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 6.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
Math.round( (m_width - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 6.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
1.0
);
int termHeight = (int)Math.max(
Math.round( ((double)m_height - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 9.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
Math.round( (m_height - 2.0 * ( TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN )) / (textScale * 9.0 * TileMonitor.RENDER_PIXEL_SCALE) ),
1.0
);
((ServerTerminal)getLocalTerminal()).resize( termWidth, termHeight );
@@ -709,11 +709,11 @@ public class TileMonitor extends TilePeripheralBase
return;
}
double xCharWidth = ((double)m_width - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / ((double)originTerminal.getWidth());
double yCharHeight = ((double)m_height - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / ((double)originTerminal.getHeight());
double xCharWidth = (m_width - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / (originTerminal.getWidth());
double yCharHeight = (m_height - ((RENDER_BORDER + RENDER_MARGIN) * 2.0)) / (originTerminal.getHeight());
int xCharPos = (int)Math.min((double)originTerminal.getWidth(), Math.max(((pair.x - RENDER_BORDER - RENDER_MARGIN) / xCharWidth) + 1.0, 1.0));
int yCharPos = (int)Math.min((double)originTerminal.getHeight(), Math.max(((pair.y - RENDER_BORDER - RENDER_MARGIN) / yCharHeight) + 1.0, 1.0));
int xCharPos = (int)Math.min(originTerminal.getWidth(), Math.max(((pair.x - RENDER_BORDER - RENDER_MARGIN) / xCharWidth) + 1.0, 1.0));
int yCharPos = (int)Math.min(originTerminal.getHeight(), Math.max(((pair.y - RENDER_BORDER - RENDER_MARGIN) / yCharHeight) + 1.0, 1.0));
for( int y=0; y<m_height; ++y )
{

View File

@@ -17,7 +17,6 @@ import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -42,7 +41,7 @@ import javax.annotation.Nullable;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
public class TilePrinter extends TilePeripheralBase
implements IInventory, ISidedInventory
implements ISidedInventory
{
// Statics
@@ -493,21 +492,6 @@ public class TilePrinter extends TilePeripheralBase
ItemStack paperStack = m_inventory.get( i );
if( !paperStack.isEmpty() && isPaper(paperStack) )
{
// Decrement ink
inkStack.shrink( 1 );
if( inkStack.isEmpty() )
{
m_inventory.set( 0, ItemStack.EMPTY );
}
// Decrement paper
paperStack.shrink( 1 );
if( paperStack.isEmpty() )
{
m_inventory.set( i, ItemStack.EMPTY );
updateAnim();
}
// Setup the new page
int colour = inkStack.getItemDamage();
if( colour >= 0 && colour < 16 ) {
@@ -532,6 +516,21 @@ public class TilePrinter extends TilePeripheralBase
m_pageTitle = "";
}
m_page.setCursorPos( 0, 0 );
// Decrement ink
inkStack.shrink( 1 );
if( inkStack.isEmpty() )
{
m_inventory.set( 0, ItemStack.EMPTY );
}
// Decrement paper
paperStack.shrink( 1 );
if( paperStack.isEmpty() )
{
m_inventory.set( i, ItemStack.EMPTY );
updateAnim();
}
markDirty();
m_printing = true;
@@ -583,9 +582,9 @@ public class TilePrinter extends TilePeripheralBase
// Spawn the item in the world
BlockPos pos = getPos();
double x = (double)pos.getX() + 0.5;
double y = (double)pos.getY() + 0.75;
double z = (double)pos.getZ() + 0.5;
double x = pos.getX() + 0.5;
double y = pos.getY() + 0.75;
double z = pos.getZ() + 0.5;
EntityItem entityitem = new EntityItem( getWorld(), x, y, z, stack );
entityitem.motionX = getWorld().rand.nextFloat() * 0.2 - 0.1;
entityitem.motionY = getWorld().rand.nextFloat() * 0.2 - 0.1;

View File

@@ -59,6 +59,11 @@ public class SpeakerPeripheral implements IPeripheral {
return m_speaker.getPos();
}
public synchronized boolean madeSound(long ticks)
{
return (m_clock - m_lastPlayTime <= ticks) ;
}
/* IPeripheral implementation */
@Override
@@ -170,7 +175,7 @@ public class SpeakerPeripheral implements IPeripheral {
{
@Nullable
@Override
public Object[] execute() throws LuaException
public Object[] execute()
{
world.playSound( null, pos, SoundEvent.REGISTRY.getObject( resource ), SoundCategory.RECORDS, Math.min( vol, 3f ), soundPitch );
return null;

View File

@@ -402,10 +402,10 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
@Override
public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
{
ServerComputer computer = createServerComputer( world, null, null, stack );
if( computer != null )
int id = getComputerID( stack );
if( id >= 0 )
{
return computer.getRootMount();
return ComputerCraft.createSaveDirMount( world, "computer/" + id, ComputerCraft.computerSpaceLimit );
}
return null;
}

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
@@ -73,6 +74,7 @@ public class PocketSpeaker implements IPocketUpgrade
speaker.setLocation( entity.getEntityWorld(), entity.posX, entity.posY, entity.posZ );
}
speaker.update();
access.setLight( speaker.madeSound(20) ? 0x3320fc : -1 );
}
}
}

View File

@@ -18,6 +18,8 @@ import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
@@ -58,6 +60,7 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.Container;
import net.minecraft.item.Item;
import net.minecraft.item.ItemRecord;
import net.minecraft.item.ItemStack;
@@ -70,6 +73,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -145,7 +149,22 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
}
@Override
public abstract void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos );
public void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos )
{
ComputerCraftPacket packet = new ComputerCraftPacket();
packet.m_packetType = ComputerCraftPacket.PlayRecord;
if( record != null )
{
packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ(), SoundEvent.REGISTRY.getIDForObject( record ) };
packet.m_dataString = new String[] { recordInfo };
}
else
{
packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ() };
}
ComputerCraft.sendToAllPlayers( packet );
}
@Override
public abstract Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive );
@@ -646,5 +665,20 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
ComputerCraft.syncConfig();
}
}
@SubscribeEvent
public void onContainerOpen( PlayerContainerEvent.Open event )
{
// If we're opening a computer container then broadcast the terminal state
Container container = event.getContainer();
if( container instanceof IContainerComputer )
{
IComputer computer = ((IContainerComputer) container).getComputer();
if( computer instanceof ServerComputer )
{
((ServerComputer) computer).sendTerminalState( event.getEntityPlayer() );
}
}
}
}
}

View File

@@ -13,6 +13,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
@@ -75,6 +76,14 @@ public class BlockTurtle extends BlockComputerBase
return false;
}
@Nonnull
@Override
@Deprecated
public BlockFaceShape getBlockFaceShape( IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side )
{
return BlockFaceShape.UNDEFINED;
}
@Nonnull
@Override
protected BlockStateContainer createBlockState()

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
@@ -43,12 +44,11 @@ import net.minecraftforge.items.wrapper.InvWrapper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
public class TileTurtle extends TileComputerBase
implements ITurtleTile, IInventory, ITickable
implements ITurtleTile, IInventory
{
// Statics
@@ -114,6 +114,12 @@ public class TileTurtle extends TileComputerBase
return createComputer( instanceID, id, ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle );
}
@Override
public ComputerProxy createProxy()
{
return m_brain.getProxy();
}
@Override
public void destroy()
{

View File

@@ -12,6 +12,8 @@ import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
@@ -104,6 +106,7 @@ public class TurtleBrain implements ITurtleAccess
private static final int ANIM_DURATION = 8;
private TileTurtle m_owner;
private ComputerProxy m_proxy;
private LinkedList<TurtleCommandQueueEntry> m_commandQueue;
private int m_commandsIssued;
@@ -169,6 +172,21 @@ public class TurtleBrain implements ITurtleAccess
return m_owner;
}
public ComputerProxy getProxy()
{
if(m_proxy == null) {
m_proxy = new ComputerProxy()
{
@Override
protected TileComputerBase getTile()
{
return m_owner;
}
};
}
return m_proxy;
}
public ComputerFamily getFamily()
{
return m_owner.getFamily();
@@ -936,11 +954,11 @@ public class TurtleBrain implements ITurtleAccess
}
}
double distance = -1.0 + (double)getAnimationFraction( f );
double distance = -1.0 + getAnimationFraction( f );
return new Vec3d(
distance * (double)dir.getFrontOffsetX(),
distance * (double)dir.getFrontOffsetY(),
distance * (double)dir.getFrontOffsetZ()
distance * dir.getFrontOffsetX(),
distance * dir.getFrontOffsetY(),
distance * dir.getFrontOffsetZ()
);
}
default:
@@ -955,7 +973,7 @@ public class TurtleBrain implements ITurtleAccess
if( (side == TurtleSide.Left && m_animation == TurtleAnimation.SwingLeftTool) ||
(side == TurtleSide.Right && m_animation == TurtleAnimation.SwingRightTool) )
{
return 45.0f * (float)Math.sin( (double) getAnimationFraction( f ) * Math.PI );
return 45.0f * (float)Math.sin( getAnimationFraction( f ) * Math.PI );
}
return 0.0f;
}
@@ -1126,39 +1144,39 @@ public class TurtleBrain implements ITurtleAccess
float push = Math.max( pushFrac + 0.0125f, 0.0f );
if (moveDir.getFrontOffsetX() < 0)
{
minX += (double)((float)moveDir.getFrontOffsetX() * push);
minX += moveDir.getFrontOffsetX() * push;
}
else
{
maxX -= (double)((float)moveDir.getFrontOffsetX() * push);
maxX -= moveDir.getFrontOffsetX() * push;
}
if (moveDir.getFrontOffsetY() < 0)
{
minY += (double)((float)moveDir.getFrontOffsetY() * push);
minY += moveDir.getFrontOffsetY() * push;
}
else
{
maxY -= (double)((float)moveDir.getFrontOffsetY() * push);
maxY -= moveDir.getFrontOffsetY() * push;
}
if (moveDir.getFrontOffsetZ() < 0)
{
minZ += (double)((float)moveDir.getFrontOffsetZ() * push);
minZ += moveDir.getFrontOffsetZ() * push;
}
else
{
maxZ -= (double)((float)moveDir.getFrontOffsetZ() * push);
maxZ -= moveDir.getFrontOffsetZ() * push;
}
AxisAlignedBB aabb = new AxisAlignedBB( minX, minY, minZ, maxX, maxY, maxZ );
List<Entity> list = world.getEntitiesWithinAABBExcludingEntity( null, aabb );
if( !list.isEmpty() )
{
double pushStep = 1.0f / (float) ANIM_DURATION;
double pushStepX = (double) moveDir.getFrontOffsetX() * pushStep;
double pushStepY = (double) moveDir.getFrontOffsetY() * pushStep;
double pushStepZ = (double) moveDir.getFrontOffsetZ() * pushStep;
double pushStep = 1.0f / ANIM_DURATION;
double pushStepX = moveDir.getFrontOffsetX() * pushStep;
double pushStepY = moveDir.getFrontOffsetY() * pushStep;
double pushStepZ = moveDir.getFrontOffsetZ() * pushStep;
for (Entity entity : list)
{
entity.move( MoverType.PISTON, pushStepX, pushStepY, pushStepZ );

View File

@@ -85,9 +85,9 @@ public class TurtleMoveCommand implements ITurtleCommand
if( entityBB != null )
{
AxisAlignedBB pushedBB = entityBB.offset(
(double) direction.getFrontOffsetX(),
(double) direction.getFrontOffsetY(),
(double) direction.getFrontOffsetZ()
direction.getFrontOffsetX(),
direction.getFrontOffsetY(),
direction.getFrontOffsetZ()
);
if( !oldWorld.getCollisionBoxes( null, pushedBB ).isEmpty() )
{

View File

@@ -9,7 +9,6 @@ package dan200.computercraft.shared.turtle.items;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.util.ColourUtils;
import net.minecraft.block.Block;
@@ -20,7 +19,7 @@ import net.minecraftforge.common.util.Constants;
import javax.annotation.Nonnull;
public class ItemTurtleNormal extends ItemTurtleBase implements IColouredItem
public class ItemTurtleNormal extends ItemTurtleBase
{
public ItemTurtleNormal( Block block )
{

View File

@@ -32,7 +32,6 @@ import javax.vecmath.Matrix4f;
public class TurtleModem implements ITurtleUpgrade
{
private static class Peripheral extends WirelessModemPeripheral
implements IPeripheral
{
private final ITurtleAccess m_turtle;
@@ -55,9 +54,9 @@ public class TurtleModem implements ITurtleUpgrade
{
BlockPos turtlePos = m_turtle.getPosition();
return new Vec3d(
(double)turtlePos.getX(),
(double)turtlePos.getY(),
(double)turtlePos.getZ()
turtlePos.getX(),
turtlePos.getY(),
turtlePos.getZ()
);
}

View File

@@ -52,9 +52,9 @@ public enum Colour
{
m_hex = hex;
m_rgb = new float[] {
(float)((hex >> 16) & 0xFF) / 255.0f,
(float)((hex >> 8 ) & 0xFF) / 255.0f,
(float)((hex ) & 0xFF) / 255.0f,
((hex >> 16) & 0xFF) / 255.0f,
((hex >> 8 ) & 0xFF) / 255.0f,
((hex ) & 0xFF) / 255.0f,
};
}

View File

@@ -38,7 +38,7 @@ public class ColourTracker
int avgB = totalB / count;
float avgTotal = (float) total / (float) count;
float avgMax = (float) Math.max( avgR, Math.max( avgG, avgB ) );
float avgMax = Math.max( avgR, Math.max( avgG, avgB ) );
avgR = (int) (avgR * avgTotal / avgMax);
avgG = (int) (avgG * avgTotal / avgMax);
avgB = (int) (avgB * avgTotal / avgMax);

View File

@@ -9,6 +9,8 @@ package dan200.computercraft.shared.util;
import dan200.computercraft.ComputerCraft;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nonnull;
@@ -16,18 +18,20 @@ public class CreativeTabMain extends CreativeTabs
{
public CreativeTabMain( int i )
{
super( i, "ComputerCraft" );
super( i, "CC: Tweaked" );
}
@Nonnull
@Override
@SideOnly(Side.CLIENT)
public ItemStack getTabIconItem()
{
return new ItemStack( ComputerCraft.Blocks.computer );
}
@Nonnull
@Override
@SideOnly(Side.CLIENT)
public String getTranslatedTabLabel()
{
return getTabLabel();

View File

@@ -6,11 +6,18 @@
package dan200.computercraft.shared.util;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
@@ -47,9 +54,9 @@ public class WorldUtil
}
// Check for entities
float xStretch = (Math.abs(vecDir.x) > 0.25f) ? 0.0f : 1.0f;
float yStretch = (Math.abs(vecDir.y) > 0.25f) ? 0.0f : 1.0f;
float zStretch = (Math.abs(vecDir.z) > 0.25f) ? 0.0f : 1.0f;
float xStretch = Math.abs(vecDir.x) > 0.25f ? 0.0f : 1.0f;
float yStretch = Math.abs(vecDir.y) > 0.25f ? 0.0f : 1.0f;
float zStretch = Math.abs(vecDir.z) > 0.25f ? 0.0f : 1.0f;
AxisAlignedBB bigBox = new AxisAlignedBB(
Math.min(vecStart.x, vecEnd.x) - 0.375f * xStretch,
Math.min(vecStart.y, vecEnd.y) - 0.375f * yStretch,
@@ -113,6 +120,35 @@ public class WorldUtil
return null;
}
public static Vec3d getRayStart( EntityLivingBase entity )
{
return new Vec3d( entity.posX, entity.posY + entity.getEyeHeight(), entity.posZ );
}
public static Vec3d getRayEnd( EntityPlayer player) {
double reach = 4.5;
if( player instanceof EntityPlayerMP )
{
reach = ((EntityPlayerMP) player).interactionManager.getBlockReachDistance();
}
else if( player.getEntityWorld().isRemote )
{
reach = Minecraft.getMinecraft().playerController.getBlockReachDistance();
}
else if( player.capabilities.isCreativeMode )
{
reach = 5.0;
}
Vec3d look = player.getLookVec();
return getRayStart( player ).addVector( look.x * reach, look.y * reach, look.z * reach );
}
public static boolean isVecInsideInclusive(AxisAlignedBB bb , Vec3d vec) {
return vec.x >= bb.minX && vec.x <= bb.maxX && vec.y >= bb.minY && vec.y <= bb.maxY && vec.z >= bb.minZ && vec.z <= bb.maxZ;
}
public static void dropItemStack( @Nonnull ItemStack stack, World world, BlockPos pos )
{
dropItemStack( stack, world, pos, null );
@@ -125,9 +161,9 @@ public class WorldUtil
double zDir;
if( direction != null )
{
xDir = (double)direction.getFrontOffsetX();
yDir = (double)direction.getFrontOffsetY();
zDir = (double)direction.getFrontOffsetZ();
xDir = direction.getFrontOffsetX();
yDir = direction.getFrontOffsetY();
zDir = direction.getFrontOffsetZ();
}
else
{

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_pocket_upgrade_computercraft_advanved_modem" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:pocket_computer", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_pocket_upgrade_computercraft_advanved_modem" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_pocket_upgrade_computercraft_speaker" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:pocket_computer", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_pocket_upgrade_computercraft_speaker" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_pocket_upgrade_computercraft_wireless_modem" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:pocket_computer", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_pocket_upgrade_computercraft_wireless_modem" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_computercraft_advanced_modem_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_computercraft_advanced_modem_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_computercraft_speaker_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_computercraft_speaker_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_computercraft_wireless_modem_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_computercraft_wireless_modem_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_crafting_table_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_crafting_table_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_axe_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_axe_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_hoe_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_hoe_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_pickaxe_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_pickaxe_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_shovel_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_shovel_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:advanced_turtle_upgrade_minecraft_diamond_sword_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:turtle_advanced", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:advanced_turtle_upgrade_minecraft_diamond_sword_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:disk_imposter_1" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:peripheral", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:disk_imposter_1" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:disk_imposter_10" ]
},
"criteria": {
"has_normal": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [ { "item": "computercraft:peripheral", "data": 0 } ]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "computercraft:disk_imposter_10" }
}
},
"requirements": [
[
"has_normal",
"has_the_recipe"
]
]
}

Some files were not shown because too many files have changed in this diff Show More