mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-08 09:23:00 +00:00
Compare commits
260 Commits
1.80pr1
...
feature/on
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8819f2559d | ||
|
|
4b741739e8 | ||
|
|
f23acef2dd | ||
|
|
ac8444b364 | ||
|
|
3b4c1eac1c | ||
|
|
7cc77cb1ed | ||
|
|
0f70d68d0d | ||
|
|
c6bd88f3ad | ||
|
|
efa57521c7 | ||
|
|
4700f8831b | ||
|
|
9428bee316 | ||
|
|
89c7183a1d | ||
|
|
d2a9e7e458 | ||
|
|
1774f1a079 | ||
|
|
de1307913b | ||
|
|
093132533d | ||
|
|
0685be6bfa | ||
|
|
f40733e9a6 | ||
|
|
a3d1cff298 | ||
|
|
b8957cab5c | ||
|
|
3ac8dde779 | ||
|
|
17dace979a | ||
|
|
d405316a4b | ||
|
|
7e18f2cead | ||
|
|
000786a1a7 | ||
|
|
0bf13562b9 | ||
|
|
45a189e834 | ||
|
|
0ce6f34a09 | ||
|
|
4d984dc5ee | ||
|
|
984d358930 | ||
|
|
a95893b823 | ||
|
|
81aaead032 | ||
|
|
a2083bcff1 | ||
|
|
052f2a16dc | ||
|
|
fd10ed6f62 | ||
|
|
c0bdd4ff1d | ||
|
|
5f0addbc3e | ||
|
|
c9589ad0e7 | ||
|
|
b21c495815 | ||
|
|
18429c50f9 | ||
|
|
7ec8ddcf7d | ||
|
|
5bf9f9e3c5 | ||
|
|
e4164ee9a1 | ||
|
|
b522af3075 | ||
|
|
8775052dee | ||
|
|
c0c5d57e10 | ||
|
|
4c2e97b1af | ||
|
|
f17df15117 | ||
|
|
bfbb18bdfc | ||
|
|
cac65ef755 | ||
|
|
a42793024b | ||
|
|
7dbc4e6455 | ||
|
|
a0d71cb3ad | ||
|
|
83546d0acb | ||
|
|
e2f9ddd534 | ||
|
|
911e404bfa | ||
|
|
bfeafe163f | ||
|
|
6cf32f1f74 | ||
|
|
04f162ef25 | ||
|
|
b2aa390ae1 | ||
|
|
6ca61f000f | ||
|
|
20a47a7f88 | ||
|
|
e2e6946c92 | ||
|
|
abe917cd54 | ||
|
|
a1d77ab8e7 | ||
|
|
c8db671409 | ||
|
|
52641b7bea | ||
|
|
3e751ee94a | ||
|
|
2b28cc3558 | ||
|
|
f9761388b1 | ||
|
|
d28694eb57 | ||
|
|
d758895578 | ||
|
|
043d5f00ca | ||
|
|
36878e75b7 | ||
|
|
ebb50cba48 | ||
|
|
7c218361d9 | ||
|
|
bb2eab0bed | ||
|
|
e8c0cf3857 | ||
|
|
db825a7aab | ||
|
|
dbcae810f0 | ||
|
|
914df8b0c7 | ||
|
|
f753513289 | ||
|
|
7bb8efed1d | ||
|
|
0cec4aee8c | ||
|
|
e0c9dc24e7 | ||
|
|
244907a39a | ||
|
|
9be61abd6b | ||
|
|
922f424a78 | ||
|
|
5c7828dd79 | ||
|
|
74f5093d2a | ||
|
|
4651e362c9 | ||
|
|
a2e2a5cb37 | ||
|
|
15a3882016 | ||
|
|
d3ecd5214b | ||
|
|
0a8a8a742e | ||
|
|
ecff23d027 | ||
|
|
20dcb32bae | ||
|
|
678462d2db | ||
|
|
61fdfec09b | ||
|
|
5eadf5533d | ||
|
|
2d3cd5dc80 | ||
|
|
5208ad0b98 | ||
|
|
662fb96beb | ||
|
|
4c14431a3d | ||
|
|
5ae38a3f18 | ||
|
|
94e10d1f67 | ||
|
|
0a50676884 | ||
|
|
41cce78fcb | ||
|
|
4c0fa1fabe | ||
|
|
54e1dafa3f | ||
|
|
3ac76bc05b | ||
|
|
83030df3ee | ||
|
|
07d15caf6f | ||
|
|
3298efe652 | ||
|
|
01d9919a3e | ||
|
|
80b1170b63 | ||
|
|
2e7302e654 | ||
|
|
ca7fb8a0b4 | ||
|
|
c9b0894f26 | ||
|
|
c3454a195d | ||
|
|
3b3dd8071b | ||
|
|
0d28c67534 | ||
|
|
d0af85754a | ||
|
|
3e265c27ff | ||
|
|
8d356f50c4 | ||
|
|
f30c4f16c0 | ||
|
|
8bb8caa315 | ||
|
|
0f17a3d72e | ||
|
|
7647369e2d | ||
|
|
4b4208e724 | ||
|
|
2a16a1df85 | ||
|
|
25f7c58400 | ||
|
|
c3db91f11f | ||
|
|
8c66ce03d4 | ||
|
|
2be2a0625e | ||
|
|
c904d5041b | ||
|
|
632762768e | ||
|
|
c69ba205f8 | ||
|
|
019f4dbea9 | ||
|
|
259ea41ce3 | ||
|
|
11290f7204 | ||
|
|
abd06133fb | ||
|
|
29a3a0c48f | ||
|
|
2728c63512 | ||
|
|
f3b11bc1c2 | ||
|
|
04590befb3 | ||
|
|
4e9034f910 | ||
|
|
ba9cfa3764 | ||
|
|
341e3e2f89 | ||
|
|
3f70ca5192 | ||
|
|
f11bfc53ee | ||
|
|
61e3967b8e | ||
|
|
add86ea100 | ||
|
|
dd51c89278 | ||
|
|
788d783745 | ||
|
|
35da60543e | ||
|
|
ce7923d248 | ||
|
|
55847460c5 | ||
|
|
893524b0a8 | ||
|
|
8fb3ae405f | ||
|
|
aa447ec101 | ||
|
|
56b1cb4521 | ||
|
|
90cc24614c | ||
|
|
d7301ff15e | ||
|
|
1cf10c5c47 | ||
|
|
6691ec8e3a | ||
|
|
a9f77221ff | ||
|
|
dd3b69a633 | ||
|
|
d766f8b34e | ||
|
|
2ae6fb47e7 | ||
|
|
dd5698241b | ||
|
|
ed8e9d7817 | ||
|
|
6c29b44c3c | ||
|
|
0caa133089 | ||
|
|
a8b08bd971 | ||
|
|
c9181a121f | ||
|
|
30f4e0829f | ||
|
|
2155fce036 | ||
|
|
27602ec8fc | ||
|
|
66f683d9c9 | ||
|
|
ac08a52323 | ||
|
|
fe0f998c27 | ||
|
|
bcf79165f9 | ||
|
|
9b2a50cdfc | ||
|
|
c5d99db654 | ||
|
|
5253ab3e58 | ||
|
|
11d8253d9c | ||
|
|
bc2b481918 | ||
|
|
b26564ccb9 | ||
|
|
28e3ffe978 | ||
|
|
540e2e25aa | ||
|
|
845118e9e2 | ||
|
|
b2b8753ee7 | ||
|
|
060fb21bdb | ||
|
|
ef008709c7 | ||
|
|
09da119f27 | ||
|
|
f8487d1e1c | ||
|
|
b6f773ffce | ||
|
|
c8673473ef | ||
|
|
3829815756 | ||
|
|
7eac8faf0d | ||
|
|
0bd0f4d313 | ||
|
|
73873eb8cb | ||
|
|
0420b6c831 | ||
|
|
bb741975b7 | ||
|
|
8bffec6964 | ||
|
|
9e19dd7070 | ||
|
|
aba0e3d2d4 | ||
|
|
d9d025e33b | ||
|
|
1fe29ab098 | ||
|
|
53f16782ab | ||
|
|
bfb4f88304 | ||
|
|
fb6d65ec23 | ||
|
|
6b364052c7 | ||
|
|
7169abcd7b | ||
|
|
75ccfbdb3d | ||
|
|
728644c104 | ||
|
|
999351e667 | ||
|
|
11e879db41 | ||
|
|
a4a774fcdf | ||
|
|
4fb0240a36 | ||
|
|
80ec54eaf6 | ||
|
|
9e4ae3a494 | ||
|
|
19e4c03d3a | ||
|
|
c6b8cb1fab | ||
|
|
f20a7afa7f | ||
|
|
01f5d006fc | ||
|
|
cd6b076efe | ||
|
|
f8193a4d23 | ||
|
|
5be2202b2e | ||
|
|
fbbfe33e21 | ||
|
|
7a916ed8c2 | ||
|
|
60305cd106 | ||
|
|
b8630f739a | ||
|
|
1c8480a329 | ||
|
|
5219648128 | ||
|
|
1ef7c8e8db | ||
|
|
1415dd0dae | ||
|
|
282aa804f8 | ||
|
|
e959051239 | ||
|
|
373b7ba293 | ||
|
|
70c6f3498b | ||
|
|
afec3743f3 | ||
|
|
5989d021c7 | ||
|
|
baa8993999 | ||
|
|
92f5860de6 | ||
|
|
12abd4292e | ||
|
|
4bd5b0d236 | ||
|
|
0115bc8dca | ||
|
|
5f323a85a7 | ||
|
|
85c556d324 | ||
|
|
0c1114edbc | ||
|
|
2c264728d9 | ||
|
|
90c4ebd208 | ||
|
|
5df97e5133 | ||
|
|
acb5f65e16 | ||
|
|
c9e7b45509 | ||
|
|
6fca136327 | ||
|
|
084bbe8480 | ||
|
|
225ec594e7 |
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
# Sadly too many files have whitespace errors, so we leave this as is for
|
||||
# now and just make sure we don't introduce any more.
|
||||
# trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.properties]
|
||||
insert_final_newline = false
|
||||
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report some misbehaviour in the mod
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
## Before reporting
|
||||
- Search for the bug both here and [on the ComputerCraft issues page](https://github.com/dan200/ComputerCraft/issues?utf8=%E2%9C%93&q=is%3Aissue+)
|
||||
- If possible, try to reproduce on vanilla ComputerCraft. If it still occurs, [report on the ComputerCraft repo](https://github.com/dan200/ComputerCraft/issues/new) instead.
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Minecraft version
|
||||
- CC: Tweaked version
|
||||
- Detailed reproduction steps!** Sometimes I can spot a bug pretty easily, but often it's much more obscure. Anything you can give which will help reproduce it means it'll get fixed quicker.
|
||||
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea or improvement
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
## Before reporting
|
||||
- Search for the suggestion both here and [on the ComputerCraft issues page](https://github.com/dan200/ComputerCraft/issues?utf8=%E2%9C%93&q=is%3Aissue+). It's possible someone's suggested it before!
|
||||
- Unless something is specific to CC:Tweaked, try to [suggest them on the ComputerCraft repo](https://github.com/dan200/ComputerCraft/issues/new). There's a lot more people watching it, so it allows the wider community to contribute.
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Explanation of how the feature/change chould work.
|
||||
- Some rationale/use case for a feature. I'd like to keep CC:T as minimal
|
||||
9
.github/pull_request_template.md
vendored
Normal file
9
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<!--
|
||||
Unless this feature is specific to CC:Tweaked, try to [target the original ComputerCraft repo](https://github.com/dan200/ComputerCraft/) instead. There's a lot more people watching it, so it allows the wider community to contribute.
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Brief explanation of the changes you've made.
|
||||
- Rationale of why this change has been made/reasoning behind it.
|
||||
|
||||
The more information you can provide, the easier it is to review something now _and_ to see why a change was made, when the code needs updating in the future.
|
||||
57
README.md
57
README.md
@@ -1,26 +1,47 @@
|
||||
ComputerCraft
|
||||
=============
|
||||
[](https://travis-ci.org/dan200/ComputerCraft)
|
||||
# 
|
||||
[](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 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 (or CC:T for short) does not aim to create a competing fork of ComputerCraft, nor am I planning to take it
|
||||
in in a vastly different direction to the original mod. In fact, CC:T aims to be a nurturing ground for various
|
||||
features, with a pull request against the original mod being the end goal.
|
||||
|
||||
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:T also includes many pull requests from the community which have not yet been merged, offering a large number
|
||||
of additional bug fixes and features over the original mod.
|
||||
|
||||
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.
|
||||
## Features
|
||||
CC: Tweaked contains the all features of the latest alpha, as well as numerous fixes, performance improvements and
|
||||
several additional features. I'd recommend checking out [the releases page](https://github.com/SquidDev-CC/CC-Tweaked/releases)
|
||||
to see the full changes, but here's a couple of the more interesting changes:
|
||||
|
||||
Contributing
|
||||
============
|
||||
- Replace LuaJ with Cobalt.
|
||||
- Allow running multiple computers at the same time.
|
||||
- Websocket support in the HTTP library.
|
||||
- Wired modems and cables act more like multiparts.
|
||||
- Add map-like rendering for pocket computers and printed pages/books.
|
||||
- Adds the `/computercraft` command, offering various diagnostic tools for server owners. This allows operators to
|
||||
track which computers are hogging resources, turn on and shutdown multiple computers at once and interact with
|
||||
computers remotely.
|
||||
- Add full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is
|
||||
installed).
|
||||
|
||||
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:
|
||||
## Relation to CCTweaks?
|
||||
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said,
|
||||
several features have been included, such as full block modems, the Cobalt runtime and map-like rendering for pocket
|
||||
computers.
|
||||
|
||||
* 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!
|
||||
## 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.
|
||||
|
||||
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.
|
||||
That being said, in order to start helping develop CC:T, you'll need to follow these steps:
|
||||
|
||||
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
|
||||
- **Setup Forge:** `./gradlew setupDecompWorkspace`
|
||||
- **Test your changes:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
||||
|
||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
||||
|
||||
150
build.gradle
150
build.gradle
@@ -13,22 +13,22 @@ buildscript {
|
||||
classpath 'org.ajoberstar:gradle-git:1.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.matthewprenger.cursegradle' version '1.0.10'
|
||||
}
|
||||
|
||||
apply plugin: 'net.minecraftforge.gradle.forge'
|
||||
apply plugin: 'org.ajoberstar.grgit'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'maven'
|
||||
|
||||
/*
|
||||
// for people who want stable - not yet functional for MC 1.8.8 - we require the forgegradle 2.1 snapshot
|
||||
plugins {
|
||||
id "net.minecraftforge.gradle.forge" version "2.0.2"
|
||||
}
|
||||
*/
|
||||
|
||||
version = "1.80pr1"
|
||||
group = "dan200.computercraft"
|
||||
archivesBaseName = "ComputerCraft"
|
||||
version = "1.80pr1.8"
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked"
|
||||
|
||||
minecraft {
|
||||
version = "1.12-14.21.1.2387"
|
||||
version = "1.12.2-14.23.4.2749"
|
||||
runDir = "run"
|
||||
replace '${version}', project.version
|
||||
|
||||
@@ -37,38 +37,61 @@ minecraft {
|
||||
// stable_# stables are built at the discretion of the MCP team.
|
||||
// Use non-default mappings at your own risk. they may not allways work.
|
||||
// simply re-run your setup task after changing the mappings to update your workspace.
|
||||
mappings = "snapshot_20170629"
|
||||
mappings = "snapshot_20180724"
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
name = "JEI"
|
||||
url = "http://dvs1.progwml6.com/files/maven"
|
||||
}
|
||||
maven {
|
||||
name = "squiddev"
|
||||
url = "https://dl.bintray.com/squiddev/maven"
|
||||
}
|
||||
|
||||
ivy { artifactPattern "https://asie.pl/files/mods/Charset/LibOnly/[module]-[revision](-[classifier]).[ext]" }
|
||||
}
|
||||
|
||||
configurations {
|
||||
shade
|
||||
compile.extendsFrom shade
|
||||
deployerJars
|
||||
}
|
||||
|
||||
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"
|
||||
deobfProvided "mezz.jei:jei_1.12.2:4.8.5.159:api"
|
||||
deobfProvided "pl.asie:Charset-Lib:0.5.4.6"
|
||||
|
||||
// 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
|
||||
runtime "mezz.jei:jei_1.12.2:4.8.5.159"
|
||||
|
||||
// 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'
|
||||
shade 'org.squiddev:Cobalt:0.3.2-nothread'
|
||||
|
||||
// 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'
|
||||
testCompile 'junit:junit:4.11'
|
||||
|
||||
// for more info...
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.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
|
||||
@@ -103,9 +126,80 @@ processResources {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
curseforge {
|
||||
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
|
||||
project {
|
||||
id = '282001'
|
||||
releaseType = 'beta'
|
||||
changelog = ''
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourceJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uploadArchives {
|
||||
repositories {
|
||||
if(project.hasProperty('mavenUploadUrl')) {
|
||||
mavenDeployer {
|
||||
configuration = configurations.deployerJars
|
||||
|
||||
repository(url: project.property('mavenUploadUrl')) {
|
||||
authentication(
|
||||
userName: project.property('mavenUploadUser'),
|
||||
privateKey: project.property('mavenUploadKey'))
|
||||
}
|
||||
|
||||
pom.project {
|
||||
name 'CC: Tweaked'
|
||||
packaging 'jar'
|
||||
description 'A fork of ComputerCraft which aims to provide earlier access to the more experimental and in-development features of the mod.'
|
||||
url 'https://github.com/SquidDev-CC/CC-Tweaked'
|
||||
|
||||
scm {
|
||||
url 'https://github.com/dan200/ComputerCraft.git'
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system 'github'
|
||||
url 'https://github.com/dan200/ComputerCraft/issues'
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name 'ComputerCraft Public License, Version 1.0'
|
||||
url 'https://github.com/dan200/ComputerCraft/blob/master/LICENSE'
|
||||
distribution 'repo'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pom.whenConfigured { pom ->
|
||||
pom.dependencies.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint"
|
||||
}
|
||||
}
|
||||
|
||||
runClient.outputs.upToDateWhen { false }
|
||||
runServer.outputs.upToDateWhen { false }
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
events "failed", "standardOut", "standardError"
|
||||
}
|
||||
}
|
||||
|
||||
25
deploy.sh
25
deploy.sh
@@ -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."
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
110
gradlew
vendored
@@ -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
14
gradlew.bat
vendored
@@ -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.
181
luaj-2.0.3/src/core/org/luaj/vm2/lib/Bit32Lib.java
Normal file
181
luaj-2.0.3/src/core/org/luaj/vm2/lib/Bit32Lib.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'cc-tweaked'
|
||||
@@ -7,26 +7,37 @@
|
||||
package dan200.computercraft;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.base.Converter;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||
import dan200.computercraft.api.permissions.ITurtlePermissionProvider;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.event.TurtleAction;
|
||||
import dan200.computercraft.core.apis.AddressPredicate;
|
||||
import dan200.computercraft.core.filesystem.ComboMount;
|
||||
import dan200.computercraft.core.filesystem.FileMount;
|
||||
import dan200.computercraft.core.filesystem.JarMount;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.command.CommandComputer;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
|
||||
@@ -36,6 +47,7 @@ import dan200.computercraft.shared.network.ComputerCraftPacket;
|
||||
import dan200.computercraft.shared.network.PacketHandler;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockCable;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.common.BlockWiredModemFull;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.modem.BlockAdvancedModem;
|
||||
import dan200.computercraft.shared.peripheral.modem.WirelessNetwork;
|
||||
@@ -49,6 +61,8 @@ import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.*;
|
||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||
import dan200.computercraft.shared.wired.WiredNode;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -56,11 +70,13 @@ import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.config.ConfigCategory;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
@@ -81,10 +97,8 @@ import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -93,7 +107,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
|
||||
@@ -109,6 +123,7 @@ public class ComputerCraft
|
||||
// ComputerCraftEdu uses ID 104
|
||||
public static final int printoutGUIID = 105;
|
||||
public static final int pocketComputerGUIID = 106;
|
||||
public static final int viewComputerGUIID = 110;
|
||||
|
||||
// Configuration options
|
||||
private static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
|
||||
@@ -119,12 +134,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;
|
||||
@@ -133,6 +151,7 @@ public class ComputerCraft
|
||||
public static int advancedTurtleFuelLimit = 100000;
|
||||
public static boolean turtlesObeyBlockProtection = true;
|
||||
public static boolean turtlesCanPush = true;
|
||||
public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf( TurtleAction.class );
|
||||
|
||||
public static final int terminalWidth_computer = 51;
|
||||
public static final int terminalHeight_computer = 19;
|
||||
@@ -165,6 +184,7 @@ public class ComputerCraft
|
||||
public static BlockTurtle turtleAdvanced;
|
||||
public static BlockCommandComputer commandComputer;
|
||||
public static BlockAdvancedModem advancedModem;
|
||||
public static BlockWiredModemFull wiredModemFull;
|
||||
}
|
||||
|
||||
public static class Items
|
||||
@@ -200,10 +220,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;
|
||||
@@ -212,6 +235,7 @@ public class ComputerCraft
|
||||
public static Property advancedTurtleFuelLimit;
|
||||
public static Property turtlesObeyBlockProtection;
|
||||
public static Property turtlesCanPush;
|
||||
public static Property turtleDisabledActions;
|
||||
|
||||
public static Property modem_range;
|
||||
public static Property modem_highAltitudeRange;
|
||||
@@ -244,6 +268,7 @@ public class ComputerCraft
|
||||
private static List<IMediaProvider> mediaProviders = new ArrayList<>();
|
||||
private static List<ITurtlePermissionProvider> permissionProviders = new ArrayList<>();
|
||||
private static final Map<String, IPocketUpgrade> pocketUpgrades = new HashMap<>();
|
||||
private static final Set<ILuaAPIFactory> apiFactories = new LinkedHashSet<>();
|
||||
|
||||
// Implementation
|
||||
@Mod.Instance( value = ComputerCraft.MOD_ID )
|
||||
@@ -266,11 +291,26 @@ public class ComputerCraft
|
||||
|
||||
// Load config
|
||||
Config.config = new Configuration( event.getSuggestedConfigurationFile() );
|
||||
loadConfig();
|
||||
|
||||
// Setup network
|
||||
networkEventChannel = NetworkRegistry.INSTANCE.newEventDrivenChannel( "CC" );
|
||||
networkEventChannel.register( new PacketHandler() );
|
||||
|
||||
proxy.preInit();
|
||||
turtleProxy.preInit();
|
||||
}
|
||||
|
||||
public static void loadConfig()
|
||||
{
|
||||
Config.config.load();
|
||||
|
||||
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 +338,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" );
|
||||
|
||||
@@ -341,6 +391,9 @@ public class ComputerCraft
|
||||
Config.turtlesCanPush = Config.config.get( Configuration.CATEGORY_GENERAL, "turtlesCanPush", turtlesCanPush );
|
||||
Config.turtlesCanPush.setComment( "If set to true, Turtles will push entities out of the way instead of stopping if there is space to do so" );
|
||||
|
||||
Config.turtleDisabledActions = Config.config.get( Configuration.CATEGORY_GENERAL, "turtle_disabled_actions", new String[ 0 ] );
|
||||
Config.turtleDisabledActions.setComment( "A list of turtle actions which are disabled." );
|
||||
|
||||
Config.maxNotesPerTick = Config.config.get( Configuration.CATEGORY_GENERAL, "maxNotesPerTick", maxNotesPerTick );
|
||||
Config.maxNotesPerTick.setComment( "Maximum amount of notes a speaker can play at once" );
|
||||
|
||||
@@ -350,22 +403,18 @@ public class ComputerCraft
|
||||
}
|
||||
|
||||
syncConfig();
|
||||
|
||||
// Setup network
|
||||
networkEventChannel = NetworkRegistry.INSTANCE.newEventDrivenChannel( "CC" );
|
||||
networkEventChannel.register( new PacketHandler() );
|
||||
|
||||
proxy.preInit();
|
||||
turtleProxy.preInit();
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -385,6 +434,20 @@ public class ComputerCraft
|
||||
turtlesObeyBlockProtection = Config.turtlesObeyBlockProtection.getBoolean();
|
||||
turtlesCanPush = Config.turtlesCanPush.getBoolean();
|
||||
|
||||
turtleDisabledActions.clear();
|
||||
Converter<String, String> converter = CaseFormat.LOWER_CAMEL.converterTo( CaseFormat.UPPER_UNDERSCORE );
|
||||
for( String value : Config.turtleDisabledActions.getStringList() )
|
||||
{
|
||||
try
|
||||
{
|
||||
turtleDisabledActions.add( TurtleAction.valueOf( converter.convert( value ) ) );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Unknown turtle action " + value );
|
||||
}
|
||||
}
|
||||
|
||||
maxNotesPerTick = Math.max(1, Config.maxNotesPerTick.getInt());
|
||||
|
||||
Config.config.save();
|
||||
@@ -400,6 +463,7 @@ public class ComputerCraft
|
||||
@Mod.EventHandler
|
||||
public void onServerStarting( FMLServerStartingEvent event )
|
||||
{
|
||||
proxy.initServer( event.getServer() );
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
@@ -409,6 +473,7 @@ public class ComputerCraft
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,6 +484,7 @@ public class ComputerCraft
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,11 +508,6 @@ public class ComputerCraft
|
||||
return proxy.getRenderFrame();
|
||||
}
|
||||
|
||||
public static void deleteDisplayLists( int list, int range )
|
||||
{
|
||||
proxy.deleteDisplayLists( list, range );
|
||||
}
|
||||
|
||||
public static Object getFixedWidthFontRenderer()
|
||||
{
|
||||
return proxy.getFixedWidthFontRenderer();
|
||||
@@ -496,6 +557,24 @@ public class ComputerCraft
|
||||
player.openGui( ComputerCraft.instance, ComputerCraft.pocketComputerGUIID, player.getEntityWorld(), hand.ordinal(), 0, 0 );
|
||||
}
|
||||
|
||||
public static void openComputerGUI( EntityPlayer player, ServerComputer computer )
|
||||
{
|
||||
ComputerFamily family = computer.getFamily();
|
||||
int width = 0, height = 0;
|
||||
Terminal terminal = computer.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
width = terminal.getWidth();
|
||||
height = terminal.getHeight();
|
||||
}
|
||||
|
||||
// Pack useful terminal information into the various coordinate bits.
|
||||
// These are extracted in ComputerCraftProxyCommon.getClientGuiElement
|
||||
player.openGui( ComputerCraft.instance, ComputerCraft.viewComputerGUIID, player.getEntityWorld(),
|
||||
computer.getInstanceID(), family.ordinal(), (width & 0xFFFF) << 16 | (height & 0xFFFF)
|
||||
);
|
||||
}
|
||||
|
||||
public static File getBaseDir()
|
||||
{
|
||||
return FMLCommonHandler.instance().getMinecraftServerInstance().getFile(".");
|
||||
@@ -533,6 +612,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 );
|
||||
@@ -644,6 +728,19 @@ public class ComputerCraft
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerAPIFactory( ILuaAPIFactory provider )
|
||||
{
|
||||
if( provider != null )
|
||||
{
|
||||
apiFactories.add( provider );
|
||||
}
|
||||
}
|
||||
|
||||
public static IWiredNode createWiredNodeForElement( IWiredElement element )
|
||||
{
|
||||
return new WiredNode( element );
|
||||
}
|
||||
|
||||
public static IPeripheral getPeripheralAt( World world, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
// Try the handlers in order:
|
||||
@@ -665,6 +762,14 @@ public class ComputerCraft
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IWiredElement getWiredElementAt( IBlockAccess world, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
return tile != null && tile.hasCapability( CapabilityWiredElement.CAPABILITY, side )
|
||||
? tile.getCapability( CapabilityWiredElement.CAPABILITY, side )
|
||||
: null;
|
||||
}
|
||||
|
||||
public static int getDefaultBundledRedstoneOutput( World world, BlockPos pos, EnumFacing side )
|
||||
{
|
||||
if( WorldUtil.isBlockInWorld( world, pos ) )
|
||||
@@ -766,11 +871,16 @@ public class ComputerCraft
|
||||
return upgrades;
|
||||
}
|
||||
|
||||
public IPacketNetwork getWirelessNetwork()
|
||||
public static IPacketNetwork getWirelessNetwork()
|
||||
{
|
||||
return WirelessNetwork.getUniversal();
|
||||
}
|
||||
|
||||
public static Iterable<ILuaAPIFactory> getAPIFactories()
|
||||
{
|
||||
return apiFactories;
|
||||
}
|
||||
|
||||
public static int createUniqueNumberedSaveDir( World world, String parentSubPath )
|
||||
{
|
||||
return IDAssigner.getNextIDFromDirectory(new File(getWorldDir(world), parentSubPath));
|
||||
@@ -1015,13 +1125,18 @@ public class ComputerCraft
|
||||
turtleProxy.addAllUpgradedTurtles( list );
|
||||
}
|
||||
|
||||
public static void setEntityDropConsumer( Entity entity, IEntityDropConsumer consumer )
|
||||
public static void setDropConsumer( Entity entity, Function<ItemStack, ItemStack> consumer )
|
||||
{
|
||||
turtleProxy.setEntityDropConsumer( entity, consumer );
|
||||
turtleProxy.setDropConsumer( entity, consumer );
|
||||
}
|
||||
|
||||
public static void clearEntityDropConsumer( Entity entity )
|
||||
public static void setDropConsumer( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer )
|
||||
{
|
||||
turtleProxy.clearEntityDropConsumer( entity );
|
||||
turtleProxy.setDropConsumer( world, pos, consumer );
|
||||
}
|
||||
|
||||
public static List<ItemStack> clearDropConsumer( )
|
||||
{
|
||||
return turtleProxy.clearDropConsumer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@ package dan200.computercraft.api;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.media.IMediaProvider;
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||
@@ -20,6 +23,7 @@ import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -311,6 +315,77 @@ public final class ComputerCraftAPI
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void registerAPIFactory( @Nonnull ILuaAPIFactory upgrade )
|
||||
{
|
||||
findCC();
|
||||
if( computerCraft_registerAPIFactory != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
computerCraft_registerAPIFactory.invoke( null, upgrade );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
// It failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new wired node for a given wired element
|
||||
*
|
||||
* @param element The element to construct it for
|
||||
* @return The element's node
|
||||
* @see IWiredElement#getNode()
|
||||
*/
|
||||
@Nonnull
|
||||
public static IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element )
|
||||
{
|
||||
findCC();
|
||||
if( computerCraft_createWiredNodeForElement != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
return (IWiredNode) computerCraft_createWiredNodeForElement.invoke( null, element );
|
||||
}
|
||||
catch( ReflectiveOperationException e )
|
||||
{
|
||||
throw new IllegalStateException( "Error creating wired node", e );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException( "ComputerCraft cannot be found" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wired network element for a block in world
|
||||
*
|
||||
* @param world The world the block exists in
|
||||
* @param pos The position the block exists in
|
||||
* @param side The side to extract the network element from
|
||||
* @return The element's node
|
||||
* @see IWiredElement#getNode()
|
||||
*/
|
||||
@Nullable
|
||||
public static IWiredElement getWiredElementAt( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
|
||||
{
|
||||
findCC();
|
||||
if( computerCraft_getWiredElementAt != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
return (IWiredElement) computerCraft_getWiredElementAt.invoke( null, world, pos, side );
|
||||
}
|
||||
catch( ReflectiveOperationException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// The functions below here are private, and are used to interface with the non-API ComputerCraft classes.
|
||||
// Reflection is used here so you can develop your mod without decompiling ComputerCraft and including
|
||||
// it in your solution, and so your mod won't crash if ComputerCraft is installed.
|
||||
@@ -354,6 +429,15 @@ public final class ComputerCraftAPI
|
||||
} );
|
||||
computerCraft_getWirelessNetwork = findCCMethod( "getWirelessNetwork", new Class<?>[] {
|
||||
} );
|
||||
computerCraft_registerAPIFactory = findCCMethod( "registerAPIFactory", new Class<?>[] {
|
||||
ILuaAPIFactory.class
|
||||
} );
|
||||
computerCraft_createWiredNodeForElement = findCCMethod( "createWiredNodeForElement", new Class<?>[] {
|
||||
IWiredElement.class
|
||||
} );
|
||||
computerCraft_getWiredElementAt = findCCMethod( "getWiredElementAt", new Class<?>[]{
|
||||
IBlockAccess.class, BlockPos.class, EnumFacing.class
|
||||
} );
|
||||
} catch( Exception e ) {
|
||||
System.out.println( "ComputerCraftAPI: ComputerCraft not found." );
|
||||
} finally {
|
||||
@@ -390,4 +474,7 @@ public final class ComputerCraftAPI
|
||||
private static Method computerCraft_registerPermissionProvider = null;
|
||||
private static Method computerCraft_registerPocketUpgrade = null;
|
||||
private static Method computerCraft_getWirelessNetwork = null;
|
||||
private static Method computerCraft_registerAPIFactory = null;
|
||||
private static Method computerCraft_createWiredNodeForElement = null;
|
||||
private static Method computerCraft_getWiredElementAt = null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package dan200.computercraft.api.filesystem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides a mount of the entire computer's file system.
|
||||
*
|
||||
* This exists for use by various APIs - one should not attempt to mount it.
|
||||
*/
|
||||
public interface IFileSystem extends IWritableMount
|
||||
{
|
||||
/**
|
||||
* Combine two paths together, reducing them into a normalised form.
|
||||
*
|
||||
* @param path The main path.
|
||||
* @param child The path to append.
|
||||
* @return The combined, normalised path.
|
||||
*/
|
||||
String combine( String path, String child );
|
||||
|
||||
/**
|
||||
* Copy files from one location to another.
|
||||
*
|
||||
* @param from The location to copy from.
|
||||
* @param to The location to copy to. This should not exist.
|
||||
* @throws IOException If the copy failed.
|
||||
*/
|
||||
void copy( String from, String to ) throws IOException;
|
||||
|
||||
/**
|
||||
* Move files from one location to another.
|
||||
*
|
||||
* @param from The location to move from.
|
||||
* @param to The location to move to. This should not exist.
|
||||
* @throws IOException If the move failed.
|
||||
*/
|
||||
void move( String from, String to ) throws IOException;
|
||||
}
|
||||
31
src/main/java/dan200/computercraft/api/lua/ICallContext.java
Normal file
31
src/main/java/dan200/computercraft/api/lua/ICallContext.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An interface passed to peripherals and {@link ILuaObject}s by computers or turtles, providing methods that allow the
|
||||
* method to interact with the invoking computer.
|
||||
*/
|
||||
public interface ICallContext
|
||||
{
|
||||
/**
|
||||
* Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to
|
||||
* complete. This should be used when you need to interact with the world in a thread-safe manner but do not care
|
||||
* about the result or you wish to run asynchronously.
|
||||
*
|
||||
* When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success
|
||||
* value and the return values, or an error message if it failed. If you need to wait on this event, it may be
|
||||
* better to use {@link MethodResult#onMainThread(ILuaCallable)}.
|
||||
*
|
||||
* @param task The task to execute on the main thread.
|
||||
* @return The "id" of the task. This will be the first argument to the {@code task_completed} event.
|
||||
* @throws LuaException If the task could not be queued.
|
||||
*/
|
||||
long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
|
||||
* about a computer.
|
||||
*/
|
||||
public interface IComputerSystem extends IComputerAccess
|
||||
{
|
||||
/**
|
||||
* Get the file system for this computer.
|
||||
*
|
||||
* @return The computer's file system, or {@code null} if it is not initialised.
|
||||
*/
|
||||
@Nullable
|
||||
IFileSystem getFileSystem();
|
||||
|
||||
/**
|
||||
* Get the label for this computer
|
||||
*
|
||||
* @return This computer's label, or {@code null} if it is not set.
|
||||
*/
|
||||
@Nullable
|
||||
String getLabel();
|
||||
}
|
||||
47
src/main/java/dan200/computercraft/api/lua/ILuaAPI.java
Normal file
47
src/main/java/dan200/computercraft/api/lua/ILuaAPI.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2016. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
/**
|
||||
* Represents a {@link ILuaObject} which is stored as a global variable on computer startup.
|
||||
*
|
||||
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
||||
* to use peripherals to provide functionality to users.
|
||||
*
|
||||
* @see ILuaAPIFactory
|
||||
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public interface ILuaAPI extends ILuaObject
|
||||
{
|
||||
/**
|
||||
* Get the globals this API will be assigned to. This will override any other global, so you should
|
||||
*
|
||||
* @return A list of globals this API will be assigned to.
|
||||
*/
|
||||
String[] getNames();
|
||||
|
||||
/**
|
||||
* Called when the computer is turned on.
|
||||
*
|
||||
* One should only interact with the file system.
|
||||
*/
|
||||
default void startup() { }
|
||||
|
||||
/**
|
||||
* Called every time the computer is ticked. This can be used to process various.
|
||||
*/
|
||||
default void update() { }
|
||||
|
||||
/**
|
||||
* Called when the computer is turned off or unloaded.
|
||||
*
|
||||
* This should reset the state of the object, disposing any remaining file handles, or other resources.
|
||||
*/
|
||||
default void shutdown() { }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Construct an {@link ILuaAPI} for a specific computer.
|
||||
*
|
||||
* @see ILuaAPI
|
||||
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public interface ILuaAPIFactory
|
||||
{
|
||||
/**
|
||||
* Create a new API instance for a given computer.
|
||||
*
|
||||
* @param computer The computer this API is for.
|
||||
* @return The created API, or {@code null} if one should not be injected.
|
||||
*/
|
||||
@Nullable
|
||||
ILuaAPI create( @Nonnull IComputerSystem computer );
|
||||
}
|
||||
31
src/main/java/dan200/computercraft/api/lua/ILuaCallable.java
Normal file
31
src/main/java/dan200/computercraft/api/lua/ILuaCallable.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A function which calls performs an action in a specific context (such as on the server thread) and returns a result.
|
||||
*
|
||||
* @see MethodResult#onMainThread(ILuaCallable)
|
||||
* @see ILuaContext#executeMainThreadTask(ILuaTask)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ILuaCallable
|
||||
{
|
||||
/**
|
||||
* Run the code within the specified context and return the result to continue with.
|
||||
*
|
||||
* @return The result of executing this function. Note that this may not be evaluated within the same context as
|
||||
* this call is.
|
||||
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||
* arguments are supplied to your method.
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult execute() throws LuaException;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
@@ -13,8 +13,11 @@ import javax.annotation.Nullable;
|
||||
* An interface passed to peripherals and {@link ILuaObject}s by computers or turtles, providing methods
|
||||
* that allow the peripheral call to wait for events before returning, just like in lua. This is very useful if you need
|
||||
* to signal work to be performed on the main thread, and don't want to return until the work has been completed.
|
||||
*
|
||||
* This interface mostly exists for integrating with older code. One should use {@link MethodResult} instead, as this
|
||||
* encourages an asynchronous way of interacting with Lua coroutines.
|
||||
*/
|
||||
public interface ILuaContext
|
||||
public interface ILuaContext extends ICallContext
|
||||
{
|
||||
/**
|
||||
* Wait for an event to occur on the computer, suspending the thread until it arises. This method is exactly
|
||||
@@ -30,8 +33,10 @@ public interface ILuaContext
|
||||
* @throws InterruptedException If the user shuts down or reboots the computer while pullEvent() is waiting for an
|
||||
* event, InterruptedException will be thrown. This exception must not be caught or
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @deprecated Use {@link MethodResult#pullEvent(String, ILuaFunction)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Object[] pullEvent( @Nullable String filter ) throws LuaException, InterruptedException;
|
||||
|
||||
/**
|
||||
@@ -45,8 +50,10 @@ public interface ILuaContext
|
||||
* an event, InterruptedException will be thrown. This exception must not be caught or
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @see #pullEvent(String)
|
||||
* @deprecated Use {@link MethodResult#pullEventRaw(String, ILuaFunction)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Object[] pullEventRaw( @Nullable String filter ) throws InterruptedException;
|
||||
|
||||
/**
|
||||
@@ -59,8 +66,10 @@ public interface ILuaContext
|
||||
* InterruptedException will be thrown. This exception must not be caught or
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @see #pullEvent(String)
|
||||
* @deprecated Use {@link MethodResult#pullEventRaw(ILuaFunction)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Object[] yield( @Nullable Object[] arguments ) throws InterruptedException;
|
||||
|
||||
/**
|
||||
@@ -76,22 +85,9 @@ public interface ILuaContext
|
||||
* @throws InterruptedException If the user shuts down or reboots the computer the coroutine is suspended,
|
||||
* InterruptedException will be thrown. This exception must not be caught or
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @deprecated Use {@link MethodResult#onMainThread(ILuaCallable)}
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
Object[] executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to
|
||||
* complete. This should be used when you need to interact with the world in a thread-safe manner but do not care
|
||||
* about the result or you wish to run asynchronously.
|
||||
*
|
||||
* When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success
|
||||
* value and the return values, or an error message if it failed. If you need to wait on this event, it may be
|
||||
* better to use {@link #executeMainThreadTask(ILuaTask)}.
|
||||
*
|
||||
* @param task The task to execute on the main thread.
|
||||
* @return The "id" of the task. This will be the first argument to the {@code task_completed} event.
|
||||
* @throws LuaException If the task could not be queued.
|
||||
*/
|
||||
long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A function which executes using a {@link ILuaContext}.
|
||||
*
|
||||
* Like {@link ILuaContext}, this is not intended for use in the future - it purely exists as an argument for
|
||||
* {@link MethodResult#withLuaContext(ILuaContextTask)}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ILuaContextTask
|
||||
{
|
||||
@Nullable
|
||||
@Deprecated
|
||||
Object[] execute( @Nonnull ILuaContext context ) throws LuaException, InterruptedException;
|
||||
}
|
||||
33
src/main/java/dan200/computercraft/api/lua/ILuaFunction.java
Normal file
33
src/main/java/dan200/computercraft/api/lua/ILuaFunction.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A Lua function which consumes some values and returns a result.
|
||||
*
|
||||
* @see MethodResult#then(ILuaFunction)
|
||||
* @see MethodResult#pullEvent(ILuaFunction)
|
||||
* @see MethodResult#pullEventRaw(String)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ILuaFunction
|
||||
{
|
||||
/**
|
||||
* Accept the values and return another method result.
|
||||
*
|
||||
* @param values The inputs for this function.
|
||||
* @return The result of executing this function.
|
||||
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||
* arguments are supplied to your method.
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult call( @Nullable Object[] values ) throws LuaException;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
@@ -41,16 +41,45 @@ public interface ILuaObject
|
||||
* wishes to call. The integer indicates the index into the getMethodNames() table
|
||||
* that corresponds to the string passed into peripheral.call()
|
||||
* @param arguments The arguments for this method. See {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}
|
||||
* the possible values and conversion rules.
|
||||
* @return An array of objects, representing the values you wish to return to the Lua program.
|
||||
* See {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])} for the valid values and
|
||||
* conversion rules.
|
||||
* @throws LuaException If the task could not be queued, or if the task threw an exception.
|
||||
* for the possible values and conversion rules.
|
||||
* @return An array of objects, representing the values you wish to return to the Lua program. See
|
||||
* {@link MethodResult#of(Object...)} for the valid values and conversion rules.
|
||||
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||
* arguments are supplied to your method.
|
||||
* @throws InterruptedException If the user shuts down or reboots the computer the coroutine is suspended,
|
||||
* InterruptedException will be thrown. This exception must not be caught or
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.w
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @see IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])
|
||||
* @deprecated Use {@link #callMethod(ICallContext, int, Object[])} instead.
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Called when a user calls one of the methods that this object implements. This works the same as
|
||||
* {@link IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])}}. See that method for detailed
|
||||
* documentation.
|
||||
*
|
||||
* @param context The context of the current call.
|
||||
* @param method An integer identifying which of the methods from getMethodNames() the computercraft
|
||||
* wishes to call. The integer indicates the index into the getMethodNames() table
|
||||
* that corresponds to the string passed into peripheral.call()
|
||||
* @param arguments The arguments for this method. See {@link IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])}
|
||||
* for the possible values and conversion rules.
|
||||
* @return The result of calling this method. Use {@link MethodResult#empty()} to return nothing or
|
||||
* {@link MethodResult#of(Object...)} to return several values.
|
||||
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||
* arguments are supplied to your method.
|
||||
* @see IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])
|
||||
* @see MethodResult
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings( { "deprecation" } )
|
||||
default MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
{
|
||||
return MethodResult.withLuaContext( lua -> callMethod( lua, method, arguments ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
/**
|
||||
* Evaluates {@link MethodResult}s within a {@link ILuaContext}.
|
||||
*
|
||||
* @see MethodResult#evaluate(ILuaContext)
|
||||
* @see MethodResult#withLuaContext(ILuaContextTask)
|
||||
* @deprecated This should not be used except to interface between the two call call systems.
|
||||
*/
|
||||
@Deprecated
|
||||
class LuaContextResultEvaluator
|
||||
{
|
||||
@Deprecated
|
||||
public static Object[] evaluate( @Nonnull ILuaContext context, @Nonnull MethodResult future ) throws LuaException, InterruptedException
|
||||
{
|
||||
Deque<ILuaFunction> callbacks = null;
|
||||
while( true )
|
||||
{
|
||||
if( future instanceof MethodResult.AndThen )
|
||||
{
|
||||
MethodResult.AndThen then = ((MethodResult.AndThen) future);
|
||||
|
||||
// Thens are "unwrapped", being pushed onto a stack
|
||||
if( callbacks == null ) callbacks = new ArrayDeque<>();
|
||||
callbacks.addLast( then.getCallback() );
|
||||
|
||||
future = then.getPrevious();
|
||||
if( future == null ) throw new NullPointerException( "Null result from " + then.getCallback() );
|
||||
}
|
||||
else if( future instanceof MethodResult.Immediate )
|
||||
{
|
||||
Object[] values = ((MethodResult.Immediate) future).getResult();
|
||||
|
||||
// Immediate values values will attempt to call the previous "then", or return if nothing
|
||||
// else needs to be done.
|
||||
ILuaFunction callback = callbacks == null ? null : callbacks.pollLast();
|
||||
if( callback == null ) return values;
|
||||
|
||||
future = callback.call( values );
|
||||
if( future == null ) throw new NullPointerException( "Null result from " + callback );
|
||||
}
|
||||
else if( future instanceof MethodResult.OnEvent )
|
||||
{
|
||||
MethodResult.OnEvent onEvent = (MethodResult.OnEvent) future;
|
||||
|
||||
// Poll for an event, and then call the previous "then" or return if nothing else needs
|
||||
// to be done.
|
||||
Object[] values = onEvent.isRaw() ? context.pullEventRaw( onEvent.getFilter() ) : context.pullEvent( onEvent.getFilter() );
|
||||
|
||||
ILuaFunction callback = callbacks == null ? null : callbacks.pollLast();
|
||||
if( callback == null ) return values;
|
||||
|
||||
future = callback.call( values );
|
||||
if( future == null ) throw new NullPointerException( "Null result from " + callback );
|
||||
}
|
||||
else if( future instanceof MethodResult.OnMainThread )
|
||||
{
|
||||
MethodResult.OnMainThread onMainThread = (MethodResult.OnMainThread) future;
|
||||
|
||||
// Evaluate our task on the main thread and mark it as the next future to evaluate.
|
||||
Reference temporary = new Reference();
|
||||
context.executeMainThreadTask( () -> {
|
||||
temporary.value = onMainThread.getTask().execute();
|
||||
return null;
|
||||
} );
|
||||
|
||||
future = temporary.value;
|
||||
if( future == null ) throw new NullPointerException( "Null result from " + onMainThread.getTask() );
|
||||
}
|
||||
else if( future instanceof MethodResult.WithLuaContext )
|
||||
{
|
||||
MethodResult.WithLuaContext withContext = (MethodResult.WithLuaContext) future;
|
||||
|
||||
// Run the task, and then call the previous "then" or return if nothing else
|
||||
// needs to be done.
|
||||
Object[] values = withContext.getConsumer().execute( context );
|
||||
|
||||
ILuaFunction callback = callbacks == null ? null : callbacks.pollLast();
|
||||
if( callback == null ) return values;
|
||||
|
||||
future = callback.call( values );
|
||||
if( future == null ) throw new NullPointerException( "Null result from " + callback );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException( "Unknown MethodResult " + future );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Reference
|
||||
{
|
||||
MethodResult value;
|
||||
}
|
||||
}
|
||||
354
src/main/java/dan200/computercraft/api/lua/MethodResult.java
Normal file
354
src/main/java/dan200/computercraft/api/lua/MethodResult.java
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The result of calling a method, such as {@link ILuaObject#callMethod(ICallContext, int, Object[])} or
|
||||
* {@link IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])}.
|
||||
*
|
||||
* This is non-dissimilar to a promise or {@link ListenableFuture}. One can either return an immediate value through
|
||||
* {@link #of(Object...)}, wait for an external action with {@link #onMainThread(ILuaCallable)} or {@link #pullEvent()}
|
||||
* and then act on the result of either of those by using {@link #then(ILuaFunction)}.
|
||||
*/
|
||||
public abstract class MethodResult
|
||||
{
|
||||
private static MethodResult empty;
|
||||
|
||||
MethodResult()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* A result which returns immediately with no value.
|
||||
*
|
||||
* Use {@link #of(Object...)} if you need to return one or more values.
|
||||
*
|
||||
* @return The empty method result.
|
||||
* @see #of(Object...)
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult empty()
|
||||
{
|
||||
if( empty == null ) empty = new Immediate( null );
|
||||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* A result which returns several values.
|
||||
*
|
||||
* @param result The values to return, this may be {@code null}. {@link Number}s, {@link String}s, {@link Boolean}s,
|
||||
* {@link Map}s, {@link ILuaObject}s, and {@code null} be converted to their corresponding lua type.
|
||||
* All other types will be converted to nil.
|
||||
* @return A result which will return these values when evaluated.
|
||||
* @see #empty()
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult of( Object... result )
|
||||
{
|
||||
return result == null ? empty() : new Immediate( result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for an event to occur on the computer, suspending the coroutine until it arises. This method is equivalent
|
||||
* to {@code os.pullEvent()} in Lua.
|
||||
*
|
||||
* Normally you'll wish to consume the event using {@link #then(ILuaFunction)}. This can be done slightly more
|
||||
* easily with {@link #pullEvent(ILuaFunction)}.
|
||||
*
|
||||
* If you want to listen to a specific event, it's easier to use {@link #pullEvent(String)} rather than
|
||||
* running until the desired event is found.
|
||||
*
|
||||
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
||||
* parameters.
|
||||
* @see #pullEvent(ILuaFunction)
|
||||
* @see #pullEvent(String)
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEvent()
|
||||
{
|
||||
return new OnEvent( false, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the specified event to occur on the computer, suspending the coroutine until it arises. This method is
|
||||
* equivalent to {@code os.pullEvent(event)} in Lua.
|
||||
*
|
||||
* Normally you'll wish to consume the event using {@link #then(ILuaFunction)}. This can be done slightly more
|
||||
* easily with {@link #pullEvent(String, ILuaFunction)}.
|
||||
*
|
||||
* @param filter The event name to filter on.
|
||||
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
||||
* parameters.
|
||||
* @see #pullEvent(String, ILuaFunction)
|
||||
* @see #pullEvent()
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEvent( @Nonnull String filter )
|
||||
{
|
||||
Preconditions.checkNotNull( filter, "event cannot be null" );
|
||||
return new OnEvent( false, filter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for an event to occur on the computer, suspending the coroutine until it arises. This method to
|
||||
* {@link #pullEvent()} and {@link #then(ILuaFunction)}.
|
||||
*
|
||||
* If you want to listen to a specific event, it's easier to use {@link #pullEvent(String, ILuaFunction)} rather
|
||||
* than running until the desired event is found.
|
||||
*
|
||||
* @param callback The function to call when the event is received.
|
||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||
* @see #pullEvent()
|
||||
* @see #pullEvent(String, ILuaFunction)
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEvent( @Nonnull ILuaFunction callback )
|
||||
{
|
||||
Preconditions.checkNotNull( callback, "callback cannot be null" );
|
||||
return new OnEvent( false, null ).then( callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the specified event to occur on the computer, suspending the coroutine until it arises. This method to
|
||||
* {@link #pullEvent(String)} and {@link #then(ILuaFunction)}.
|
||||
*
|
||||
* @param filter The event name to filter on.
|
||||
* @param callback The function to call when the event is received.
|
||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||
* @see #pullEvent(String)
|
||||
* @see #pullEvent(ILuaFunction)
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEvent( @Nullable String filter, @Nonnull ILuaFunction callback )
|
||||
{
|
||||
Preconditions.checkNotNull( callback, "callback cannot be null" );
|
||||
return new OnEvent( false, filter ).then( callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #pullEvent()}, except {@code terminated} events are also passed to the callback, instead of
|
||||
* throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
||||
*
|
||||
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
||||
* parameters.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEventRaw()
|
||||
{
|
||||
return new OnEvent( true, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #pullEvent(String)}, except {@code terminated} events are also passed to the callback, instead
|
||||
* of throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
||||
*
|
||||
* @param filter The event name to filter on.
|
||||
* @return The constructed method result. This evaluates to the name of the event that occurred, and any event
|
||||
* parameters.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEventRaw( @Nonnull String filter )
|
||||
{
|
||||
return new OnEvent( true, filter );
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #pullEvent(ILuaFunction)}, except {@code terminated} events are also passed to the callback,
|
||||
* instead of throwing an error. Only use this if you want to prevent program termination, which is not recommended.
|
||||
*
|
||||
* @param callback The function to call when the event is received.
|
||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEventRaw( @Nonnull ILuaFunction callback )
|
||||
{
|
||||
Preconditions.checkNotNull( callback, "callback cannot be null" );
|
||||
return new OnEvent( true, null ).then( callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #pullEvent(String, ILuaFunction)}, except {@code terminated} events are also passed to the
|
||||
* callback, instead of throwing an error. Only use this if you want to prevent program termination, which is not
|
||||
* recommended.
|
||||
*
|
||||
* @param filter The event name to filter on.
|
||||
* @param callback The function to call when the event is received.
|
||||
* @return The constructed method result. This evaluates to the result of the {@code callback}.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEventRaw( @Nullable String filter, @Nonnull ILuaFunction callback )
|
||||
{
|
||||
Preconditions.checkNotNull( callback, "callback cannot be null" );
|
||||
return new OnEvent( true, filter ).then( callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a task to be executed on the main server thread at the beginning of next tick, waiting for it to complete.
|
||||
* This should be used when you need to interact with the world in a thread-safe manner.
|
||||
*
|
||||
* @param callback The task to execute on the server thread.
|
||||
* @return The constructed method result, which evaluates to the result of the {@code callback}.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult onMainThread( @Nonnull ILuaCallable callback )
|
||||
{
|
||||
Preconditions.checkNotNull( callback, "callback cannot be null" );
|
||||
return new OnMainThread( callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the result of this {@link MethodResult} and return another result.
|
||||
*
|
||||
* Note this does NOT modify the current method result, rather returning a new (wrapped) one. You must return the
|
||||
* result of this call if you wish to use it.
|
||||
*
|
||||
* @param callback The function which consumes the provided values.
|
||||
* @return The constructed method result.
|
||||
*/
|
||||
@Nonnull
|
||||
public final MethodResult then( @Nonnull ILuaFunction callback )
|
||||
{
|
||||
Preconditions.checkNotNull( callback, "callback cannot be null" );
|
||||
return new AndThen( this, callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a blocking task within a {@link ILuaContext} and return its result.
|
||||
*
|
||||
* @param consumer The task to execute with the provided Lua context.
|
||||
* @return The constructed method result.
|
||||
* @see #evaluate(ILuaContext)
|
||||
* @deprecated This should not be used except to interface between the two call systems.
|
||||
*/
|
||||
@Deprecated
|
||||
public static MethodResult withLuaContext( @Nonnull ILuaContextTask consumer )
|
||||
{
|
||||
Preconditions.checkNotNull( consumer, "consumer cannot be null" );
|
||||
return new WithLuaContext( consumer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate this result task using {@link ILuaContext} and return its result.
|
||||
*
|
||||
* @param context The context to execute with.
|
||||
* @return The resulting values.
|
||||
* @throws LuaException If an error was thrown while executing one of the methods within this future.
|
||||
* @throws InterruptedException If the user shuts down or reboots the computer while the coroutine is suspended.
|
||||
* @see #withLuaContext(ILuaContextTask)
|
||||
* @deprecated This should not be used except to interface between the two call systems.
|
||||
*/
|
||||
@Deprecated
|
||||
public final Object[] evaluate( @Nonnull ILuaContext context ) throws LuaException, InterruptedException
|
||||
{
|
||||
return LuaContextResultEvaluator.evaluate( context, this );
|
||||
}
|
||||
|
||||
public static class Immediate extends MethodResult
|
||||
{
|
||||
@Nullable
|
||||
private final Object[] values;
|
||||
|
||||
@Nullable
|
||||
private Immediate( Object[] values )
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public Object[] getResult()
|
||||
{
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnEvent extends MethodResult
|
||||
{
|
||||
private final boolean raw;
|
||||
private final String filter;
|
||||
|
||||
private OnEvent( boolean raw, String filter )
|
||||
{
|
||||
this.raw = raw;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public boolean isRaw()
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFilter()
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnMainThread extends MethodResult
|
||||
{
|
||||
private final ILuaCallable task;
|
||||
|
||||
public OnMainThread( ILuaCallable task )
|
||||
{
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ILuaCallable getTask()
|
||||
{
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AndThen extends MethodResult
|
||||
{
|
||||
private final MethodResult previous;
|
||||
private final ILuaFunction callback;
|
||||
|
||||
private AndThen( MethodResult previous, ILuaFunction callback )
|
||||
{
|
||||
this.previous = previous;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MethodResult getPrevious()
|
||||
{
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ILuaFunction getCallback()
|
||||
{
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
|
||||
public static class WithLuaContext extends MethodResult
|
||||
{
|
||||
private final ILuaContextTask consumer;
|
||||
|
||||
private WithLuaContext( ILuaContextTask consumer )
|
||||
{
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ILuaContextTask getConsumer()
|
||||
{
|
||||
return consumer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,8 @@ public interface IPacketNetwork
|
||||
* to all receivers within range (or any interdimensional ones).
|
||||
*
|
||||
* @param packet The packet to send.
|
||||
* @see #transmitInterdimensional(Packet)
|
||||
* @param range The maximum distance this packet will be sent.
|
||||
* @see #transmitInterdimensional(Packet)
|
||||
* @see IPacketReceiver#receiveSameDimension(Packet, double)
|
||||
*/
|
||||
void transmitSameDimension( @Nonnull Packet packet, double range );
|
||||
@@ -51,7 +52,7 @@ public interface IPacketNetwork
|
||||
* to all receivers across all dimensions.
|
||||
*
|
||||
* @param packet The packet to send.
|
||||
* @see #transmitSameDimension(Packet, double)
|
||||
* @see #transmitSameDimension(Packet, double)
|
||||
* @see IPacketReceiver#receiveDifferentDimension(Packet)
|
||||
*/
|
||||
void transmitInterdimensional( @Nonnull Packet packet );
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An object which may be part of a wired network.
|
||||
*
|
||||
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts
|
||||
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
||||
* for its lifespan.
|
||||
*
|
||||
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
|
||||
* {@link IWiredElement} capability for the appropriate sides.
|
||||
*/
|
||||
public interface IWiredElement extends IWiredSender
|
||||
{
|
||||
/**
|
||||
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
|
||||
* peripherals change.
|
||||
*
|
||||
* @param change The change which occurred.
|
||||
* @see IWiredNetworkChange
|
||||
*/
|
||||
default void networkChanged( @Nonnull IWiredNetworkChange change )
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series
|
||||
* of peripherals.
|
||||
*
|
||||
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
|
||||
* there is some path between two nodes then they must be on the same network. {@link IWiredNetwork} will automatically
|
||||
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
|
||||
* change.
|
||||
*
|
||||
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
|
||||
* it is generally preferred to use the methods provided by {@link IWiredNode}.
|
||||
*
|
||||
* @see IWiredNode#getNetwork()
|
||||
*/
|
||||
public interface IWiredNetwork
|
||||
{
|
||||
/**
|
||||
* Create a connection between two nodes.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @param left The first node to connect
|
||||
* @param right The second node to connect
|
||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||
* @throws IllegalStateException If neither node is on the network.
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see IWiredNode#connectTo(IWiredNode)
|
||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
||||
*/
|
||||
boolean connect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
|
||||
|
||||
/**
|
||||
* Destroy a connection between this node and another.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @param left The first node in the connection.
|
||||
* @param right The second node in the connection.
|
||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
||||
* @throws IllegalArgumentException If either node is not on the network.
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see IWiredNode#disconnectFrom(IWiredNode)
|
||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
||||
*/
|
||||
boolean disconnect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
|
||||
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
*
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param node The node to remove
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredNode#remove()
|
||||
*/
|
||||
boolean remove( @Nonnull IWiredNode node );
|
||||
|
||||
/**
|
||||
* Update the peripherals a node provides.
|
||||
*
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param node The node to attach peripherals for.
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredNode#updatePeripherals(Map)
|
||||
*/
|
||||
void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals );
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a change to the objects on a wired network.
|
||||
*
|
||||
* @see IWiredElement#networkChanged(IWiredNetworkChange)
|
||||
*/
|
||||
public interface IWiredNetworkChange
|
||||
{
|
||||
/**
|
||||
* A set of peripherals which have been removed. Note that there may be entries with the same name
|
||||
* in the added and removed set, but with a different peripheral.
|
||||
*
|
||||
* @return The set of removed peripherals.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<String, IPeripheral> peripheralsRemoved();
|
||||
|
||||
/**
|
||||
* A set of peripherals which have been added. Note that there may be entries with the same name
|
||||
* in the added and removed set, but with a different peripheral.
|
||||
*
|
||||
* @return The set of added peripherals.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<String, IPeripheral> peripheralsAdded();
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s.
|
||||
*
|
||||
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
||||
* methods may be safely used on any thread.
|
||||
*
|
||||
* When sending a packet, the system will attempt to find the shortest path between the two nodes based on their
|
||||
* element's position. Note that packet senders and receivers can have different locations from their associated
|
||||
* element: the distance between the two will be added to the total packet's distance.
|
||||
*
|
||||
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
||||
* be used on the main server thread.
|
||||
*/
|
||||
public interface IWiredNode extends IPacketNetwork
|
||||
{
|
||||
/**
|
||||
* The associated element for this network node.
|
||||
*
|
||||
* @return This node's element.
|
||||
*/
|
||||
@Nonnull
|
||||
IWiredElement getElement();
|
||||
|
||||
/**
|
||||
* The network this node is currently connected to. Note that this may change
|
||||
* after any network operation, so it should not be cached.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @return This node's network.
|
||||
*/
|
||||
@Nonnull
|
||||
IWiredNetwork getNetwork();
|
||||
|
||||
/**
|
||||
* Create a connection from this node to another.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @param node The other node to connect to.
|
||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
||||
* @see IWiredNode#disconnectFrom(IWiredNode)
|
||||
*/
|
||||
default boolean connectTo( @Nonnull IWiredNode node )
|
||||
{
|
||||
return getNetwork().connect( this, node );
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a connection between this node and another.
|
||||
*
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @param node The other node to disconnect from.
|
||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
||||
* @throws IllegalArgumentException If {@code node} is not on the same network.
|
||||
* @see IWiredNetwork#disconnect(IWiredNode, IWiredNode)
|
||||
* @see IWiredNode#connectTo(IWiredNode)
|
||||
*/
|
||||
default boolean disconnectFrom( @Nonnull IWiredNode node )
|
||||
{
|
||||
return getNetwork().disconnect( this, node );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
*
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredNetwork#remove(IWiredNode)
|
||||
*/
|
||||
default boolean remove()
|
||||
{
|
||||
return getNetwork().remove( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this node's peripherals as having changed.
|
||||
*
|
||||
* This should only be used on the server thread. You should only call this on nodes
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @see IWiredNetwork#updatePeripherals(IWiredNode, Map)
|
||||
*/
|
||||
default void updatePeripherals( @Nonnull Map<String, IPeripheral> peripherals )
|
||||
{
|
||||
getNetwork().updatePeripherals( this, peripherals );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.IPacketSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An object on a {@link IWiredNetwork} capable of sending packets.
|
||||
*
|
||||
* Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to
|
||||
* to send the packet from.
|
||||
*/
|
||||
public interface IWiredSender extends IPacketSender
|
||||
{
|
||||
/**
|
||||
* The node in the network representing this object.
|
||||
*
|
||||
* This should be used as a proxy for the main network. One should send packets
|
||||
* and register receivers through this object.
|
||||
*
|
||||
* @return The node for this element.
|
||||
*/
|
||||
@Nonnull
|
||||
IWiredNode getNode();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
@API( owner="ComputerCraft", provides="ComputerCraft|API|Network|Wired", apiVersion="${version}" )
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import net.minecraftforge.fml.common.API;
|
||||
@@ -13,6 +13,8 @@ import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The interface passed to peripherals by computers or turtles, providing methods
|
||||
@@ -154,4 +156,33 @@ public interface IComputerAccess
|
||||
*/
|
||||
@Nonnull
|
||||
String getAttachmentName();
|
||||
|
||||
/**
|
||||
* Get a set of peripherals that this computer access can "see", along with their attachment name.
|
||||
*
|
||||
* This may include other peripherals on the wired network or peripherals on other sides of the computer.
|
||||
*
|
||||
* @return All reachable peripherals
|
||||
* @see #getAttachmentName()
|
||||
* @see #getAvailablePeripheral(String)
|
||||
*/
|
||||
@Nonnull
|
||||
default Map<String, IPeripheral> getAvailablePeripherals()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reachable peripheral with the given attachement name. This is a equivalent to
|
||||
* {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more performant.
|
||||
*
|
||||
* @param name The peripheral's attached name
|
||||
* @return The reachable peripheral, or {@code null} if none can be found.
|
||||
* @see #getAvailablePeripherals()
|
||||
*/
|
||||
@Nullable
|
||||
default IPeripheral getAvailablePeripheral( @Nonnull String name )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.peripheral;
|
||||
|
||||
import dan200.computercraft.api.lua.ICallContext;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -70,10 +72,46 @@ public interface IPeripheral
|
||||
* InterruptedException will be thrown. This exception must not be caught or
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @see #getMethodNames
|
||||
* @deprecated Use {@link #callMethod(IComputerAccess, ICallContext, int, Object[])} instead.
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException;
|
||||
|
||||
/**
|
||||
* This is called when a lua program on an attached computer calls {@code peripheral.call()} with
|
||||
* one of the methods exposed by {@link #getMethodNames()}.
|
||||
*
|
||||
* Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe
|
||||
* when interacting with Minecraft objects.
|
||||
*
|
||||
* @param computer The interface to the computer that is making the call. Remember that multiple
|
||||
* computers can be attached to a peripheral at once.
|
||||
* @param context The context of the current call.
|
||||
* @param method An integer identifying which of the methods from getMethodNames() the computercraft
|
||||
* wishes to call. The integer indicates the index into the getMethodNames() table
|
||||
* that corresponds to the string passed into peripheral.call()
|
||||
* @param arguments An array of objects, representing the arguments passed into {@code peripheral.call()}.<br>
|
||||
* Lua values of type "string" will be represented by Object type String.<br>
|
||||
* Lua values of type "number" will be represented by Object type Double.<br>
|
||||
* Lua values of type "boolean" will be represented by Object type Boolean.<br>
|
||||
* Lua values of type "table" will be represented by Object type Map.<br>
|
||||
* Lua values of any other type will be represented by a null object.<br>
|
||||
* This array will be empty if no arguments are passed.
|
||||
* @return The result of calling this method. Use {@link MethodResult#empty()} to return nothing or
|
||||
* {@link MethodResult#of(Object...)} to return several values.
|
||||
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the
|
||||
* same message as your exception. Use this to throw appropriate errors if the wrong
|
||||
* arguments are supplied to your method.
|
||||
* @see #getMethodNames
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings({ "deprecation" })
|
||||
default MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
{
|
||||
return MethodResult.withLuaContext( lua -> callMethod( computer, lua, method, arguments ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called when canAttachToSide has returned true, and a computer is attaching to the peripheral.
|
||||
*
|
||||
@@ -114,6 +152,18 @@ public interface IPeripheral
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object that this peripheral provides methods for. This will generally be the tile entity
|
||||
* or block, but may be an inventory, entity, etc...
|
||||
*
|
||||
* @return The object this peripheral targets
|
||||
*/
|
||||
@Nonnull
|
||||
default Object getTarget()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this peripheral is equivalent to another one.
|
||||
*
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -135,6 +137,14 @@ public interface ITurtleAccess
|
||||
*/
|
||||
int getColour();
|
||||
|
||||
/**
|
||||
* Get the player who owns this turtle, namely whoever placed it.
|
||||
*
|
||||
* @return This turtle's owner.
|
||||
*/
|
||||
@Nonnull
|
||||
GameProfile getOwningPlayer();
|
||||
|
||||
/**
|
||||
* Get the inventory of this turtle
|
||||
*
|
||||
@@ -148,7 +158,7 @@ public interface ITurtleAccess
|
||||
* Get the inventory of this turtle as an {@link IItemHandlerModifiable}.
|
||||
*
|
||||
* @return This turtle's inventory
|
||||
* @see #getInventory()
|
||||
* @see #getInventory()
|
||||
* @see IItemHandlerModifiable
|
||||
* @see net.minecraftforge.items.CapabilityItemHandler#ITEM_HANDLER_CAPABILITY
|
||||
*/
|
||||
@@ -229,10 +239,30 @@ public interface ITurtleAccess
|
||||
* intercepted, or the computer will leak memory and end up in a broken state.
|
||||
* @see ITurtleCommand
|
||||
* @see ILuaContext#pullEvent(String)
|
||||
* @deprecated Use {@link #executeCommand(ITurtleCommand)} instead.
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Object[] executeCommand( @Nonnull ILuaContext context, @Nonnull ITurtleCommand command ) throws LuaException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be
|
||||
* executed on the main thread, so are guaranteed to be able to access Minecraft objects safely, and will be queued
|
||||
* up with the turtles standard movement and tool commands.
|
||||
*
|
||||
* An issued command will return an unique integer, which will be supplied as a parameter to a "turtle_response"
|
||||
* event issued to the turtle after the command has completed. Look at the Lua source code for "rom/apis/turtle" for
|
||||
* how to build a Lua wrapper around this functionality.
|
||||
*
|
||||
* @param command An object which will execute the custom command when its point in the queue is reached
|
||||
* @return The constructed method result. This evaluates to the result of the provided {@code command}.
|
||||
* @throws UnsupportedOperationException When attempting to execute a command on the client side.
|
||||
* @see ITurtleCommand
|
||||
* @see MethodResult#pullEvent(String)
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult executeCommand( @Nonnull ITurtleCommand command );
|
||||
|
||||
/**
|
||||
* Start playing a specific animation. This will prevent other turtle commands from executing until
|
||||
* it is finished.
|
||||
|
||||
@@ -8,11 +8,15 @@ package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
|
||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -100,6 +104,9 @@ public interface ITurtleUpgrade
|
||||
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called
|
||||
* by the turtle, and the tool is required to do some work.
|
||||
*
|
||||
* Conforming implementations should fire {@link BlockEvent.BreakEvent} and {@link TurtleBlockEvent.Dig}for digging,
|
||||
* {@link AttackEntityEvent} and {@link TurtleAttackEvent} for attacking.
|
||||
*
|
||||
* @param turtle Access to the turtle that the tool resides on.
|
||||
* @param side Which side of the turtle (left or right) the tool resides on.
|
||||
* @param verb Which action (dig or attack) the turtle is being called on to perform.
|
||||
@@ -126,7 +133,7 @@ public interface ITurtleUpgrade
|
||||
* @return The model that you wish to be used to render your upgrade, and a transformation to apply to it. Returning
|
||||
* a transformation of {@code null} has the same effect as the identify matrix.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
@SideOnly( Side.CLIENT )
|
||||
@Nonnull
|
||||
Pair<IBakedModel, Matrix4f> getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
/**
|
||||
* A basic action that a turtle may perform, as accessed by the {@code turtle} API.
|
||||
*
|
||||
* @see TurtleActionEvent
|
||||
*/
|
||||
public enum TurtleAction
|
||||
{
|
||||
/**
|
||||
* A turtle moves to a new position.
|
||||
*
|
||||
* @see TurtleBlockEvent.Move
|
||||
*/
|
||||
MOVE,
|
||||
|
||||
/**
|
||||
* A turtle turns in a specific direction.
|
||||
*/
|
||||
TURN,
|
||||
|
||||
/**
|
||||
* A turtle attempts to dig a block.
|
||||
*
|
||||
* @see TurtleBlockEvent.Dig
|
||||
*/
|
||||
DIG,
|
||||
|
||||
/**
|
||||
* A turtle attempts to place a block or item in the world.
|
||||
*
|
||||
* @see TurtleBlockEvent.Place
|
||||
*/
|
||||
PLACE,
|
||||
|
||||
/**
|
||||
* A turtle attempts to attack an entity.
|
||||
*
|
||||
* @see TurtleActionEvent
|
||||
*/
|
||||
ATTACK,
|
||||
|
||||
/**
|
||||
* Drop an item into an inventory/the world.
|
||||
*
|
||||
* @see TurtleInventoryEvent.Drop
|
||||
*/
|
||||
DROP,
|
||||
|
||||
/**
|
||||
* Suck an item from an inventory or the world.
|
||||
*
|
||||
* @see TurtleInventoryEvent.Suck
|
||||
*/
|
||||
SUCK,
|
||||
|
||||
/**
|
||||
* Refuel the turtle's fuel levels.
|
||||
*/
|
||||
REFUEL,
|
||||
|
||||
/**
|
||||
* Equip or unequip an item.
|
||||
*/
|
||||
EQUIP,
|
||||
|
||||
/**
|
||||
* Inspect a block in world
|
||||
*
|
||||
* @see TurtleBlockEvent.Inspect
|
||||
*/
|
||||
INSPECT,
|
||||
|
||||
/**
|
||||
* Gather metdata about an item in the turtle's inventory.
|
||||
*/
|
||||
INSPECT_ITEM,
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An event fired when a turtle is performing a known action.
|
||||
*/
|
||||
@Cancelable
|
||||
public class TurtleActionEvent extends TurtleEvent
|
||||
{
|
||||
private final TurtleAction action;
|
||||
private String failureMessage;
|
||||
|
||||
public TurtleActionEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action )
|
||||
{
|
||||
super( turtle );
|
||||
|
||||
Preconditions.checkNotNull( action, "action cannot be null" );
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public TurtleAction getAction()
|
||||
{
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cancellation state of this action.
|
||||
*
|
||||
* If {@code cancel} is {@code true}, this action will not be carried out.
|
||||
*
|
||||
* @param cancel The new canceled value.
|
||||
* @see TurtleCommandResult#failure()
|
||||
* @deprecated Use {@link #setCanceled(boolean, String)} instead.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setCanceled( boolean cancel )
|
||||
{
|
||||
setCanceled( cancel, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cancellation state of this action, setting a failure message if required.
|
||||
*
|
||||
* If {@code cancel} is {@code true}, this action will not be carried out.
|
||||
*
|
||||
* @param cancel The new canceled value.
|
||||
* @param failureMessage The message to return to the user explaining the failure.
|
||||
* @see TurtleCommandResult#failure(String)
|
||||
*/
|
||||
public void setCanceled( boolean cancel, @Nullable String failureMessage )
|
||||
{
|
||||
super.setCanceled( cancel );
|
||||
this.failureMessage = cancel ? failureMessage : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message with which this will fail.
|
||||
*
|
||||
* @return The failure message.
|
||||
* @see TurtleCommandResult#failure()
|
||||
* @see #setCanceled(boolean, String)
|
||||
*/
|
||||
@Nullable
|
||||
public String getFailureMessage()
|
||||
{
|
||||
return failureMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleVerb;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to attack an entity.
|
||||
*
|
||||
* This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)},
|
||||
* as the base {@code turtle.attack()} command does not fire it.
|
||||
*
|
||||
* Note that such commands should also fire {@link AttackEntityEvent}, so you do not need to listen to both.
|
||||
*
|
||||
* @see TurtleAction#ATTACK
|
||||
*/
|
||||
public class TurtleAttackEvent extends TurtlePlayerEvent
|
||||
{
|
||||
private final Entity target;
|
||||
private final ITurtleUpgrade upgrade;
|
||||
private final TurtleSide side;
|
||||
|
||||
public TurtleAttackEvent( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side )
|
||||
{
|
||||
super( turtle, TurtleAction.ATTACK, player );
|
||||
Preconditions.checkNotNull( target, "target cannot be null" );
|
||||
Preconditions.checkNotNull( upgrade, "upgrade cannot be null" );
|
||||
Preconditions.checkNotNull( side, "side cannot be null" );
|
||||
this.target = target;
|
||||
this.upgrade = upgrade;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity being attacked by this turtle.
|
||||
*
|
||||
* @return The entity being attacked.
|
||||
*/
|
||||
@Nonnull
|
||||
public Entity getTarget()
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upgrade responsible for attacking.
|
||||
*
|
||||
* @return The upgrade responsible for attacking.
|
||||
*/
|
||||
@Nonnull
|
||||
public ITurtleUpgrade getUpgrade()
|
||||
{
|
||||
return upgrade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the side the attacking upgrade is on.
|
||||
*
|
||||
* @return The upgrade's side.
|
||||
*/
|
||||
@Nonnull
|
||||
public TurtleSide getSide()
|
||||
{
|
||||
return side;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleVerb;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A general event for when a turtle interacts with a block or region.
|
||||
*
|
||||
* You should generally listen to one of the sub-events instead, cancelling them where
|
||||
* appropriate.
|
||||
*
|
||||
* Note that you are not guaranteed to receive this event, if it has been cancelled by other
|
||||
* mechanisms, such as block protection systems.
|
||||
*
|
||||
* Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact
|
||||
* with a block, simply objects within that block space.
|
||||
*/
|
||||
@Cancelable
|
||||
public abstract class TurtleBlockEvent extends TurtlePlayerEvent
|
||||
{
|
||||
private final World world;
|
||||
private final BlockPos pos;
|
||||
|
||||
protected TurtleBlockEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos )
|
||||
{
|
||||
super( turtle, action, player );
|
||||
|
||||
Preconditions.checkNotNull( world, "world cannot be null" );
|
||||
Preconditions.checkNotNull( pos, "pos cannot be null" );
|
||||
this.world = world;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world the turtle is interacting in.
|
||||
*
|
||||
* @return The world the turtle is interacting in.
|
||||
*/
|
||||
public World getWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position the turtle is interacting with. Note that this is different
|
||||
* to {@link ITurtleAccess#getPosition()}.
|
||||
*
|
||||
* @return The position the turtle is interacting with.
|
||||
*/
|
||||
public BlockPos getPos()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to dig a block.
|
||||
*
|
||||
* This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)},
|
||||
* as the base {@code turtle.dig()} command does not fire it.
|
||||
*
|
||||
* Note that such commands should also fire {@link BlockEvent.BreakEvent}, so you do not need to listen to both.
|
||||
*
|
||||
* @see TurtleAction#DIG
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Dig extends TurtleBlockEvent
|
||||
{
|
||||
private final IBlockState block;
|
||||
private final ITurtleUpgrade upgrade;
|
||||
private final TurtleSide side;
|
||||
|
||||
public Dig( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState block, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side )
|
||||
{
|
||||
super( turtle, TurtleAction.DIG, player, world, pos );
|
||||
|
||||
Preconditions.checkNotNull( block, "block cannot be null" );
|
||||
Preconditions.checkNotNull( upgrade, "upgrade cannot be null" );
|
||||
Preconditions.checkNotNull( side, "side cannot be null" );
|
||||
this.block = block;
|
||||
this.upgrade = upgrade;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block which is about to be broken.
|
||||
*
|
||||
* @return The block which is going to be broken.
|
||||
*/
|
||||
@Nonnull
|
||||
public IBlockState getBlock()
|
||||
{
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upgrade doing the digging
|
||||
*
|
||||
* @return The upgrade doing the digging.
|
||||
*/
|
||||
@Nonnull
|
||||
public ITurtleUpgrade getUpgrade()
|
||||
{
|
||||
return upgrade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the side the upgrade doing the digging is on.
|
||||
*
|
||||
* @return The upgrade's side.
|
||||
*/
|
||||
@Nonnull
|
||||
public TurtleSide getSide()
|
||||
{
|
||||
return side;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to move into a block.
|
||||
*
|
||||
* @see TurtleAction#MOVE
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Move extends TurtleBlockEvent
|
||||
{
|
||||
public Move( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos )
|
||||
{
|
||||
super( turtle, TurtleAction.MOVE, player, world, pos );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to place a block in the world.
|
||||
*
|
||||
* @see TurtleAction#PLACE
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Place extends TurtleBlockEvent
|
||||
{
|
||||
private final ItemStack stack;
|
||||
|
||||
public Place( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack )
|
||||
{
|
||||
super( turtle, TurtleAction.PLACE, player, world, pos );
|
||||
|
||||
Preconditions.checkNotNull( stack, "stack cannot be null" );
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item stack that will be placed. This should not be modified.
|
||||
*
|
||||
* @return The item stack to be placed.
|
||||
*/
|
||||
@Nonnull
|
||||
public ItemStack getStack()
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a turtle gathers data on a block in world.
|
||||
*
|
||||
* You may prevent blocks being inspected, or add additional information to the result.
|
||||
*
|
||||
* @see TurtleAction#INSPECT
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Inspect extends TurtleBlockEvent
|
||||
{
|
||||
private final IBlockState state;
|
||||
private final Map<String, Object> data;
|
||||
|
||||
public Inspect( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Map<String, Object> data )
|
||||
{
|
||||
super( turtle, TurtleAction.INSPECT, player, world, pos );
|
||||
|
||||
Preconditions.checkNotNull( state, "state cannot be null" );
|
||||
Preconditions.checkNotNull( data, "data cannot be null" );
|
||||
this.data = data;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block state which is being inspected.
|
||||
*
|
||||
* @return The inspected block state.
|
||||
*/
|
||||
@Nonnull
|
||||
public IBlockState getState()
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "inspection data" from this block, which will be returned to the user.
|
||||
*
|
||||
* @return This block's inspection data.
|
||||
*/
|
||||
@Nonnull
|
||||
public Map<String, Object> getData()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new information to the inspection result. Note this will override fields with the same name.
|
||||
*
|
||||
* @param newData The data to add. Note all values should be convertable to Lua (see
|
||||
* {@link dan200.computercraft.api.peripheral.IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}).
|
||||
*/
|
||||
public void addData( @Nonnull Map<String, ?> newData )
|
||||
{
|
||||
Preconditions.checkNotNull( newData, "newData cannot be null" );
|
||||
data.putAll( newData );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A base class for all events concerning a turtle. This will only ever constructed and fired on the server side,
|
||||
* so sever specific methods on {@link ITurtleAccess} are safe to use.
|
||||
*
|
||||
* You should generally not need to subscribe to this event, preferring one of the more specific classes.
|
||||
*
|
||||
* @see TurtleActionEvent
|
||||
*/
|
||||
public abstract class TurtleEvent extends Event
|
||||
{
|
||||
private final ITurtleAccess turtle;
|
||||
|
||||
protected TurtleEvent( @Nonnull ITurtleAccess turtle )
|
||||
{
|
||||
Preconditions.checkNotNull( turtle, "turtle cannot be null" );
|
||||
this.turtle = turtle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the turtle which is performing this action.
|
||||
*
|
||||
* @return The access for this turtle.
|
||||
*/
|
||||
@Nonnull
|
||||
public ITurtleAccess getTurtle()
|
||||
{
|
||||
return turtle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.fml.common.eventhandler.Cancelable;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to interact with an inventory.
|
||||
*/
|
||||
@Cancelable
|
||||
public abstract class TurtleInventoryEvent extends TurtleBlockEvent
|
||||
{
|
||||
private final IItemHandler handler;
|
||||
|
||||
protected TurtleInventoryEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler )
|
||||
{
|
||||
super( turtle, action, player, world, pos );
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the inventory being interacted with
|
||||
*
|
||||
* @return The inventory being interacted with, {@code null} if the item will be dropped to/sucked from the world.
|
||||
*/
|
||||
@Nullable
|
||||
public IItemHandler getItemHandler()
|
||||
{
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to suck from an inventory.
|
||||
*
|
||||
* @see TurtleAction#SUCK
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Suck extends TurtleInventoryEvent
|
||||
{
|
||||
public Suck( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler )
|
||||
{
|
||||
super( turtle, TurtleAction.SUCK, player, world, pos, handler );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a turtle attempts to drop an item into an inventory.
|
||||
*
|
||||
* @see TurtleAction#DROP
|
||||
*/
|
||||
@Cancelable
|
||||
public static class Drop extends TurtleInventoryEvent
|
||||
{
|
||||
private final ItemStack stack;
|
||||
|
||||
public Drop( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler, @Nonnull ItemStack stack )
|
||||
{
|
||||
super( turtle, TurtleAction.DROP, player, world, pos, handler );
|
||||
|
||||
Preconditions.checkNotNull( stack, "stack cannot be null" );
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* The item which will be inserted into the inventory/dropped on the ground.
|
||||
*
|
||||
* Note that this is a copy of the original stack, and so should not be modified, as that will have no effect.
|
||||
*
|
||||
* @return The item stack which will be dropped.
|
||||
*/
|
||||
public ItemStack getStack()
|
||||
{
|
||||
return stack.copy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An action done by a turtle which is normally done by a player.
|
||||
*
|
||||
* {@link #getPlayer()} may be used to modify the player's attributes or perform permission checks.
|
||||
*/
|
||||
public abstract class TurtlePlayerEvent extends TurtleActionEvent
|
||||
{
|
||||
private final FakePlayer player;
|
||||
|
||||
protected TurtlePlayerEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player )
|
||||
{
|
||||
super( turtle, action );
|
||||
|
||||
Preconditions.checkNotNull( player, "player cannot be null" );
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fake player, representing this turtle.
|
||||
*
|
||||
* This may be used for triggering permission checks.
|
||||
*
|
||||
* @return A {@link FakePlayer} representing this turtle.
|
||||
*/
|
||||
@Nonnull
|
||||
public FakePlayer getPlayer()
|
||||
{
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
|
||||
@API(owner = "ComputerCraft", provides = "ComputerCraft|API|Turtle|Event", apiVersion = "${version}")
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import net.minecraftforge.fml.common.API;
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class GuiComputer extends GuiContainer
|
||||
private final int m_termHeight;
|
||||
private WidgetTerminal m_terminal;
|
||||
|
||||
protected GuiComputer( Container container, ComputerFamily family, IComputer computer, int termWidth, int termHeight )
|
||||
public GuiComputer( Container container, ComputerFamily family, IComputer computer, int termWidth, int termHeight )
|
||||
{
|
||||
super( container );
|
||||
m_family = family;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,25 +6,19 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
|
||||
public class GuiPrintout extends GuiContainer
|
||||
{
|
||||
private static final ResourceLocation background = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
|
||||
|
||||
private static final int xSize = 172;
|
||||
private static final int ySize = 209;
|
||||
|
||||
private final boolean m_book;
|
||||
private final int m_pages;
|
||||
private final TextBuffer[] m_text;
|
||||
@@ -34,23 +28,18 @@ public class GuiPrintout extends GuiContainer
|
||||
public GuiPrintout( ContainerHeldItem container )
|
||||
{
|
||||
super( container );
|
||||
m_book = (ItemPrintout.getType( container.getStack() ) == ItemPrintout.Type.Book);
|
||||
|
||||
String[] text = ItemPrintout.getText( container.getStack() );
|
||||
m_text = new TextBuffer[ text.length ];
|
||||
for( int i=0; i<m_text.length; ++i )
|
||||
{
|
||||
m_text[i] = new TextBuffer( text[i] );
|
||||
}
|
||||
for( int i = 0; i < m_text.length; ++i ) m_text[ i ] = new TextBuffer( text[ i ] );
|
||||
|
||||
String[] colours = ItemPrintout.getColours( container.getStack() );
|
||||
m_colours = new TextBuffer[ colours.length ];
|
||||
for( int i=0; i<m_colours.length; ++i )
|
||||
{
|
||||
m_colours[i] = new TextBuffer( colours[i] );
|
||||
}
|
||||
for( int i = 0; i < m_colours.length; ++i ) m_colours[ i ] = new TextBuffer( colours[ i ] );
|
||||
|
||||
m_pages = Math.max( m_text.length / ItemPrintout.LINES_PER_PAGE, 1 );
|
||||
m_page = 0;
|
||||
m_pages = Math.max( m_text.length / ItemPrintout.LINES_PER_PAGE, 1 );
|
||||
m_book = ItemPrintout.getType( container.getStack() ) == ItemPrintout.Type.Book;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,25 +67,19 @@ public class GuiPrintout extends GuiContainer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char c, int k) throws IOException
|
||||
protected void keyTyped( char c, int k ) throws IOException
|
||||
{
|
||||
super.keyTyped( c, k );
|
||||
|
||||
if( k == 205 )
|
||||
{
|
||||
// Right
|
||||
if( m_page < m_pages - 1 )
|
||||
{
|
||||
m_page = m_page + 1;
|
||||
}
|
||||
if( m_page < m_pages - 1 ) m_page++;
|
||||
}
|
||||
else if( k == 203 )
|
||||
{
|
||||
// Left
|
||||
if( m_page > 0 )
|
||||
{
|
||||
m_page = m_page - 1;
|
||||
}
|
||||
// Left
|
||||
if( m_page > 0 ) m_page--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,21 +89,15 @@ public class GuiPrintout extends GuiContainer
|
||||
super.handleMouseInput();
|
||||
|
||||
int mouseWheelChange = Mouse.getEventDWheel();
|
||||
if (mouseWheelChange < 0)
|
||||
if( mouseWheelChange < 0 )
|
||||
{
|
||||
// Up
|
||||
if( m_page < m_pages - 1 )
|
||||
{
|
||||
m_page = m_page + 1;
|
||||
}
|
||||
if( m_page < m_pages - 1 ) m_page++;
|
||||
}
|
||||
else if (mouseWheelChange > 0)
|
||||
else if( mouseWheelChange > 0 )
|
||||
{
|
||||
// Down
|
||||
if( m_page > 0 )
|
||||
{
|
||||
m_page = m_page - 1;
|
||||
}
|
||||
if( m_page > 0 ) m_page--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,78 +112,20 @@ public class GuiPrintout extends GuiContainer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float f)
|
||||
public void drawScreen( int mouseX, int mouseY, float f )
|
||||
{
|
||||
// Draw background
|
||||
zLevel = zLevel - 1;
|
||||
drawDefaultBackground();
|
||||
|
||||
zLevel = zLevel + 1;
|
||||
|
||||
// Draw the printout
|
||||
GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
this.mc.getTextureManager().bindTexture( background );
|
||||
|
||||
int startY = (height - ySize) / 2;
|
||||
//int startX = (width - xSize) / 2 - (m_page * 8);
|
||||
int startX = (width - (xSize + (m_pages - 1)*8)) / 2;
|
||||
|
||||
if( m_book )
|
||||
{
|
||||
// Border
|
||||
drawTexturedModalRect( startX - 8, startY - 8, xSize + 48, 0, 12, ySize + 24);
|
||||
drawTexturedModalRect( startX + xSize + (m_pages - 1)*8 - 4, startY - 8, xSize + 48 + 12, 0, 12, ySize + 24);
|
||||
|
||||
drawTexturedModalRect( startX, startY - 8, 0, ySize, xSize, 12);
|
||||
drawTexturedModalRect( startX, startY + ySize - 4, 0, ySize + 12, xSize, 12);
|
||||
for( int n=1; n<m_pages; ++n )
|
||||
{
|
||||
drawTexturedModalRect( startX + xSize + (n-1)*8, startY - 8, 0, ySize, 8, 12);
|
||||
drawTexturedModalRect( startX + xSize + (n-1)*8, startY + ySize - 4, 0, ySize + 12, 8, 12);
|
||||
}
|
||||
}
|
||||
|
||||
// Left half
|
||||
if( m_page == 0 )
|
||||
{
|
||||
drawTexturedModalRect( startX, startY, 24, 0, xSize / 2, ySize);
|
||||
drawTexturedModalRect( startX, startY, 0, 0, 12, ySize);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawTexturedModalRect( startX, startY, 0, 0, 12, ySize);
|
||||
for( int n=1; n<m_page; ++n )
|
||||
{
|
||||
drawTexturedModalRect( startX + n*8, startY, 12, 0, 12, ySize);
|
||||
}
|
||||
drawTexturedModalRect( startX + m_page*8, startY, 24, 0, xSize / 2, ySize);
|
||||
}
|
||||
|
||||
// Right half
|
||||
if( m_page == (m_pages - 1) )
|
||||
{
|
||||
drawTexturedModalRect( startX + m_page*8 + xSize/2, startY, 24 + xSize / 2, 0, xSize / 2, ySize);
|
||||
drawTexturedModalRect( startX + m_page*8 + (xSize - 12), startY, 24 + xSize + 12, 0, 12, ySize);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawTexturedModalRect( startX + (m_pages - 1)*8 + (xSize - 12), startY, 24 + xSize + 12, 0, 12, ySize);
|
||||
for( int n=m_pages-2; n>=m_page; --n )
|
||||
{
|
||||
drawTexturedModalRect( startX + n*8 + (xSize - 12), startY, 24 + xSize, 0, 12, ySize);
|
||||
}
|
||||
drawTexturedModalRect( startX + m_page*8 + xSize/2, startY, 24 + xSize / 2, 0, xSize / 2, ySize);
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer)ComputerCraft.getFixedWidthFontRenderer();
|
||||
int x = startX + m_page * 8 + 13;
|
||||
int y = startY + 11;
|
||||
for( int line=0; line<ItemPrintout.LINES_PER_PAGE; ++line )
|
||||
{
|
||||
int lineIdx = ItemPrintout.LINES_PER_PAGE * m_page + line;
|
||||
if( lineIdx >= 0 && lineIdx < m_text.length )
|
||||
{
|
||||
fontRenderer.drawString( m_text[lineIdx], x, y, m_colours[lineIdx], null, 0, 0, false, Palette.DEFAULT );
|
||||
}
|
||||
y = y + FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
}
|
||||
int startY = (height - Y_SIZE) / 2;
|
||||
int startX = (width - X_SIZE) / 2;
|
||||
|
||||
drawBorder( startX, startY, zLevel, m_page, m_pages, m_book );
|
||||
drawText( startX + X_TEXT_MARGIN, startY + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.client.renderer.color.IItemColor;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.SimpleReloadableResourceManager;
|
||||
@@ -115,8 +116,23 @@ public class CCTurtleProxyClient extends CCTurtleProxyCommon
|
||||
MinecraftForge.EVENT_BUS.register( handlers );
|
||||
}
|
||||
|
||||
public class ForgeHandlers
|
||||
public static class ForgeHandlers
|
||||
{
|
||||
private static final String[] TURTLE_UPGRADES = {
|
||||
"turtle_modem_off_left",
|
||||
"turtle_modem_on_left",
|
||||
"turtle_modem_off_right",
|
||||
"turtle_modem_on_right",
|
||||
"turtle_crafting_table_left",
|
||||
"turtle_crafting_table_right",
|
||||
"advanced_turtle_modem_off_left",
|
||||
"advanced_turtle_modem_on_left",
|
||||
"advanced_turtle_modem_off_right",
|
||||
"advanced_turtle_modem_on_right",
|
||||
"turtle_speaker_upgrade_left",
|
||||
"turtle_speaker_upgrade_right",
|
||||
};
|
||||
|
||||
private TurtleSmartItemModel m_turtleSmartItemModel;
|
||||
|
||||
public ForgeHandlers()
|
||||
@@ -142,24 +158,27 @@ public class CCTurtleProxyClient extends CCTurtleProxyCommon
|
||||
@SubscribeEvent
|
||||
public void onTextureStitchEvent( TextureStitchEvent.Pre event )
|
||||
{
|
||||
event.getMap().registerSprite( new ResourceLocation( "computercraft", "blocks/crafty_upgrade" ) );
|
||||
// Load all textures for upgrades
|
||||
TextureMap map = event.getMap();
|
||||
for( String upgrade : TURTLE_UPGRADES )
|
||||
{
|
||||
IModel model = ModelLoaderRegistry.getModelOrMissing( new ResourceLocation( "computercraft", "block/" + upgrade ) );
|
||||
for( ResourceLocation texture : model.getTextures() )
|
||||
{
|
||||
map.registerSprite( texture );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onModelBakeEvent( ModelBakeEvent event )
|
||||
{
|
||||
loadModel( event, "turtle_modem_off_left" );
|
||||
loadModel( event, "turtle_modem_on_left" );
|
||||
loadModel( event, "turtle_modem_off_right" );
|
||||
loadModel( event, "turtle_modem_on_right" );
|
||||
loadModel( event, "turtle_crafting_table_left" );
|
||||
loadModel( event, "turtle_crafting_table_right" );
|
||||
loadModel( event, "advanced_turtle_modem_off_left" );
|
||||
loadModel( event, "advanced_turtle_modem_on_left" );
|
||||
loadModel( event, "advanced_turtle_modem_off_right" );
|
||||
loadModel( event, "advanced_turtle_modem_on_right" );
|
||||
loadModel( event, "turtle_speaker_upgrade_left" );
|
||||
loadModel( event, "turtle_speaker_upgrade_right" );
|
||||
// Load all upgrade models
|
||||
for( String upgrade : TURTLE_UPGRADES )
|
||||
{
|
||||
loadModel( event, upgrade );
|
||||
}
|
||||
|
||||
loadSmartModel( event, "turtle_dynamic", m_turtleSmartItemModel );
|
||||
}
|
||||
|
||||
@@ -191,7 +210,7 @@ public class CCTurtleProxyClient extends CCTurtleProxyCommon
|
||||
private static class TurtleItemColour implements IItemColor
|
||||
{
|
||||
@Override
|
||||
public int getColorFromItemstack( @Nonnull ItemStack stack, int tintIndex )
|
||||
public int colorMultiplier( @Nonnull ItemStack stack, int tintIndex )
|
||||
{
|
||||
if( tintIndex == 0 )
|
||||
{
|
||||
|
||||
@@ -8,17 +8,25 @@ package dan200.computercraft.client.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.ItemPocketRenderer;
|
||||
import dan200.computercraft.client.render.ItemPrintoutRenderer;
|
||||
import dan200.computercraft.client.render.RenderOverlayCable;
|
||||
import dan200.computercraft.client.render.TileEntityCableRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.shared.command.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerState;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
|
||||
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.ClientMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
@@ -27,9 +35,10 @@ import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import gnu.trove.map.hash.TIntIntHashMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.gui.GuiNewChat;
|
||||
import net.minecraft.client.renderer.ItemMeshDefinition;
|
||||
import net.minecraft.client.renderer.block.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
@@ -44,6 +53,7 @@ import net.minecraft.util.IThreadListener;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
@@ -51,7 +61,7 @@ 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.event.world.WorldEvent;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
@@ -65,6 +75,8 @@ import java.util.List;
|
||||
|
||||
public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
{
|
||||
private static TIntIntHashMap lastCounts = new TIntIntHashMap();
|
||||
|
||||
private long m_tick;
|
||||
private long m_renderFrame;
|
||||
private FixedWidthFontRenderer m_fixedWidthFontRenderer;
|
||||
@@ -110,6 +122,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
registerItemModel( ComputerCraft.Blocks.commandComputer, "command_computer" );
|
||||
registerItemModel( ComputerCraft.Blocks.advancedModem, "advanced_modem" );
|
||||
registerItemModel( ComputerCraft.Blocks.peripheral, 5, "speaker" );
|
||||
registerItemModel( ComputerCraft.Blocks.wiredModemFull, "wired_modem_full" );
|
||||
|
||||
registerItemModel( ComputerCraft.Items.disk, "disk" );
|
||||
registerItemModel( ComputerCraft.Items.diskExpanded, "disk_expanded" );
|
||||
@@ -222,6 +235,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 )
|
||||
@@ -290,12 +304,6 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
return m_renderFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDisplayLists( int list, int range )
|
||||
{
|
||||
GlStateManager.glDeleteLists( list, range );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFixedWidthFontRenderer()
|
||||
{
|
||||
@@ -314,17 +322,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 )
|
||||
{
|
||||
@@ -371,6 +368,13 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getComputerGUI( IComputer computer, int width, int height, ComputerFamily family )
|
||||
{
|
||||
ContainerViewComputer container = new ContainerViewComputer( computer );
|
||||
return new GuiComputer( container, family, computer, width, height );
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getWorldDir( World world )
|
||||
{
|
||||
@@ -383,7 +387,10 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
switch( packet.m_packetType )
|
||||
{
|
||||
case ComputerCraftPacket.ComputerChanged:
|
||||
case ComputerCraftPacket.ComputerTerminalChanged:
|
||||
case ComputerCraftPacket.ComputerDeleted:
|
||||
case ComputerCraftPacket.PlayRecord:
|
||||
case ComputerCraftPacket.PostChat:
|
||||
{
|
||||
// Packet from Server to Client
|
||||
IThreadListener listener = Minecraft.getMinecraft();
|
||||
@@ -417,6 +424,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 +443,52 @@ 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 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComputerCraftPacket.PostChat:
|
||||
{
|
||||
/*
|
||||
This allows us to send delete chat messages of the same "category" as the previous one.
|
||||
It's used by the various /computercraft commands to avoid filling the chat with repetitive
|
||||
messages.
|
||||
*/
|
||||
|
||||
int id = packet.m_dataInt[0];
|
||||
ITextComponent[] components = new ITextComponent[packet.m_dataString.length];
|
||||
for( int i = 0; i < packet.m_dataString.length; i++ )
|
||||
{
|
||||
components[i] = ITextComponent.Serializer.jsonToComponent( packet.m_dataString[i] );
|
||||
}
|
||||
|
||||
GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI();
|
||||
|
||||
// Keep track of how many lines we wrote last time, deleting any extra ones.
|
||||
int lastCount = lastCounts.get( id );
|
||||
for( int i = components.length; i < lastCount; i++ ) chat.deleteChatLine( i + id );
|
||||
lastCounts.put( id, components.length );
|
||||
|
||||
// Add new lines
|
||||
for( int i = 0; i < components.length; i++ )
|
||||
{
|
||||
chat.printChatMessageWithOptionalDeletion( components[i], id + i );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +496,9 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
{
|
||||
ForgeHandlers handlers = new ForgeHandlers();
|
||||
MinecraftForge.EVENT_BUS.register( handlers );
|
||||
MinecraftForge.EVENT_BUS.register( new RenderOverlayCable() );
|
||||
MinecraftForge.EVENT_BUS.register( new ItemPocketRenderer() );
|
||||
MinecraftForge.EVENT_BUS.register( new ItemPrintoutRenderer() );
|
||||
}
|
||||
|
||||
public class ForgeHandlers
|
||||
@@ -528,6 +585,15 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
m_renderFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldUnload( WorldEvent.Unload event )
|
||||
{
|
||||
if( event.getWorld().isRemote )
|
||||
{
|
||||
ClientMonitor.destroyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -541,7 +607,7 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColorFromItemstack( @Nonnull ItemStack stack, int layer )
|
||||
public int colorMultiplier( @Nonnull ItemStack stack, int layer )
|
||||
{
|
||||
return layer == 0 ? 0xFFFFFF : disk.getColour( stack );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.client.renderer.RenderItem;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
|
||||
/**
|
||||
* Emulates map rendering for pocket computers
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ItemPocketRenderer
|
||||
{
|
||||
@SubscribeEvent
|
||||
public void renderItem( RenderSpecificHandEvent event )
|
||||
{
|
||||
ItemStack stack = event.getItemStack();
|
||||
if( !(stack.getItem() instanceof ItemPocketComputer) ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
if( event.getHand() == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
|
||||
{
|
||||
renderItemFirstCentre(
|
||||
event.getInterpolatedPitch(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderItemFirstPersonSide(
|
||||
event.getHand() == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* The main rendering method for pocket computers and their associated terminal
|
||||
*
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPerson(ItemStack)
|
||||
*/
|
||||
private void renderPocketComputerItem( ItemStack stack )
|
||||
{
|
||||
// Setup various transformations. Note that these are partially adapated from the corresponding method
|
||||
// in ItemRenderer
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
GlStateManager.rotate( 180f, 0f, 1f, 0f );
|
||||
GlStateManager.rotate( 180f, 0f, 0f, 1f );
|
||||
GlStateManager.scale( 0.5, 0.5, 0.5 );
|
||||
|
||||
ItemPocketComputer pocketComputer = ComputerCraft.Items.pocketComputer;
|
||||
ClientComputer computer = pocketComputer.createClientComputer( stack );
|
||||
|
||||
{
|
||||
// First render the background item. We use the item's model rather than a direct texture as this ensures
|
||||
// we display the pocket light and other such decorations.
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
|
||||
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
TextureManager textureManager = minecraft.getTextureManager();
|
||||
RenderItem renderItem = minecraft.getRenderItem();
|
||||
|
||||
// Copy of RenderItem#renderItemModelIntoGUI but without the translation or scaling
|
||||
textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
|
||||
textureManager.getTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ).setBlurMipmap( false, false );
|
||||
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
|
||||
GlStateManager.color( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
|
||||
IBakedModel bakedmodel = renderItem.getItemModelWithOverrides( stack, null, null );
|
||||
bakedmodel = ForgeHooksClient.handleCameraTransforms( bakedmodel, ItemCameraTransforms.TransformType.GUI, false );
|
||||
renderItem.renderItem( stack, bakedmodel );
|
||||
|
||||
GlStateManager.disableAlpha();
|
||||
GlStateManager.disableRescaleNormal();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// If we've a computer and terminal then attempt to render it.
|
||||
if( computer != null )
|
||||
{
|
||||
Terminal terminal = computer.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
synchronized( terminal )
|
||||
{
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.disableDepth();
|
||||
|
||||
// Reset the position to be at the top left corner of the pocket computer
|
||||
// Note we translate towards the screen slightly too.
|
||||
GlStateManager.translate( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 );
|
||||
// Translate to the top left of the screen.
|
||||
GlStateManager.translate( 4 / 16.0, 3 / 16.0, 0 );
|
||||
|
||||
// Work out the scaling required to resize the terminal in order to fit on the computer
|
||||
final int margin = 2;
|
||||
int tw = terminal.getWidth();
|
||||
int th = terminal.getHeight();
|
||||
int width = tw * FONT_WIDTH + margin * 2;
|
||||
int height = th * FONT_HEIGHT + margin * 2;
|
||||
int max = Math.max( height, width );
|
||||
|
||||
// The grid is 8 * 8 wide, so we start with a base of 1/2 (8 / 16).
|
||||
double scale = 1.0 / 2.0 / max;
|
||||
GlStateManager.scale( scale, scale, scale );
|
||||
|
||||
// The margin/start positions are determined in order for the terminal to be centred.
|
||||
int startX = (max - width) / 2 + margin;
|
||||
int startY = (max - height) / 2 + margin;
|
||||
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
boolean greyscale = !computer.isColour();
|
||||
Palette palette = terminal.getPalette();
|
||||
|
||||
// Render the actual text
|
||||
for( int line = 0; line < th; ++line )
|
||||
{
|
||||
TextBuffer text = terminal.getLine( line );
|
||||
TextBuffer colour = terminal.getTextColourLine( line );
|
||||
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
|
||||
fontRenderer.drawString(
|
||||
text, startX, startY + line * FONT_HEIGHT,
|
||||
colour, backgroundColour, margin, margin, greyscale, palette
|
||||
);
|
||||
}
|
||||
|
||||
// And render the cursor;
|
||||
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
|
||||
if( terminal.getCursorBlink() && ComputerCraft.getGlobalCursorBlink() &&
|
||||
tx >= 0 && ty >= 0 && tx < tw && ty < th )
|
||||
{
|
||||
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
|
||||
fontRenderer.drawString(
|
||||
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
|
||||
cursorColour, null, 0, 0, greyscale, palette
|
||||
);
|
||||
}
|
||||
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a pocket computer to one side of the player.
|
||||
*
|
||||
* @param side The side to render on
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack)
|
||||
*/
|
||||
private void renderItemFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
float offset = side == EnumHandSide.RIGHT ? 1f : -1f;
|
||||
GlStateManager.translate( offset * 0.125f, -0.125f, 0f );
|
||||
|
||||
// If the player is not invisible then render a single arm
|
||||
if( !minecraft.player.isInvisible() )
|
||||
{
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.rotate( offset * 10f, 0f, 0f, 1f );
|
||||
minecraft.getItemRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
|
||||
float f1 = MathHelper.sqrt( swingProgress );
|
||||
float f2 = MathHelper.sin( f1 * (float) Math.PI );
|
||||
float f3 = -0.5f * f2;
|
||||
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
|
||||
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
GlStateManager.translate( offset * f3, f4 - 0.3f * f2, f5 );
|
||||
GlStateManager.rotate( f2 * -45f, 1f, 0f, 0f );
|
||||
GlStateManager.rotate( offset * f2 * -30f, 0f, 1f, 0f );
|
||||
|
||||
renderPocketComputerItem( stack );
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an item in the middle of the screen
|
||||
*
|
||||
* @param pitch The pitch of the player
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPerson(float, float, float)
|
||||
*/
|
||||
private void renderItemFirstCentre( float pitch, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
ItemRenderer itemRenderer = Minecraft.getMinecraft().getItemRenderer();
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
float swingRt = MathHelper.sqrt( swingProgress );
|
||||
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.translate( 0f, -tX / 2f, tZ );
|
||||
float pitchAngle = itemRenderer.getMapAngleFromPitch( pitch );
|
||||
GlStateManager.translate( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
|
||||
GlStateManager.rotate( pitchAngle * -85f, 1f, 0f, 0f );
|
||||
itemRenderer.renderArms();
|
||||
float rX = MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.rotate( rX * 20f, 1f, 0f, 0f );
|
||||
GlStateManager.scale( 2f, 2f, 2f );
|
||||
|
||||
renderPocketComputerItem( stack );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.event.RenderItemInFrameEvent;
|
||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
|
||||
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
|
||||
|
||||
public class ItemPrintoutRenderer
|
||||
{
|
||||
@SubscribeEvent
|
||||
public void onRenderInHand( RenderSpecificHandEvent event )
|
||||
{
|
||||
ItemStack stack = event.getItemStack();
|
||||
if( stack.getItem() != ComputerCraft.Items.printout ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
if( event.getHand() == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
|
||||
{
|
||||
renderPrintoutFirstPersonCentre(
|
||||
event.getInterpolatedPitch(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderPrintoutFirstPersonSide(
|
||||
event.getHand() == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
|
||||
event.getEquipProgress(),
|
||||
event.getSwingProgress(),
|
||||
stack
|
||||
);
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a pocket computer to one side of the player.
|
||||
*
|
||||
* @param side The side to render on
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack)
|
||||
*/
|
||||
private void renderPrintoutFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
float offset = side == EnumHandSide.RIGHT ? 1f : -1f;
|
||||
GlStateManager.translate( offset * 0.125f, -0.125f, 0f );
|
||||
|
||||
// If the player is not invisible then render a single arm
|
||||
if( !minecraft.player.isInvisible() )
|
||||
{
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.rotate( offset * 10f, 0f, 0f, 1f );
|
||||
minecraft.getItemRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
|
||||
float f1 = MathHelper.sqrt( swingProgress );
|
||||
float f2 = MathHelper.sin( f1 * (float) Math.PI );
|
||||
float f3 = -0.5f * f2;
|
||||
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
|
||||
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
GlStateManager.translate( offset * f3, f4 - 0.3f * f2, f5 );
|
||||
GlStateManager.rotate( f2 * -45f, 1f, 0f, 0f );
|
||||
GlStateManager.rotate( offset * f2 * -30f, 0f, 1f, 0f );
|
||||
|
||||
renderPrintoutFirstPerson( stack );
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an item in the middle of the screen
|
||||
*
|
||||
* @param pitch The pitch of the player
|
||||
* @param equipProgress The equip progress of this item
|
||||
* @param swingProgress The swing progress of this item
|
||||
* @param stack The stack to render
|
||||
* @see ItemRenderer#renderMapFirstPerson(float, float, float)
|
||||
*/
|
||||
private void renderPrintoutFirstPersonCentre( float pitch, float equipProgress, float swingProgress, ItemStack stack )
|
||||
{
|
||||
ItemRenderer itemRenderer = Minecraft.getMinecraft().getItemRenderer();
|
||||
|
||||
// Setup the appropriate transformations. This is just copied from the
|
||||
// corresponding method in ItemRenderer.
|
||||
float swingRt = MathHelper.sqrt( swingProgress );
|
||||
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
|
||||
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.translate( 0f, -tX / 2f, tZ );
|
||||
float pitchAngle = itemRenderer.getMapAngleFromPitch( pitch );
|
||||
GlStateManager.translate( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
|
||||
GlStateManager.rotate( pitchAngle * -85f, 1f, 0f, 0f );
|
||||
itemRenderer.renderArms();
|
||||
float rX = MathHelper.sin( swingRt * (float) Math.PI );
|
||||
GlStateManager.rotate( rX * 20f, 1f, 0f, 0f );
|
||||
GlStateManager.scale( 2f, 2f, 2f );
|
||||
|
||||
renderPrintoutFirstPerson( stack );
|
||||
}
|
||||
|
||||
|
||||
private static void renderPrintoutFirstPerson( ItemStack stack )
|
||||
{
|
||||
// Setup various transformations. Note that these are partially adapated from the corresponding method
|
||||
// in ItemRenderer.renderMapFirstPerson
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
GlStateManager.rotate( 180f, 0f, 1f, 0f );
|
||||
GlStateManager.rotate( 180f, 0f, 0f, 1f );
|
||||
GlStateManager.scale( 0.42f, 0.42f, -0.42f );
|
||||
GlStateManager.translate( -0.5f, -0.48f, 0.0f );
|
||||
|
||||
drawPrintout( stack );
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderInFrame( RenderItemInFrameEvent event )
|
||||
{
|
||||
ItemStack stack = event.getItem();
|
||||
if( stack.getItem() != ComputerCraft.Items.printout ) return;
|
||||
|
||||
event.setCanceled( true );
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
// Move a little bit forward to ensure we're not clipping with the frame
|
||||
GlStateManager.translate( 0.0f, 0.0f, -0.001f );
|
||||
GlStateManager.rotate( 180f, 0f, 0f, 1f );
|
||||
GlStateManager.scale( 0.95f, 0.95f, -0.95f );
|
||||
GlStateManager.translate( -0.5f, -0.5f, 0.0f );
|
||||
|
||||
drawPrintout( stack );
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
private static void drawPrintout( ItemStack stack )
|
||||
{
|
||||
int pages = ItemPrintout.getPageCount( stack );
|
||||
boolean book = ItemPrintout.getType( stack ) == ItemPrintout.Type.Book;
|
||||
|
||||
double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
||||
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
||||
|
||||
// Non-books will be left aligned
|
||||
if( !book ) width += offsetAt( pages );
|
||||
|
||||
double visualWidth = width, visualHeight = height;
|
||||
|
||||
// Meanwhile books will be centred
|
||||
if( book )
|
||||
{
|
||||
visualWidth += 2 * COVER_SIZE + 2 * offsetAt( pages );
|
||||
visualHeight += 2 * COVER_SIZE;
|
||||
}
|
||||
|
||||
double max = Math.max( visualHeight, visualWidth );
|
||||
|
||||
// Scale the printout to fit correctly.
|
||||
double scale = 1.0 / max;
|
||||
GlStateManager.scale( scale, scale, scale );
|
||||
GlStateManager.translate( (max - width) / 2.0f, (max - height) / 2.0f, 0.0f );
|
||||
|
||||
drawBorder( 0, 0, -0.01, 0, pages, book );
|
||||
drawText( X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
|
||||
import net.minecraftforge.client.model.pipeline.LightUtil;
|
||||
import net.minecraftforge.client.model.pipeline.VertexTransformer;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Point3f;
|
||||
import javax.vecmath.Vector3f;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Transforms vertices of a model, remaining aware of winding order, and rearranging
|
||||
* vertices if needed.
|
||||
*/
|
||||
public final class ModelTransformer
|
||||
{
|
||||
private static final Matrix4f identity;
|
||||
|
||||
static
|
||||
{
|
||||
identity = new Matrix4f();
|
||||
identity.setIdentity();
|
||||
}
|
||||
|
||||
private ModelTransformer()
|
||||
{
|
||||
}
|
||||
|
||||
public static void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform )
|
||||
{
|
||||
if( transform == null || transform.equals( identity ) )
|
||||
{
|
||||
output.addAll( input );
|
||||
}
|
||||
else
|
||||
{
|
||||
Matrix4f normalMatrix = new Matrix4f( transform );
|
||||
normalMatrix.invert();
|
||||
normalMatrix.transpose();
|
||||
|
||||
for( BakedQuad quad : input ) output.add( doTransformQuad( quad, transform, normalMatrix ) );
|
||||
}
|
||||
}
|
||||
|
||||
public static BakedQuad transformQuad( BakedQuad input, Matrix4f transform )
|
||||
{
|
||||
if( transform == null || transform.equals( identity ) ) return input;
|
||||
|
||||
Matrix4f normalMatrix = new Matrix4f( transform );
|
||||
normalMatrix.invert();
|
||||
normalMatrix.transpose();
|
||||
return doTransformQuad( input, transform, normalMatrix );
|
||||
}
|
||||
|
||||
private static BakedQuad doTransformQuad( BakedQuad input, Matrix4f positionMatrix, Matrix4f normalMatrix )
|
||||
{
|
||||
|
||||
BakedQuadBuilder builder = new BakedQuadBuilder( input.getFormat() );
|
||||
NormalAwareTransformer transformer = new NormalAwareTransformer( builder, positionMatrix, normalMatrix );
|
||||
input.pipe( transformer );
|
||||
|
||||
if( transformer.areNormalsInverted() )
|
||||
{
|
||||
builder.swap( 1, 3 );
|
||||
transformer.areNormalsInverted();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* A vertex transformer that tracks whether the normals have been inverted and so the vertices
|
||||
* should be reordered so backface culling works as expected.
|
||||
*/
|
||||
private static class NormalAwareTransformer extends VertexTransformer
|
||||
{
|
||||
private final Matrix4f positionMatrix;
|
||||
private final Matrix4f normalMatrix;
|
||||
|
||||
private int vertexIndex = 0, elementIndex = 0;
|
||||
private final Point3f[] before = new Point3f[ 4 ];
|
||||
private final Point3f[] after = new Point3f[ 4 ];
|
||||
|
||||
public NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
|
||||
{
|
||||
super( parent );
|
||||
this.positionMatrix = positionMatrix;
|
||||
this.normalMatrix = normalMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadOrientation( EnumFacing orientation )
|
||||
{
|
||||
super.setQuadOrientation( orientation == null ? orientation : TRSRTransformation.rotate( positionMatrix, orientation ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put( int element, @Nonnull float... data )
|
||||
{
|
||||
switch( getVertexFormat().getElement( element ).getUsage() )
|
||||
{
|
||||
case POSITION:
|
||||
{
|
||||
Point3f vec = new Point3f( data );
|
||||
Point3f newVec = new Point3f();
|
||||
positionMatrix.transform( vec, newVec );
|
||||
|
||||
float[] newData = new float[ 4 ];
|
||||
newVec.get( newData );
|
||||
super.put( element, newData );
|
||||
|
||||
|
||||
before[ vertexIndex ] = vec;
|
||||
after[ vertexIndex ] = newVec;
|
||||
break;
|
||||
}
|
||||
case NORMAL:
|
||||
{
|
||||
Vector3f vec = new Vector3f( data );
|
||||
normalMatrix.transform( vec );
|
||||
|
||||
float[] newData = new float[ 4 ];
|
||||
vec.get( newData );
|
||||
super.put( element, newData );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
super.put( element, data );
|
||||
break;
|
||||
}
|
||||
|
||||
elementIndex++;
|
||||
if( elementIndex == getVertexFormat().getElementCount() )
|
||||
{
|
||||
vertexIndex++;
|
||||
elementIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areNormalsInverted()
|
||||
{
|
||||
Vector3f temp1 = new Vector3f(), temp2 = new Vector3f();
|
||||
Vector3f crossBefore = new Vector3f(), crossAfter = new Vector3f();
|
||||
|
||||
// Determine what cross product we expect to have
|
||||
temp1.sub( before[ 1 ], before[ 0 ] );
|
||||
temp2.sub( before[ 1 ], before[ 2 ] );
|
||||
crossBefore.cross( temp1, temp2 );
|
||||
normalMatrix.transform( crossBefore );
|
||||
|
||||
// And determine what cross product we actually have
|
||||
temp1.sub( after[ 1 ], after[ 0 ] );
|
||||
temp2.sub( after[ 1 ], after[ 2 ] );
|
||||
crossAfter.cross( temp1, temp2 );
|
||||
|
||||
// If the angle between expected and actual cross product is greater than
|
||||
// pi/2 radians then we will need to reorder our quads.
|
||||
return Math.abs( crossBefore.angle( crossAfter ) ) >= Math.PI / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A vertex consumer which is capable of building {@link BakedQuad}s.
|
||||
*
|
||||
* Equivalent to {@link net.minecraftforge.client.model.pipeline.UnpackedBakedQuad.Builder} but more memory
|
||||
* efficient.
|
||||
*
|
||||
* This also provides the ability to swap vertices through {@link #swap(int, int)} to allow reordering.
|
||||
*/
|
||||
private static class BakedQuadBuilder implements IVertexConsumer
|
||||
{
|
||||
private final VertexFormat format;
|
||||
|
||||
private final int[] vertexData;
|
||||
private int vertexIndex = 0, elementIndex = 0;
|
||||
|
||||
private EnumFacing orientation;
|
||||
private int quadTint;
|
||||
private boolean diffuse;
|
||||
private TextureAtlasSprite texture;
|
||||
|
||||
private BakedQuadBuilder( VertexFormat format )
|
||||
{
|
||||
this.format = format;
|
||||
this.vertexData = new int[ format.getSize() ];
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public VertexFormat getVertexFormat()
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadTint( int tint )
|
||||
{
|
||||
this.quadTint = tint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadOrientation( @Nonnull EnumFacing orientation )
|
||||
{
|
||||
this.orientation = orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplyDiffuseLighting( boolean diffuse )
|
||||
{
|
||||
this.diffuse = diffuse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexture( @Nonnull TextureAtlasSprite texture )
|
||||
{
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put( int element, @Nonnull float... data )
|
||||
{
|
||||
LightUtil.pack( data, vertexData, format, vertexIndex, element );
|
||||
|
||||
elementIndex++;
|
||||
if( elementIndex == getVertexFormat().getElementCount() )
|
||||
{
|
||||
vertexIndex++;
|
||||
elementIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void swap( int a, int b )
|
||||
{
|
||||
int length = vertexData.length / 4;
|
||||
for( int i = 0; i < length; i++ )
|
||||
{
|
||||
int temp = vertexData[ a * length + i ];
|
||||
vertexData[ a * length + i ] = vertexData[ b * length + i ];
|
||||
vertexData[ b * length + i ] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public BakedQuad build()
|
||||
{
|
||||
if( elementIndex != 0 || vertexIndex != 4 )
|
||||
{
|
||||
throw new IllegalStateException( "Got an unexpected number of elements/vertices" );
|
||||
}
|
||||
if( texture == null )
|
||||
{
|
||||
throw new IllegalStateException( "Texture has not been set" );
|
||||
}
|
||||
|
||||
return new BakedQuad( vertexData, quadTint, orientation, texture, diffuse, format );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
|
||||
|
||||
public class PrintoutRenderer
|
||||
{
|
||||
private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
|
||||
private static final double BG_SIZE = 256.0;
|
||||
|
||||
/**
|
||||
* Width of a page
|
||||
*/
|
||||
public static final int X_SIZE = 172;
|
||||
|
||||
/**
|
||||
* Height of a page
|
||||
*/
|
||||
public static final int Y_SIZE = 209;
|
||||
|
||||
/**
|
||||
* Padding between the left and right of a page and the text
|
||||
*/
|
||||
public static final int X_TEXT_MARGIN = 13;
|
||||
|
||||
/**
|
||||
* Padding between the top and bottom of a page and the text
|
||||
*/
|
||||
public static final int Y_TEXT_MARGIN = 11;
|
||||
|
||||
/**
|
||||
* Width of the extra page texture
|
||||
*/
|
||||
private static final int X_FOLD_SIZE = 12;
|
||||
|
||||
/**
|
||||
* Size of the leather cover
|
||||
*/
|
||||
public static final int COVER_SIZE = 12;
|
||||
|
||||
private static final int COVER_Y = Y_SIZE;
|
||||
private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE;
|
||||
|
||||
public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
|
||||
{
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
|
||||
for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line )
|
||||
{
|
||||
fontRenderer.drawString( text[ start + line ], x, y + line * FONT_HEIGHT, colours[ start + line ], null, 0, 0, false, Palette.DEFAULT );
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawText( int x, int y, int start, String[] text, String[] colours )
|
||||
{
|
||||
GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.enableTexture2D();
|
||||
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
|
||||
for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line )
|
||||
{
|
||||
fontRenderer.drawString( new TextBuffer( text[ start + line ] ), x, y + line * FONT_HEIGHT, new TextBuffer( colours[ start + line ] ), null, 0, 0, false, Palette.DEFAULT );
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook )
|
||||
{
|
||||
GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.enableTexture2D();
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture( BG );
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
|
||||
|
||||
int leftPages = page;
|
||||
int rightPages = pages - page - 1;
|
||||
|
||||
if( isBook )
|
||||
{
|
||||
// Border
|
||||
double offset = offsetAt( pages );
|
||||
final double left = x - 4 - offset;
|
||||
final double right = x + X_SIZE + offset - 4;
|
||||
|
||||
// Left and right border
|
||||
drawTexture( buffer, left - 4, y - 8, z - 0.02, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
|
||||
drawTexture( buffer, right, y - 8, z - 0.02, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
|
||||
|
||||
// Draw centre panel (just stretched texture, sorry).
|
||||
drawTexture( buffer,
|
||||
x - offset, y, z - 0.02, X_SIZE + offset * 2, Y_SIZE,
|
||||
COVER_X + COVER_SIZE / 2, COVER_SIZE, COVER_SIZE, Y_SIZE
|
||||
);
|
||||
|
||||
double borderX = left;
|
||||
while( borderX < right )
|
||||
{
|
||||
double thisWidth = Math.min( right - borderX, X_SIZE );
|
||||
drawTexture( buffer, borderX, y - 8, z - 0.02, 0, COVER_Y, thisWidth, COVER_SIZE );
|
||||
drawTexture( buffer, borderX, y + Y_SIZE - 4, z - 0.02, 0, COVER_Y + COVER_SIZE, thisWidth, COVER_SIZE );
|
||||
borderX += thisWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Left half
|
||||
drawTexture( buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2, Y_SIZE );
|
||||
for( int n = 0; n <= leftPages; n++ )
|
||||
{
|
||||
drawTexture( buffer,
|
||||
x - offsetAt( n ), y, z - 1e-3 * n,
|
||||
// Use the left "bold" fold for the outermost page
|
||||
n == leftPages ? 0 : X_FOLD_SIZE, 0,
|
||||
X_FOLD_SIZE, Y_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
// Right half
|
||||
drawTexture( buffer, x + X_SIZE / 2, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2, 0, X_SIZE / 2, Y_SIZE );
|
||||
for( int n = 0; n <= rightPages; n++ )
|
||||
{
|
||||
drawTexture( buffer,
|
||||
x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3 * n,
|
||||
// Two folds, then the main page. Use the right "bold" fold for the outermost page.
|
||||
X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0,
|
||||
X_FOLD_SIZE, Y_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double u, double v, double width, double height )
|
||||
{
|
||||
buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
|
||||
buffer.pos( x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
|
||||
buffer.pos( x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex();
|
||||
buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
|
||||
}
|
||||
|
||||
private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double width, double height, double u, double v, double tWidth, double tHeight )
|
||||
{
|
||||
buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
|
||||
buffer.pos( x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
|
||||
buffer.pos( x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex();
|
||||
buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
|
||||
}
|
||||
|
||||
public static double offsetAt( int page )
|
||||
{
|
||||
return 32 * (1 - Math.pow( 1.2, -page ));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
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();
|
||||
|
||||
{
|
||||
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( BlockCable.doesConnectVisually( state, world, pos, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.getRenderLayer() );
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.common.ClientTerminal;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
@@ -18,6 +18,7 @@ import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
@@ -43,25 +44,24 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
private void renderMonitorAt( TileMonitor monitor, double posX, double posY, double posZ, float f, int i )
|
||||
{
|
||||
// Render from the origin monitor
|
||||
TileMonitor origin = monitor.getOrigin();
|
||||
if( origin == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
ClientMonitor originTerminal = monitor.getClientMonitor();
|
||||
|
||||
// Ensure each monitor is rendered only once
|
||||
long renderFrame = ComputerCraft.getRenderFrame();
|
||||
if( origin.m_lastRenderFrame == renderFrame )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
origin.m_lastRenderFrame = renderFrame;
|
||||
}
|
||||
|
||||
boolean redraw = origin.pollChanged();
|
||||
if( originTerminal == null ) return;
|
||||
TileMonitor origin = originTerminal.getOrigin();
|
||||
BlockPos monitorPos = monitor.getPos();
|
||||
|
||||
// Ensure each monitor terminal is rendered only once. We allow rendering a specific tile
|
||||
// multiple times in a single frame to ensure compatibility with shaders which may run a
|
||||
// pass multiple times.
|
||||
long renderFrame = ComputerCraft.getRenderFrame();
|
||||
if( originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals( originTerminal.lastRenderPos ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
originTerminal.lastRenderFrame = renderFrame;
|
||||
originTerminal.lastRenderPos = monitorPos;
|
||||
|
||||
BlockPos originPos = origin.getPos();
|
||||
posX += originPos.getX() - monitorPos.getX();
|
||||
posY += originPos.getY() - monitorPos.getY();
|
||||
@@ -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();
|
||||
@@ -94,40 +94,40 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
BufferBuilder renderer = tessellator.getBuffer();
|
||||
|
||||
// Get terminal
|
||||
ClientTerminal clientTerminal = (ClientTerminal)origin.getTerminal();
|
||||
Terminal terminal = (clientTerminal != null) ? clientTerminal.getTerminal() : null;
|
||||
redraw = redraw || (clientTerminal != null && clientTerminal.hasTerminalChanged());
|
||||
boolean redraw = originTerminal.pollTerminalChanged();
|
||||
|
||||
// Draw the contents
|
||||
GlStateManager.depthMask( false );
|
||||
OpenGlHelper.setLightmapTextureCoords( OpenGlHelper.lightmapTexUnit, 0xFF, 0xFF );
|
||||
GlStateManager.disableLighting();
|
||||
mc.entityRenderer.disableLightmap();
|
||||
try
|
||||
{
|
||||
Terminal terminal = originTerminal.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
Palette palette = terminal.getPalette();
|
||||
|
||||
// Allocate display lists
|
||||
if( origin.m_renderDisplayList < 0 )
|
||||
if( originTerminal.renderDisplayLists == null )
|
||||
{
|
||||
origin.m_renderDisplayList = GlStateManager.glGenLists( 3 );
|
||||
originTerminal.createLists();
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
// Draw a terminal
|
||||
boolean greyscale = !clientTerminal.isColour();
|
||||
boolean greyscale = !originTerminal.isColour();
|
||||
int width = terminal.getWidth();
|
||||
int height = terminal.getHeight();
|
||||
int cursorX = terminal.getCursorX();
|
||||
int cursorY = terminal.getCursorY();
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer)ComputerCraft.getFixedWidthFontRenderer();
|
||||
FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer();
|
||||
|
||||
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
|
||||
@@ -135,12 +135,12 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
if( redraw )
|
||||
{
|
||||
// Build background display list
|
||||
GlStateManager.glNewList( origin.m_renderDisplayList, GL11.GL_COMPILE );
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayLists[0], GL11.GL_COMPILE );
|
||||
try
|
||||
{
|
||||
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();
|
||||
@@ -149,7 +149,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.scale( 1.0, marginSquash, 1.0 );
|
||||
GlStateManager.translate( 0.0, -marginYSize / marginSquash, 0.0 );
|
||||
fontRenderer.drawStringBackgroundPart( 0, 0, terminal.getBackgroundColourLine( 0 ), marginXSize, marginXSize, greyscale, palette );
|
||||
GlStateManager.translate( 0.0, ( marginYSize + height * FixedWidthFontRenderer.FONT_HEIGHT ) / marginSquash, 0.0 );
|
||||
GlStateManager.translate( 0.0, (marginYSize + height * FixedWidthFontRenderer.FONT_HEIGHT) / marginSquash, 0.0 );
|
||||
fontRenderer.drawStringBackgroundPart( 0, 0, terminal.getBackgroundColourLine( height - 1 ), marginXSize, marginXSize, greyscale, palette );
|
||||
}
|
||||
finally
|
||||
@@ -174,7 +174,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.glEndList();
|
||||
}
|
||||
}
|
||||
GlStateManager.callList( origin.m_renderDisplayList );
|
||||
GlStateManager.callList( originTerminal.renderDisplayLists[0] );
|
||||
GlStateManager.resetColor();
|
||||
|
||||
// Draw text
|
||||
@@ -182,7 +182,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
if( redraw )
|
||||
{
|
||||
// Build text display list
|
||||
GlStateManager.glNewList( origin.m_renderDisplayList + 1, GL11.GL_COMPILE );
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayLists[1], GL11.GL_COMPILE );
|
||||
try
|
||||
{
|
||||
// Lines
|
||||
@@ -202,7 +202,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
GlStateManager.glEndList();
|
||||
}
|
||||
}
|
||||
GlStateManager.callList( origin.m_renderDisplayList + 1 );
|
||||
GlStateManager.callList( originTerminal.renderDisplayLists[1] );
|
||||
GlStateManager.resetColor();
|
||||
|
||||
// Draw cursor
|
||||
@@ -210,7 +210,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
if( redraw )
|
||||
{
|
||||
// Build cursor display list
|
||||
GlStateManager.glNewList( origin.m_renderDisplayList + 2, GL11.GL_COMPILE );
|
||||
GlStateManager.glNewList( originTerminal.renderDisplayLists[2], GL11.GL_COMPILE );
|
||||
try
|
||||
{
|
||||
// Cursor
|
||||
@@ -236,7 +236,7 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer<TileMon
|
||||
}
|
||||
if( ComputerCraft.getGlobalCursorBlink() )
|
||||
{
|
||||
GlStateManager.callList( origin.m_renderDisplayList + 2 );
|
||||
GlStateManager.callList( originTerminal.renderDisplayLists[2] );
|
||||
GlStateManager.resetColor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package dan200.computercraft.client.render;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.entity.TurtleVisionCamera;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
@@ -116,47 +115,34 @@ 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;
|
||||
String label = (computer != null) ? computer.getLabel() : null;
|
||||
String label = turtle.createProxy().getLabel();
|
||||
if( label != null )
|
||||
{
|
||||
renderLabel( turtle.getAccess().getPosition(), label );
|
||||
}
|
||||
|
||||
// Render the turtle
|
||||
GlStateManager.translate( 0.5f, 0.0f, 0.5f );
|
||||
GlStateManager.translate( 0.5f, 0.5f, 0.5f );
|
||||
GlStateManager.rotate( 180.0f - yaw, 0.0f, 1.0f, 0.0f );
|
||||
GlStateManager.translate( -0.5f, 0.0f, -0.5f );
|
||||
|
||||
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
|
||||
{
|
||||
// Flip the model and swap the cull face as winding order will have changed.
|
||||
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
|
||||
GlStateManager.cullFace( GlStateManager.CullFace.FRONT );
|
||||
}
|
||||
GlStateManager.translate( -0.5f, -0.5f, -0.5f );
|
||||
// Render the turtle
|
||||
int colour;
|
||||
ComputerFamily family;
|
||||
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,15 +169,13 @@ 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
|
||||
{
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.cullFace( GlStateManager.CullFace.BACK );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,10 +294,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
|
||||
|
||||
@@ -6,13 +6,10 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Point3f;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -20,16 +17,17 @@ import java.util.Map;
|
||||
|
||||
public class TurtleMultiModel implements IBakedModel
|
||||
{
|
||||
private IBakedModel m_baseModel;
|
||||
private IBakedModel m_overlayModel;
|
||||
private IBakedModel m_leftUpgradeModel;
|
||||
private Matrix4f m_leftUpgradeTransform;
|
||||
private IBakedModel m_rightUpgradeModel;
|
||||
private Matrix4f m_rightUpgradeTransform;
|
||||
private final IBakedModel m_baseModel;
|
||||
private final IBakedModel m_overlayModel;
|
||||
private final Matrix4f m_generalTransform;
|
||||
private final IBakedModel m_leftUpgradeModel;
|
||||
private final Matrix4f m_leftUpgradeTransform;
|
||||
private final IBakedModel m_rightUpgradeModel;
|
||||
private final Matrix4f m_rightUpgradeTransform;
|
||||
private List<BakedQuad> m_generalQuads;
|
||||
private Map<EnumFacing, List<BakedQuad>> m_faceQuads;
|
||||
|
||||
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
|
||||
public TurtleMultiModel( IBakedModel baseModel, IBakedModel overlayModel, Matrix4f generalTransform, IBakedModel leftUpgradeModel, Matrix4f leftUpgradeTransform, IBakedModel rightUpgradeModel, Matrix4f rightUpgradeTransform )
|
||||
{
|
||||
// Get the models
|
||||
m_baseModel = baseModel;
|
||||
@@ -38,6 +36,7 @@ public class TurtleMultiModel implements IBakedModel
|
||||
m_leftUpgradeTransform = leftUpgradeTransform;
|
||||
m_rightUpgradeModel = rightUpgradeModel;
|
||||
m_rightUpgradeTransform = rightUpgradeTransform;
|
||||
m_generalTransform = generalTransform;
|
||||
m_generalQuads = null;
|
||||
m_faceQuads = new HashMap<>();
|
||||
}
|
||||
@@ -48,51 +47,52 @@ public class TurtleMultiModel implements IBakedModel
|
||||
{
|
||||
if( side != null )
|
||||
{
|
||||
if( !m_faceQuads.containsKey( side ) )
|
||||
{
|
||||
ArrayList<BakedQuad> quads = new ArrayList<>();
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
quads.addAll( m_overlayModel.getQuads( state, side, rand ) );
|
||||
}
|
||||
if( m_leftUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_leftUpgradeModel.getQuads( state, side, rand ), m_leftUpgradeTransform ) );
|
||||
}
|
||||
if( m_rightUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_rightUpgradeModel.getQuads( state, side, rand ), m_rightUpgradeTransform ) );
|
||||
}
|
||||
quads.trimToSize();
|
||||
m_faceQuads.put( side, quads );
|
||||
}
|
||||
if( !m_faceQuads.containsKey( side ) ) m_faceQuads.put( side, buildQuads( state, side, rand ) );
|
||||
return m_faceQuads.get( side );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_generalQuads == null )
|
||||
{
|
||||
ArrayList<BakedQuad> quads = new ArrayList<>();
|
||||
quads.addAll( m_baseModel.getQuads( state, side, rand ) );
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
quads.addAll( m_overlayModel.getQuads( state, side, rand ) );
|
||||
}
|
||||
if( m_leftUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_leftUpgradeModel.getQuads( state, side, rand ), m_leftUpgradeTransform ) );
|
||||
}
|
||||
if( m_rightUpgradeModel != null )
|
||||
{
|
||||
quads.addAll( transformQuads( m_rightUpgradeModel.getQuads( state, side, rand ), m_rightUpgradeTransform ) );
|
||||
}
|
||||
quads.trimToSize();
|
||||
m_generalQuads = quads;
|
||||
}
|
||||
if( m_generalQuads == null ) m_generalQuads = buildQuads( state, side, rand );
|
||||
return m_generalQuads;
|
||||
}
|
||||
}
|
||||
|
||||
private List<BakedQuad> buildQuads( IBlockState state, EnumFacing side, long rand )
|
||||
{
|
||||
ArrayList<BakedQuad> quads = new ArrayList<>();
|
||||
ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand ), m_generalTransform );
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
|
||||
}
|
||||
if( m_overlayModel != null )
|
||||
{
|
||||
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform );
|
||||
}
|
||||
if( m_leftUpgradeModel != null )
|
||||
{
|
||||
Matrix4f upgradeTransform = m_generalTransform;
|
||||
if( m_leftUpgradeTransform != null )
|
||||
{
|
||||
upgradeTransform = new Matrix4f( m_generalTransform );
|
||||
upgradeTransform.mul( m_leftUpgradeTransform );
|
||||
}
|
||||
ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getQuads( state, side, rand ), upgradeTransform );
|
||||
}
|
||||
if( m_rightUpgradeModel != null )
|
||||
{
|
||||
Matrix4f upgradeTransform = m_generalTransform;
|
||||
if( m_rightUpgradeTransform != null )
|
||||
{
|
||||
upgradeTransform = new Matrix4f( m_generalTransform );
|
||||
upgradeTransform.mul( m_rightUpgradeTransform );
|
||||
}
|
||||
ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getQuads( state, side, rand ), upgradeTransform );
|
||||
}
|
||||
quads.trimToSize();
|
||||
return quads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion()
|
||||
{
|
||||
@@ -132,63 +132,4 @@ public class TurtleMultiModel implements IBakedModel
|
||||
{
|
||||
return ItemOverrideList.NONE;
|
||||
}
|
||||
|
||||
private List<BakedQuad> transformQuads( List<BakedQuad> input, Matrix4f transform )
|
||||
{
|
||||
if( transform == null || input.size() == 0 )
|
||||
{
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<BakedQuad> output = new ArrayList<>( input.size() );
|
||||
for( BakedQuad quad : input )
|
||||
{
|
||||
output.add( transformQuad( quad, transform ) );
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
private BakedQuad transformQuad( BakedQuad quad, Matrix4f transform )
|
||||
{
|
||||
int[] vertexData = quad.getVertexData().clone();
|
||||
int offset = 0;
|
||||
BakedQuad copy = new BakedQuad( vertexData, -1, quad.getFace(), quad.getSprite(), quad.shouldApplyDiffuseLighting(), quad.getFormat() );
|
||||
VertexFormat format = copy.getFormat();
|
||||
for( int i=0; i<format.getElementCount(); ++i ) // For each vertex element
|
||||
{
|
||||
VertexFormatElement element = format.getElement( i );
|
||||
if( element.isPositionElement() &&
|
||||
element.getType() == VertexFormatElement.EnumType.FLOAT &&
|
||||
element.getElementCount() == 3 ) // When we find a position element
|
||||
{
|
||||
for( int j=0; j<4; ++j ) // For each corner of the quad
|
||||
{
|
||||
int start = offset + j * format.getNextOffset();
|
||||
if( (start % 4) == 0 )
|
||||
{
|
||||
start = start / 4;
|
||||
|
||||
// Extract the position
|
||||
Point3f pos = new Point3f(
|
||||
Float.intBitsToFloat( vertexData[ start ] ),
|
||||
Float.intBitsToFloat( vertexData[ start + 1 ] ),
|
||||
Float.intBitsToFloat( vertexData[ start + 2 ] )
|
||||
);
|
||||
|
||||
// Transform the position
|
||||
transform.transform( pos );
|
||||
|
||||
// Insert the position
|
||||
vertexData[ start ] = Float.floatToRawIntBits( pos.x );
|
||||
vertexData[ start + 1 ] = Float.floatToRawIntBits( pos.y );
|
||||
vertexData[ start + 2 ] = Float.floatToRawIntBits( pos.z );
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += element.getSize();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,19 @@ import java.util.List;
|
||||
|
||||
public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReloadListener
|
||||
{
|
||||
private static final Matrix4f s_identity, s_flip;
|
||||
|
||||
static
|
||||
{
|
||||
s_identity = new Matrix4f();
|
||||
s_identity.setIdentity();
|
||||
|
||||
s_flip = new Matrix4f();
|
||||
s_flip.setIdentity();
|
||||
s_flip.m11 = -1; // Flip on the y axis
|
||||
s_flip.m13 = 1; // Models go from (0,0,0) to (1,1,1), so push back up.
|
||||
}
|
||||
|
||||
private static class TurtleModelCombination
|
||||
{
|
||||
public final ComputerFamily m_family;
|
||||
@@ -43,8 +56,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
public final ITurtleUpgrade m_rightUpgrade;
|
||||
public final ResourceLocation m_overlay;
|
||||
public final boolean m_christmas;
|
||||
public final boolean m_flip;
|
||||
|
||||
public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas )
|
||||
public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas, boolean flip )
|
||||
{
|
||||
m_family = family;
|
||||
m_colour = colour;
|
||||
@@ -52,22 +66,26 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
m_rightUpgrade = rightUpgrade;
|
||||
m_overlay = overlay;
|
||||
m_christmas = christmas;
|
||||
m_flip = flip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object other )
|
||||
{
|
||||
if( other == this ) {
|
||||
if( other == this )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if( other instanceof TurtleModelCombination ) {
|
||||
TurtleModelCombination otherCombo = (TurtleModelCombination)other;
|
||||
if( other instanceof TurtleModelCombination )
|
||||
{
|
||||
TurtleModelCombination otherCombo = (TurtleModelCombination) other;
|
||||
if( otherCombo.m_family == m_family &&
|
||||
otherCombo.m_colour == m_colour &&
|
||||
otherCombo.m_leftUpgrade == m_leftUpgrade &&
|
||||
otherCombo.m_rightUpgrade == m_rightUpgrade &&
|
||||
Objects.equal( otherCombo.m_overlay, m_overlay ) &&
|
||||
otherCombo.m_christmas == m_christmas )
|
||||
otherCombo.m_christmas == m_christmas &&
|
||||
otherCombo.m_flip == m_flip )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -86,10 +104,11 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
result = prime * result + (m_rightUpgrade != null ? m_rightUpgrade.hashCode() : 0);
|
||||
result = prime * result + (m_overlay != null ? m_overlay.hashCode() : 0);
|
||||
result = prime * result + (m_christmas ? 1 : 0);
|
||||
result = prime * result + (m_flip ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private HashMap<TurtleModelCombination, IBakedModel> m_cachedModels;
|
||||
private ItemOverrideList m_overrides;
|
||||
private final TurtleModelCombination m_defaultCombination;
|
||||
@@ -97,12 +116,12 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
public TurtleSmartItemModel()
|
||||
{
|
||||
m_cachedModels = new HashMap<>();
|
||||
m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false );
|
||||
m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false, false );
|
||||
m_overrides = new ItemOverrideList( new ArrayList<>() )
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
|
||||
public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity )
|
||||
{
|
||||
ItemTurtleBase turtle = (ItemTurtleBase) stack.getItem();
|
||||
ComputerFamily family = turtle.getFamily( stack );
|
||||
@@ -111,7 +130,9 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.Right );
|
||||
ResourceLocation overlay = turtle.getOverlay( stack );
|
||||
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.Christmas;
|
||||
TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas );
|
||||
String label = turtle.getLabel( stack );
|
||||
boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" ));
|
||||
TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip );
|
||||
if( m_cachedModels.containsKey( combo ) )
|
||||
{
|
||||
return m_cachedModels.get( combo );
|
||||
@@ -146,28 +167,25 @@ public class TurtleSmartItemModel implements IBakedModel, IResourceManagerReload
|
||||
ModelResourceLocation baseModelLocation = TileEntityTurtleRenderer.getTurtleModel( combo.m_family, combo.m_colour );
|
||||
ModelResourceLocation overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.m_family, combo.m_overlay, combo.m_christmas );
|
||||
IBakedModel baseModel = modelManager.getModel( baseModelLocation );
|
||||
IBakedModel overlayModel = (overlayModelLocation != null) ? modelManager.getModel( baseModelLocation ) : null;
|
||||
IBakedModel overlayModel = (overlayModelLocation != null) ? modelManager.getModel( overlayModelLocation ) : null;
|
||||
Matrix4f transform = combo.m_flip ? s_flip : s_identity;
|
||||
Pair<IBakedModel, Matrix4f> leftModel = (combo.m_leftUpgrade != null) ? combo.m_leftUpgrade.getModel( null, TurtleSide.Left ) : null;
|
||||
Pair<IBakedModel, Matrix4f> rightModel = (combo.m_rightUpgrade != null) ? combo.m_rightUpgrade.getModel( null, TurtleSide.Right ) : null;
|
||||
if( leftModel != null && rightModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), rightModel.getLeft(), rightModel.getRight() );
|
||||
}
|
||||
else if( leftModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, leftModel.getLeft(), leftModel.getRight(), null, null );
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel.getLeft(), leftModel.getRight(), null, null );
|
||||
}
|
||||
else if( rightModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, null, null, rightModel.getLeft(), rightModel.getRight() );
|
||||
}
|
||||
else if( overlayModel != null )
|
||||
{
|
||||
return new TurtleMultiModel( baseModel, overlayModel, null, null, null, null );
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, rightModel.getLeft(), rightModel.getRight() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseModel;
|
||||
return new TurtleMultiModel( baseModel, overlayModel, transform, null, null, null, null );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
|
||||
@@ -40,27 +40,28 @@ public class BufferAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// len
|
||||
return new Object[] { m_buffer.length() };
|
||||
return MethodResult.of( m_buffer.length() );
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// tostring
|
||||
return new Object[] { m_buffer.toString() };
|
||||
return MethodResult.of( m_buffer.toString() );
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// read
|
||||
int start = optInt( arguments, 0, 0 );
|
||||
int end = optInt( arguments, 1, m_buffer.length() );
|
||||
return new Object[] { m_buffer.read( start, end ) };
|
||||
return MethodResult.of( m_buffer.read( start, end ) );
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
@@ -69,7 +70,7 @@ public class BufferAPI implements ILuaAPI
|
||||
int start = optInt( arguments, 1, 0 );
|
||||
int end = optInt( arguments, 2, start + text.length() );
|
||||
m_buffer.write( text, start, end );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
@@ -78,14 +79,22 @@ public class BufferAPI implements ILuaAPI
|
||||
int start = optInt( arguments, 1, 0 );
|
||||
int end = optInt( arguments, 2, m_buffer.length() );
|
||||
m_buffer.fill( text, start, end );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
}
|
||||
|
||||
public BufferAPI( IAPIEnvironment _env )
|
||||
@@ -100,21 +109,6 @@ public class BufferAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
@@ -124,8 +118,9 @@ public class BufferAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -138,12 +133,20 @@ public class BufferAPI implements ILuaAPI
|
||||
throw ArgumentHelper.badArgument( 1, "positive number", Integer.toString( repetitions ) );
|
||||
}
|
||||
TextBuffer buffer = new TextBuffer( text, repetitions );
|
||||
return new Object[] { new BufferLuaObject( buffer ) };
|
||||
return MethodResult.of( new BufferLuaObject( buffer ) );
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
}
|
||||
|
||||
165
src/main/java/dan200/computercraft/core/apis/ComputerAccess.java
Normal file
165
src/main/java/dan200/computercraft/core/apis/ComputerAccess.java
Normal file
@@ -0,0 +1,165 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerOwned;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class ComputerAccess implements IComputerAccess, IComputerOwned
|
||||
{
|
||||
private final IAPIEnvironment m_environment;
|
||||
private final Set<String> m_mounts = new HashSet<>();
|
||||
|
||||
protected ComputerAccess( IAPIEnvironment m_environment )
|
||||
{
|
||||
this.m_environment = m_environment;
|
||||
}
|
||||
|
||||
public void unmountAll()
|
||||
{
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
for( String m_mount : m_mounts )
|
||||
{
|
||||
fileSystem.unmount( m_mount );
|
||||
}
|
||||
m_mounts.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mount( @Nonnull String desiredLoc, @Nonnull IMount mount )
|
||||
{
|
||||
return mount( desiredLoc, mount, getAttachmentName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
|
||||
{
|
||||
// Mount the location
|
||||
String location;
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
if( fileSystem == null )
|
||||
{
|
||||
throw new IllegalStateException( "File system has not been created" );
|
||||
}
|
||||
|
||||
synchronized( fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
fileSystem.mount( driveName, location, mount );
|
||||
}
|
||||
catch( FileSystemException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount )
|
||||
{
|
||||
return mountWritable( desiredLoc, mount, getAttachmentName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName )
|
||||
{
|
||||
// Mount the location
|
||||
String location;
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
if( fileSystem == null )
|
||||
{
|
||||
throw new IllegalStateException( "File system has not been created" );
|
||||
}
|
||||
|
||||
synchronized( fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
fileSystem.mountWritable( driveName, location, mount );
|
||||
}
|
||||
catch( FileSystemException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unmount( String location )
|
||||
{
|
||||
if( location != null )
|
||||
{
|
||||
if( !m_mounts.contains( location ) )
|
||||
{
|
||||
throw new RuntimeException( "You didn't mount this location" );
|
||||
}
|
||||
|
||||
m_environment.getFileSystem().unmount( location );
|
||||
m_mounts.remove( location );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID()
|
||||
{
|
||||
return m_environment.getComputerID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
{
|
||||
m_environment.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Computer getComputer()
|
||||
{
|
||||
return m_environment.getComputer();
|
||||
}
|
||||
|
||||
private String findFreeLocation( String desiredLoc )
|
||||
{
|
||||
try
|
||||
{
|
||||
FileSystem fileSystem = m_environment.getFileSystem();
|
||||
if( !fileSystem.exists( desiredLoc ) )
|
||||
{
|
||||
return desiredLoc;
|
||||
}
|
||||
|
||||
// We used to check foo2,foo3,foo4,etc here
|
||||
// but the disk drive does this itself now
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,18 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.BinaryOutputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedOutputHandle;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
@@ -48,11 +50,6 @@ public class FSAPI implements ILuaAPI
|
||||
m_fileSystem = m_env.getFileSystem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
@@ -83,8 +80,9 @@ public class FSAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -92,13 +90,14 @@ public class FSAPI implements ILuaAPI
|
||||
{
|
||||
// list
|
||||
String path = getString( args, 0 );
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
try {
|
||||
String[] results = m_fileSystem.list( path );
|
||||
Map<Object,Object> table = new HashMap<>();
|
||||
for(int i=0; i<results.length; ++i ) {
|
||||
table.put( i+1, results[i] );
|
||||
}
|
||||
return new Object[] { table };
|
||||
return MethodResult.of( table );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
@@ -110,13 +109,13 @@ public class FSAPI implements ILuaAPI
|
||||
// combine
|
||||
String pathA = getString( args, 0 );
|
||||
String pathB = getString( args, 1 );
|
||||
return new Object[] { m_fileSystem.combine( pathA, pathB ) };
|
||||
return MethodResult.of( m_fileSystem.combine( pathA, pathB ) );
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// getName
|
||||
String path = getString( args, 0 );
|
||||
return new Object[]{ FileSystem.getName( path ) };
|
||||
return MethodResult.of( FileSystem.getName( path ) );
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
@@ -124,7 +123,7 @@ public class FSAPI implements ILuaAPI
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
return new Object[]{ m_fileSystem.getSize( path ) };
|
||||
return MethodResult.of( m_fileSystem.getSize( path ) );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
@@ -136,9 +135,9 @@ public class FSAPI implements ILuaAPI
|
||||
// exists
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
return new Object[]{ m_fileSystem.exists( path ) };
|
||||
return MethodResult.of( m_fileSystem.exists( path ) );
|
||||
} catch( FileSystemException e ) {
|
||||
return new Object[]{ false };
|
||||
return MethodResult.of( false );
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
@@ -146,9 +145,9 @@ public class FSAPI implements ILuaAPI
|
||||
// isDir
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
return new Object[]{ m_fileSystem.isDir( path ) };
|
||||
return MethodResult.of( m_fileSystem.isDir( path ) );
|
||||
} catch( FileSystemException e ) {
|
||||
return new Object[]{ false };
|
||||
return MethodResult.of( false );
|
||||
}
|
||||
}
|
||||
case 6:
|
||||
@@ -156,9 +155,9 @@ public class FSAPI implements ILuaAPI
|
||||
// isReadOnly
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
return new Object[]{ m_fileSystem.isReadOnly( path ) };
|
||||
return MethodResult.of( m_fileSystem.isReadOnly( path ) );
|
||||
} catch( FileSystemException e ) {
|
||||
return new Object[]{ false };
|
||||
return MethodResult.of( false );
|
||||
}
|
||||
}
|
||||
case 7:
|
||||
@@ -166,8 +165,9 @@ public class FSAPI implements ILuaAPI
|
||||
// makeDir
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.makeDir( path );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -178,8 +178,9 @@ public class FSAPI implements ILuaAPI
|
||||
String path = getString( args, 0 );
|
||||
String dest = getString( args, 1 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.move( path, dest );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -190,8 +191,9 @@ public class FSAPI implements ILuaAPI
|
||||
String path = getString( args, 0 );
|
||||
String dest = getString( args, 1 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.copy( path, dest );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -201,8 +203,9 @@ public class FSAPI implements ILuaAPI
|
||||
// delete
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.delete( path );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -212,6 +215,7 @@ public class FSAPI implements ILuaAPI
|
||||
// open
|
||||
String path = getString( args, 0 );
|
||||
String mode = getString( args, 1 );
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
try {
|
||||
switch( mode )
|
||||
{
|
||||
@@ -219,43 +223,43 @@ public class FSAPI implements ILuaAPI
|
||||
{
|
||||
// Open the file for reading, then create a wrapper around the reader
|
||||
InputStream reader = m_fileSystem.openForRead( path );
|
||||
return new Object[] { new EncodedInputHandle( reader ) };
|
||||
return MethodResult.of( new EncodedInputHandle( reader ) );
|
||||
}
|
||||
case "w":
|
||||
{
|
||||
// Open the file for writing, then create a wrapper around the writer
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, false );
|
||||
return new Object[] { new EncodedOutputHandle( writer ) };
|
||||
return MethodResult.of( new EncodedOutputHandle( writer ) );
|
||||
}
|
||||
case "a":
|
||||
{
|
||||
// Open the file for appending, then create a wrapper around the writer
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, true );
|
||||
return new Object[] { new EncodedOutputHandle( writer ) };
|
||||
return MethodResult.of( new EncodedOutputHandle( writer ) );
|
||||
}
|
||||
case "rb":
|
||||
{
|
||||
// Open the file for binary reading, then create a wrapper around the reader
|
||||
InputStream reader = m_fileSystem.openForRead( path );
|
||||
return new Object[] { new BinaryInputHandle( reader ) };
|
||||
return MethodResult.of( new BinaryInputHandle( reader ) );
|
||||
}
|
||||
case "wb":
|
||||
{
|
||||
// Open the file for binary writing, then create a wrapper around the writer
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, false );
|
||||
return new Object[] { new BinaryOutputHandle( writer ) };
|
||||
return MethodResult.of( new BinaryOutputHandle( writer ) );
|
||||
}
|
||||
case "ab":
|
||||
{
|
||||
// Open the file for binary appending, then create a wrapper around the reader
|
||||
OutputStream writer = m_fileSystem.openForWrite( path, true );
|
||||
return new Object[] { new BinaryOutputHandle( writer ) };
|
||||
return MethodResult.of( new BinaryOutputHandle( writer ) );
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported mode" );
|
||||
}
|
||||
} catch( FileSystemException e ) {
|
||||
return new Object[] { null, e.getMessage() };
|
||||
return MethodResult.of( null, e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 12:
|
||||
@@ -265,9 +269,9 @@ public class FSAPI implements ILuaAPI
|
||||
try {
|
||||
if( !m_fileSystem.exists( path ) )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
return new Object[]{ m_fileSystem.getMountLabel( path ) };
|
||||
return MethodResult.of( m_fileSystem.getMountLabel( path ) );
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -280,9 +284,9 @@ public class FSAPI implements ILuaAPI
|
||||
long freeSpace = m_fileSystem.getFreeSpace( path );
|
||||
if( freeSpace >= 0 )
|
||||
{
|
||||
return new Object[]{ freeSpace };
|
||||
return MethodResult.of( freeSpace );
|
||||
}
|
||||
return new Object[]{ "unlimited" };
|
||||
return MethodResult.of( "unlimited" );
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -292,12 +296,13 @@ public class FSAPI implements ILuaAPI
|
||||
// find
|
||||
String path = getString( args, 0 );
|
||||
try {
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
String[] results = m_fileSystem.find( path );
|
||||
Map<Object,Object> table = new HashMap<>();
|
||||
for(int i=0; i<results.length; ++i ) {
|
||||
table.put( i+1, results[i] );
|
||||
}
|
||||
return new Object[] { table };
|
||||
return MethodResult.of( table );
|
||||
} catch( FileSystemException e ) {
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
@@ -306,13 +311,21 @@ public class FSAPI implements ILuaAPI
|
||||
{
|
||||
// getDir
|
||||
String path = getString( args, 0 );
|
||||
return new Object[]{ FileSystem.getDirectory( path ) };
|
||||
return MethodResult.of( FileSystem.getDirectory( path ) );
|
||||
}
|
||||
default:
|
||||
{
|
||||
assert( false );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,135 +6,170 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
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 com.google.common.collect.ImmutableSet;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.apis.http.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
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.*;
|
||||
import static dan200.computercraft.core.apis.TableHelper.*;
|
||||
|
||||
public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
private static final Set<String> HTTP_METHODS = ImmutableSet.of(
|
||||
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE"
|
||||
);
|
||||
|
||||
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()
|
||||
{
|
||||
return new String[] {
|
||||
return new String[]{
|
||||
"http"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
public void update()
|
||||
{
|
||||
}
|
||||
|
||||
@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( )
|
||||
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",
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
case 0: // request
|
||||
{
|
||||
// request
|
||||
// Get URL
|
||||
String urlString = getString( args, 0 );
|
||||
String urlString, postString, requestMethod;
|
||||
Map<Object, Object> headerTable;
|
||||
boolean binary, redirect;
|
||||
|
||||
// Get POST
|
||||
String postString = optString( args, 1, null );
|
||||
|
||||
// Get Headers
|
||||
Map<String, String> headers = null;
|
||||
Map<Object, Object> table = optTable( args, 2, null );
|
||||
if( table != null )
|
||||
if( args.length >= 1 && args[0] instanceof Map )
|
||||
{
|
||||
headers = new HashMap<>( table.size() );
|
||||
for( Object key : table.keySet() )
|
||||
Map<?, ?> options = (Map) args[0];
|
||||
urlString = getStringField( options, "url" );
|
||||
postString = optStringField( options, "body", null );
|
||||
headerTable = optTableField( options, "headers", null );
|
||||
binary = optBooleanField( options, "binary", false );
|
||||
requestMethod = optStringField( options, "method", null );
|
||||
redirect = optBooleanField( options, "redirect", true );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get URL and post information
|
||||
urlString = getString( args, 0 );
|
||||
postString = optString( args, 1, null );
|
||||
headerTable = optTable( args, 2, null );
|
||||
binary = optBoolean( args, 3, false );
|
||||
requestMethod = null;
|
||||
redirect = true;
|
||||
}
|
||||
|
||||
Map<String, String> headers = null;
|
||||
if( headerTable != null )
|
||||
{
|
||||
headers = new HashMap<>( headerTable.size() );
|
||||
for( Object key : headerTable.keySet() )
|
||||
{
|
||||
Object value = table.get( key );
|
||||
Object value = headerTable.get( key );
|
||||
if( key instanceof String && value instanceof String )
|
||||
{
|
||||
headers.put( (String)key, (String)value );
|
||||
headers.put( (String) key, (String) value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get binary
|
||||
boolean binary = false;
|
||||
if( args.length >= 4 )
|
||||
|
||||
|
||||
if( requestMethod != null && !HTTP_METHODS.contains( requestMethod ) )
|
||||
{
|
||||
binary = args[ 3 ] != null && !args[ 3 ].equals( Boolean.FALSE );
|
||||
throw new LuaException( "Unsupported HTTP method" );
|
||||
}
|
||||
|
||||
// Make the request
|
||||
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, requestMethod, redirect );
|
||||
synchronized( m_httpTasks )
|
||||
{
|
||||
m_httpTasks.add( HTTPTask.submit( request ) );
|
||||
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
|
||||
}
|
||||
return new Object[] { true };
|
||||
return MethodResult.of(true);
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
return MethodResult.of( false, e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
@@ -147,21 +182,83 @@ 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 };
|
||||
return MethodResult.of( true );
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
return MethodResult.of( 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 MethodResult.of(true);
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return MethodResult.of( false, e.getMessage() );
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
|
||||
public void addCloseable( Closeable closeable )
|
||||
{
|
||||
synchronized( m_closeables )
|
||||
{
|
||||
m_closeables.add( closeable );
|
||||
}
|
||||
}
|
||||
|
||||
public void removeCloseable( Closeable closeable )
|
||||
{
|
||||
synchronized( m_closeables )
|
||||
{
|
||||
m_closeables.remove( closeable );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -9,16 +9,19 @@ package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
import dan200.computercraft.core.computer.IComputerOwned;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
public interface IAPIEnvironment
|
||||
public interface IAPIEnvironment extends IComputerOwned
|
||||
{
|
||||
interface IPeripheralChangeListener
|
||||
{
|
||||
void onPeripheralChanged( int side, IPeripheral newPeripheral );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
Computer getComputer();
|
||||
int getComputerID();
|
||||
IComputerEnvironment getComputerEnvironment();
|
||||
@@ -42,4 +45,11 @@ public interface IAPIEnvironment
|
||||
|
||||
String getLabel();
|
||||
void setLabel( String label );
|
||||
|
||||
void addTrackingChange( TrackingField field, long change );
|
||||
|
||||
default void addTrackingChange( TrackingField field )
|
||||
{
|
||||
addTrackingChange( field, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
|
||||
public interface ILuaAPI extends ILuaObject
|
||||
/**
|
||||
* This exists purely to ensure binary compatibility.
|
||||
*
|
||||
* @see dan200.computercraft.api.lua.ILuaAPI
|
||||
*/
|
||||
public interface ILuaAPI extends dan200.computercraft.api.lua.ILuaAPI
|
||||
{
|
||||
String[] getNames();
|
||||
void advance( double v );
|
||||
|
||||
void startup(); // LT
|
||||
void advance( double _dt ); // MT
|
||||
void shutdown(); // LT
|
||||
default void update()
|
||||
{
|
||||
advance( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||
@@ -52,8 +53,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 ) {
|
||||
@@ -102,7 +103,7 @@ public class OSAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double dt )
|
||||
public void update()
|
||||
{
|
||||
synchronized( m_timers )
|
||||
{
|
||||
@@ -135,13 +136,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 +197,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;
|
||||
}
|
||||
|
||||
@@ -220,7 +221,8 @@ public class OSAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
@Nonnull
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -228,7 +230,7 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
// queueEvent
|
||||
queueLuaEvent( getString( args, 0 ), trimArray( args, 1 ) );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
@@ -237,7 +239,7 @@ public class OSAPI implements ILuaAPI
|
||||
synchronized( m_timers )
|
||||
{
|
||||
m_timers.put( m_nextTimerToken, new Timer( (int)Math.round( timer / 0.05 ) ) );
|
||||
return new Object[] { m_nextTimerToken++ };
|
||||
return MethodResult.of( m_nextTimerToken++ );
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
@@ -252,33 +254,33 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
int day = (time > m_time) ? m_day : (m_day + 1);
|
||||
m_alarms.put( m_nextAlarmToken, new Alarm( time, day ) );
|
||||
return new Object[] { m_nextAlarmToken++ };
|
||||
return MethodResult.of( m_nextAlarmToken++ );
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// shutdown
|
||||
m_apiEnvironment.shutdown();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
// reboot
|
||||
m_apiEnvironment.reboot();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 5:
|
||||
case 6:
|
||||
{
|
||||
// computerID/getComputerID
|
||||
return new Object[] { getComputerID() };
|
||||
return MethodResult.of( getComputerID() );
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
// setComputerLabel
|
||||
String label = optString( args, 0, null );
|
||||
m_apiEnvironment.setLabel( StringUtil.normaliseLabel( label ) );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
@@ -287,16 +289,16 @@ public class OSAPI implements ILuaAPI
|
||||
String label = m_apiEnvironment.getLabel();
|
||||
if( label != null )
|
||||
{
|
||||
return new Object[] { label };
|
||||
return MethodResult.of( label );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 10:
|
||||
{
|
||||
// clock
|
||||
synchronized( m_timers )
|
||||
{
|
||||
return new Object[] { (double)m_clock * 0.05 };
|
||||
return MethodResult.of( m_clock * 0.05 );
|
||||
}
|
||||
}
|
||||
case 11:
|
||||
@@ -309,19 +311,19 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
// Get Hour of day (UTC)
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return new Object[] { getTimeForCalendar( c ) };
|
||||
return MethodResult.of( getTimeForCalendar( c ) );
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get Hour of day (local time)
|
||||
Calendar c = Calendar.getInstance();
|
||||
return new Object[] { getTimeForCalendar( c ) };
|
||||
return MethodResult.of( getTimeForCalendar( c ) );
|
||||
}
|
||||
case "ingame":
|
||||
// Get ingame hour
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return new Object[] { m_time };
|
||||
return MethodResult.of( m_time );
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
@@ -337,19 +339,19 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
// Get numbers of days since 1970-01-01 (utc)
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return new Object[] { getDayForCalendar( c ) };
|
||||
return MethodResult.of( getDayForCalendar( c ) );
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get numbers of days since 1970-01-01 (local time)
|
||||
Calendar c = Calendar.getInstance();
|
||||
return new Object[] { getDayForCalendar( c ) };
|
||||
return MethodResult.of( getDayForCalendar( c ) );
|
||||
}
|
||||
case "ingame":
|
||||
// Get game day
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return new Object[] { m_day };
|
||||
return MethodResult.of( m_day );
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
@@ -366,7 +368,7 @@ public class OSAPI implements ILuaAPI
|
||||
m_timers.remove( token );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 14:
|
||||
{
|
||||
@@ -379,7 +381,7 @@ public class OSAPI implements ILuaAPI
|
||||
m_alarms.remove( token );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 15:
|
||||
{
|
||||
@@ -391,21 +393,21 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
// Get utc epoch
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return new Object[] { getEpochForCalendar( c ) };
|
||||
return MethodResult.of( getEpochForCalendar( c ) );
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get local epoch
|
||||
Calendar c = Calendar.getInstance();
|
||||
return new Object[] { getEpochForCalendar( c ) };
|
||||
return MethodResult.of( getEpochForCalendar( c ) );
|
||||
}
|
||||
case "ingame":
|
||||
// Get in-game epoch
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return new Object[] {
|
||||
return MethodResult.of(
|
||||
m_day * 86400000 + (int) (m_time * 3600000.0f)
|
||||
};
|
||||
);
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
@@ -413,11 +415,18 @@ public class OSAPI implements ILuaAPI
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
// Private methods
|
||||
|
||||
private void queueLuaEvent( String event, Object[] args )
|
||||
|
||||
@@ -8,24 +8,26 @@ package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.ComputerThread;
|
||||
import dan200.computercraft.core.computer.ITask;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
|
||||
|
||||
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener
|
||||
{
|
||||
private class PeripheralWrapper implements IComputerAccess
|
||||
private class PeripheralWrapper extends ComputerAccess
|
||||
{
|
||||
private final String m_side;
|
||||
private final IPeripheral m_peripheral;
|
||||
@@ -34,74 +36,71 @@ 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 )
|
||||
{
|
||||
super(m_environment);
|
||||
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 );
|
||||
// Unmount everything the detach function forgot to do
|
||||
unmountAll();
|
||||
}
|
||||
m_mounts.clear();
|
||||
|
||||
m_attached = false;
|
||||
}
|
||||
|
||||
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
|
||||
|
||||
public MethodResult call( ICallContext context, String methodName, Object[] arguments ) throws LuaException
|
||||
{
|
||||
int method = -1;
|
||||
synchronized( this )
|
||||
synchronized( this )
|
||||
{
|
||||
if( m_methodMap.containsKey( methodName ) )
|
||||
{
|
||||
@@ -110,6 +109,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
if( method >= 0 )
|
||||
{
|
||||
m_environment.addTrackingChange( TrackingField.PERIPHERAL_OPS );
|
||||
return m_peripheral.callMethod( this, context, method, arguments );
|
||||
}
|
||||
else
|
||||
@@ -119,13 +119,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
|
||||
// IComputerAccess implementation
|
||||
|
||||
@Override
|
||||
public String mount( @Nonnull String desiredLoc, @Nonnull IMount mount )
|
||||
{
|
||||
return mount( desiredLoc, mount, m_side );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
|
||||
{
|
||||
@@ -133,32 +126,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
// Mount the location
|
||||
String location;
|
||||
synchronized( m_fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try {
|
||||
m_fileSystem.mount( driveName, location, mount );
|
||||
} catch( FileSystemException e ) {
|
||||
// fail and return null
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount )
|
||||
{
|
||||
return mountWritable( desiredLoc, mount, m_side );
|
||||
return super.mount( desiredLoc, mount, driveName );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,77 +137,94 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
// Mount the location
|
||||
String location;
|
||||
synchronized( m_fileSystem )
|
||||
{
|
||||
location = findFreeLocation( desiredLoc );
|
||||
if( location != null )
|
||||
{
|
||||
try {
|
||||
m_fileSystem.mountWritable( driveName, location, mount );
|
||||
} catch( FileSystemException e ) {
|
||||
// fail and return null
|
||||
}
|
||||
}
|
||||
}
|
||||
if( location != null )
|
||||
{
|
||||
m_mounts.add( location );
|
||||
}
|
||||
return location;
|
||||
|
||||
return super.mountWritable( desiredLoc, mount, driveName );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void unmount( String location )
|
||||
{
|
||||
if( !m_attached ) {
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
if( location != null )
|
||||
if( !m_attached )
|
||||
{
|
||||
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()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
return m_environment.getComputerID();
|
||||
|
||||
super.unmount( location );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
public int getID()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
m_environment.queueEvent( event, arguments );
|
||||
}
|
||||
return super.getID();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
{
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
super.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public synchronized String getAttachmentName()
|
||||
public String getAttachmentName()
|
||||
{
|
||||
if( !m_attached ) {
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
return m_side;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<String, IPeripheral> getAvailablePeripherals()
|
||||
{
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
Map<String, IPeripheral> peripherals = new HashMap<>();
|
||||
for( PeripheralWrapper wrapper : m_peripherals )
|
||||
{
|
||||
if( wrapper != null && wrapper.isAttached() )
|
||||
{
|
||||
peripherals.put( wrapper.getAttachmentName(), wrapper.getPeripheral() );
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap( peripherals );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IPeripheral getAvailablePeripheral( @Nonnull String name )
|
||||
{
|
||||
if( !m_attached )
|
||||
{
|
||||
throw new RuntimeException( "You are not attached to this Computer" );
|
||||
}
|
||||
|
||||
for( PeripheralWrapper wrapper : m_peripherals )
|
||||
{
|
||||
if( wrapper != null && wrapper.isAttached() && wrapper.getAttachmentName().equals( name ) )
|
||||
{
|
||||
return wrapper.getPeripheral();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final IAPIEnvironment m_environment;
|
||||
private FileSystem m_fileSystem;
|
||||
private final PeripheralWrapper[] m_peripherals;
|
||||
private boolean m_running;
|
||||
|
||||
@@ -246,16 +232,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 +268,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 +282,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
m_peripherals[side] = null;
|
||||
}
|
||||
|
||||
|
||||
if( m_peripherals[side] != null )
|
||||
{
|
||||
// Queue an attachment
|
||||
@@ -318,7 +304,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 +312,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
|
||||
// ILuaAPI implementation
|
||||
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
@@ -340,7 +326,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
{
|
||||
m_fileSystem = m_environment.getFileSystem();
|
||||
m_running = true;
|
||||
for( int i=0; i<6; ++i )
|
||||
{
|
||||
@@ -352,12 +337,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
@@ -372,7 +352,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
wrapper.detach();
|
||||
}
|
||||
}
|
||||
m_fileSystem = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,8 +367,9 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -409,7 +389,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Object[] { present };
|
||||
return MethodResult.of( present );
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
@@ -428,10 +408,10 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
if( type != null )
|
||||
{
|
||||
return new Object[] { type };
|
||||
return MethodResult.of( type );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
@@ -455,17 +435,17 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
for(int i=0; i<methods.length; ++i ) {
|
||||
table.put( i+1, methods[i] );
|
||||
}
|
||||
return new Object[] { table };
|
||||
return MethodResult.of( table );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// 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;
|
||||
@@ -482,18 +462,21 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
|
||||
// Privates
|
||||
|
||||
private Object[] trimArray( Object[] array, int skip )
|
||||
{
|
||||
return Arrays.copyOfRange( array, skip, array.length );
|
||||
}
|
||||
|
||||
private int parseSide( Object[] args ) throws LuaException
|
||||
{
|
||||
String side = getString( args, 0 );
|
||||
@@ -506,25 +489,4 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String findFreeLocation( String desiredLoc )
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized( m_fileSystem )
|
||||
{
|
||||
if( !m_fileSystem.exists( desiredLoc ) )
|
||||
{
|
||||
return desiredLoc;
|
||||
}
|
||||
// We used to check foo2,foo3,foo4,etc here
|
||||
// but the disk drive does this itself now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -33,21 +34,6 @@ public class RedstoneAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
@@ -70,8 +56,9 @@ public class RedstoneAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -83,7 +70,7 @@ public class RedstoneAPI implements ILuaAPI
|
||||
{
|
||||
table.put( i+1, Computer.s_sideNames[i] );
|
||||
}
|
||||
return new Object[] { table };
|
||||
return MethodResult.of( table );
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
@@ -91,19 +78,19 @@ public class RedstoneAPI implements ILuaAPI
|
||||
int side = parseSide( args );
|
||||
boolean output = getBoolean( args, 1 );
|
||||
m_environment.setOutput( side, output ? 15 : 0 );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// getOutput
|
||||
int side = parseSide( args );
|
||||
return new Object[] { m_environment.getOutput( side ) > 0 };
|
||||
return MethodResult.of( m_environment.getOutput( side ) > 0 );
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// getInput
|
||||
int side = parseSide( args );
|
||||
return new Object[] { m_environment.getInput( side ) > 0 };
|
||||
return MethodResult.of( m_environment.getInput( side ) > 0 );
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
@@ -111,19 +98,19 @@ public class RedstoneAPI implements ILuaAPI
|
||||
int side = parseSide( args );
|
||||
int output = getInt( args, 1 );
|
||||
m_environment.setBundledOutput( side, output );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
// getBundledOutput
|
||||
int side = parseSide( args );
|
||||
return new Object[] { m_environment.getBundledOutput( side ) };
|
||||
return MethodResult.of( m_environment.getBundledOutput( side ) );
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
// getBundledInput
|
||||
int side = parseSide( args );
|
||||
return new Object[] { m_environment.getBundledInput( side ) };
|
||||
return MethodResult.of( m_environment.getBundledInput( side ) );
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
@@ -131,7 +118,7 @@ public class RedstoneAPI implements ILuaAPI
|
||||
int side = parseSide( args );
|
||||
int mask = getInt( args, 1 );
|
||||
int input = m_environment.getBundledInput( side );
|
||||
return new Object[] { ((input & mask) == mask) };
|
||||
return MethodResult.of( ((input & mask) == mask) );
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
@@ -144,28 +131,36 @@ public class RedstoneAPI implements ILuaAPI
|
||||
throw new LuaException( "Expected number in range 0-15" );
|
||||
}
|
||||
m_environment.setOutput( side, output );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 10:
|
||||
case 11:
|
||||
{
|
||||
// getAnalogOutput/getAnalogueOutput
|
||||
int side = parseSide( args );
|
||||
return new Object[] { m_environment.getOutput( side ) };
|
||||
return MethodResult.of( m_environment.getOutput( side ) );
|
||||
}
|
||||
case 12:
|
||||
case 13:
|
||||
{
|
||||
// getAnalogInput/getAnalogueInput
|
||||
int side = parseSide( args );
|
||||
return new Object[] { m_environment.getInput( side ) };
|
||||
return MethodResult.of( m_environment.getInput( side ) );
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
|
||||
private int parseSide( Object[] args ) throws LuaException
|
||||
{
|
||||
|
||||
214
src/main/java/dan200/computercraft/core/apis/TableHelper.java
Normal file
214
src/main/java/dan200/computercraft/core/apis/TableHelper.java
Normal file
@@ -0,0 +1,214 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Various helpers for tables
|
||||
*/
|
||||
public final class TableHelper
|
||||
{
|
||||
private TableHelper()
|
||||
{
|
||||
throw new IllegalStateException( "Cannot instantiate singleton " + getClass().getName() );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nullable Object actual )
|
||||
{
|
||||
return badKey( key, expected, ArgumentHelper.getType( actual ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nonnull String actual )
|
||||
{
|
||||
return new LuaException( "bad field '" + key + "' (" + expected + " expected, got " + actual + ")" );
|
||||
}
|
||||
|
||||
public static double getNumberField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Number )
|
||||
{
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static int getIntField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Number )
|
||||
{
|
||||
return (int) ((Number) value).longValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static double getRealField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
return checkReal( key, getNumberField( table, key ) );
|
||||
}
|
||||
|
||||
public static boolean getBooleanField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Boolean )
|
||||
{
|
||||
return (Boolean) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "boolean", value );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String getStringField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof String )
|
||||
{
|
||||
return (String) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "string", value );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
@Nonnull
|
||||
public static Map<Object, Object> getTableField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value instanceof Map )
|
||||
{
|
||||
return (Map<Object, Object>) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "table", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static double optNumberField( @Nonnull Map<?, ?> table, @Nonnull String key, double def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Number )
|
||||
{
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static int optIntField( @Nonnull Map<?, ?> table, @Nonnull String key, int def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Number )
|
||||
{
|
||||
return (int) ((Number) value).longValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "number", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static double optRealField( @Nonnull Map<?, ?> table, @Nonnull String key, double def ) throws LuaException
|
||||
{
|
||||
return checkReal( key, optNumberField( table, key, def ) );
|
||||
}
|
||||
|
||||
public static boolean optBooleanField( @Nonnull Map<?, ?> table, @Nonnull String key, boolean def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Boolean )
|
||||
{
|
||||
return (Boolean) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "boolean", value );
|
||||
}
|
||||
}
|
||||
|
||||
public static String optStringField( @Nonnull Map<?, ?> table, @Nonnull String key, String def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof String )
|
||||
{
|
||||
return (String) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "string", value );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public static Map<Object, Object> optTableField( @Nonnull Map<?, ?> table, @Nonnull String key, Map<Object, Object> def ) throws LuaException
|
||||
{
|
||||
Object value = table.get( key );
|
||||
if( value == null )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else if( value instanceof Map )
|
||||
{
|
||||
return (Map<Object, Object>) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw badKey( key, "table", value );
|
||||
}
|
||||
}
|
||||
|
||||
private static double checkReal( @Nonnull String key, double value ) throws LuaException
|
||||
{
|
||||
if( Double.isNaN( value ) )
|
||||
{
|
||||
throw badKey( key, "number", "nan" );
|
||||
}
|
||||
else if( value == Double.POSITIVE_INFINITY )
|
||||
{
|
||||
throw badKey( key, "number", "inf" );
|
||||
}
|
||||
else if( value == Double.NEGATIVE_INFINITY )
|
||||
{
|
||||
throw badKey( key, "number", "-inf" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,15 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||
|
||||
@@ -36,21 +37,6 @@ public class TermAPI implements ILuaAPI
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup( )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance( double _dt )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown( )
|
||||
{
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
@@ -97,11 +83,9 @@ public class TermAPI implements ILuaAPI
|
||||
return colour;
|
||||
}
|
||||
|
||||
public static Object[] encodeColour( int colour ) throws LuaException
|
||||
public static MethodResult encodeColour( int colour )
|
||||
{
|
||||
return new Object[] {
|
||||
1 << colour
|
||||
};
|
||||
return MethodResult.of( 1 << colour );
|
||||
}
|
||||
|
||||
public static void setColour( Terminal terminal, int colour, double r, double g, double b )
|
||||
@@ -113,8 +97,9 @@ public class TermAPI implements ILuaAPI
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -133,7 +118,7 @@ public class TermAPI implements ILuaAPI
|
||||
m_terminal.write( text );
|
||||
m_terminal.setCursorPos( m_terminal.getCursorX() + text.length(), m_terminal.getCursorY() );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
@@ -143,7 +128,7 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.scroll(y);
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
@@ -154,7 +139,7 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.setCursorPos( x, y );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
@@ -164,7 +149,7 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.setCursorBlink( b );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
@@ -175,7 +160,7 @@ public class TermAPI implements ILuaAPI
|
||||
x = m_terminal.getCursorX();
|
||||
y = m_terminal.getCursorY();
|
||||
}
|
||||
return new Object[] { x + 1, y + 1 };
|
||||
return MethodResult.of( x + 1, y + 1 );
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
@@ -186,7 +171,7 @@ public class TermAPI implements ILuaAPI
|
||||
width = m_terminal.getWidth();
|
||||
height = m_terminal.getHeight();
|
||||
}
|
||||
return new Object[] { width, height };
|
||||
return MethodResult.of( width, height );
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
@@ -195,7 +180,7 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.clear();
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
@@ -204,7 +189,7 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.clearLine();
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
@@ -215,7 +200,7 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.setTextColour( colour );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 10:
|
||||
case 11:
|
||||
@@ -226,13 +211,13 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
m_terminal.setBackgroundColour( colour );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 12:
|
||||
case 13:
|
||||
{
|
||||
// isColour/isColor
|
||||
return new Object[] { m_environment.isColour() };
|
||||
return MethodResult.of( m_environment.isColour() );
|
||||
}
|
||||
case 14:
|
||||
case 15:
|
||||
@@ -262,7 +247,7 @@ public class TermAPI implements ILuaAPI
|
||||
m_terminal.blit( text, textColour, backgroundColour );
|
||||
m_terminal.setCursorPos( m_terminal.getCursorX() + text.length(), m_terminal.getCursorY() );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 19:
|
||||
case 20:
|
||||
@@ -282,7 +267,7 @@ public class TermAPI implements ILuaAPI
|
||||
double b = getReal( args, 3 );
|
||||
setColour( m_terminal, colour, r, g, b );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 21:
|
||||
case 22:
|
||||
@@ -293,18 +278,26 @@ public class TermAPI implements ILuaAPI
|
||||
{
|
||||
if ( m_terminal.getPalette() != null )
|
||||
{
|
||||
return ArrayUtils.toObject( m_terminal.getPalette().getColour( colour ) );
|
||||
return MethodResult.of( (Object[]) ArrayUtils.toObject( m_terminal.getPalette().getColour( colour ) ) );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
|
||||
private static int getHighestBit( int group )
|
||||
{
|
||||
int bit = 0;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ICallContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
@@ -13,6 +15,8 @@ import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
|
||||
|
||||
public class BinaryInputHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final InputStream m_stream;
|
||||
|
||||
public BinaryInputHandle( InputStream reader )
|
||||
@@ -32,8 +36,9 @@ public class BinaryInputHandle extends HandleGeneric
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -45,26 +50,56 @@ public class BinaryInputHandle extends HandleGeneric
|
||||
if( args.length > 0 && args[ 0 ] != null )
|
||||
{
|
||||
int count = getInt( args, 0 );
|
||||
if( count <= 0 || count >= 1024 * 16 )
|
||||
if( count < 0 )
|
||||
{
|
||||
throw new LuaException( "Count out of range" );
|
||||
// Whilst this may seem absurd to allow reading 0 bytes, PUC Lua it so
|
||||
// it seems best to remain somewhat consistent.
|
||||
throw new LuaException( "Cannot read a negative number of bytes" );
|
||||
}
|
||||
else if( count <= BUFFER_SIZE )
|
||||
{
|
||||
// If we've got a small count, then allocate that and read it.
|
||||
byte[] bytes = new byte[ count ];
|
||||
int read = m_stream.read( bytes );
|
||||
|
||||
byte[] bytes = new byte[ count ];
|
||||
count = m_stream.read( bytes );
|
||||
if( count < 0 ) return null;
|
||||
if( count < bytes.length ) bytes = Arrays.copyOf( bytes, count );
|
||||
return new Object[] { bytes };
|
||||
if( read < 0 ) return MethodResult.empty();
|
||||
if( read < count ) bytes = Arrays.copyOf( bytes, read );
|
||||
return MethodResult.of( bytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] buffer = new byte[ BUFFER_SIZE ];
|
||||
|
||||
// Read the initial set of bytes, failing if none are read.
|
||||
int read = m_stream.read( buffer, 0, Math.min( buffer.length, count ) );
|
||||
if( read == -1 ) return null;
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream( read );
|
||||
count -= read;
|
||||
out.write( buffer, 0, read );
|
||||
|
||||
// Otherwise read until we either reach the limit or we no longer consume
|
||||
// the full buffer.
|
||||
while( read >= buffer.length && count > 0 )
|
||||
{
|
||||
read = m_stream.read( buffer, 0, Math.min( BUFFER_SIZE, count ) );
|
||||
if( read == -1 ) break;
|
||||
count -= read;
|
||||
out.write( buffer, 0, read );
|
||||
}
|
||||
|
||||
return MethodResult.of( new Object[]{ out.toByteArray() } );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int b = m_stream.read();
|
||||
return b == -1 ? null : new Object[] { b };
|
||||
return b == -1 ? MethodResult.empty() : MethodResult.of( b );
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 1:
|
||||
// readAll
|
||||
@@ -72,18 +107,18 @@ public class BinaryInputHandle extends HandleGeneric
|
||||
try
|
||||
{
|
||||
byte[] out = ByteStreams.toByteArray( m_stream );
|
||||
return out == null ? null : new Object[] { out };
|
||||
return out == null ? MethodResult.empty() : MethodResult.of( out );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
//close
|
||||
close();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
default:
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ICallContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.core.apis.ArgumentHelper;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
|
||||
@@ -30,8 +31,9 @@ public class BinaryOutputHandle extends HandleGeneric
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -54,7 +56,7 @@ public class BinaryOutputHandle extends HandleGeneric
|
||||
{
|
||||
throw ArgumentHelper.badArgument( 0, "string or number", args.length > 0 ? args[ 0 ] : null );
|
||||
}
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
@@ -66,18 +68,18 @@ public class BinaryOutputHandle extends HandleGeneric
|
||||
try
|
||||
{
|
||||
m_writer.flush();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
//close
|
||||
close();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
default:
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ICallContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
|
||||
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
|
||||
|
||||
public class EncodedInputHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private final BufferedReader m_reader;
|
||||
|
||||
public EncodedInputHandle( BufferedReader reader )
|
||||
@@ -49,11 +54,13 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
"readLine",
|
||||
"readAll",
|
||||
"close",
|
||||
"read",
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -65,16 +72,16 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
String line = m_reader.readLine();
|
||||
if( line != null )
|
||||
{
|
||||
return new Object[] { line };
|
||||
return MethodResult.of( line );
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 1:
|
||||
// readAll
|
||||
@@ -92,18 +99,68 @@ public class EncodedInputHandle extends HandleGeneric
|
||||
result.append( "\n" );
|
||||
}
|
||||
}
|
||||
return new Object[] { result.toString() };
|
||||
return MethodResult.of( result.toString() );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
// close
|
||||
close();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
case 3:
|
||||
// read
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
int count = optInt( args, 0, 1 );
|
||||
if( count < 0 )
|
||||
{
|
||||
// Whilst this may seem absurd to allow reading 0 characters, PUC Lua it so
|
||||
// it seems best to remain somewhat consistent.
|
||||
throw new LuaException( "Cannot read a negative number of characters" );
|
||||
}
|
||||
else if( count <= BUFFER_SIZE )
|
||||
{
|
||||
// If we've got a small count, then allocate that and read it.
|
||||
char[] chars = new char[ count ];
|
||||
int read = m_reader.read( chars );
|
||||
|
||||
return read < 0 ? MethodResult.empty() : MethodResult.of( new String( chars, 0, read ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we've got a large count, read in bunches of 8192.
|
||||
char[] buffer = new char[ BUFFER_SIZE ];
|
||||
|
||||
// Read the initial set of characters, failing if none are read.
|
||||
int read = m_reader.read( buffer, 0, Math.min( buffer.length, count ) );
|
||||
if( read == -1 ) return null;
|
||||
|
||||
StringBuilder out = new StringBuilder( read );
|
||||
count -= read;
|
||||
out.append( buffer, 0, read );
|
||||
|
||||
// Otherwise read until we either reach the limit or we no longer consume
|
||||
// the full buffer.
|
||||
while( read >= BUFFER_SIZE && count > 0 )
|
||||
{
|
||||
read = m_reader.read( buffer, 0, Math.min( BUFFER_SIZE, count ) );
|
||||
if( read == -1 ) break;
|
||||
count -= read;
|
||||
out.append( buffer, 0, read );
|
||||
}
|
||||
|
||||
return MethodResult.of( out.toString() );
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return MethodResult.empty();
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ICallContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
@@ -53,8 +54,9 @@ public class EncodedOutputHandle extends HandleGeneric
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
@@ -74,7 +76,7 @@ public class EncodedOutputHandle extends HandleGeneric
|
||||
try
|
||||
{
|
||||
m_writer.write( text, 0, text.length() );
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
@@ -98,7 +100,7 @@ public class EncodedOutputHandle extends HandleGeneric
|
||||
{
|
||||
m_writer.write( text, 0, text.length() );
|
||||
m_writer.newLine();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
@@ -111,18 +113,18 @@ public class EncodedOutputHandle extends HandleGeneric
|
||||
try
|
||||
{
|
||||
m_writer.flush();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 3:
|
||||
// close
|
||||
close();
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
default:
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ICallContext;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -32,4 +36,12 @@ public abstract class HandleGeneric implements ILuaObject
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() } );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,14 @@ package dan200.computercraft.core.apis.http;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.io.ByteStreams;
|
||||
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.api.lua.*;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.Arrays;
|
||||
@@ -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,37 +72,25 @@ 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;
|
||||
private final String m_method;
|
||||
private final boolean m_followRedirects;
|
||||
|
||||
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, final String method, final boolean followRedirects ) throws HTTPRequestException
|
||||
{
|
||||
// Parse the URL
|
||||
m_environment = environment;
|
||||
m_urlString = urlString;
|
||||
m_url = url;
|
||||
m_binary = binary;
|
||||
m_postText = postText;
|
||||
m_headers = headers;
|
||||
}
|
||||
|
||||
public InputStream getContents()
|
||||
{
|
||||
byte[] result = m_result;
|
||||
if( result != null )
|
||||
{
|
||||
return new ByteArrayInputStream( result );
|
||||
}
|
||||
return null;
|
||||
m_method = method;
|
||||
m_followRedirects = followRedirects;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,12 +99,13 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -135,6 +123,8 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
{
|
||||
connection.setRequestMethod( "GET" );
|
||||
}
|
||||
if( m_method != null ) connection.setRequestMethod( m_method );
|
||||
connection.setInstanceFollowRedirects( m_followRedirects );
|
||||
|
||||
// Set headers
|
||||
connection.setRequestProperty( "accept-charset", "UTF-8" );
|
||||
@@ -150,6 +140,11 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
}
|
||||
}
|
||||
|
||||
// Add request size and count to the tracker before opening the connection
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_REQUESTS );
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_UPLOAD,
|
||||
getHeaderSize( connection.getRequestProperties() ) + (m_postText == null ? 0 : m_postText.length()) );
|
||||
|
||||
// Send POST text
|
||||
if( m_postText != null )
|
||||
{
|
||||
@@ -186,58 +181,39 @@ 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() ) );
|
||||
}
|
||||
|
||||
m_environment.addTrackingChange( TrackingField.HTTP_DOWNLOAD,
|
||||
getHeaderSize( connection.getHeaderFields() ) + result.length );
|
||||
|
||||
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 } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,8 +223,8 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
final int methodOffset = oldMethods.length;
|
||||
|
||||
final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 );
|
||||
newMethods[ methodOffset + 0 ] = "getResponseCode";
|
||||
newMethods[ methodOffset + 1 ] = "getResponseHeaders";
|
||||
newMethods[methodOffset + 0] = "getResponseCode";
|
||||
newMethods[methodOffset + 1] = "getResponseHeaders";
|
||||
|
||||
return new ILuaObject()
|
||||
{
|
||||
@@ -259,8 +235,9 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
return newMethods;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
if( method < methodOffset )
|
||||
{
|
||||
@@ -271,19 +248,38 @@ public class HTTPRequest implements HTTPTask.IHTTPTask
|
||||
case 0:
|
||||
{
|
||||
// getResponseCode
|
||||
return new Object[] { responseCode };
|
||||
return MethodResult.of( responseCode );
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// getResponseHeaders
|
||||
return new Object[] { responseHeaders };
|
||||
return MethodResult.of( responseHeaders );
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static long getHeaderSize( Map<String, List<String>> headers )
|
||||
{
|
||||
long size = 0;
|
||||
for( Map.Entry<String, List<String>> header : headers.entrySet() )
|
||||
{
|
||||
size += header.getKey() == null ? 0 : header.getKey().length();
|
||||
for( String value : header.getValue() ) size += value == null ? 0 : value.length() + 1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
public class HTTPRequestException extends Exception
|
||||
{
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.*;
|
||||
import dan200.computercraft.core.apis.HTTPAPI;
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
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 )
|
||||
{
|
||||
String data = ((TextWebSocketFrame) frame).text();
|
||||
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_INCOMING, data.length() );
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[]{ url, data } );
|
||||
}
|
||||
else if( frame instanceof BinaryWebSocketFrame )
|
||||
{
|
||||
ByteBuf data = frame.content();
|
||||
byte[] converted = new byte[data.readableBytes()];
|
||||
data.readBytes( converted );
|
||||
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_INCOMING, converted.length );
|
||||
computer.queueEvent( MESSAGE_EVENT, new Object[]{ url, converted } );
|
||||
}
|
||||
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
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return callMethod( (ICallContext) context, method, arguments ).evaluate( context );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
checkOpen();
|
||||
return MethodResult.pullEvent( MESSAGE_EVENT, new ILuaFunction()
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public MethodResult call( @Nullable Object[] event ) throws LuaException
|
||||
{
|
||||
if( event != null && event.length >= 3 && Objects.equal( event[1], url ) )
|
||||
{
|
||||
return MethodResult.of( event[2] );
|
||||
}
|
||||
|
||||
checkOpen();
|
||||
return MethodResult.pullEvent( MESSAGE_EVENT, this );
|
||||
}
|
||||
} );
|
||||
case 1:
|
||||
{
|
||||
checkOpen();
|
||||
String text = arguments.length > 0 && arguments[0] != null ? arguments[0].toString() : "";
|
||||
computer.addTrackingChange( TrackingField.WEBSOCKET_OUTGOING, text.length() );
|
||||
channel.writeAndFlush( new TextWebSocketFrame( text ) );
|
||||
return MethodResult.empty();
|
||||
}
|
||||
case 2:
|
||||
close( true );
|
||||
return MethodResult.empty();
|
||||
default:
|
||||
return MethodResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOpen() throws LuaException
|
||||
{
|
||||
if( !open ) throw new LuaException( "attempt to use a closed file" );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
|
||||
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, true, 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 ),
|
||||
WebSocketClientCompressionHandler.INSTANCE,
|
||||
connection
|
||||
);
|
||||
}
|
||||
} )
|
||||
.remoteAddress( socketAddress )
|
||||
.connect();
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,23 @@ package dan200.computercraft.core.computer;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.apis.*;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.LuaJLuaMachine;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@@ -162,6 +169,12 @@ public class Computer
|
||||
m_computer.setLabel( label );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackingChange( TrackingField field, long change )
|
||||
{
|
||||
Tracking.addValue( m_computer, field, change );
|
||||
}
|
||||
|
||||
public void onPeripheralChanged( int side, IPeripheral peripheral )
|
||||
{
|
||||
synchronized( m_computer.m_peripherals )
|
||||
@@ -173,7 +186,99 @@ public class Computer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ComputerSystem extends ComputerAccess implements IComputerSystem
|
||||
{
|
||||
private final IAPIEnvironment m_environment;
|
||||
|
||||
private ComputerSystem( IAPIEnvironment m_environment )
|
||||
{
|
||||
super( m_environment );
|
||||
this.m_environment = m_environment;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getAttachmentName()
|
||||
{
|
||||
return "computer";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IFileSystem getFileSystem()
|
||||
{
|
||||
FileSystem fs = m_environment.getFileSystem();
|
||||
return fs == null ? null : fs.getMountWrapper();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getLabel()
|
||||
{
|
||||
return m_environment.getLabel();
|
||||
}
|
||||
}
|
||||
|
||||
private static class APIWrapper implements ILuaAPI
|
||||
{
|
||||
private final ILuaAPI delegate;
|
||||
private final ComputerSystem system;
|
||||
|
||||
private APIWrapper( ILuaAPI delegate, ComputerSystem system )
|
||||
{
|
||||
this.delegate = delegate;
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
return delegate.getNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
delegate.startup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
delegate.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
delegate.shutdown();
|
||||
system.unmountAll();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return delegate.getMethodNames();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
|
||||
{
|
||||
return delegate.callMethod( context, method, arguments );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException
|
||||
{
|
||||
return delegate.callMethod( context, method, arguments );
|
||||
}
|
||||
}
|
||||
|
||||
private static IMount s_romMount = null;
|
||||
|
||||
private int m_id;
|
||||
@@ -371,7 +476,7 @@ public class Computer
|
||||
{
|
||||
for(ILuaAPI api : m_apis)
|
||||
{
|
||||
api.advance( _dt );
|
||||
api.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,14 +525,13 @@ public class Computer
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pollChanged()
|
||||
public boolean pollAndResetChanged()
|
||||
{
|
||||
return m_externalOutputChanged;
|
||||
}
|
||||
|
||||
public void clearChanged()
|
||||
{
|
||||
m_externalOutputChanged = false;
|
||||
synchronized(this) {
|
||||
boolean changed = m_externalOutputChanged;
|
||||
m_externalOutputChanged = false;
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBlinking()
|
||||
@@ -452,7 +556,7 @@ public class Computer
|
||||
private boolean initFileSystem()
|
||||
{
|
||||
// Create the file system
|
||||
int id = assignID();
|
||||
assignID();
|
||||
try
|
||||
{
|
||||
m_fileSystem = new FileSystem( "hdd", getRootMount() );
|
||||
@@ -578,6 +682,11 @@ public class Computer
|
||||
{
|
||||
m_apis.add( api );
|
||||
}
|
||||
|
||||
public void addAPI( dan200.computercraft.core.apis.ILuaAPI api )
|
||||
{
|
||||
addAPI( (ILuaAPI) api );
|
||||
}
|
||||
|
||||
public void setPeripheral( int side, IPeripheral peripheral )
|
||||
{
|
||||
@@ -611,18 +720,27 @@ 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 )
|
||||
{
|
||||
m_apis.add( new HTTPAPI( m_apiEnvironment ) );
|
||||
}
|
||||
|
||||
for( ILuaAPIFactory factory : ComputerCraft.getAPIFactories() )
|
||||
{
|
||||
ComputerSystem system = new ComputerSystem( m_apiEnvironment );
|
||||
ILuaAPI api = factory.create( system );
|
||||
if( api != null )
|
||||
{
|
||||
m_apis.add( api );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initLua()
|
||||
{
|
||||
// Create the lua machine
|
||||
ILuaMachine machine = new LuaJLuaMachine( this );
|
||||
ILuaMachine machine = new CobaltLuaMachine( this );
|
||||
|
||||
// Add the APIs
|
||||
for(ILuaAPI api : m_apis)
|
||||
@@ -688,6 +806,7 @@ public class Computer
|
||||
return;
|
||||
}
|
||||
m_state = State.Starting;
|
||||
m_externalOutputChanged = true;
|
||||
m_ticksSinceStart = 0;
|
||||
}
|
||||
|
||||
@@ -747,6 +866,7 @@ public class Computer
|
||||
|
||||
// Start a new state
|
||||
m_state = State.Running;
|
||||
m_externalOutputChanged = true;
|
||||
synchronized( m_machine )
|
||||
{
|
||||
m_machine.handleEvent( null, null );
|
||||
@@ -765,6 +885,7 @@ public class Computer
|
||||
return;
|
||||
}
|
||||
m_state = State.Stopping;
|
||||
m_externalOutputChanged = true;
|
||||
}
|
||||
|
||||
// Turn the computercraft off
|
||||
@@ -789,7 +910,7 @@ public class Computer
|
||||
// Shutdown our APIs
|
||||
synchronized( m_apis )
|
||||
{
|
||||
for(ILuaAPI api : m_apis)
|
||||
for( ILuaAPI api : m_apis )
|
||||
{
|
||||
api.shutdown();
|
||||
}
|
||||
@@ -828,6 +949,7 @@ public class Computer
|
||||
}
|
||||
|
||||
m_state = State.Off;
|
||||
m_externalOutputChanged = true;
|
||||
if( reboot )
|
||||
{
|
||||
m_startRequested = true;
|
||||
@@ -846,7 +968,7 @@ public class Computer
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Computer computer = this;
|
||||
ITask task = new ITask() {
|
||||
@Override
|
||||
|
||||
@@ -7,224 +7,347 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
long start = System.nanoTime();
|
||||
|
||||
// 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 )
|
||||
{
|
||||
StringBuilder builder = new StringBuilder( "Terminating " );
|
||||
if( computer != null )
|
||||
{
|
||||
builder.append( "computer " ).append( computer.getID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append( "unknown computer" );
|
||||
}
|
||||
|
||||
builder.append( ". Thread is currently running" );
|
||||
for( StackTraceElement element : thread.getStackTrace() )
|
||||
{
|
||||
builder.append( "\n at " ).append( element );
|
||||
}
|
||||
ComputerCraft.log.error( builder.toString() );
|
||||
|
||||
thread.interrupt();
|
||||
thread = null;
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
//System.out.println( "Event queue overflow" );
|
||||
long stop = System.nanoTime();
|
||||
Computer computer = task.getOwner();
|
||||
if( computer != null ) Tracking.addTaskTiming( computer, stop - start );
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IComputerOwned
|
||||
{
|
||||
@Nullable
|
||||
Computer getComputer();
|
||||
}
|
||||
@@ -6,14 +6,17 @@
|
||||
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
public class MainThread
|
||||
{
|
||||
private static final int MAX_TASKS_PER_TICK = 1000;
|
||||
private static final int MAX_TASKS_TOTAL = 50000;
|
||||
|
||||
private static final LinkedList<ITask> m_outstandingTasks = new LinkedList<>();
|
||||
private static final Queue<ITask> m_outstandingTasks = new ArrayDeque<>();
|
||||
private static final Object m_nextUnusedTaskIDLock = new Object();
|
||||
private static long m_nextUnusedTaskID = 0;
|
||||
|
||||
@@ -31,7 +34,7 @@ public class MainThread
|
||||
{
|
||||
if( m_outstandingTasks.size() < MAX_TASKS_TOTAL )
|
||||
{
|
||||
m_outstandingTasks.addLast( task );
|
||||
m_outstandingTasks.offer( task );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -46,14 +49,17 @@ public class MainThread
|
||||
ITask task = null;
|
||||
synchronized( m_outstandingTasks )
|
||||
{
|
||||
if( m_outstandingTasks.size() > 0 )
|
||||
{
|
||||
task = m_outstandingTasks.removeFirst();
|
||||
}
|
||||
task = m_outstandingTasks.poll();
|
||||
}
|
||||
if( task != null )
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
task.execute();
|
||||
|
||||
long stop = System.nanoTime();
|
||||
Computer computer = task.getOwner();
|
||||
if( computer != null ) Tracking.addServerTiming( computer, stop - start );
|
||||
|
||||
++tasksThisTick;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user