mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 06:27:39 +00:00
Compare commits
168 Commits
v1.15.2-1.
...
v1.16.3-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
71563a52ff | ||
![]() |
0c6e7b5db5 | ||
![]() |
334ca65482 | ||
![]() |
8472112fc1 | ||
![]() |
84036d97d9 | ||
![]() |
0832974725 | ||
![]() |
6cee4efcd3 | ||
![]() |
6f868849ab | ||
![]() |
275ca58a82 | ||
![]() |
87393e8aef | ||
![]() |
86bf57e3cd | ||
![]() |
72c1d451fe | ||
![]() |
8b4a01df27 | ||
![]() |
d0a973fa46 | ||
![]() |
748ebbe66b | ||
![]() |
59de21eae2 | ||
![]() |
50473afea8 | ||
![]() |
37f925de0a | ||
![]() |
cefde3f003 | ||
![]() |
ae6124d1f4 | ||
![]() |
7e121ff72f | ||
![]() |
5155e18de2 | ||
![]() |
7365741088 | ||
![]() |
d5368d0719 | ||
![]() |
26c12ac1a9 | ||
![]() |
2c67849b35 | ||
![]() |
04509cefec | ||
![]() |
74b9f5dcb0 | ||
![]() |
7809a2eddd | ||
![]() |
183b342071 | ||
![]() |
0bb5515055 | ||
![]() |
e8e9294fdf | ||
![]() |
9acfc0316f | ||
![]() |
29fb0baa09 | ||
![]() |
d5de39ebd4 | ||
![]() |
0faf76e4bd | ||
![]() |
99581e1f40 | ||
![]() |
e8e2ed9fe5 | ||
![]() |
9f72448ecd | ||
![]() |
3da3f16deb | ||
![]() |
0e2ce3c634 | ||
![]() |
fe00e00537 | ||
![]() |
29646a7f61 | ||
![]() |
50d2712581 | ||
![]() |
3093f882d8 | ||
![]() |
e5cf0d1c61 | ||
![]() |
cd879b067f | ||
![]() |
053cb1b53c | ||
![]() |
6b102a8142 | ||
![]() |
ac7979fb46 | ||
![]() |
c8a6888a2f | ||
![]() |
9ce33f8a3f | ||
![]() |
d51851e763 | ||
![]() |
fb70a1a998 | ||
![]() |
a1dcd59d95 | ||
![]() |
2a17585702 | ||
![]() |
2f323f23d7 | ||
![]() |
087c305b0d | ||
![]() |
31764f6d65 | ||
![]() |
4efde2b294 | ||
![]() |
95554a53d1 | ||
![]() |
89c1b2771d | ||
![]() |
8f069a9b72 | ||
![]() |
2e9d6603e3 | ||
![]() |
46595e73df | ||
![]() |
a6a1b9b8e5 | ||
![]() |
3f277a7a7b | ||
![]() |
90c5d3f1e8 | ||
![]() |
a5f7cf8334 | ||
![]() |
3075f89797 | ||
![]() |
45297665c6 | ||
![]() |
ddbf3fc111 | ||
![]() |
da82b89676 | ||
![]() |
d5f1a2c817 | ||
![]() |
6020adef6b | ||
![]() |
d2a52a8b5d | ||
![]() |
9f8774960f | ||
![]() |
36bb8b67c9 | ||
![]() |
8f3a56dd32 | ||
![]() |
113d5d982f | ||
![]() |
37a447e745 | ||
![]() |
9e2232d240 | ||
![]() |
514db30fb1 | ||
![]() |
08181f72d4 | ||
![]() |
613a28a5af | ||
![]() |
e4c422d6f9 | ||
![]() |
478f992dea | ||
![]() |
b54519d0e6 | ||
![]() |
9499654757 | ||
![]() |
c5138c535c | ||
![]() |
5bd8d84d14 | ||
![]() |
ab0310e27c | ||
![]() |
607751da40 | ||
![]() |
1efabccd14 | ||
![]() |
029374e9aa | ||
![]() |
2a8efb3fd5 | ||
![]() |
48edcde4ef | ||
![]() |
58a2995bbc | ||
![]() |
a35dcb28ef | ||
![]() |
7b2d482387 | ||
![]() |
2b077554f7 | ||
![]() |
9134f243c1 | ||
![]() |
c0f3ca81fb | ||
![]() |
190ed4fd20 | ||
![]() |
b9ff9b7f90 | ||
![]() |
b9b8121be9 | ||
![]() |
014bf55cd4 | ||
![]() |
085ae2e74a | ||
![]() |
4ff33f165d | ||
![]() |
d929c02d2a | ||
![]() |
d50a08a549 | ||
![]() |
c493d668c8 | ||
![]() |
53477fd3a1 | ||
![]() |
87aa839b60 | ||
![]() |
e02ccdcb1a | ||
![]() |
f36f532c63 | ||
![]() |
5a816917d5 | ||
![]() |
7af63d052d | ||
![]() |
4f8217d1ab | ||
![]() |
5409d441b5 | ||
![]() |
d5f82fa458 | ||
![]() |
d0deab3519 | ||
![]() |
d5a8df753a | ||
![]() |
13de2c4dd0 | ||
![]() |
906280225e | ||
![]() |
161a5b4707 | ||
![]() |
c6b6b4479c | ||
![]() |
96e7b60285 | ||
![]() |
086fccd997 | ||
![]() |
5dfaf6eee9 | ||
![]() |
e251dd066c | ||
![]() |
9abcfe56ea | ||
![]() |
abbc641fd4 | ||
![]() |
c60dcb4f5a | ||
![]() |
4be0b15afa | ||
![]() |
a4ae36b6b3 | ||
![]() |
ac075d9f53 | ||
![]() |
05d7be0362 | ||
![]() |
9a71dc1a26 | ||
![]() |
156023b154 | ||
![]() |
6b3773a862 | ||
![]() |
376d628cf0 | ||
![]() |
44062ebd52 | ||
![]() |
5739285fc2 | ||
![]() |
70b457ed18 | ||
![]() |
ca2995ed38 | ||
![]() |
6816931659 | ||
![]() |
1547ecbeb3 | ||
![]() |
e918f55b58 | ||
![]() |
c28b468844 | ||
![]() |
052cf8ee7d | ||
![]() |
550ada2f9e | ||
![]() |
17b7727262 | ||
![]() |
4553e404b2 | ||
![]() |
a565a571f9 | ||
![]() |
fb64b6017b | ||
![]() |
ed4229ab70 | ||
![]() |
3fb906ef6c | ||
![]() |
e1663f3df0 | ||
![]() |
52c6584c81 | ||
![]() |
9f87eda5de | ||
![]() |
697e9449cf | ||
![]() |
76c3e4c155 | ||
![]() |
358289b5f9 | ||
![]() |
5eec24676f | ||
![]() |
f52b8fa2de | ||
![]() |
447c3ab125 | ||
![]() |
8fac68386e |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -12,4 +12,5 @@ labels: bug
|
||||
## Useful information to include:
|
||||
- Minecraft version
|
||||
- CC: Tweaked version
|
||||
- Logs: These will be located in the `logs/` directory of your Minecraft instance. Please upload them as a gist or directly into this editor.
|
||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
||||
|
13
.github/workflows/main-ci.yml
vendored
13
.github/workflows/main-ci.yml
vendored
@@ -10,10 +10,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK 1.8
|
||||
- name: Set up Java 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
java-version: 8
|
||||
|
||||
- name: Cache gradle dependencies
|
||||
uses: actions/cache@v1
|
||||
@@ -32,12 +32,11 @@ jobs:
|
||||
name: CC-Tweaked
|
||||
path: build/libs
|
||||
|
||||
lint-lua:
|
||||
name: Lint Lua
|
||||
runs-on: ubuntu-latest
|
||||
- name: Upload Coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Generate Java documentation stubs
|
||||
run: ./gradlew luaJavadoc --no-daemon
|
||||
|
||||
- name: Lint Lua code
|
||||
run: |
|
||||
|
23
.github/workflows/make-doc.yml
vendored
23
.github/workflows/make-doc.yml
vendored
@@ -2,7 +2,9 @@ name: Build documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
- mc-1.15.x
|
||||
tags:
|
||||
release:
|
||||
types: [ published ]
|
||||
@@ -15,6 +17,25 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Set up Java 8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
|
||||
- name: Cache gradle dependencies
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('gradle.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
||||
|
||||
- name: Generate Java documentation stubs
|
||||
run: ./gradlew luaJavadoc --no-daemon
|
||||
|
||||
- name: Build documentation
|
||||
run: |
|
||||
test -d bin || mkdir bin
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/build
|
||||
/out
|
||||
/doc/**/*.html
|
||||
/doc/javadoc/
|
||||
/doc/index.json
|
||||
|
||||
# Runtime directories
|
||||
|
@@ -10,7 +10,7 @@ do use the issue templates - they provide a useful hint on what information to p
|
||||
|
||||
## Developing
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it. This is a pretty simple
|
||||
process.
|
||||
process. When building on Windows, Use `gradlew.bat` instead of `./gradlew`.
|
||||
|
||||
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
|
||||
- **Setup Forge:** `./gradlew build`
|
||||
@@ -29,8 +29,13 @@ are run whenever you submit a PR, it's often useful to run this before committin
|
||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. See [the usage section][illuaminate-usage] for
|
||||
how to download and run it.
|
||||
|
||||
## Translations
|
||||
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
|
||||
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
||||
|
||||
[new-issue]: https://github.com/SquidDev-CC/CC-Tweaked/issues/new/choose "Create a new issue"
|
||||
[community]: README.md#Community "Get in touch with the community."
|
||||
[checkstyle]: https://checkstyle.org/
|
||||
[illuaminate]: https://github.com/SquidDev/illuaminate/
|
||||
[illuaminate-usage]: https://github.com/SquidDev/illuaminate/blob/master/README.md#usage
|
||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/
|
||||
|
10
README.md
10
README.md
@@ -1,4 +1,4 @@
|
||||
# 
|
||||
# 
|
||||
[](https://github.com/SquidDev-CC/CC-Tweaked/actions "Current build status") [](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge")
|
||||
|
||||
CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
|
||||
@@ -50,12 +50,12 @@ I'd generally recommend you don't contact me directly (email, DM, etc...) unless
|
||||
report exploits). You'll get a far quicker response if you ask the whole community!
|
||||
|
||||
## Using
|
||||
If you want to depend on CC: Tweaked, we have a maven repo. However, you should be wary that some functionality is only
|
||||
exposed by CC:T's API and not vanilla ComputerCraft. If you wish to support all variations of ComputerCraft, I recommend
|
||||
using [cc.crzd.me's maven](https://cc.crzd.me/maven/) instead.
|
||||
CC: Tweaked is hosted on my maven repo, and so is relatively simple to depend on. You may wish to add a soft (or hard)
|
||||
dependency in your `mods.toml` file, with the appropriate version bounds, to ensure that API functionality you depend
|
||||
on is present.
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
repositories {
|
||||
maven { url 'https://squiddev.cc/maven/' }
|
||||
}
|
||||
|
||||
|
56
build.gradle
56
build.gradle
@@ -9,7 +9,7 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.google.code.gson:gson:2.8.1'
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.169'
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.181'
|
||||
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
|
||||
classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
|
||||
}
|
||||
@@ -17,6 +17,7 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
id "checkstyle"
|
||||
id "jacoco"
|
||||
id "com.github.hierynomus.license" version "0.15.0"
|
||||
id "com.matthewprenger.cursegradle" version "1.3.0"
|
||||
id "com.github.breadmoirai.github-release" version "2.2.4"
|
||||
@@ -32,6 +33,8 @@ version = mod_version
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked-${mc_version}"
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
|
||||
|
||||
minecraft {
|
||||
runs {
|
||||
client {
|
||||
@@ -77,11 +80,14 @@ minecraft {
|
||||
accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
}
|
||||
|
||||
sourceSets.main.resources {
|
||||
srcDir 'src/generated/resources'
|
||||
sourceSets {
|
||||
main.resources {
|
||||
srcDir 'src/generated/resources'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name "SquidDev"
|
||||
url "https://squiddev.cc/maven"
|
||||
@@ -92,6 +98,7 @@ configurations {
|
||||
shade
|
||||
compile.extendsFrom shade
|
||||
deployerJars
|
||||
cctJavadoc
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -102,14 +109,20 @@ dependencies {
|
||||
compileOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3:api")
|
||||
compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.15.2:6.0.0.9")
|
||||
|
||||
runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3")
|
||||
// runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.3")
|
||||
|
||||
compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
|
||||
shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
|
||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||
|
||||
deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0"
|
||||
|
||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.1.0'
|
||||
}
|
||||
|
||||
// Compile tasks
|
||||
@@ -118,6 +131,24 @@ javadoc {
|
||||
include "dan200/computercraft/api/**/*.java"
|
||||
}
|
||||
|
||||
task luaJavadoc(type: Javadoc) {
|
||||
description "Generates documentation for Java-side Lua functions."
|
||||
group "documentation"
|
||||
|
||||
source = sourceSets.main.allJava
|
||||
destinationDir = file("doc/javadoc")
|
||||
classpath = sourceSets.main.compileClasspath
|
||||
|
||||
options.docletpath = configurations.cctJavadoc.files as List
|
||||
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
|
||||
|
||||
// Attempt to run under Java 11 (any Java >= 9 will work though).
|
||||
if(System.getProperty("java.version").startsWith("1.")
|
||||
&& (System.getenv("JAVA_HOME_11_X64") != null || project.hasProperty("java11Home"))) {
|
||||
executable = "${System.getenv("JAVA_HOME_11_X64") ?: project.property("java11Home")}/bin/javadoc"
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn javadoc
|
||||
|
||||
@@ -144,8 +175,6 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.*
|
||||
import java.util.zip.*
|
||||
@@ -200,8 +229,6 @@ task proguardMove(dependsOn: proguard) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
processResources {
|
||||
inputs.property "version", mod_version
|
||||
inputs.property "mcversion", mc_version
|
||||
@@ -288,6 +315,15 @@ test {
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled true
|
||||
html.enabled true
|
||||
}
|
||||
}
|
||||
|
||||
check.dependsOn jacocoTestReport
|
||||
|
||||
license {
|
||||
mapping("java", "SLASHSTAR_STYLE")
|
||||
strictCheck true
|
||||
@@ -373,7 +409,7 @@ curseforge {
|
||||
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
|
||||
project {
|
||||
id = '282001'
|
||||
releaseType = 'alpha'
|
||||
releaseType = 'release'
|
||||
changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
|
||||
|
||||
relations {
|
||||
@@ -451,7 +487,7 @@ githubRelease {
|
||||
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
|
||||
.join("\n").trim()
|
||||
}
|
||||
prerelease true
|
||||
prerelease false
|
||||
}
|
||||
|
||||
def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"]
|
||||
|
@@ -114,11 +114,11 @@
|
||||
</module>
|
||||
<module name="ParameterName" />
|
||||
<module name="StaticVariableName">
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z]+)?$" />
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
||||
<property name="applyToPrivate" value="false" />
|
||||
</module>
|
||||
<module name="StaticVariableName">
|
||||
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z]+)?$" />
|
||||
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
||||
<property name="applyToPrivate" value="true" />
|
||||
</module>
|
||||
<module name="TypeName" />
|
||||
|
@@ -9,4 +9,7 @@
|
||||
|
||||
<!-- Do not check for missing package Javadoc. -->
|
||||
<suppress checks="JavadocStyle" files=".*[\\/]package-info.java" />
|
||||
|
||||
<!-- The commands API is documented in Lua. -->
|
||||
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
|
||||
</suppressions>
|
||||
|
@@ -1,6 +0,0 @@
|
||||
function exec(command) end
|
||||
function execAsync(commad) end
|
||||
function list() end
|
||||
function getBlockPosition() end
|
||||
function getBlockInfos(min_x, min_y, min_z, max_x, max_y, max_z) end
|
||||
function getBlockInfo(x, y, z) end
|
@@ -2,41 +2,33 @@
|
||||
--
|
||||
-- @module fs
|
||||
|
||||
function list(path) end
|
||||
function combine(base, child) end
|
||||
function getName(path) end
|
||||
function getSize(path) end
|
||||
function exists(path) end
|
||||
function isDir(path) end
|
||||
function isReadOnly(path) end
|
||||
function makeDir(path) end
|
||||
function move(from, to) end
|
||||
function copy(from, to) end
|
||||
function delete(path) end
|
||||
function open(path, mode) end
|
||||
function getDrive(path) end
|
||||
function getFreeSpace(path) end
|
||||
function find(pattern) end
|
||||
function getDir(path) end
|
||||
|
||||
--- A file handle which can be read from.
|
||||
--- Returns true if a path is mounted to the parent filesystem.
|
||||
--
|
||||
-- @type ReadHandle
|
||||
-- @see fs.open
|
||||
local ReadHandle = {}
|
||||
function ReadHandle.read(count) end
|
||||
function ReadHandle.readAll() end
|
||||
function ReadHandle.readLine(with_trailing) end
|
||||
function ReadHandle.seek(whence, offset) end
|
||||
function ReadHandle.close() end
|
||||
|
||||
--- A file handle which can be written to.
|
||||
-- The root filesystem "/" is considered a mount, along with disk folders and
|
||||
-- the rom folder. Other programs (such as network shares) can exstend this to
|
||||
-- make other mount types by correctly assigning their return value for getDrive.
|
||||
--
|
||||
-- @type WriteHandle
|
||||
-- @see fs.open
|
||||
local WriteHandle = {}
|
||||
function WriteHandle.write(text) end
|
||||
function WriteHandle.writeLine(text) end
|
||||
function WriteHandle.flush(text) end
|
||||
function WriteHandle.seek(whence, offset) end
|
||||
function WriteHandle.close() end
|
||||
-- @tparam string path The path to check.
|
||||
-- @treturn boolean If the path is mounted, rather than a normal file/folder.
|
||||
-- @throws If the path does not exist.
|
||||
-- @see getDrive
|
||||
function isDriveRoot(path) end
|
||||
|
||||
--[[- Provides completion for a file or directory name, suitable for use with
|
||||
@{read}.
|
||||
|
||||
When a directory is a possible candidate for completion, two entries are
|
||||
included - one with a trailing slash (indicating that entries within this
|
||||
directory exist) and one without it (meaning this entry is an immediate
|
||||
completion candidate). `include_dirs` can be set to @{false} to only include
|
||||
those with a trailing slash.
|
||||
|
||||
@tparam string path The path to complete.
|
||||
@tparam string location The location where paths are resolved from.
|
||||
@tparam[opt] boolean include_files When @{false}, only directories will be
|
||||
included in the returned list.
|
||||
@tparam[opt] boolean include_dirs When @{false}, "raw" directories will not be
|
||||
included in the returned list.
|
||||
@treturn { string... } A list of possible completion candidates.
|
||||
]]
|
||||
function complete(path, location, include_files, include_dirs) end
|
||||
|
@@ -93,47 +93,6 @@ function get(...) end
|
||||
-- @treturn Response|nil The failing http response, if available.
|
||||
function post(...) end
|
||||
|
||||
--- A http response. This acts very much like a @{fs.ReadHandle|file}, though
|
||||
-- provides some http specific methods.
|
||||
--
|
||||
-- #### `http_success` event
|
||||
-- #### `http_failure` event
|
||||
--
|
||||
-- @type Response
|
||||
-- @see http.request On how to make a http request.
|
||||
local Response = {}
|
||||
|
||||
--- Returns the response code and response message returned by the server
|
||||
--
|
||||
-- @treturn number The response code (i.e. 200)
|
||||
-- @treturn string The response message (i.e. "OK")
|
||||
function Response.getResponseCode() end
|
||||
|
||||
--- Get a table containing the response's headers, in a format similar to that
|
||||
-- required by @{http.request}. If multiple headers are sent with the same
|
||||
-- name, they will be combined with a comma.
|
||||
--
|
||||
-- @treturn { [string]=string } The response's headers.
|
||||
-- Make a request to [example.computercraft.cc](https://example.computercraft.cc),
|
||||
-- and print the returned headers.
|
||||
-- ```lua
|
||||
-- local request = http.get("https://example.computercraft.cc")
|
||||
-- print(textutils.serialize(request.getResponseHeaders()))
|
||||
-- -- => {
|
||||
-- -- [ "Content-Type" ] = "text/plain; charset=utf8",
|
||||
-- -- [ "content-length" ] = 17,
|
||||
-- -- ...
|
||||
-- -- }
|
||||
-- request.close()
|
||||
-- ```
|
||||
function Response.getResponseHeaders() end
|
||||
|
||||
function Response.read(count) end
|
||||
function Response.readAll() end
|
||||
function Response.readLine(with_trailing) end
|
||||
function Response.seek(whence, offset) end
|
||||
function Response.close() end
|
||||
|
||||
--- Asynchronously determine whether a URL can be requested.
|
||||
--
|
||||
-- If this returns `true`, one should also listen for [`http_check`
|
||||
@@ -198,32 +157,3 @@ function websocket(url, headers) end
|
||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
||||
-- of the initial websocket connection.
|
||||
function websocketAsync(url, headers) end
|
||||
|
||||
--- A websocket, which can be used to send an receive messages with a web
|
||||
-- server.
|
||||
--
|
||||
-- @type Websocket
|
||||
-- @see http.websocket On how to open a websocket.
|
||||
local Websocket = {}
|
||||
|
||||
--- Send a websocket message to the connected server.
|
||||
--
|
||||
-- @tparam string message The message to send.
|
||||
-- @tparam[opt] boolean binary Whether this message should be treated as a
|
||||
-- binary string, rather than encoded text.
|
||||
-- @throws If the websocket has been closed.
|
||||
function Websocket.send(message, binary) end
|
||||
|
||||
--- Wait for a message from the server.
|
||||
--
|
||||
-- @tparam[opt] number timeout The number of seconds to wait if no message is
|
||||
-- received.
|
||||
-- @treturn[1] string The received message.
|
||||
-- @treturn boolean If this was a binary message.
|
||||
-- @treturn[2] nil If the websocket was closed while waiting, or if we timed out.
|
||||
-- @throws If the websocket has been closed.
|
||||
function Websocket.receive(timeout) end
|
||||
|
||||
--- Close this websocket. This will terminate the connection, meaning messages
|
||||
-- can no longer be sent or received along it.
|
||||
function Websocket.close() end
|
||||
|
@@ -1,17 +1,6 @@
|
||||
function queueEvent(event, ...) end
|
||||
function startTimer(delay) end
|
||||
function setAlarm(time) end
|
||||
function shutdown() end
|
||||
function reboot() end
|
||||
function getComputerID() end
|
||||
computerID = getComputerID
|
||||
function setComputerLabel(label) end
|
||||
function getComputerLabel() end
|
||||
computerLabel = getComputerLabel
|
||||
function clock() end
|
||||
function time(timezone) end
|
||||
function day(timezone) end
|
||||
function cancelTimer(id) end
|
||||
function cancelAlarm(id) end
|
||||
function epoch(timezone) end
|
||||
function date(format, time) end
|
||||
-- Defined in bios.lua
|
||||
function loadAPI(path) end
|
||||
function pullEvent(filter) end
|
||||
function pullEventRaw(filter) end
|
||||
function version() end
|
||||
function run(env, path, ...) end
|
||||
|
@@ -1,14 +0,0 @@
|
||||
function getSides() end
|
||||
function setOutput(side, on) end
|
||||
function getOutput(side) end
|
||||
function getInput(side) end
|
||||
function setBundledOutput(side, output) end
|
||||
function getBundledOutput(side) end
|
||||
function getBundledInput(side) end
|
||||
function testBundledInput(side, mask) end
|
||||
function setAnalogOutput(side, value) end
|
||||
setAnalogueOutput = setAnalogOutput
|
||||
function getAnalogOutput(sid) end
|
||||
getAnalogueOutput = getAnalogOutput
|
||||
function getAnalogInput(side) end
|
||||
getAnalogueInput = getAnaloguInput
|
@@ -1,52 +0,0 @@
|
||||
function write(text) end
|
||||
function scroll(lines) end
|
||||
function setCursorPos(x, y) end
|
||||
function setCursorBlink(blink) end
|
||||
function getCursorPos() end
|
||||
function getSize() end
|
||||
function clear() end
|
||||
function clearLine() end
|
||||
function setTextColour(colour) end
|
||||
setTextColor = setTextColour
|
||||
function setBackgroundColour(colour) end
|
||||
setBackgroundColor = setBackgroundColour
|
||||
function isColour() end
|
||||
isColor = isColour
|
||||
function getTextColour() end
|
||||
getTextColor = getTextColor
|
||||
function getBackgroundColour() end
|
||||
getBackgroundColour = getBackgroundColour
|
||||
function blit(text, text_colours, background_colours) end
|
||||
function setPaletteColour(colour, ...) end
|
||||
setPaletteColour = setPaletteColour
|
||||
function getPaletteColour(colour, ...) end
|
||||
getPaletteColour = getPaletteColour
|
||||
function nativePaletteColour(colour) end
|
||||
nativePaletteColour = nativePaletteColour
|
||||
|
||||
--- @type Redirect
|
||||
local Redirect = {}
|
||||
|
||||
Redirect.write = write
|
||||
Redirect.scroll = scroll
|
||||
Redirect.setCursorPos = setCursorPos
|
||||
Redirect.setCursorBlink = setCursorBlink
|
||||
Redirect.getCursorPos = getCursorPos
|
||||
Redirect.getSize = getSize
|
||||
Redirect.clear = clear
|
||||
Redirect.clearLine = clearLine
|
||||
Redirect.setTextColour = setTextColour
|
||||
Redirect.setTextColor = setTextColor
|
||||
Redirect.setBackgroundColour = setBackgroundColour
|
||||
Redirect.setBackgroundColor = setBackgroundColor
|
||||
Redirect.isColour = isColour
|
||||
Redirect.isColor = isColor
|
||||
Redirect.getTextColour = getTextColour
|
||||
Redirect.getTextColor = getTextColor
|
||||
Redirect.getBackgroundColour = getBackgroundColour
|
||||
Redirect.getBackgroundColor = getBackgroundColor
|
||||
Redirect.blit = blit
|
||||
Redirect.setPaletteColour = setPaletteColour
|
||||
Redirect.setPaletteColor = setPaletteColor
|
||||
Redirect.getPaletteColour = getPaletteColour
|
||||
Redirect.getPaletteColor = getPaletteColor
|
@@ -1,230 +1 @@
|
||||
--- Move the turtle forward one block.
|
||||
-- @treturn boolean Whether the turtle could successfully move.
|
||||
-- @treturn string|nil The reason the turtle could not move.
|
||||
function forward() end
|
||||
|
||||
--- Move the turtle backwards one block.
|
||||
-- @treturn boolean Whether the turtle could successfully move.
|
||||
-- @treturn string|nil The reason the turtle could not move.
|
||||
function back() end
|
||||
|
||||
--- Move the turtle up one block.
|
||||
-- @treturn boolean Whether the turtle could successfully move.
|
||||
-- @treturn string|nil The reason the turtle could not move.
|
||||
function up() end
|
||||
|
||||
--- Move the turtle down one block.
|
||||
-- @treturn boolean Whether the turtle could successfully move.
|
||||
-- @treturn string|nil The reason the turtle could not move.
|
||||
function down() end
|
||||
|
||||
--- Rotate the turtle 90 degress to the left.
|
||||
function turnLeft() end
|
||||
|
||||
--- Rotate the turtle 90 degress to the right.
|
||||
function turnRight() end
|
||||
|
||||
--- Attempt to break the block in front of the turtle.
|
||||
--
|
||||
-- This requires a turtle tool capable of breaking the block. Diamond pickaxes
|
||||
-- (mining turtles) can break any vanilla block, but other tools (such as axes)
|
||||
-- are more limited.
|
||||
--
|
||||
-- @tparam[opt] "left"|"right" side The specific tool to use.
|
||||
-- @treturn boolean Whether a block was broken.
|
||||
-- @treturn string|nil The reason no block was broken.
|
||||
function dig(side) end
|
||||
|
||||
--- Attempt to break the block above the turtle. See @{dig} for full details.
|
||||
--
|
||||
-- @tparam[opt] "left"|"right" side The specific tool to use.
|
||||
-- @treturn boolean Whether a block was broken.
|
||||
-- @treturn string|nil The reason no block was broken.
|
||||
function digUp(side) end
|
||||
|
||||
--- Attempt to break the block below the turtle. See @{dig} for full details.
|
||||
--
|
||||
-- @tparam[opt] "left"|"right" side The specific tool to use.
|
||||
-- @treturn boolean Whether a block was broken.
|
||||
-- @treturn string|nil The reason no block was broken.
|
||||
function digDown(side) end
|
||||
|
||||
--- Attack the entity in front of the turtle.
|
||||
--
|
||||
-- @tparam[opt] "left"|"right" side The specific tool to use.
|
||||
-- @treturn boolean Whether an entity was attacked.
|
||||
-- @treturn string|nil The reason nothing was attacked.
|
||||
function attack(side) end
|
||||
|
||||
--- Attack the entity above the turtle.
|
||||
--
|
||||
-- @tparam[opt] "left"|"right" side The specific tool to use.
|
||||
-- @treturn boolean Whether an entity was attacked.
|
||||
-- @treturn string|nil The reason nothing was attacked.
|
||||
function attackUp(side) end
|
||||
|
||||
--- Attack the entity below the turtle.
|
||||
--
|
||||
-- @tparam[opt] "left"|"right" side The specific tool to use.
|
||||
-- @treturn boolean Whether an entity was attacked.
|
||||
-- @treturn string|nil The reason nothing was attacked.
|
||||
function attackDown(side) end
|
||||
|
||||
--- Place a block or item into the world in front of the turtle.
|
||||
--
|
||||
-- @treturn boolean Whether the block could be placed.
|
||||
-- @treturn string|nil The reason the block was not placed.
|
||||
function place() end
|
||||
|
||||
--- Place a block or item into the world above the turtle.
|
||||
--
|
||||
-- @treturn boolean Whether the block could be placed.
|
||||
-- @treturn string|nil The reason the block was not placed.
|
||||
function placeUp() end
|
||||
|
||||
--- Place a block or item into the world below the turtle.
|
||||
--
|
||||
-- @treturn boolean Whether the block could be placed.
|
||||
-- @treturn string|nil The reason the block was not placed.
|
||||
function placeDown() end
|
||||
|
||||
--- Drop the currently selected stack into the inventory in front of the turtle,
|
||||
-- or as an item into the world if there is no inventory.
|
||||
--
|
||||
-- @tparam[opt] number count The number of items to drop. If not given, the
|
||||
-- entire stack will be dropped.
|
||||
-- @treturn boolean Whether items were dropped.
|
||||
-- @treturn string|nil The reason the no items were dropped.
|
||||
-- @see select
|
||||
function drop(count) end
|
||||
|
||||
--- Drop the currently selected stack into the inventory above the turtle, or as
|
||||
-- an item into the world if there is no inventory.
|
||||
--
|
||||
-- @tparam[opt] number count The number of items to drop. If not given, the
|
||||
-- entire stack will be dropped.
|
||||
-- @treturn boolean Whether items were dropped.
|
||||
-- @treturn string|nil The reason the no items were dropped.
|
||||
-- @see select
|
||||
function dropUp(count) end
|
||||
|
||||
--- Drop the currently selected stack into the inventory below the turtle, or as
|
||||
-- an item into the world if there is no inventory.
|
||||
--
|
||||
-- @tparam[opt] number count The number of items to drop. If not given, the
|
||||
-- entire stack will be dropped.
|
||||
-- @treturn boolean Whether items were dropped.
|
||||
-- @treturn string|nil The reason the no items were dropped.
|
||||
-- @see select
|
||||
function dropDown(count) end
|
||||
|
||||
--- Suck an item from the inventory in front of the turtle, or from an item
|
||||
-- floating in the world.
|
||||
--
|
||||
-- This will pull items into the first acceptable slot, starting at the
|
||||
-- @{select|currently selected} one.
|
||||
--
|
||||
-- @tparam[opt] number count The number of items to suck. If not given, up to a
|
||||
-- stack of items will be picked up.
|
||||
-- @treturn boolean Whether items were picked up.
|
||||
-- @treturn string|nil The reason the no items were picked up.
|
||||
function suck(count) end
|
||||
|
||||
--- Suck an item from the inventory above the turtle, or from an item floating
|
||||
-- in the world.
|
||||
--
|
||||
-- @tparam[opt] number count The number of items to suck. If not given, up to a
|
||||
-- stack of items will be picked up.
|
||||
-- @treturn boolean Whether items were picked up.
|
||||
-- @treturn string|nil The reason the no items were picked up.
|
||||
function suckUp(count) end
|
||||
|
||||
--- Suck an item from the inventory below the turtle, or from an item floating
|
||||
-- in the world.
|
||||
--
|
||||
-- @tparam[opt] number count The number of items to suck. If not given, up to a
|
||||
-- stack of items will be picked up.
|
||||
-- @treturn boolean Whether items were picked up.
|
||||
-- @treturn string|nil The reason the no items were picked up.
|
||||
function suckDown(count) end
|
||||
|
||||
--- Check if there is a solid block in front of the turtle. In this case, solid
|
||||
-- refers to any non-air or liquid block.
|
||||
--
|
||||
-- @treturn boolean If there is a solid block in front.
|
||||
function detect() end
|
||||
|
||||
--- Check if there is a solid block above the turtle.
|
||||
--
|
||||
-- @treturn boolean If there is a solid block above.
|
||||
function detectUp() end
|
||||
|
||||
--- Check if there is a solid block below the turtle.
|
||||
--
|
||||
-- @treturn boolean If there is a solid block below.
|
||||
function detectDown() end
|
||||
|
||||
function compare() end
|
||||
function compareUp() end
|
||||
function compareDown() end
|
||||
|
||||
function inspect() end
|
||||
function inspectUp() end
|
||||
function inspectDown() end
|
||||
|
||||
|
||||
--- Change the currently selected slot.
|
||||
--
|
||||
-- The selected slot is determines what slot actions like @{drop} or
|
||||
-- @{getItemCount} act on.
|
||||
--
|
||||
-- @tparam number slot The slot to select.
|
||||
-- @see getSelectedSlot
|
||||
function select(slot) end
|
||||
|
||||
--- Get the currently selected slot.
|
||||
--
|
||||
-- @treturn number The current slot.
|
||||
-- @see select
|
||||
function getSelectedSlot() end
|
||||
|
||||
--- Get the number of items in the given slot.
|
||||
--
|
||||
-- @tparam[opt] number slot The slot we wish to check. Defaults to the @{turtle.select|selected slot}.
|
||||
-- @treturn number The number of items in this slot.
|
||||
function getItemCount(slot) end
|
||||
|
||||
--- Get the remaining number of items which may be stored in this stack.
|
||||
--
|
||||
-- For instance, if a slot contains 13 blocks of dirt, it has room for another 51.
|
||||
--
|
||||
-- @tparam[opt] number slot The slot we wish to check. Defaults to the @{turtle.select|selected slot}.
|
||||
-- @treturn number The space left in this slot.
|
||||
function getItemSpace(slot) end
|
||||
|
||||
|
||||
--- Get detailed information about the items in the given slot.
|
||||
--
|
||||
-- @tparam[opt] number slot The slot to get information about. Defaults to the @{turtle.select|selected slot}.
|
||||
-- @treturn nil|table Information about the given slot, or @{nil} if it is empty.
|
||||
-- @usage Print the current slot, assuming it contains 13 dirt.
|
||||
--
|
||||
-- print(textutils.serialize(turtle.getItemDetail()))
|
||||
-- -- => {
|
||||
-- -- name = "minecraft:dirt",
|
||||
-- -- damage = 0,
|
||||
-- -- count = 13,
|
||||
-- -- }
|
||||
function getItemDetail(slot) end
|
||||
|
||||
function getFuelLevel() end
|
||||
|
||||
function refuel(count) end
|
||||
function compareTo(slot) end
|
||||
function transferTo(slot, count) end
|
||||
|
||||
function getFuelLimit() end
|
||||
function equipLeft() end
|
||||
function equipRight() end
|
||||
|
||||
function craft(limit) end
|
||||
|
@@ -51,7 +51,12 @@ h4 { font-size: 1.06em; }
|
||||
a, a:visited, a:active { font-weight: bold; color: #004080; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
blockquote {
|
||||
padding: 0.3em;
|
||||
margin: 1em 0;
|
||||
background: #f0f0f0;
|
||||
border-left: solid 0.5em #ccc;
|
||||
}
|
||||
|
||||
/* Stop sublists from having initial vertical space */
|
||||
ul ul { margin-top: 0px; }
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Mod properties
|
||||
mod_version=1.87.1
|
||||
mod_version=1.93.0
|
||||
|
||||
# Minecraft properties (update mods.toml when changing)
|
||||
mc_version=1.15.2
|
||||
forge_version=31.1.41
|
||||
mappings_version=20200410-1.15.1
|
||||
mc_version=1.16.3
|
||||
forge_version=34.0.1
|
||||
mappings_version=20200723-1.16.1
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
(sources
|
||||
/doc/stub/
|
||||
/doc/javadoc/
|
||||
/src/main/resources/*/computercraft/lua/bios.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/
|
||||
/src/test/resources/test-rom)
|
||||
@@ -12,8 +13,12 @@
|
||||
(index doc/index.md)
|
||||
(source-link https://github.com/SquidDev-CC/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||
|
||||
(module-kinds
|
||||
(peripheral Peripherals))
|
||||
|
||||
(library-path
|
||||
/doc/stub/
|
||||
/doc/javadoc/
|
||||
|
||||
/src/main/resources/*/computercraft/lua/rom/apis
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/command
|
||||
@@ -33,17 +38,27 @@
|
||||
|
||||
;; It's useful to name arguments for documentation, so we allow this. It'd
|
||||
;; be good to find a compromise in the future, but this works for now.
|
||||
-var:unused-arg
|
||||
-var:unused-arg)
|
||||
|
||||
;; Some APIS (keys, colour and os mainly) are incomplete right now.
|
||||
-var:unresolved-member)
|
||||
(lint
|
||||
(bracket-spaces
|
||||
(call no-space)
|
||||
(function-args no-space)
|
||||
(parens no-space)
|
||||
(table space)
|
||||
(index no-space))))
|
||||
(index no-space))
|
||||
|
||||
;; colours imports from colors, and we don't handle that right now.
|
||||
;; keys is entirely dynamic, so we skip it.
|
||||
(dynamic-modules colours keys)
|
||||
|
||||
(globals
|
||||
:max
|
||||
_CC_DEFAULT_SETTINGS
|
||||
_CC_DISABLE_LUA51_FEATURES
|
||||
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
|
||||
;; isn't smart enough.
|
||||
sleep write printError read rs)))
|
||||
|
||||
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
||||
;; hopefully we'll get illuaminate to handle this.
|
||||
@@ -54,31 +69,42 @@
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
;; Silence some variable warnings in documentation stubs.
|
||||
(at /doc/stub
|
||||
(at (/doc/stub/ /doc/javadoc/)
|
||||
(linters -var:unused-global)
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
;; Suppress warnings for currently undocumented modules.
|
||||
(at
|
||||
(/doc/stub/commands.lua
|
||||
/doc/stub/fs.lua
|
||||
(; Java APIs
|
||||
/doc/stub/http.lua
|
||||
/doc/stub/os.lua
|
||||
/doc/stub/redstone.lua
|
||||
/doc/stub/term.lua
|
||||
/doc/stub/turtle.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/command/commands.lua
|
||||
; Java generated APIs
|
||||
/doc/javadoc/turtle.lua
|
||||
; Peripherals
|
||||
/doc/javadoc/drive.lua
|
||||
/doc/javadoc/speaker.lua
|
||||
/doc/javadoc/printer.lua
|
||||
; Lua APIs
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/io.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/window.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua)
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/window.lua)
|
||||
|
||||
(linters -doc:undocumented -doc:undocumented-arg))
|
||||
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
||||
|
||||
;; These currently rely on unknown references.
|
||||
(at
|
||||
(/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/programs/advanced/multishell.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/programs/shell.lua)
|
||||
/src/main/resources/*/computercraft/lua/rom/programs/shell.lua
|
||||
/doc/stub/fs.lua)
|
||||
(linters -doc:unresolved-reference))
|
||||
|
||||
(at /src/test/resources/test-rom
|
||||
; We should still be able to test deprecated members.
|
||||
(linters -var:deprecated)
|
||||
|
||||
(lint
|
||||
(globals
|
||||
:max sleep write
|
||||
cct_test describe expect howlci fail it pending stub)))
|
||||
|
@@ -17,6 +17,9 @@
|
||||
{
|
||||
"condition": "computercraft:block_named"
|
||||
},
|
||||
{
|
||||
"condition": "computercraft:has_id"
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:inverted",
|
||||
"term": {
|
||||
|
@@ -17,6 +17,9 @@
|
||||
{
|
||||
"condition": "computercraft:block_named"
|
||||
},
|
||||
{
|
||||
"condition": "computercraft:has_id"
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:inverted",
|
||||
"term": {
|
||||
|
@@ -17,6 +17,9 @@
|
||||
{
|
||||
"condition": "computercraft:block_named"
|
||||
},
|
||||
{
|
||||
"condition": "computercraft:has_id"
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:inverted",
|
||||
"term": {
|
||||
|
@@ -17,6 +17,9 @@
|
||||
{
|
||||
"condition": "computercraft:block_named"
|
||||
},
|
||||
{
|
||||
"condition": "computercraft:has_id"
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:inverted",
|
||||
"term": {
|
||||
|
1
src/generated/resources/data/computercraft/loot_tables/treasure_disk.json
generated
Normal file
1
src/generated/resources/data/computercraft/loot_tables/treasure_disk.json
generated
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
10
src/generated/resources/data/minecraft/tags/items/piglin_loved.json
generated
Normal file
10
src/generated/resources/data/minecraft/tags/items/piglin_loved.json
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"computercraft:computer_advanced",
|
||||
"computercraft:turtle_advanced",
|
||||
"computercraft:wireless_modem_advanced",
|
||||
"computercraft:pocket_computer_advanced",
|
||||
"computercraft:monitor_advanced"
|
||||
]
|
||||
}
|
@@ -6,39 +6,22 @@
|
||||
package dan200.computercraft;
|
||||
|
||||
import dan200.computercraft.api.turtle.event.TurtleAction;
|
||||
import dan200.computercraft.core.apis.http.AddressRule;
|
||||
import dan200.computercraft.core.apis.http.options.Action;
|
||||
import dan200.computercraft.core.apis.http.options.AddressRule;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import dan200.computercraft.shared.Config;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||
import dan200.computercraft.shared.media.items.ItemDisk;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.BlockDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockWiredModemFull;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.ItemBlockCable;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.BlockWirelessModem;
|
||||
import dan200.computercraft.shared.peripheral.monitor.BlockMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
|
||||
import dan200.computercraft.shared.peripheral.printer.BlockPrinter;
|
||||
import dan200.computercraft.shared.peripheral.speaker.BlockSpeaker;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||
import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
|
||||
import dan200.computercraft.shared.turtle.items.ItemTurtle;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import net.minecraft.resources.IReloadableResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import dan200.computercraft.shared.util.ServiceUtil;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@@ -52,12 +35,11 @@ public final class ComputerCraft
|
||||
{
|
||||
public static final String MOD_ID = "computercraft";
|
||||
|
||||
public static final int DATAFIXER_VERSION = 0;
|
||||
|
||||
// Configuration options
|
||||
public static final String[] DEFAULT_HTTP_ALLOW = new String[] { "*" };
|
||||
public static final String[] DEFAULT_HTTP_DENY = new String[] {
|
||||
"127.0.0.0/8",
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16",
|
||||
@@ -67,36 +49,39 @@ public final class ComputerCraft
|
||||
public static int computerSpaceLimit = 1000 * 1000;
|
||||
public static int floppySpaceLimit = 125 * 1000;
|
||||
public static int maximumFilesOpen = 128;
|
||||
public static boolean disable_lua51_features = false;
|
||||
public static String default_computer_settings = "";
|
||||
public static boolean debug_enable = true;
|
||||
public static boolean logPeripheralErrors = false;
|
||||
public static boolean disableLua51Features = false;
|
||||
public static String defaultComputerSettings = "";
|
||||
public static boolean debugEnable = true;
|
||||
public static boolean logComputerErrors = true;
|
||||
public static boolean commandRequireCreative = true;
|
||||
|
||||
public static int computer_threads = 1;
|
||||
public static int computerThreads = 1;
|
||||
public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( 10 );
|
||||
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( 5 );
|
||||
|
||||
public static boolean httpEnabled = true;
|
||||
public static boolean httpWebsocketEnabled = true;
|
||||
public static List<AddressRule> httpRules = Collections.unmodifiableList( Stream.concat(
|
||||
Stream.of( DEFAULT_HTTP_DENY ).map( x -> AddressRule.parse( x, AddressRule.Action.DENY ) ).filter( Objects::nonNull ),
|
||||
Stream.of( DEFAULT_HTTP_ALLOW ).map( x -> AddressRule.parse( x, AddressRule.Action.ALLOW ) ).filter( Objects::nonNull )
|
||||
Stream.of( DEFAULT_HTTP_DENY )
|
||||
.map( x -> AddressRule.parse( x, null, Action.DENY.toPartial() ) )
|
||||
.filter( Objects::nonNull ),
|
||||
Stream.of( DEFAULT_HTTP_ALLOW )
|
||||
.map( x -> AddressRule.parse( x, null, Action.ALLOW.toPartial() ) )
|
||||
.filter( Objects::nonNull )
|
||||
).collect( Collectors.toList() ) );
|
||||
|
||||
public static int httpTimeout = 30000;
|
||||
public static int httpMaxRequests = 16;
|
||||
public static long httpMaxDownload = 16 * 1024 * 1024;
|
||||
public static long httpMaxUpload = 4 * 1024 * 1024;
|
||||
public static int httpMaxWebsockets = 4;
|
||||
public static int httpMaxWebsocketMessage = 128 * 1024;
|
||||
|
||||
public static boolean enableCommandBlock = false;
|
||||
public static int modem_range = 64;
|
||||
public static int modem_highAltitudeRange = 384;
|
||||
public static int modem_rangeDuringStorm = 64;
|
||||
public static int modem_highAltitudeRangeDuringStorm = 384;
|
||||
public static int modemRange = 64;
|
||||
public static int modemHighAltitudeRange = 384;
|
||||
public static int modemRangeDuringStorm = 64;
|
||||
public static int modemHighAltitudeRangeDuringStorm = 384;
|
||||
public static int maxNotesPerTick = 8;
|
||||
public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST;
|
||||
public static double monitorDistanceSq = 4096;
|
||||
public static long monitorBandwidth = 1_000_000;
|
||||
|
||||
public static boolean turtlesNeedFuel = true;
|
||||
public static int turtleFuelLimit = 20000;
|
||||
@@ -105,61 +90,19 @@ public final class ComputerCraft
|
||||
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;
|
||||
public static boolean genericPeripheral = false;
|
||||
|
||||
public static final int terminalWidth_turtle = 39;
|
||||
public static final int terminalHeight_turtle = 13;
|
||||
public static int computerTermWidth = 51;
|
||||
public static int computerTermHeight = 19;
|
||||
|
||||
public static final int terminalWidth_pocketComputer = 26;
|
||||
public static final int terminalHeight_pocketComputer = 20;
|
||||
public static final int turtleTermWidth = 39;
|
||||
public static final int turtleTermHeight = 13;
|
||||
|
||||
// Blocks and Items
|
||||
public static final class Blocks
|
||||
{
|
||||
public static BlockComputer computerNormal;
|
||||
public static BlockComputer computerAdvanced;
|
||||
public static BlockComputer computerCommand;
|
||||
public static int pocketTermWidth = 26;
|
||||
public static int pocketTermHeight = 20;
|
||||
|
||||
public static BlockTurtle turtleNormal;
|
||||
public static BlockTurtle turtleAdvanced;
|
||||
|
||||
public static BlockSpeaker speaker;
|
||||
public static BlockDiskDrive diskDrive;
|
||||
public static BlockPrinter printer;
|
||||
|
||||
public static BlockMonitor monitorNormal;
|
||||
public static BlockMonitor monitorAdvanced;
|
||||
|
||||
public static BlockWirelessModem wirelessModemNormal;
|
||||
public static BlockWirelessModem wirelessModemAdvanced;
|
||||
|
||||
public static BlockWiredModemFull wiredModemFull;
|
||||
public static BlockCable cable;
|
||||
}
|
||||
|
||||
public static final class Items
|
||||
{
|
||||
public static ItemComputer computerNormal;
|
||||
public static ItemComputer computerAdvanced;
|
||||
public static ItemComputer computerCommand;
|
||||
|
||||
public static ItemPocketComputer pocketComputerNormal;
|
||||
public static ItemPocketComputer pocketComputerAdvanced;
|
||||
|
||||
public static ItemTurtle turtleNormal;
|
||||
public static ItemTurtle turtleAdvanced;
|
||||
|
||||
public static ItemDisk disk;
|
||||
public static ItemTreasureDisk treasureDisk;
|
||||
|
||||
public static ItemPrintout printedPage;
|
||||
public static ItemPrintout printedPages;
|
||||
public static ItemPrintout printedBook;
|
||||
|
||||
public static ItemBlockCable.Cable cable;
|
||||
public static ItemBlockCable.WiredModem wiredModem;
|
||||
}
|
||||
public static int monitorWidth = 8;
|
||||
public static int monitorHeight = 6;
|
||||
|
||||
public static final class TurtleUpgrades
|
||||
{
|
||||
@@ -191,24 +134,8 @@ public final class ComputerCraft
|
||||
|
||||
public ComputerCraft()
|
||||
{
|
||||
Config.load();
|
||||
}
|
||||
|
||||
public static String getVersion()
|
||||
{
|
||||
return "${version}";
|
||||
}
|
||||
|
||||
public static InputStream getResourceFile( String domain, String subPath )
|
||||
{
|
||||
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();
|
||||
try
|
||||
{
|
||||
return manager.getResource( new ResourceLocation( domain, subPath ) ).getInputStream();
|
||||
}
|
||||
catch( IOException ignored )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Config.setup();
|
||||
Registry.setup();
|
||||
GenericSource.setup( () -> ServiceUtil.loadServicesForge( GenericSource.class ) );
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
package dan200.computercraft;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
@@ -24,7 +23,6 @@ import dan200.computercraft.core.filesystem.ResourceMount;
|
||||
import dan200.computercraft.shared.*;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.wired.CapabilityWiredElement;
|
||||
import dan200.computercraft.shared.wired.WiredNode;
|
||||
import net.minecraft.resources.IReloadableResourceManager;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
@@ -34,29 +32,47 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT;
|
||||
|
||||
public final class ComputerCraftAPIImpl implements IComputerCraftAPI
|
||||
{
|
||||
public static final ComputerCraftAPIImpl INSTANCE = new ComputerCraftAPIImpl();
|
||||
|
||||
private String version;
|
||||
|
||||
private ComputerCraftAPIImpl()
|
||||
{
|
||||
}
|
||||
|
||||
private WeakReference<IReloadableResourceManager> currentResources;
|
||||
private final Map<ResourceLocation, ResourceMount> mountCache = new MapMaker().weakValues().concurrencyLevel( 1 ).makeMap();
|
||||
public static InputStream getResourceFile( String domain, String subPath )
|
||||
{
|
||||
IReloadableResourceManager manager = (IReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getDataPackRegistries().getResourceManager();
|
||||
try
|
||||
{
|
||||
return manager.getResource( new ResourceLocation( domain, subPath ) ).getInputStream();
|
||||
}
|
||||
catch( IOException ignored )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getInstalledVersion()
|
||||
{
|
||||
return "${version}";
|
||||
if( version != null ) return version;
|
||||
return version = ModList.get().getModContainerById( ComputerCraft.MOD_ID )
|
||||
.map( x -> x.getModInfo().getVersion().toString() )
|
||||
.orElse( "unknown" );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,7 +97,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
|
||||
@Override
|
||||
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath )
|
||||
{
|
||||
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();
|
||||
IReloadableResourceManager manager = (IReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getDataPackRegistries().getResourceManager();
|
||||
ResourceMount mount = ResourceMount.get( domain, subPath, manager );
|
||||
return mount.exists( "" ) ? mount : null;
|
||||
}
|
||||
@@ -147,6 +163,6 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
|
||||
public LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull Direction side )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
return tile == null ? LazyOptional.empty() : tile.getCapability( CapabilityWiredElement.CAPABILITY, side );
|
||||
return tile == null ? LazyOptional.empty() : tile.getCapability( CAPABILITY_WIRED_ELEMENT, side );
|
||||
}
|
||||
}
|
||||
|
@@ -43,9 +43,10 @@ public final class ComputerCraftAPI
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
public static String getAPIVersion()
|
||||
{
|
||||
return "${version}";
|
||||
return getInstalledVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -6,11 +6,11 @@
|
||||
package dan200.computercraft.api.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.TransformationMatrix;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.ModelManager;
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Objects;
|
||||
|
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides methods for extracting values and validating Lua arguments, such as those provided to
|
||||
* {@link ILuaObject#callMethod(ILuaContext, int, Object[])} or
|
||||
* {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}.
|
||||
*
|
||||
* This provides two sets of functions: the {@code get*} methods, which require an argument to be valid, and
|
||||
* {@code opt*}, which accept a default value and return that if the argument was not present or was {@code null}.
|
||||
* If the argument is of the wrong type, a suitable error message will be thrown, with a similar format to Lua's own
|
||||
* error messages.
|
||||
*
|
||||
* <h2>Example usage:</h2>
|
||||
* <pre>
|
||||
* {@code
|
||||
* int slot = getInt( args, 0 );
|
||||
* int amount = optInt( args, 1, 64 );
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public final class ArgumentHelper
|
||||
{
|
||||
private ArgumentHelper()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string representation of the given value's type.
|
||||
*
|
||||
* @param value The value whose type we are trying to compute.
|
||||
* @return A string representation of the given value's type, in a similar format to that provided by Lua's
|
||||
* {@code type} function.
|
||||
*/
|
||||
@Nonnull
|
||||
public static String getType( @Nullable Object value )
|
||||
{
|
||||
if( value == null ) return "nil";
|
||||
if( value instanceof String ) return "string";
|
||||
if( value instanceof Boolean ) return "boolean";
|
||||
if( value instanceof Number ) return "number";
|
||||
if( value instanceof Map ) return "table";
|
||||
return "userdata";
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a "bad argument" exception, from an expected type and the actual value provided.
|
||||
*
|
||||
* @param index The argument number, starting from 0.
|
||||
* @param expected The expected type for this argument.
|
||||
* @param actual The actual value provided for this argument.
|
||||
* @return The constructed exception, which should be thrown immediately.
|
||||
*/
|
||||
@Nonnull
|
||||
public static LuaException badArgumentOf( int index, @Nonnull String expected, @Nullable Object actual )
|
||||
{
|
||||
return badArgument( index, expected, getType( actual ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a "bad argument" exception, from an expected and actual type.
|
||||
*
|
||||
* @param index The argument number, starting from 0.
|
||||
* @param expected The expected type for this argument.
|
||||
* @param actual The provided type for this argument.
|
||||
* @return The constructed exception, which should be thrown immediately.
|
||||
*/
|
||||
@Nonnull
|
||||
public static LuaException badArgument( int index, @Nonnull String expected, @Nonnull String actual )
|
||||
{
|
||||
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a double.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a number.
|
||||
* @see #getFiniteDouble(Object[], int) if you require this to be finite (i.e. not infinite or NaN).
|
||||
*/
|
||||
public static double getDouble( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
if( index >= args.length ) throw badArgument( index, "number", "nil" );
|
||||
Object value = args[index];
|
||||
if( !(value instanceof Number) ) throw badArgumentOf( index, "number", value );
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as an integer.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not an integer.
|
||||
*/
|
||||
public static int getInt( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
return (int) getLong( args, index );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a long.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a long.
|
||||
*/
|
||||
public static long getLong( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
if( index >= args.length ) throw badArgument( index, "number", "nil" );
|
||||
Object value = args[index];
|
||||
if( !(value instanceof Number) ) throw badArgumentOf( index, "number", value );
|
||||
return checkFinite( index, (Number) value ).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a finite number (not infinite or NaN).
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
public static double getFiniteDouble( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
return checkFinite( index, getDouble( args, index ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a boolean.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
public static boolean getBoolean( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
if( index >= args.length ) throw badArgument( index, "boolean", "nil" );
|
||||
Object value = args[index];
|
||||
if( !(value instanceof Boolean) ) throw badArgumentOf( index, "boolean", value );
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a string.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
@Nonnull
|
||||
public static String getString( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
if( index >= args.length ) throw badArgument( index, "string", "nil" );
|
||||
Object value = args[index];
|
||||
if( !(value instanceof String) ) throw badArgumentOf( index, "string", value );
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a table.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
@Nonnull
|
||||
public static Map<?, ?> getTable( @Nonnull Object[] args, int index ) throws LuaException
|
||||
{
|
||||
if( index >= args.length ) throw badArgument( index, "table", "nil" );
|
||||
Object value = args[index];
|
||||
if( !(value instanceof Map) ) throw badArgumentOf( index, "table", value );
|
||||
return (Map<?, ?>) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a double.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
public static double optDouble( @Nonnull Object[] args, int index, double def ) throws LuaException
|
||||
{
|
||||
Object value = index < args.length ? args[index] : null;
|
||||
if( value == null ) return def;
|
||||
if( !(value instanceof Number) ) throw badArgumentOf( index, "number", value );
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as an int.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
public static int optInt( @Nonnull Object[] args, int index, int def ) throws LuaException
|
||||
{
|
||||
return (int) optLong( args, index, def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a long.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
public static long optLong( @Nonnull Object[] args, int index, long def ) throws LuaException
|
||||
{
|
||||
Object value = index < args.length ? args[index] : null;
|
||||
if( value == null ) return def;
|
||||
if( !(value instanceof Number) ) throw badArgumentOf( index, "number", value );
|
||||
return checkFinite( index, (Number) value ).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a finite number (not infinite or NaN).
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
public static double optFiniteDouble( @Nonnull Object[] args, int index, double def ) throws LuaException
|
||||
{
|
||||
return checkFinite( index, optDouble( args, index, def ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a boolean.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
public static boolean optBoolean( @Nonnull Object[] args, int index, boolean def ) throws LuaException
|
||||
{
|
||||
Object value = index < args.length ? args[index] : null;
|
||||
if( value == null ) return def;
|
||||
if( !(value instanceof Boolean) ) throw badArgumentOf( index, "boolean", value );
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a string.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
public static String optString( @Nonnull Object[] args, int index, String def ) throws LuaException
|
||||
{
|
||||
Object value = index < args.length ? args[index] : null;
|
||||
if( value == null ) return def;
|
||||
if( !(value instanceof String) ) throw badArgumentOf( index, "string", value );
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a table.
|
||||
*
|
||||
* @param args The arguments to extract from.
|
||||
* @param index The index into the argument array to read from.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
public static Map<?, ?> optTable( @Nonnull Object[] args, int index, Map<Object, Object> def ) throws LuaException
|
||||
{
|
||||
Object value = index < args.length ? args[index] : null;
|
||||
if( value == null ) return def;
|
||||
if( !(value instanceof Map) ) throw badArgumentOf( index, "table", value );
|
||||
return (Map<?, ?>) value;
|
||||
}
|
||||
|
||||
private static Number checkFinite( int index, Number value ) throws LuaException
|
||||
{
|
||||
checkFinite( index, value.doubleValue() );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static double checkFinite( int index, double value ) throws LuaException
|
||||
{
|
||||
if( !Double.isFinite( value ) ) throw badArgument( index, "number", getNumericType( value ) );
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a more detailed representation of this number's type. If this is finite, it will just return "number",
|
||||
* otherwise it returns whether it is infinite or NaN.
|
||||
*
|
||||
* @param value The value to extract the type for.
|
||||
* @return This value's numeric type.
|
||||
*/
|
||||
@Nonnull
|
||||
public static String getNumericType( double value )
|
||||
{
|
||||
if( Double.isNaN( value ) ) return "nan";
|
||||
if( value == Double.POSITIVE_INFINITY ) return "inf";
|
||||
if( value == Double.NEGATIVE_INFINITY ) return "-inf";
|
||||
return "number";
|
||||
}
|
||||
}
|
407
src/main/java/dan200/computercraft/api/lua/IArguments.java
Normal file
407
src/main/java/dan200/computercraft/api/lua/IArguments.java
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
|
||||
/**
|
||||
* The arguments passed to a function.
|
||||
*/
|
||||
public interface IArguments
|
||||
{
|
||||
/**
|
||||
* Get the number of arguments passed to this function.
|
||||
*
|
||||
* @return The number of passed arguments.
|
||||
*/
|
||||
int count();
|
||||
|
||||
/**
|
||||
* Get the argument at the specific index. The returned value must obey the following conversion rules:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Lua values of type "string" will be represented by a {@link String}.</li>
|
||||
* <li>Lua values of type "number" will be represented by a {@link Number}.</li>
|
||||
* <li>Lua values of type "boolean" will be represented by a {@link Boolean}.</li>
|
||||
* <li>Lua values of type "table" will be represented by a {@link Map}.</li>
|
||||
* <li>Lua values of any other type will be represented by a {@code null} value.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@code null} if not present.
|
||||
*/
|
||||
@Nullable
|
||||
Object get( int index );
|
||||
|
||||
/**
|
||||
* Drop a number of arguments. The returned arguments instance will access arguments at position {@code i + count},
|
||||
* rather than {@code i}. However, errors will still use the given argument index.
|
||||
*
|
||||
* @param count The number of arguments to drop.
|
||||
* @return The new {@link IArguments} instance.
|
||||
*/
|
||||
IArguments drop( int count );
|
||||
|
||||
default Object[] getAll()
|
||||
{
|
||||
Object[] result = new Object[count()];
|
||||
for( int i = 0; i < result.length; i++ ) result[i] = get( i );
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a double.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a number.
|
||||
* @see #getFiniteDouble(int) if you require this to be finite (i.e. not infinite or NaN).
|
||||
*/
|
||||
default double getDouble( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as an integer.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not an integer.
|
||||
*/
|
||||
default int getInt( int index ) throws LuaException
|
||||
{
|
||||
return (int) getLong( index );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a long.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a long.
|
||||
*/
|
||||
default long getLong( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
|
||||
return LuaValues.checkFiniteNum( index, (Number) value ).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a finite number (not infinite or NaN).
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
default double getFiniteDouble( int index ) throws LuaException
|
||||
{
|
||||
return checkFinite( index, getDouble( index ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a boolean.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
default boolean getBoolean( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( !(value instanceof Boolean) ) throw LuaValues.badArgumentOf( index, "boolean", value );
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a string.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
@Nonnull
|
||||
default String getString( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( !(value instanceof String) ) throw LuaValues.badArgumentOf( index, "string", value );
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string argument as a byte array.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value. This is a <em>read only</em> buffer.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
@Nonnull
|
||||
default ByteBuffer getBytes( int index ) throws LuaException
|
||||
{
|
||||
return LuaValues.encode( getString( index ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string argument as an enum value.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param klass The type of enum to parse.
|
||||
* @param <T> The type of enum to parse.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a string or not a valid option for this enum.
|
||||
*/
|
||||
@Nonnull
|
||||
default <T extends Enum<T>> T getEnum( int index, Class<T> klass ) throws LuaException
|
||||
{
|
||||
return LuaValues.checkEnum( index, klass, getString( index ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a table.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
@Nonnull
|
||||
default Map<?, ?> getTable( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( !(value instanceof Map) ) throw LuaValues.badArgumentOf( index, "table", value );
|
||||
return (Map<?, ?>) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a double.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
@Nonnull
|
||||
default Optional<Double> optDouble( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( value == null ) return Optional.empty();
|
||||
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
|
||||
return Optional.of( ((Number) value).doubleValue() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as an int.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
@Nonnull
|
||||
default Optional<Integer> optInt( int index ) throws LuaException
|
||||
{
|
||||
return optLong( index ).map( Long::intValue );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a long.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
default Optional<Long> optLong( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( value == null ) return Optional.empty();
|
||||
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
|
||||
return Optional.of( LuaValues.checkFiniteNum( index, (Number) value ).longValue() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a finite number (not infinite or NaN).
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
default Optional<Double> optFiniteDouble( int index ) throws LuaException
|
||||
{
|
||||
Optional<Double> value = optDouble( index );
|
||||
if( value.isPresent() ) LuaValues.checkFiniteNum( index, value.get() );
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a boolean.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
default Optional<Boolean> optBoolean( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( value == null ) return Optional.empty();
|
||||
if( !(value instanceof Boolean) ) throw LuaValues.badArgumentOf( index, "boolean", value );
|
||||
return Optional.of( (Boolean) value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a string.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
default Optional<String> optString( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( value == null ) return Optional.empty();
|
||||
if( !(value instanceof String) ) throw LuaValues.badArgumentOf( index, "string", value );
|
||||
return Optional.of( (String) value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string argument as a byte array.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present. This is a <em>read only</em> buffer.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
default Optional<ByteBuffer> optBytes( int index ) throws LuaException
|
||||
{
|
||||
return optString( index ).map( LuaValues::encode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string argument as an enum value.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param klass The type of enum to parse.
|
||||
* @param <T> The type of enum to parse.
|
||||
* @return The argument's value.
|
||||
* @throws LuaException If the value is not a string or not a valid option for this enum.
|
||||
*/
|
||||
@Nonnull
|
||||
default <T extends Enum<T>> Optional<T> optEnum( int index, Class<T> klass ) throws LuaException
|
||||
{
|
||||
Optional<String> str = optString( index );
|
||||
return str.isPresent() ? Optional.of( LuaValues.checkEnum( index, klass, str.get() ) ) : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a table.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
default Optional<Map<?, ?>> optTable( int index ) throws LuaException
|
||||
{
|
||||
Object value = get( index );
|
||||
if( value == null ) return Optional.empty();
|
||||
if( !(value instanceof Map) ) throw LuaValues.badArgumentOf( index, "map", value );
|
||||
return Optional.of( (Map<?, ?>) value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a double.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
default double optDouble( int index, double def ) throws LuaException
|
||||
{
|
||||
return optDouble( index ).orElse( def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as an int.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
default int optInt( int index, int def ) throws LuaException
|
||||
{
|
||||
return optInt( index ).orElse( def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a long.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
default long optLong( int index, long def ) throws LuaException
|
||||
{
|
||||
return optLong( index ).orElse( def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a finite number (not infinite or NaN).
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
default double optFiniteDouble( int index, double def ) throws LuaException
|
||||
{
|
||||
return optFiniteDouble( index ).orElse( def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a boolean.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
default boolean optBoolean( int index, boolean def ) throws LuaException
|
||||
{
|
||||
return optBoolean( index ).orElse( def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a string.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
default String optString( int index, String def ) throws LuaException
|
||||
{
|
||||
return optString( index ).orElse( def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an argument as a table.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
default Map<?, ?> optTable( int index, Map<Object, Object> def ) throws LuaException
|
||||
{
|
||||
return optTable( index ).orElse( def );
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An interface for representing custom objects returned by peripherals or other Lua objects.
|
||||
*
|
||||
* Generally, one does not need to implement this type - it is sufficient to return an object with some methods
|
||||
* annotated with {@link LuaFunction}. {@link IDynamicLuaObject} is useful when you wish your available methods to
|
||||
* change at runtime.
|
||||
*/
|
||||
public interface IDynamicLuaObject
|
||||
{
|
||||
/**
|
||||
* Get the names of the methods that this object implements. This should not change over the course of the object's
|
||||
* lifetime.
|
||||
*
|
||||
* @return The method names this object provides.
|
||||
* @see IDynamicPeripheral#getMethodNames()
|
||||
*/
|
||||
@Nonnull
|
||||
String[] getMethodNames();
|
||||
|
||||
/**
|
||||
* Called when a user calls one of the methods that this object implements.
|
||||
*
|
||||
* @param context The context of the currently running lua thread. This can be used to wait for events
|
||||
* or otherwise yield.
|
||||
* @param method An integer identifying which method index from {@link #getMethodNames()} the computer wishes
|
||||
* to call.
|
||||
* @param arguments The arguments for this method.
|
||||
* @return The result of this function. Either an immediate value ({@link MethodResult#of(Object...)} or an
|
||||
* instruction to yield.
|
||||
* @throws LuaException If the function threw an exception.
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult callMethod( @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException;
|
||||
}
|
@@ -8,7 +8,8 @@ package dan200.computercraft.api.lua;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
/**
|
||||
* Represents a {@link ILuaObject} which is stored as a global variable on computer startup.
|
||||
* Represents a Lua object which is stored as a global variable on computer startup. This must either provide
|
||||
* {@link LuaFunction} annotated functions or implement {@link IDynamicLuaObject}.
|
||||
*
|
||||
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
||||
* to use peripherals to provide functionality to users.
|
||||
@@ -16,7 +17,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
* @see ILuaAPIFactory
|
||||
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public interface ILuaAPI extends ILuaObject
|
||||
public interface ILuaAPI
|
||||
{
|
||||
/**
|
||||
* Get the globals this API will be assigned to. This will override any other global, so you should
|
||||
|
27
src/main/java/dan200/computercraft/api/lua/ILuaCallback.java
Normal file
27
src/main/java/dan200/computercraft/api/lua/ILuaCallback.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A continuation which is called when this coroutine is resumed.
|
||||
*
|
||||
* @see MethodResult#yield(Object[], ILuaCallback)
|
||||
*/
|
||||
public interface ILuaCallback
|
||||
{
|
||||
/**
|
||||
* Resume this coroutine.
|
||||
*
|
||||
* @param args The result of resuming this coroutine. These will have the same form as described in
|
||||
* {@link LuaFunction}.
|
||||
* @return The result of this continuation. Either the result to return to the callee, or another yield.
|
||||
* @throws LuaException On an error.
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult resume( Object[] args ) throws LuaException;
|
||||
}
|
@@ -6,99 +6,25 @@
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
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.
|
||||
* An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods
|
||||
* that allow the peripheral call to interface with the computer.
|
||||
*/
|
||||
public interface ILuaContext
|
||||
{
|
||||
/**
|
||||
* Wait for an event to occur on the computer, suspending the thread until it arises. This method is exactly
|
||||
* equivalent to {@code os.pullEvent()} in lua.
|
||||
*
|
||||
* @param filter A specific event to wait for, or null to wait for any event.
|
||||
* @return An object array containing the name of the event that occurred, and any event parameters.
|
||||
* @throws LuaException If the user presses CTRL+T to terminate the current program while pullEvent() is
|
||||
* waiting for an event, a "Terminated" exception will be thrown here.
|
||||
*
|
||||
* Do not attempt to catch this exception. You should use {@link #pullEventRaw(String)}
|
||||
* should you wish to disable termination.
|
||||
* @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.
|
||||
*/
|
||||
@Nonnull
|
||||
default Object[] pullEvent( @Nullable String filter ) throws LuaException, InterruptedException
|
||||
{
|
||||
Object[] results = pullEventRaw( filter );
|
||||
if( results.length >= 1 && results[0].equals( "terminate" ) ) throw new LuaException( "Terminated", 0 );
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #pullEvent(String)}, except "terminated" events are ignored. Only use this if you want to
|
||||
* prevent program termination, which is not recommended. This method is exactly equivalent to
|
||||
* {@code os.pullEventRaw()} in lua.
|
||||
*
|
||||
* @param filter A specific event to wait for, or null to wait for any event.
|
||||
* @return An object array containing the name of the event that occurred, and any event parameters.
|
||||
* @throws InterruptedException If the user shuts down or reboots the computer while pullEventRaw() 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.
|
||||
* @see #pullEvent(String)
|
||||
*/
|
||||
@Nonnull
|
||||
default Object[] pullEventRaw( @Nullable String filter ) throws InterruptedException
|
||||
{
|
||||
return yield( new Object[] { filter } );
|
||||
}
|
||||
|
||||
/**
|
||||
* Yield the current coroutine with some arguments until it is resumed. This method is exactly equivalent to
|
||||
* {@code coroutine.yield()} in lua. Use {@code pullEvent()} if you wish to wait for events.
|
||||
*
|
||||
* @param arguments An object array containing the arguments to pass to coroutine.yield()
|
||||
* @return An object array containing the return values from coroutine.yield()
|
||||
* @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.
|
||||
* @see #pullEvent(String)
|
||||
*/
|
||||
@Nonnull
|
||||
Object[] yield( @Nullable Object[] arguments ) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Note that the return values of your task are handled as events, meaning more complex objects such as maps or
|
||||
* {@link ILuaObject} will not preserve their identities.
|
||||
*
|
||||
* @param task The task to execute on the main thread.
|
||||
* @return The objects returned by {@code task}.
|
||||
* @throws LuaException If the task could not be queued, or if the task threw an exception.
|
||||
* @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.
|
||||
*/
|
||||
@Nullable
|
||||
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)}.
|
||||
* value and the return values, or an error message if it failed.
|
||||
*
|
||||
* @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.
|
||||
* @see LuaFunction#mainThread() To run functions on the main thread and return their results synchronously.
|
||||
*/
|
||||
long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
|
||||
}
|
||||
|
29
src/main/java/dan200/computercraft/api/lua/ILuaFunction.java
Normal file
29
src/main/java/dan200/computercraft/api/lua/ILuaFunction.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A function, which can be called from Lua. If you need to return a table of functions, it is recommended to use
|
||||
* an object with {@link LuaFunction} methods, or implement {@link IDynamicLuaObject}.
|
||||
*
|
||||
* @see MethodResult#of(Object)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ILuaFunction
|
||||
{
|
||||
/**
|
||||
* Call this function with a series of arguments. Note, this will <em>always</em> be called on the computer thread,
|
||||
* and so its implementation must be thread-safe.
|
||||
*
|
||||
* @param arguments The arguments for this function
|
||||
* @return The result of calling this function.
|
||||
* @throws LuaException Upon Lua errors.
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult call( @Nonnull IArguments arguments ) throws LuaException;
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An interface for representing custom objects returned by {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}
|
||||
* calls.
|
||||
*
|
||||
* Return objects implementing this interface to expose objects with methods to lua.
|
||||
*/
|
||||
public interface ILuaObject
|
||||
{
|
||||
/**
|
||||
* Get the names of the methods that this object implements. This works the same as {@link IPeripheral#getMethodNames()}.
|
||||
* See that method for detailed documentation.
|
||||
*
|
||||
* @return The method names this object provides.
|
||||
* @see IPeripheral#getMethodNames()
|
||||
*/
|
||||
@Nonnull
|
||||
String[] getMethodNames();
|
||||
|
||||
/**
|
||||
* Called when a user calls one of the methods that this object implements. This works the same as
|
||||
* {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}}. See that method for detailed
|
||||
* documentation.
|
||||
*
|
||||
* @param context The context of the currently running lua thread. This can be used to wait for events
|
||||
* or otherwise yield.
|
||||
* @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, 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.
|
||||
* @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
|
||||
* @see IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])
|
||||
*/
|
||||
@Nullable
|
||||
Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException;
|
||||
}
|
@@ -8,11 +8,10 @@ package dan200.computercraft.api.lua;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A task which can be executed via {@link ILuaContext#executeMainThreadTask(ILuaTask)} or
|
||||
* {@link ILuaContext#issueMainThreadTask(ILuaTask)}. This will be run on the main thread, at the beginning of the
|
||||
* A task which can be executed via {@link ILuaContext#issueMainThreadTask(ILuaTask)} This will be run on the main
|
||||
* thread, at the beginning of the
|
||||
* next tick.
|
||||
*
|
||||
* @see ILuaContext#executeMainThreadTask(ILuaTask)
|
||||
* @see ILuaContext#issueMainThreadTask(ILuaTask)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@@ -21,8 +20,7 @@ public interface ILuaTask
|
||||
/**
|
||||
* Execute this task.
|
||||
*
|
||||
* @return The arguments to add to the {@code task_completed} event. These will be returned by
|
||||
* {@link ILuaContext#executeMainThreadTask(ILuaTask)}.
|
||||
* @return The arguments to add to the {@code task_completed} event.
|
||||
* @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.
|
||||
|
@@ -13,24 +13,33 @@ import javax.annotation.Nullable;
|
||||
public class LuaException extends Exception
|
||||
{
|
||||
private static final long serialVersionUID = -6136063076818512651L;
|
||||
private final boolean hasLevel;
|
||||
private final int level;
|
||||
|
||||
public LuaException()
|
||||
{
|
||||
this( "error", 1 );
|
||||
}
|
||||
|
||||
public LuaException( @Nullable String message )
|
||||
{
|
||||
this( message, 1 );
|
||||
super( message );
|
||||
this.hasLevel = false;
|
||||
this.level = 1;
|
||||
}
|
||||
|
||||
public LuaException( @Nullable String message, int level )
|
||||
{
|
||||
super( message );
|
||||
this.hasLevel = true;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a level was explicitly specified when constructing. This is used to determine
|
||||
*
|
||||
* @return Whether this has an explicit level.
|
||||
*/
|
||||
public boolean hasLevel()
|
||||
{
|
||||
return hasLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The level this error is raised at. Level 1 is the function's caller, level 2 is that function's caller, and so
|
||||
* on.
|
||||
|
58
src/main/java/dan200/computercraft/api/lua/LuaFunction.java
Normal file
58
src/main/java/dan200/computercraft/api/lua/LuaFunction.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Used to mark a Java function which is callable from Lua.
|
||||
*
|
||||
* Methods annotated with {@link LuaFunction} must be public final instance methods. They can have any number of
|
||||
* parameters, but they must be of the following types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ILuaContext} (and {@link IComputerAccess} if on a {@link IPeripheral})</li>
|
||||
* <li>{@link IArguments}: The arguments supplied to this function.</li>
|
||||
* <li>
|
||||
* Alternatively, one may specify the desired arguments as normal parameters and the argument parsing code will
|
||||
* be generated automatically.
|
||||
*
|
||||
* Each parameter must be one of the given types supported by {@link IArguments} (for instance, {@link int} or
|
||||
* {@link Map}). Optional values are supported by accepting a parameter of type {@link Optional}.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* This function may return {@link MethodResult}. However, if you simply return a value (rather than having to yield),
|
||||
* you may return {@code void}, a single value (either an object or a primitive like {@code int}) or array of objects.
|
||||
* These will be treated the same as {@link MethodResult#of()}, {@link MethodResult#of(Object)} and
|
||||
* {@link MethodResult#of(Object...)}.
|
||||
*/
|
||||
@Documented
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
@Target( ElementType.METHOD )
|
||||
public @interface LuaFunction
|
||||
{
|
||||
/**
|
||||
* Explicitly specify the method names of this function. If not given, it uses the name of the annotated method.
|
||||
*
|
||||
* @return This function's name(s).
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Run this function on the main server thread. This should be specified for any method which interacts with
|
||||
* Minecraft in a thread-unsafe manner.
|
||||
*
|
||||
* @return Whether this functi
|
||||
* @see ILuaContext#issueMainThreadTask(ILuaTask)
|
||||
*/
|
||||
boolean mainThread() default false;
|
||||
}
|
152
src/main/java/dan200/computercraft/api/lua/LuaValues.java
Normal file
152
src/main/java/dan200/computercraft/api/lua/LuaValues.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Various utility functions for operating with Lua values.
|
||||
*
|
||||
* @see IArguments
|
||||
*/
|
||||
public final class LuaValues
|
||||
{
|
||||
private LuaValues()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a Lua string into a read-only {@link ByteBuffer}.
|
||||
*
|
||||
* @param string The string to encode.
|
||||
* @return The encoded string.
|
||||
*/
|
||||
@Nonnull
|
||||
public static ByteBuffer encode( @Nonnull String string )
|
||||
{
|
||||
byte[] chars = new byte[string.length()];
|
||||
for( int i = 0; i < chars.length; i++ )
|
||||
{
|
||||
char c = string.charAt( i );
|
||||
chars[i] = c < 256 ? (byte) c : 63;
|
||||
}
|
||||
|
||||
return ByteBuffer.wrap( chars ).asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a more detailed representation of this number's type. If this is finite, it will just return "number",
|
||||
* otherwise it returns whether it is infinite or NaN.
|
||||
*
|
||||
* @param value The value to extract the type for.
|
||||
* @return This value's numeric type.
|
||||
*/
|
||||
@Nonnull
|
||||
public static String getNumericType( double value )
|
||||
{
|
||||
if( Double.isNaN( value ) ) return "nan";
|
||||
if( value == Double.POSITIVE_INFINITY ) return "inf";
|
||||
if( value == Double.NEGATIVE_INFINITY ) return "-inf";
|
||||
return "number";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string representation of the given value's type.
|
||||
*
|
||||
* @param value The value whose type we are trying to compute.
|
||||
* @return A string representation of the given value's type, in a similar format to that provided by Lua's
|
||||
* {@code type} function.
|
||||
*/
|
||||
@Nonnull
|
||||
public static String getType( @Nullable Object value )
|
||||
{
|
||||
if( value == null ) return "nil";
|
||||
if( value instanceof String ) return "string";
|
||||
if( value instanceof Boolean ) return "boolean";
|
||||
if( value instanceof Number ) return "number";
|
||||
if( value instanceof Map ) return "table";
|
||||
return "userdata";
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a "bad argument" exception, from an expected type and the actual value provided.
|
||||
*
|
||||
* @param index The argument number, starting from 0.
|
||||
* @param expected The expected type for this argument.
|
||||
* @param actual The actual value provided for this argument.
|
||||
* @return The constructed exception, which should be thrown immediately.
|
||||
*/
|
||||
@Nonnull
|
||||
public static LuaException badArgumentOf( int index, @Nonnull String expected, @Nullable Object actual )
|
||||
{
|
||||
return badArgument( index, expected, getType( actual ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a "bad argument" exception, from an expected and actual type.
|
||||
*
|
||||
* @param index The argument number, starting from 0.
|
||||
* @param expected The expected type for this argument.
|
||||
* @param actual The provided type for this argument.
|
||||
* @return The constructed exception, which should be thrown immediately.
|
||||
*/
|
||||
@Nonnull
|
||||
public static LuaException badArgument( int index, @Nonnull String expected, @Nonnull String actual )
|
||||
{
|
||||
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a numeric argument is finite (i.e. not infinite or {@link Double#NaN}.
|
||||
*
|
||||
* @param index The argument index to check.
|
||||
* @param value The value to check.
|
||||
* @return The input {@code value}.
|
||||
* @throws LuaException If this is not a finite number.
|
||||
*/
|
||||
public static Number checkFiniteNum( int index, Number value ) throws LuaException
|
||||
{
|
||||
checkFinite( index, value.doubleValue() );
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a numeric argument is finite (i.e. not infinite or {@link Double#NaN}.
|
||||
*
|
||||
* @param index The argument index to check.
|
||||
* @param value The value to check.
|
||||
* @return The input {@code value}.
|
||||
* @throws LuaException If this is not a finite number.
|
||||
*/
|
||||
public static double checkFinite( int index, double value ) throws LuaException
|
||||
{
|
||||
if( !Double.isFinite( value ) ) throw badArgument( index, "number", getNumericType( value ) );
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a string is a valid enum value.
|
||||
*
|
||||
* @param index The argument index to check.
|
||||
* @param klass The class of the enum instance.
|
||||
* @param value The value to extract.
|
||||
* @param <T> The type of enum we are extracting.
|
||||
* @return The parsed enum value.
|
||||
* @throws LuaException If this is not a known enum value.
|
||||
*/
|
||||
public static <T extends Enum<T>> T checkEnum( int index, Class<T> klass, String value ) throws LuaException
|
||||
{
|
||||
for( T possibility : klass.getEnumConstants() )
|
||||
{
|
||||
if( possibility.name().equalsIgnoreCase( value ) ) return possibility;
|
||||
}
|
||||
|
||||
throw new LuaException( "bad argument #" + (index + 1) + " (unknown option " + value + ")" );
|
||||
}
|
||||
}
|
170
src/main/java/dan200/computercraft/api/lua/MethodResult.java
Normal file
170
src/main/java/dan200/computercraft/api/lua/MethodResult.java
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The result of invoking a Lua method.
|
||||
*
|
||||
* Method results either return a value immediately ({@link #of(Object...)} or yield control to the parent coroutine.
|
||||
* When the current coroutine is resumed, we invoke the provided {@link ILuaCallback#resume(Object[])} callback.
|
||||
*/
|
||||
public final class MethodResult
|
||||
{
|
||||
private static final MethodResult empty = new MethodResult( null, null );
|
||||
|
||||
private final Object[] result;
|
||||
private final ILuaCallback callback;
|
||||
private final int adjust;
|
||||
|
||||
private MethodResult( Object[] arguments, ILuaCallback callback )
|
||||
{
|
||||
this.result = arguments;
|
||||
this.callback = callback;
|
||||
this.adjust = 0;
|
||||
}
|
||||
|
||||
private MethodResult( Object[] arguments, ILuaCallback callback, int adjust )
|
||||
{
|
||||
this.result = arguments;
|
||||
this.callback = callback;
|
||||
this.adjust = adjust;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return no values immediately.
|
||||
*
|
||||
* @return A method result which returns immediately with no values.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult of()
|
||||
{
|
||||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a single value immediately.
|
||||
*
|
||||
* Integers, doubles, floats, strings, booleans, {@link Map}, {@link Collection}s, arrays and {@code null} will be
|
||||
* converted to their corresponding Lua type. {@code byte[]} and {@link ByteBuffer} will be treated as binary
|
||||
* strings. {@link ILuaFunction} will be treated as a function.
|
||||
*
|
||||
* In order to provide a custom object with methods, one may return a {@link IDynamicLuaObject}, or an arbitrary
|
||||
* class with {@link LuaFunction} annotations. Anything else will be converted to {@code nil}.
|
||||
*
|
||||
* @param value The value to return to the calling Lua function.
|
||||
* @return A method result which returns immediately with the given value.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult of( @Nullable Object value )
|
||||
{
|
||||
return new MethodResult( new Object[] { value }, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return any number of values immediately.
|
||||
*
|
||||
* @param values The values to return. See {@link #of(Object)} for acceptable values.
|
||||
* @return A method result which returns immediately with the given values.
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult of( @Nullable Object... values )
|
||||
{
|
||||
return values == null || values.length == 0 ? empty : new MethodResult( values, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for an event to occur on the computer, suspending the thread until it arises. This method is exactly
|
||||
* equivalent to {@code os.pullEvent()} in lua.
|
||||
*
|
||||
* @param filter A specific event to wait for, or null to wait for any event.
|
||||
* @param callback The callback to resume with the name of the event that occurred, and any event parameters.
|
||||
* @return The method result which represents this yield.
|
||||
* @see IComputerAccess#queueEvent(String, Object[])
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEvent( @Nullable String filter, @Nonnull ILuaCallback callback )
|
||||
{
|
||||
Objects.requireNonNull( callback, "callback cannot be null" );
|
||||
return new MethodResult( new Object[] { filter }, results -> {
|
||||
if( results.length >= 1 && results[0].equals( "terminate" ) ) throw new LuaException( "Terminated", 0 );
|
||||
return callback.resume( results );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #pullEvent(String, ILuaCallback)}, except "terminated" events are ignored. Only use this if
|
||||
* you want to prevent program termination, which is not recommended. This method is exactly equivalent to
|
||||
* {@code os.pullEventRaw()} in Lua.
|
||||
*
|
||||
* @param filter A specific event to wait for, or null to wait for any event.
|
||||
* @param callback The callback to resume with the name of the event that occurred, and any event parameters.
|
||||
* @return The method result which represents this yield.
|
||||
* @see #pullEvent(String, ILuaCallback)
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult pullEventRaw( @Nullable String filter, @Nonnull ILuaCallback callback )
|
||||
{
|
||||
Objects.requireNonNull( callback, "callback cannot be null" );
|
||||
return new MethodResult( new Object[] { filter }, callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* Yield the current coroutine with some arguments until it is resumed. This method is exactly equivalent to
|
||||
* {@code coroutine.yield()} in lua. Use {@code pullEvent()} if you wish to wait for events.
|
||||
*
|
||||
* @param arguments An object array containing the arguments to pass to coroutine.yield()
|
||||
* @param callback The callback to resume with an array containing the return values from coroutine.yield()
|
||||
* @return The method result which represents this yield.
|
||||
* @see #pullEvent(String, ILuaCallback)
|
||||
*/
|
||||
@Nonnull
|
||||
public static MethodResult yield( @Nullable Object[] arguments, @Nonnull ILuaCallback callback )
|
||||
{
|
||||
Objects.requireNonNull( callback, "callback cannot be null" );
|
||||
return new MethodResult( arguments, callback );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object[] getResult()
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ILuaCallback getCallback()
|
||||
{
|
||||
return callback;
|
||||
}
|
||||
|
||||
public int getErrorAdjust()
|
||||
{
|
||||
return adjust;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the Lua error by a specific amount. One should never need to use this function - it largely exists for
|
||||
* some CC internal code.
|
||||
*
|
||||
* @param adjust The amount to increase the level by.
|
||||
* @return The new {@link MethodResult} with an adjusted error. This has no effect on immediate results.
|
||||
*/
|
||||
@Nonnull
|
||||
public MethodResult adjustError( int adjust )
|
||||
{
|
||||
if( adjust < 0 ) throw new IllegalArgumentException( "cannot adjust by a negative amount" );
|
||||
if( adjust == 0 || callback == null ) return this;
|
||||
return new MethodResult( result, callback, this.adjust + adjust );
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An implementation of {@link IArguments} which wraps an array of {@link Object}.
|
||||
*/
|
||||
public final class ObjectArguments implements IArguments
|
||||
{
|
||||
private static final IArguments EMPTY = new ObjectArguments();
|
||||
private final List<Object> args;
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings( "unused" )
|
||||
public ObjectArguments( IArguments arguments )
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public ObjectArguments( Object... args )
|
||||
{
|
||||
this.args = Arrays.asList( args );
|
||||
}
|
||||
|
||||
public ObjectArguments( List<Object> args )
|
||||
{
|
||||
this.args = Objects.requireNonNull( args );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count()
|
||||
{
|
||||
return args.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IArguments drop( int count )
|
||||
{
|
||||
if( count < 0 ) throw new IllegalStateException( "count cannot be negative" );
|
||||
if( count == 0 ) return this;
|
||||
if( count >= args.size() ) return EMPTY;
|
||||
|
||||
return new ObjectArguments( args.subList( count, args.size() ) );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get( int index )
|
||||
{
|
||||
return index >= args.size() ? null : args.get( index );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getAll()
|
||||
{
|
||||
return args.toArray();
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.api.network;
|
||||
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -29,7 +29,7 @@ public interface IPacketReceiver
|
||||
* @return The receiver's position.
|
||||
*/
|
||||
@Nonnull
|
||||
Vec3d getPosition();
|
||||
Vector3d getPosition();
|
||||
|
||||
/**
|
||||
* Get the maximum distance this receiver can send and receive messages.
|
||||
|
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.api.network;
|
||||
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -29,7 +29,7 @@ public interface IPacketSender
|
||||
* @return The sender's position.
|
||||
*/
|
||||
@Nonnull
|
||||
Vec3d getPosition();
|
||||
Vector3d getPosition();
|
||||
|
||||
/**
|
||||
* Get some sort of identification string for this sender. This does not strictly need to be unique, but you
|
||||
|
@@ -8,8 +8,10 @@ package dan200.computercraft.api.peripheral;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaCallback;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaTask;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -146,9 +148,9 @@ public interface IComputerAccess
|
||||
*
|
||||
* You may supply {@code null} to indicate that no arguments are to be supplied.
|
||||
* @throws NotAttachedException If the peripheral has been detached.
|
||||
* @see IPeripheral#callMethod
|
||||
* @see MethodResult#pullEvent(String, ILuaCallback)
|
||||
*/
|
||||
void queueEvent( @Nonnull String event, @Nullable Object[] arguments );
|
||||
void queueEvent( @Nonnull String event, @Nullable Object... arguments );
|
||||
|
||||
/**
|
||||
* Get a string, unique to the computer, by which the computer refers to this peripheral.
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.peripheral;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A peripheral whose methods are not known at runtime.
|
||||
*
|
||||
* This behaves similarly to {@link IDynamicLuaObject}, though also accepting the current {@link IComputerAccess}.
|
||||
* Generally one may use {@link LuaFunction} instead of implementing this interface.
|
||||
*/
|
||||
public interface IDynamicPeripheral extends IPeripheral
|
||||
{
|
||||
/**
|
||||
* Should return an array of strings that identify the methods that this peripheral exposes to Lua. This will be
|
||||
* called once before each attachment, and should not change when called multiple times.
|
||||
*
|
||||
* @return An array of strings representing method names.
|
||||
* @see #callMethod
|
||||
*/
|
||||
@Nonnull
|
||||
String[] getMethodNames();
|
||||
|
||||
/**
|
||||
* 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 currently running lua thread. This can be used to wait for events
|
||||
* or otherwise yield.
|
||||
* @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.
|
||||
* @return A {@link MethodResult} containing the values to return or the action to perform.
|
||||
* @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
|
||||
MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException;
|
||||
}
|
@@ -5,15 +5,20 @@
|
||||
*/
|
||||
package dan200.computercraft.api.peripheral;
|
||||
|
||||
import dan200.computercraft.api.lua.ArgumentHelper;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The interface that defines a peripheral. See {@link IPeripheralProvider} for how to associate blocks with peripherals.
|
||||
* The interface that defines a peripheral.
|
||||
*
|
||||
* In order to expose a peripheral for your block or tile entity, you may either attach a {@link Capability}, or
|
||||
* register a {@link IPeripheralProvider}. This <em>cannot</em> be implemented {@link IPeripheral} directly on the tile.
|
||||
*
|
||||
* Peripherals should provide a series of methods to the user, either using {@link LuaFunction} or by implementing
|
||||
* {@link IDynamicPeripheral}.
|
||||
*/
|
||||
public interface IPeripheral
|
||||
{
|
||||
@@ -26,57 +31,6 @@ public interface IPeripheral
|
||||
@Nonnull
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* Should return an array of strings that identify the methods that this
|
||||
* peripheral exposes to Lua. This will be called once before each attachment,
|
||||
* and should not change when called multiple times.
|
||||
*
|
||||
* @return An array of strings representing method names.
|
||||
* @see #callMethod
|
||||
*/
|
||||
@Nonnull
|
||||
String[] getMethodNames();
|
||||
|
||||
/**
|
||||
* 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 currently running lua thread. This can be used to wait for events
|
||||
* or otherwise yield.
|
||||
* @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.
|
||||
*
|
||||
* It is recommended you use {@link ArgumentHelper} in order to validate and process arguments.
|
||||
* @return An array of objects, representing values you wish to return to the lua program. Integers, Doubles, Floats,
|
||||
* Strings, Booleans, Maps, ILuaObject and null be converted to their corresponding lua type. All other types will
|
||||
* be converted to nil.
|
||||
*
|
||||
* You may return null to indicate no values should be returned.
|
||||
* @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.
|
||||
* @see #getMethodNames
|
||||
* @see ArgumentHelper
|
||||
*/
|
||||
@Nullable
|
||||
Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Is called when when a computer is attaching to the peripheral.
|
||||
*
|
||||
@@ -126,10 +80,10 @@ public interface IPeripheral
|
||||
*
|
||||
* @return The object this peripheral targets
|
||||
*/
|
||||
@Nonnull
|
||||
@Nullable
|
||||
default Object getTarget()
|
||||
{
|
||||
return this;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -9,14 +9,15 @@ import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This interface is used to create peripheral implementations for blocks.
|
||||
*
|
||||
* If you have a {@link TileEntity} which acts as a peripheral, you may alternatively implement {@link IPeripheralTile}.
|
||||
* If you have a {@link TileEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral}
|
||||
* capability.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
|
||||
*/
|
||||
@@ -29,9 +30,9 @@ public interface IPeripheralProvider
|
||||
* @param world The world the block is in.
|
||||
* @param pos The position the block is at.
|
||||
* @param side The side to get the peripheral from.
|
||||
* @return A peripheral, or {@code null} if there is not a peripheral here you'd like to handle.
|
||||
* @return A peripheral, or {@link LazyOptional#empty()} if there is not a peripheral here you'd like to handle.
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
|
||||
*/
|
||||
@Nullable
|
||||
IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side );
|
||||
@Nonnull
|
||||
LazyOptional<IPeripheral> getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side );
|
||||
}
|
||||
|
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.peripheral;
|
||||
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link net.minecraft.tileentity.TileEntity} which may act as a peripheral.
|
||||
*
|
||||
* If you need more complex capabilities (such as handling TEs not belonging to your mod), you should use
|
||||
* {@link IPeripheralProvider}.
|
||||
*/
|
||||
public interface IPeripheralTile
|
||||
{
|
||||
/**
|
||||
* Get the peripheral on the given {@code side}.
|
||||
*
|
||||
* @param side The side to get the peripheral from.
|
||||
* @return A peripheral, or {@code null} if there is not a peripheral here.
|
||||
* @see IPeripheralProvider#getPeripheral(World, BlockPos, Direction)
|
||||
*/
|
||||
@Nullable
|
||||
IPeripheral getPeripheral( @Nonnull Direction side );
|
||||
}
|
@@ -15,7 +15,7 @@ public class NotAttachedException extends IllegalStateException
|
||||
|
||||
public NotAttachedException()
|
||||
{
|
||||
super( "You are not attached to this Computer" );
|
||||
super( "You are not attached to this computer" );
|
||||
}
|
||||
|
||||
public NotAttachedException( String s )
|
||||
|
@@ -5,12 +5,15 @@
|
||||
*/
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IItemProvider;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraftforge.common.util.NonNullSupplier;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A base class for {@link IPocketUpgrade}s.
|
||||
@@ -21,23 +24,48 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
|
||||
{
|
||||
private final ResourceLocation id;
|
||||
private final String adjective;
|
||||
private final ItemStack stack;
|
||||
private final NonNullSupplier<ItemStack> stack;
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, NonNullSupplier<ItemStack> stack )
|
||||
{
|
||||
this.id = id;
|
||||
this.adjective = adjective;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, NonNullSupplier<ItemStack> item )
|
||||
{
|
||||
this( id, Util.makeTranslationKey( "upgrade", id ) + ".adjective", item );
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
|
||||
{
|
||||
this( id, adjective, () -> stack );
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, ItemStack stack )
|
||||
{
|
||||
this( id, () -> stack );
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, IItemProvider item )
|
||||
{
|
||||
this( id, adjective, new ItemStack( item ) );
|
||||
this( id, adjective, new CachedStack( () -> item ) );
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, IItemProvider item )
|
||||
{
|
||||
this( id, Util.makeTranslationKey( "upgrade", id ) + ".adjective", new ItemStack( item ) );
|
||||
this( id, new CachedStack( () -> item ) );
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, Supplier<? extends IItemProvider> item )
|
||||
{
|
||||
this( id, adjective, new CachedStack( item ) );
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, Supplier<? extends IItemProvider> item )
|
||||
{
|
||||
this( id, new CachedStack( item ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -58,6 +86,32 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
|
||||
@Override
|
||||
public final ItemStack getCraftingItem()
|
||||
{
|
||||
return stack;
|
||||
return stack.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the construction of an item stack.
|
||||
*
|
||||
* @see dan200.computercraft.api.turtle.AbstractTurtleUpgrade For explanation of this class.
|
||||
*/
|
||||
private static final class CachedStack implements NonNullSupplier<ItemStack>
|
||||
{
|
||||
private final Supplier<? extends IItemProvider> provider;
|
||||
private Item item;
|
||||
private ItemStack stack;
|
||||
|
||||
CachedStack( Supplier<? extends IItemProvider> provider )
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack get()
|
||||
{
|
||||
Item item = provider.get().asItem();
|
||||
if( item == this.item && stack != null ) return stack;
|
||||
return stack = new ItemStack( this.item = item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,12 +5,15 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IItemProvider;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraftforge.common.util.NonNullSupplier;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A base class for {@link ITurtleUpgrade}s.
|
||||
@@ -22,9 +25,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
|
||||
private final ResourceLocation id;
|
||||
private final TurtleUpgradeType type;
|
||||
private final String adjective;
|
||||
private final ItemStack stack;
|
||||
private final NonNullSupplier<ItemStack> stack;
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack )
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, NonNullSupplier<ItemStack> stack )
|
||||
{
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
@@ -32,19 +35,39 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, IItemProvider item )
|
||||
{
|
||||
this( id, type, adjective, new ItemStack( item ) );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack )
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, NonNullSupplier<ItemStack> stack )
|
||||
{
|
||||
this( id, type, Util.makeTranslationKey( "upgrade", id ) + ".adjective", stack );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack )
|
||||
{
|
||||
this( id, type, adjective, () -> stack );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack )
|
||||
{
|
||||
this( id, type, () -> stack );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, IItemProvider item )
|
||||
{
|
||||
this( id, type, adjective, new CachedStack( () -> item ) );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, IItemProvider item )
|
||||
{
|
||||
this( id, type, new ItemStack( item ) );
|
||||
this( id, type, new CachedStack( () -> item ) );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, Supplier<? extends IItemProvider> item )
|
||||
{
|
||||
this( id, type, adjective, new CachedStack( item ) );
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, Supplier<? extends IItemProvider> item )
|
||||
{
|
||||
this( id, type, new CachedStack( item ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -72,6 +95,32 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
|
||||
@Override
|
||||
public final ItemStack getCraftingItem()
|
||||
{
|
||||
return stack;
|
||||
return stack.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* A supplier which converts an item into an item stack.
|
||||
*
|
||||
* Constructing item stacks is somewhat expensive due to attaching capabilities. We cache it if given a consistent item.
|
||||
*/
|
||||
private static final class CachedStack implements NonNullSupplier<ItemStack>
|
||||
{
|
||||
private final Supplier<? extends IItemProvider> provider;
|
||||
private Item item;
|
||||
private ItemStack stack;
|
||||
|
||||
CachedStack( Supplier<? extends IItemProvider> provider )
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack get()
|
||||
{
|
||||
Item item = provider.get().asItem();
|
||||
if( item == this.item && stack != null ) return stack;
|
||||
return stack = new ItemStack( this.item = item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,14 +6,14 @@
|
||||
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.ILuaCallback;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
|
||||
@@ -67,7 +67,7 @@ public interface ITurtleAccess
|
||||
* @see #getVisualYaw(float)
|
||||
*/
|
||||
@Nonnull
|
||||
Vec3d getVisualPosition( float f );
|
||||
Vector3d getVisualPosition( float f );
|
||||
|
||||
/**
|
||||
* Returns the yaw the turtle is facing when it is rendered.
|
||||
@@ -228,21 +228,15 @@ public interface ITurtleAccess
|
||||
* 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 context The Lua context to pull events from.
|
||||
* @param command An object which will execute the custom command when its point in the queue is reached
|
||||
* @return The objects the command returned when executed. you should probably return these to the player
|
||||
* unchanged if called from a peripheral method.
|
||||
* @throws UnsupportedOperationException When attempting to execute a command on the client side.
|
||||
* @throws LuaException If the user presses CTRL+T to terminate the current program while {@code executeCommand()} is
|
||||
* waiting for an event, a "Terminated" exception will be thrown here.
|
||||
* @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.
|
||||
* @see ITurtleCommand
|
||||
* @see ILuaContext#pullEvent(String)
|
||||
* @see MethodResult#pullEvent(String, ILuaCallback)
|
||||
*/
|
||||
@Nonnull
|
||||
Object[] executeCommand( @Nonnull ILuaContext context, @Nonnull ITurtleCommand command ) throws LuaException, InterruptedException;
|
||||
MethodResult executeCommand( @Nonnull ITurtleCommand command );
|
||||
|
||||
/**
|
||||
* Start playing a specific animation. This will prevent other turtle commands from executing until
|
||||
|
@@ -5,14 +5,12 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An interface for objects executing custom turtle commands, used with {@link ITurtleAccess#executeCommand(ILuaContext, ITurtleCommand)}.
|
||||
* An interface for objects executing custom turtle commands, used with {@link ITurtleAccess#executeCommand(ITurtleCommand)}.
|
||||
*
|
||||
* @see ITurtleAccess#executeCommand(ILuaContext, ITurtleCommand)
|
||||
* @see ITurtleAccess#executeCommand(ITurtleCommand)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ITurtleCommand
|
||||
@@ -25,7 +23,7 @@ public interface ITurtleCommand
|
||||
*
|
||||
* @param turtle Access to the turtle for whom the command was issued.
|
||||
* @return A result, indicating whether this action succeeded or not.
|
||||
* @see ITurtleAccess#executeCommand(ILuaContext, ITurtleCommand)
|
||||
* @see ITurtleAccess#executeCommand(ITurtleCommand)
|
||||
* @see TurtleCommandResult#success()
|
||||
* @see TurtleCommandResult#failure(String)
|
||||
* @see TurtleCommandResult
|
||||
|
@@ -5,8 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
@@ -223,7 +222,7 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
|
||||
* 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 convertible to Lua (see
|
||||
* {@link dan200.computercraft.api.peripheral.IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}).
|
||||
* {@link MethodResult#of(Object)}).
|
||||
*/
|
||||
public void addData( @Nonnull Map<String, ?> newData )
|
||||
{
|
||||
|
@@ -5,8 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle.event;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
@@ -17,8 +16,8 @@ import java.util.Objects;
|
||||
/**
|
||||
* Fired when a turtle gathers data on an item in its inventory.
|
||||
*
|
||||
* You may prevent items being inspected, or add additional information to the result. Be aware that this is fired on
|
||||
* the computer thread, and so any operations on it must be thread safe.
|
||||
* You may prevent items being inspected, or add additional information to the result. Be aware that this may be fired
|
||||
* on the computer thread, and so any operations on it must be thread safe.
|
||||
*
|
||||
* @see TurtleAction#INSPECT_ITEM
|
||||
*/
|
||||
@@ -26,8 +25,15 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
|
||||
{
|
||||
private final ItemStack stack;
|
||||
private final Map<String, Object> data;
|
||||
private final boolean mainThread;
|
||||
|
||||
@Deprecated
|
||||
public TurtleInspectItemEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data )
|
||||
{
|
||||
this( turtle, stack, data, false );
|
||||
}
|
||||
|
||||
public TurtleInspectItemEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data, boolean mainThread )
|
||||
{
|
||||
super( turtle, TurtleAction.INSPECT_ITEM );
|
||||
|
||||
@@ -35,6 +41,7 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
|
||||
Objects.requireNonNull( data, "data cannot be null" );
|
||||
this.stack = stack;
|
||||
this.data = data;
|
||||
this.mainThread = mainThread;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,11 +66,22 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this event is being fired on the server thread. When true, information which relies on server state may be
|
||||
* exposed.
|
||||
*
|
||||
* @return If this is run on the main thread.
|
||||
*/
|
||||
public boolean onMainThread()
|
||||
{
|
||||
return mainThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 convertible to Lua (see
|
||||
* {@link dan200.computercraft.api.peripheral.IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}).
|
||||
* {@link MethodResult#of(Object)}).
|
||||
*/
|
||||
public void addData( @Nonnull Map<String, ?> newData )
|
||||
{
|
||||
|
@@ -7,8 +7,10 @@ package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.render.TurtleModelLoader;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.media.items.ItemDisk;
|
||||
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
@@ -108,7 +110,7 @@ public final class ClientRegistry
|
||||
@SubscribeEvent
|
||||
public static void onItemColours( ColorHandlerEvent.Item event )
|
||||
{
|
||||
if( ComputerCraft.Items.disk == null || ComputerCraft.Blocks.turtleNormal == null )
|
||||
if( Registry.ModItems.DISK == null || Registry.ModBlocks.TURTLE_NORMAL == null )
|
||||
{
|
||||
ComputerCraft.log.warn( "Block/item registration has failed. Skipping registration of item colours." );
|
||||
return;
|
||||
@@ -116,7 +118,12 @@ public final class ClientRegistry
|
||||
|
||||
event.getItemColors().register(
|
||||
( stack, layer ) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour( stack ) : 0xFFFFFF,
|
||||
ComputerCraft.Items.disk
|
||||
Registry.ModItems.DISK.get()
|
||||
);
|
||||
|
||||
event.getItemColors().register(
|
||||
( stack, layer ) -> layer == 1 ? ItemTreasureDisk.getColour( stack ) : 0xFFFFFF,
|
||||
Registry.ModItems.TREASURE_DISK.get()
|
||||
);
|
||||
|
||||
event.getItemColors().register( ( stack, layer ) -> {
|
||||
@@ -133,12 +140,12 @@ public final class ClientRegistry
|
||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||
}
|
||||
}
|
||||
}, ComputerCraft.Items.pocketComputerNormal, ComputerCraft.Items.pocketComputerAdvanced );
|
||||
}, Registry.ModItems.POCKET_COMPUTER_NORMAL.get(), Registry.ModItems.POCKET_COMPUTER_ADVANCED.get() );
|
||||
|
||||
// Setup turtle colours
|
||||
event.getItemColors().register(
|
||||
( stack, tintIndex ) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour( stack ) : 0xFFFFFF,
|
||||
ComputerCraft.Blocks.turtleNormal, ComputerCraft.Blocks.turtleAdvanced
|
||||
Registry.ModBlocks.TURTLE_NORMAL.get(), Registry.ModBlocks.TURTLE_ADVANCED.get()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -12,14 +12,12 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.NewChatGui;
|
||||
import net.minecraft.client.gui.RenderComponentsUtil;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class ClientTableFormatter implements TableFormatter
|
||||
{
|
||||
@@ -57,7 +55,7 @@ public class ClientTableFormatter implements TableFormatter
|
||||
@Override
|
||||
public int getWidth( ITextComponent component )
|
||||
{
|
||||
return renderer().getStringWidth( component.getFormattedText() );
|
||||
return renderer().func_238414_a_( component );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,10 +64,11 @@ public class ClientTableFormatter implements TableFormatter
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
NewChatGui chat = mc.ingameGUI.getChatGUI();
|
||||
|
||||
// Trim the text if it goes over the allowed length
|
||||
int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() );
|
||||
List<ITextComponent> list = RenderComponentsUtil.splitText( component, maxWidth, mc.fontRenderer, false, false );
|
||||
if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id );
|
||||
// TODO: Trim the text if it goes over the allowed length
|
||||
// int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() );
|
||||
// List<ITextProperties> list = RenderComponentsUtil.func_238505_a_( component, maxWidth, mc.fontRenderer );
|
||||
// if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id );
|
||||
chat.printChatMessageWithOptionalDeletion( component, id );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -13,10 +13,14 @@ import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.RenderState;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -41,15 +45,14 @@ public final class FixedWidthFontRenderer
|
||||
{
|
||||
}
|
||||
|
||||
private static float toGreyscale( double[] rgb )
|
||||
public static float toGreyscale( double[] rgb )
|
||||
{
|
||||
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
|
||||
}
|
||||
|
||||
private static int getColour( char c )
|
||||
public static int getColour( char c, Colour def )
|
||||
{
|
||||
int i = "0123456789abcdef".indexOf( c );
|
||||
return i < 0 ? 0 : 15 - i;
|
||||
return 15 - Terminal.getColour( c, def );
|
||||
}
|
||||
|
||||
private static void drawChar( Matrix4f transform, IVertexBuilder buffer, float x, float y, int index, float r, float g, float b )
|
||||
@@ -83,7 +86,7 @@ public final class FixedWidthFontRenderer
|
||||
|
||||
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
|
||||
{
|
||||
double[] colour = palette.getColour( getColour( colourIndex ) );
|
||||
double[] colour = palette.getColour( getColour( colourIndex, Colour.BLACK ) );
|
||||
float r, g, b;
|
||||
if( greyscale )
|
||||
{
|
||||
@@ -151,7 +154,7 @@ public final class FixedWidthFontRenderer
|
||||
|
||||
for( int i = 0; i < text.length(); i++ )
|
||||
{
|
||||
double[] colour = palette.getColour( 15 - "0123456789abcdef".indexOf( textColour.charAt( i ) ) );
|
||||
double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.BLACK ) );
|
||||
float r, g, b;
|
||||
if( greyscale )
|
||||
{
|
||||
|
@@ -5,10 +5,12 @@
|
||||
*/
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
@@ -17,21 +19,20 @@ import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
|
||||
public final class GuiComputer<T extends ContainerComputerBase> extends ContainerScreen<T>
|
||||
{
|
||||
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_normal.png" );
|
||||
public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" );
|
||||
public static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" );
|
||||
public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" );
|
||||
|
||||
private final ComputerFamily m_family;
|
||||
private final ClientComputer m_computer;
|
||||
private final int m_termWidth;
|
||||
private final int m_termHeight;
|
||||
private final ComputerFamily family;
|
||||
private final ClientComputer computer;
|
||||
private final int termWidth;
|
||||
private final int termHeight;
|
||||
|
||||
private WidgetTerminal terminal;
|
||||
private WidgetWrapper terminalWrapper;
|
||||
@@ -41,10 +42,10 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
|
||||
)
|
||||
{
|
||||
super( container, player, title );
|
||||
m_family = container.getFamily();
|
||||
m_computer = (ClientComputer) container.getComputer();
|
||||
m_termWidth = termWidth;
|
||||
m_termHeight = termHeight;
|
||||
family = container.getFamily();
|
||||
computer = (ClientComputer) container.getComputer();
|
||||
this.termWidth = termWidth;
|
||||
this.termHeight = termHeight;
|
||||
terminal = null;
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
|
||||
{
|
||||
return new GuiComputer<>(
|
||||
container, inventory, component,
|
||||
ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer
|
||||
ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
|
||||
{
|
||||
return new GuiComputer<>(
|
||||
container, inventory, component,
|
||||
ComputerCraft.terminalWidth_pocketComputer, ComputerCraft.terminalHeight_pocketComputer
|
||||
ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,25 +79,25 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
|
||||
{
|
||||
minecraft.keyboardListener.enableRepeatEvents( true );
|
||||
|
||||
int termPxWidth = m_termWidth * FixedWidthFontRenderer.FONT_WIDTH;
|
||||
int termPxHeight = m_termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
int termPxWidth = termWidth * FixedWidthFontRenderer.FONT_WIDTH;
|
||||
int termPxHeight = termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
|
||||
xSize = termPxWidth + 4 + 24;
|
||||
ySize = termPxHeight + 4 + 24;
|
||||
xSize = termPxWidth + MARGIN * 2 + BORDER * 2;
|
||||
ySize = termPxHeight + MARGIN * 2 + BORDER * 2;
|
||||
|
||||
super.init();
|
||||
|
||||
terminal = new WidgetTerminal( minecraft, () -> m_computer, m_termWidth, m_termHeight, 2, 2, 2, 2 );
|
||||
terminalWrapper = new WidgetWrapper( terminal, 2 + 12 + guiLeft, 2 + 12 + guiTop, termPxWidth, termPxHeight );
|
||||
terminal = new WidgetTerminal( minecraft, () -> computer, termWidth, termHeight, MARGIN, MARGIN, MARGIN, MARGIN );
|
||||
terminalWrapper = new WidgetWrapper( terminal, MARGIN + BORDER + guiLeft, MARGIN + BORDER + guiTop, termPxWidth, termPxHeight );
|
||||
|
||||
children.add( terminalWrapper );
|
||||
setFocused( terminalWrapper );
|
||||
setListener( terminalWrapper );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed()
|
||||
public void onClose()
|
||||
{
|
||||
super.removed();
|
||||
super.onClose();
|
||||
children.remove( terminal );
|
||||
terminal = null;
|
||||
minecraft.keyboardListener.enableRepeatEvents( false );
|
||||
@@ -113,66 +114,46 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
|
||||
public boolean keyPressed( int key, int scancode, int modifiers )
|
||||
{
|
||||
// Forward the tab key to the terminal, rather than moving between controls.
|
||||
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
|
||||
if( key == GLFW.GLFW_KEY_TAB && getListener() != null && getListener() == terminalWrapper )
|
||||
{
|
||||
return getFocused().keyPressed( key, scancode, modifiers );
|
||||
return getListener().keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
return super.keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
|
||||
public void drawGuiContainerBackgroundLayer( @Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
// Work out where to draw
|
||||
int startX = terminalWrapper.getX() - 2;
|
||||
int startY = terminalWrapper.getY() - 2;
|
||||
int endX = startX + terminalWrapper.getWidth() + 4;
|
||||
int endY = startY + terminalWrapper.getHeight() + 4;
|
||||
|
||||
// Draw terminal
|
||||
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
|
||||
|
||||
// Draw a border around the terminal
|
||||
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
switch( m_family )
|
||||
{
|
||||
case NORMAL:
|
||||
default:
|
||||
minecraft.getTextureManager().bindTexture( BACKGROUND_NORMAL );
|
||||
break;
|
||||
case ADVANCED:
|
||||
minecraft.getTextureManager().bindTexture( BACKGROUND_ADVANCED );
|
||||
break;
|
||||
case COMMAND:
|
||||
minecraft.getTextureManager().bindTexture( BACKGROUND_COMMAND );
|
||||
break;
|
||||
}
|
||||
|
||||
blit( startX - 12, startY - 12, 12, 28, 12, 12 );
|
||||
blit( startX - 12, endY, 12, 40, 12, 12 );
|
||||
blit( endX, startY - 12, 24, 28, 12, 12 );
|
||||
blit( endX, endY, 24, 40, 12, 12 );
|
||||
|
||||
blit( startX, startY - 12, 0, 0, endX - startX, 12 );
|
||||
blit( startX, endY, 0, 12, endX - startX, 12 );
|
||||
|
||||
blit( startX - 12, startY, 0, 28, 12, endY - startY );
|
||||
blit( endX, startY, 36, 28, 12, endY - startY );
|
||||
RenderSystem.color4f( 1, 1, 1, 1 );
|
||||
minecraft.getTextureManager().bindTexture( ComputerBorderRenderer.getTexture( family ) );
|
||||
ComputerBorderRenderer.render(
|
||||
terminalWrapper.getX() - MARGIN, terminalWrapper.getY() - MARGIN, getBlitOffset(),
|
||||
terminalWrapper.getWidth() + MARGIN * 2, terminalWrapper.getHeight() + MARGIN * 2
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderHoveredToolTip( mouseX, mouseY );
|
||||
super.render( stack, mouseX, mouseY, partialTicks );
|
||||
func_230459_a_( stack, mouseX, mouseY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
|
||||
{
|
||||
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
return (getListener() != null && getListener().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
|| super.mouseDragged( x, y, button, deltaX, deltaY );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer( @Nonnull MatrixStack transform, int mouseX, int mouseY )
|
||||
{
|
||||
// Skip rendering labels.
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
@@ -12,6 +13,8 @@ import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class GuiDiskDrive extends ContainerScreen<ContainerDiskDrive>
|
||||
{
|
||||
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/disk_drive.png" );
|
||||
@@ -22,26 +25,18 @@ public class GuiDiskDrive extends ContainerScreen<ContainerDiskDrive>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer( int mouseX, int mouseY )
|
||||
{
|
||||
String title = this.title.getFormattedText();
|
||||
font.drawString( title, (xSize - font.getStringWidth( title )) / 2.0f, 6, 0x404040 );
|
||||
font.drawString( title, 8, ySize - 96 + 2, 0x404040 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
|
||||
protected void drawGuiContainerBackgroundLayer( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
minecraft.getTextureManager().bindTexture( BACKGROUND );
|
||||
blit( guiLeft, guiTop, 0, 0, xSize, ySize );
|
||||
blit( transform, guiLeft, guiTop, 0, 0, xSize, ySize );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderHoveredToolTip( mouseX, mouseY );
|
||||
renderBackground( transform );
|
||||
super.render( transform, mouseX, mouseY, partialTicks );
|
||||
func_230459_a_( transform, mouseX, mouseY );
|
||||
}
|
||||
}
|
||||
|
@@ -5,14 +5,16 @@
|
||||
*/
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class GuiPrinter extends ContainerScreen<ContainerPrinter>
|
||||
{
|
||||
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/printer.png" );
|
||||
@@ -22,29 +24,29 @@ public class GuiPrinter extends ContainerScreen<ContainerPrinter>
|
||||
super( container, player, title );
|
||||
}
|
||||
|
||||
@Override
|
||||
/*@Override
|
||||
protected void drawGuiContainerForegroundLayer( int mouseX, int mouseY )
|
||||
{
|
||||
String title = getTitle().getFormattedText();
|
||||
font.drawString( title, (xSize - font.getStringWidth( title )) / 2.0f, 6, 0x404040 );
|
||||
font.drawString( I18n.format( "container.inventory" ), 8, ySize - 96 + 2, 0x404040 );
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
|
||||
protected void drawGuiContainerBackgroundLayer( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
minecraft.getTextureManager().bindTexture( BACKGROUND );
|
||||
blit( guiLeft, guiTop, 0, 0, xSize, ySize );
|
||||
blit( transform, guiLeft, guiTop, 0, 0, xSize, ySize );
|
||||
|
||||
if( getContainer().isPrinting() ) blit( guiLeft + 34, guiTop + 21, 176, 0, 25, 45 );
|
||||
if( getContainer().isPrinting() ) blit( transform, guiLeft + 34, guiTop + 21, 176, 0, 25, 45 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderHoveredToolTip( mouseX, mouseY );
|
||||
renderBackground( stack );
|
||||
super.render( stack, mouseX, mouseY, partialTicks );
|
||||
func_230459_a_( stack, mouseX, mouseY );
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.common.ContainerHeldItem;
|
||||
@@ -12,18 +13,17 @@ import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.TransformationMatrix;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
|
||||
public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
|
||||
{
|
||||
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
|
||||
|
||||
private final boolean m_book;
|
||||
private final int m_pages;
|
||||
private final TextBuffer[] m_text;
|
||||
@@ -91,27 +91,33 @@ public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
|
||||
protected void drawGuiContainerBackgroundLayer( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
// Draw the printout
|
||||
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
|
||||
drawBorder( IDENTITY, renderer, guiLeft, guiTop, getBlitOffset(), m_page, m_pages, m_book );
|
||||
drawText( IDENTITY, renderer, guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
|
||||
Matrix4f matrix = transform.getLast().getMatrix();
|
||||
drawBorder( matrix, renderer, guiLeft, guiTop, getBlitOffset(), m_page, m_pages, m_book );
|
||||
drawText( matrix, renderer, guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
|
||||
renderer.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
// We must take the background further back in order to not overlap with our printed pages.
|
||||
setBlitOffset( getBlitOffset() - 1 );
|
||||
renderBackground();
|
||||
renderBackground( stack );
|
||||
setBlitOffset( getBlitOffset() + 1 );
|
||||
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderHoveredToolTip( mouseX, mouseY );
|
||||
super.render( stack, mouseX, mouseY, partialTicks );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer( @Nonnull MatrixStack transform, int mouseX, int mouseY )
|
||||
{
|
||||
// Skip rendering labels.
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
|
||||
@@ -18,6 +19,8 @@ import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class GuiTurtle extends ContainerScreen<ContainerTurtle>
|
||||
{
|
||||
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( "computercraft", "textures/gui/turtle_normal.png" );
|
||||
@@ -49,25 +52,25 @@ public class GuiTurtle extends ContainerScreen<ContainerTurtle>
|
||||
super.init();
|
||||
minecraft.keyboardListener.enableRepeatEvents( true );
|
||||
|
||||
int termPxWidth = ComputerCraft.terminalWidth_turtle * FixedWidthFontRenderer.FONT_WIDTH;
|
||||
int termPxHeight = ComputerCraft.terminalHeight_turtle * FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
int termPxWidth = ComputerCraft.turtleTermWidth * FixedWidthFontRenderer.FONT_WIDTH;
|
||||
int termPxHeight = ComputerCraft.turtleTermHeight * FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
|
||||
terminal = new WidgetTerminal(
|
||||
minecraft, () -> m_computer,
|
||||
ComputerCraft.terminalWidth_turtle,
|
||||
ComputerCraft.terminalHeight_turtle,
|
||||
ComputerCraft.turtleTermWidth,
|
||||
ComputerCraft.turtleTermHeight,
|
||||
2, 2, 2, 2
|
||||
);
|
||||
terminalWrapper = new WidgetWrapper( terminal, 2 + 8 + guiLeft, 2 + 8 + guiTop, termPxWidth, termPxHeight );
|
||||
|
||||
children.add( terminalWrapper );
|
||||
setFocused( terminalWrapper );
|
||||
setListener( terminalWrapper );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed()
|
||||
public void onClose()
|
||||
{
|
||||
super.removed();
|
||||
super.onClose();
|
||||
children.remove( terminal );
|
||||
terminal = null;
|
||||
minecraft.keyboardListener.enableRepeatEvents( false );
|
||||
@@ -84,55 +87,58 @@ public class GuiTurtle extends ContainerScreen<ContainerTurtle>
|
||||
public boolean keyPressed( int key, int scancode, int modifiers )
|
||||
{
|
||||
// Forward the tab key to the terminal, rather than moving between controls.
|
||||
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
|
||||
if( key == GLFW.GLFW_KEY_TAB && getListener() != null && getListener() == terminalWrapper )
|
||||
{
|
||||
return getFocused().keyPressed( key, scancode, modifiers );
|
||||
return getListener().keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
return super.keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
private void drawSelectionSlot( boolean advanced )
|
||||
{
|
||||
// Draw selection slot
|
||||
int slot = m_container.getSelectedSlot();
|
||||
if( slot >= 0 )
|
||||
{
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
int slotX = slot % 4;
|
||||
int slotY = slot / 4;
|
||||
minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
|
||||
blit( guiLeft + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18, guiTop + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18, 0, 217, 24, 24 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
|
||||
protected void drawGuiContainerBackgroundLayer( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
// Draw term
|
||||
boolean advanced = m_family == ComputerFamily.ADVANCED;
|
||||
ResourceLocation texture = m_family == ComputerFamily.ADVANCED ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
|
||||
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
|
||||
|
||||
// Draw border/inventory
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
minecraft.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
|
||||
blit( guiLeft, guiTop, 0, 0, xSize, ySize );
|
||||
minecraft.getTextureManager().bindTexture( texture );
|
||||
blit( transform, guiLeft, guiTop, 0, 0, xSize, ySize );
|
||||
|
||||
drawSelectionSlot( advanced );
|
||||
// Draw selection slot
|
||||
int slot = m_container.getSelectedSlot();
|
||||
if( slot >= 0 )
|
||||
{
|
||||
int slotX = slot % 4;
|
||||
int slotY = slot / 4;
|
||||
blit( transform,
|
||||
guiLeft + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18,
|
||||
guiTop + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18,
|
||||
0, 217, 24, 24
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderHoveredToolTip( mouseX, mouseY );
|
||||
renderBackground( stack );
|
||||
super.render( stack, mouseX, mouseY, partialTicks );
|
||||
func_230459_a_( stack, mouseX, mouseY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
|
||||
{
|
||||
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
return (getListener() != null && getListener().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
|| super.mouseDragged( x, y, button, deltaX, deltaY );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer( @Nonnull MatrixStack transform, int mouseX, int mouseY )
|
||||
{
|
||||
// Skip rendering labels.
|
||||
}
|
||||
}
|
||||
|
@@ -10,20 +10,20 @@ import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
|
||||
import dan200.computercraft.client.render.TurtlePlayerRenderer;
|
||||
import dan200.computercraft.shared.common.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import net.minecraft.client.gui.ScreenManager;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.RenderTypeLookup;
|
||||
import net.minecraft.item.IItemPropertyGetter;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemModelsProperties;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
@@ -32,6 +32,8 @@ import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD )
|
||||
public final class ComputerCraftProxyClient
|
||||
{
|
||||
@@ -41,36 +43,55 @@ public final class ComputerCraftProxyClient
|
||||
registerContainers();
|
||||
|
||||
// While turtles themselves are not transparent, their upgrades may be.
|
||||
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.turtleNormal, RenderType.getTranslucent() );
|
||||
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.turtleAdvanced, RenderType.getTranslucent() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_NORMAL.get(), RenderType.getTranslucent() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderType.getTranslucent() );
|
||||
|
||||
// Monitors' textures have transparent fronts and so count as cutouts.
|
||||
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.monitorNormal, RenderType.getCutout() );
|
||||
RenderTypeLookup.setRenderLayer( ComputerCraft.Blocks.monitorAdvanced, RenderType.getCutout() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_NORMAL.get(), RenderType.getCutout() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderType.getCutout() );
|
||||
|
||||
// Setup TESRs
|
||||
ClientRegistry.bindTileEntityRenderer( TileMonitor.FACTORY_NORMAL, TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( TileMonitor.FACTORY_ADVANCED, TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( TileTurtle.FACTORY_NORMAL, TileEntityTurtleRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( TileTurtle.FACTORY_ADVANCED, TileEntityTurtleRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
|
||||
// TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler( TurtlePlayer.TYPE, TurtlePlayerRenderer::new );
|
||||
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
|
||||
|
||||
registerItemProperty( "state",
|
||||
( stack, world, player ) -> ItemPocketComputer.getState( stack ).ordinal(),
|
||||
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
registerItemProperty( "state",
|
||||
( stack, world, player ) -> IColouredItem.getColourBasic( stack ) != -1 ? 1 : 0,
|
||||
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty( String name, IItemPropertyGetter getter, Supplier<? extends Item>... items )
|
||||
{
|
||||
ResourceLocation id = new ResourceLocation( ComputerCraft.MOD_ID, name );
|
||||
for( Supplier<? extends Item> item : items )
|
||||
{
|
||||
ItemModelsProperties.func_239418_a_( item.get(), id, getter );
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerContainers()
|
||||
{
|
||||
// My IDE doesn't think so, but we do actually need these generics.
|
||||
|
||||
ScreenManager.<ContainerComputer, GuiComputer<ContainerComputer>>registerFactory( ContainerComputer.TYPE, GuiComputer::create );
|
||||
ScreenManager.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>registerFactory( ContainerPocketComputer.TYPE, GuiComputer::createPocket );
|
||||
ScreenManager.registerFactory( ContainerTurtle.TYPE, GuiTurtle::new );
|
||||
ScreenManager.<ContainerComputer, GuiComputer<ContainerComputer>>registerFactory( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
|
||||
ScreenManager.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>registerFactory( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
|
||||
ScreenManager.registerFactory( Registry.ModContainers.TURTLE.get(), GuiTurtle::new );
|
||||
|
||||
ScreenManager.registerFactory( ContainerPrinter.TYPE, GuiPrinter::new );
|
||||
ScreenManager.registerFactory( ContainerDiskDrive.TYPE, GuiDiskDrive::new );
|
||||
ScreenManager.registerFactory( ContainerHeldItem.PRINTOUT_TYPE, GuiPrintout::new );
|
||||
ScreenManager.registerFactory( Registry.ModContainers.PRINTER.get(), GuiPrinter::new );
|
||||
ScreenManager.registerFactory( Registry.ModContainers.DISK_DRIVE.get(), GuiDiskDrive::new );
|
||||
ScreenManager.registerFactory( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new );
|
||||
|
||||
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>registerFactory( ContainerViewComputer.TYPE, GuiComputer::createView );
|
||||
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>registerFactory( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView );
|
||||
}
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
|
@@ -8,19 +8,20 @@ package dan200.computercraft.client.render;
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.shapes.VoxelShape;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.DrawHighlightEvent;
|
||||
@@ -51,7 +52,7 @@ public final class CableHighlightRenderer
|
||||
BlockState state = world.getBlockState( pos );
|
||||
|
||||
// We only care about instances with both cable and modem.
|
||||
if( state.getBlock() != ComputerCraft.Blocks.cable || state.get( BlockCable.MODEM ).getFacing() == null || !state.get( BlockCable.CABLE ) )
|
||||
if( state.getBlock() != Registry.ModBlocks.CABLE.get() || state.get( BlockCable.MODEM ).getFacing() == null || !state.get( BlockCable.CABLE ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -62,7 +63,7 @@ public final class CableHighlightRenderer
|
||||
? CableShapes.getModemShape( state )
|
||||
: CableShapes.getCableShape( state );
|
||||
|
||||
Vec3d cameraPos = info.getProjectedView();
|
||||
Vector3d cameraPos = info.getProjectedView();
|
||||
double xOffset = pos.getX() - cameraPos.getX();
|
||||
double yOffset = pos.getY() - cameraPos.getY();
|
||||
double zOffset = pos.getZ() - cameraPos.getZ();
|
||||
|
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ComputerBorderRenderer
|
||||
{
|
||||
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_normal.png" );
|
||||
public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" );
|
||||
public static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" );
|
||||
public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" );
|
||||
|
||||
private static final Matrix4f IDENTITY = new Matrix4f();
|
||||
|
||||
static
|
||||
{
|
||||
IDENTITY.setIdentity();
|
||||
}
|
||||
|
||||
/**
|
||||
* The margin between the terminal and its border.
|
||||
*/
|
||||
public static final int MARGIN = 2;
|
||||
|
||||
/**
|
||||
* The width of the terminal border.
|
||||
*/
|
||||
public static final int BORDER = 12;
|
||||
|
||||
private static final int CORNER_TOP_Y = 28;
|
||||
private static final int CORNER_BOTTOM_Y = CORNER_TOP_Y + BORDER;
|
||||
private static final int CORNER_LEFT_X = BORDER;
|
||||
private static final int CORNER_RIGHT_X = CORNER_LEFT_X + BORDER;
|
||||
private static final int BORDER_RIGHT_X = 36;
|
||||
private static final int GAP = 4;
|
||||
|
||||
private static final float TEX_SCALE = 1 / 256.0f;
|
||||
|
||||
private final Matrix4f transform;
|
||||
private final IVertexBuilder builder;
|
||||
private final int z;
|
||||
private final float r, g, b;
|
||||
|
||||
public ComputerBorderRenderer( Matrix4f transform, IVertexBuilder builder, int z, float r, float g, float b )
|
||||
{
|
||||
this.transform = transform;
|
||||
this.builder = builder;
|
||||
this.z = z;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
public static ResourceLocation getTexture( @Nonnull ComputerFamily family )
|
||||
{
|
||||
switch( family )
|
||||
{
|
||||
case NORMAL:
|
||||
default:
|
||||
return BACKGROUND_NORMAL;
|
||||
case ADVANCED:
|
||||
return BACKGROUND_ADVANCED;
|
||||
case COMMAND:
|
||||
return BACKGROUND_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
public static void render( int x, int y, int z, int width, int height )
|
||||
{
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX );
|
||||
|
||||
render( IDENTITY, buffer, x, y, z, width, height );
|
||||
|
||||
RenderSystem.enableAlphaTest();
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height )
|
||||
{
|
||||
render( transform, buffer, x, y, z, width, height, 1, 1, 1 );
|
||||
}
|
||||
|
||||
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, float r, float g, float b )
|
||||
{
|
||||
render( transform, buffer, x, y, z, width, height, 0, r, g, b );
|
||||
}
|
||||
|
||||
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, int borderHeight, float r, float g, float b )
|
||||
{
|
||||
new ComputerBorderRenderer( transform, buffer, z, r, g, b ).doRender( x, y, width, height, borderHeight );
|
||||
}
|
||||
|
||||
public void doRender( int x, int y, int width, int height, int bottomHeight )
|
||||
{
|
||||
int endX = x + width;
|
||||
int endY = y + height;
|
||||
|
||||
// Vertical bars
|
||||
renderLine( x - BORDER, y, 0, CORNER_TOP_Y, BORDER, endY - y );
|
||||
renderLine( endX, y, BORDER_RIGHT_X, CORNER_TOP_Y, BORDER, endY - y );
|
||||
|
||||
// Top bar
|
||||
renderLine( x, y - BORDER, 0, 0, endX - x, BORDER );
|
||||
renderCorner( x - BORDER, y - BORDER, CORNER_LEFT_X, CORNER_TOP_Y );
|
||||
renderCorner( endX, y - BORDER, CORNER_RIGHT_X, CORNER_TOP_Y );
|
||||
|
||||
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
|
||||
// pocket computer's lights).
|
||||
if( bottomHeight <= 0 )
|
||||
{
|
||||
renderLine( x, endY, 0, BORDER, endX - x, BORDER );
|
||||
renderCorner( x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y );
|
||||
renderCorner( endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
|
||||
// lights, and then the bottom outer corners.
|
||||
renderTexture( x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2 );
|
||||
renderTexture( x, endY, 0, BORDER, width, BORDER / 2, BORDER, BORDER / 2 );
|
||||
renderTexture( endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2 );
|
||||
|
||||
renderTexture( x - BORDER, endY + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP );
|
||||
renderTexture( x, endY + BORDER / 2, 0, BORDER + GAP, width, bottomHeight, BORDER, GAP );
|
||||
renderTexture( endX, endY + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP );
|
||||
|
||||
renderTexture( x - BORDER, endY + bottomHeight + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2 );
|
||||
renderTexture( x, endY + bottomHeight + BORDER / 2, 0, BORDER + BORDER / 2, width, BORDER / 2 );
|
||||
renderTexture( endX, endY + bottomHeight + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2 );
|
||||
}
|
||||
}
|
||||
|
||||
private void renderCorner( int x, int y, int u, int v )
|
||||
{
|
||||
renderTexture( x, y, u, v, BORDER, BORDER, BORDER, BORDER );
|
||||
}
|
||||
|
||||
private void renderLine( int x, int y, int u, int v, int width, int height )
|
||||
{
|
||||
renderTexture( x, y, u, v, width, height, BORDER, BORDER );
|
||||
}
|
||||
|
||||
private void renderTexture( int x, int y, int u, int v, int width, int height )
|
||||
{
|
||||
renderTexture( x, y, u, v, width, height, width, height );
|
||||
}
|
||||
|
||||
private void renderTexture( int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight )
|
||||
{
|
||||
builder.pos( transform, x, y + height, z ).color( r, g, b, 1.0f ).tex( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).endVertex();
|
||||
builder.pos( transform, x + width, y + height, z ).color( r, g, b, 1.0f ).tex( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).endVertex();
|
||||
builder.pos( transform, x + width, y, z ).color( r, g, b, 1.0f ).tex( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ).endVertex();
|
||||
builder.pos( transform, x, y, z ).color( r, g, b, 1.0f ).tex( u * TEX_SCALE, v * TEX_SCALE ).endVertex();
|
||||
}
|
||||
}
|
@@ -10,12 +10,12 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.player.AbstractClientPlayerEntity;
|
||||
import net.minecraft.client.renderer.FirstPersonRenderer;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Vector3f;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.HandSide;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
|
||||
public abstract class ItemMapLikeRenderer
|
||||
{
|
||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
@@ -16,9 +15,13 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.RenderHandEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
@@ -27,7 +30,8 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
import static dan200.computercraft.client.gui.GuiComputer.*;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
|
||||
/**
|
||||
* Emulates map rendering for pocket computers.
|
||||
@@ -35,8 +39,6 @@ import static dan200.computercraft.client.gui.GuiComputer.*;
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
{
|
||||
private static final int MARGIN = 2;
|
||||
private static final int FRAME = 12;
|
||||
private static final int LIGHT_HEIGHT = 8;
|
||||
|
||||
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
|
||||
@@ -67,8 +69,8 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
int termWidth, termHeight;
|
||||
if( terminal == null )
|
||||
{
|
||||
termWidth = ComputerCraft.terminalWidth_pocketComputer;
|
||||
termHeight = ComputerCraft.terminalHeight_pocketComputer;
|
||||
termWidth = ComputerCraft.pocketTermWidth;
|
||||
termHeight = ComputerCraft.pocketTermHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -86,7 +88,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
transform.rotate( Vector3f.ZP.rotationDegrees( 180f ) );
|
||||
transform.scale( 0.5f, 0.5f, 0.5f );
|
||||
|
||||
float scale = 0.75f / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
|
||||
float scale = 0.75f / Math.max( width + BORDER * 2, height + BORDER * 2 + LIGHT_HEIGHT );
|
||||
transform.scale( scale, scale, 0 );
|
||||
transform.translate( -0.5 * width, -0.5 * height, 0 );
|
||||
|
||||
@@ -117,10 +119,8 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
|
||||
private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height )
|
||||
{
|
||||
Minecraft.getInstance().getTextureManager().bindTexture( colour != -1
|
||||
? BACKGROUND_COLOUR
|
||||
: family == ComputerFamily.NORMAL ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
|
||||
);
|
||||
Minecraft.getInstance().getTextureManager()
|
||||
.bindTexture( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) );
|
||||
|
||||
float r = ((colour >>> 16) & 0xFF) / 255.0f;
|
||||
float g = ((colour >>> 8) & 0xFF) / 255.0f;
|
||||
@@ -130,28 +130,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX );
|
||||
|
||||
// Top left, middle, right
|
||||
renderTexture( transform, buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
|
||||
renderTexture( transform, buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
|
||||
renderTexture( transform, buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
|
||||
|
||||
// Left and bright border
|
||||
renderTexture( transform, buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
|
||||
renderTexture( transform, buffer, width, 0, 36, 28, FRAME, height, r, g, b );
|
||||
|
||||
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
|
||||
// lights, and then the bottom outer corners.
|
||||
renderTexture( transform, buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
|
||||
renderTexture( transform, buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
|
||||
renderTexture( transform, buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
|
||||
|
||||
renderTexture( transform, buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
|
||||
renderTexture( transform, buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
|
||||
renderTexture( transform, buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
|
||||
|
||||
renderTexture( transform, buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
|
||||
renderTexture( transform, buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
|
||||
renderTexture( transform, buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
|
||||
ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, width, height, LIGHT_HEIGHT, r, g, b );
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
@@ -168,26 +147,12 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
|
||||
buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width, height + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width, height + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
buffer.pos( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
RenderSystem.enableTexture();
|
||||
}
|
||||
|
||||
private static void renderTexture( Matrix4f transform, IVertexBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
|
||||
{
|
||||
renderTexture( transform, builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
|
||||
}
|
||||
|
||||
private static void renderTexture( Matrix4f transform, IVertexBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
|
||||
{
|
||||
float scale = 1 / 255.0f;
|
||||
builder.pos( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).tex( textureX * scale, (textureY + textureHeight) * scale ).endVertex();
|
||||
builder.pos( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).endVertex();
|
||||
builder.pos( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).tex( (textureX + textureWidth) * scale, textureY * scale ).endVertex();
|
||||
builder.pos( transform, x, y, 0 ).color( r, g, b, 1.0f ).tex( textureX * scale, textureY * scale ).endVertex();
|
||||
}
|
||||
}
|
||||
|
@@ -9,9 +9,9 @@ import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.Vector3f;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.RenderHandEvent;
|
||||
import net.minecraftforge.client.event.RenderItemInFrameEvent;
|
||||
|
@@ -9,12 +9,12 @@ import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.DrawHighlightEvent;
|
||||
@@ -61,7 +61,7 @@ public final class MonitorHighlightRenderer
|
||||
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
|
||||
|
||||
MatrixStack transformStack = event.getMatrix();
|
||||
Vec3d cameraPos = event.getInfo().getProjectedView();
|
||||
Vector3d cameraPos = event.getInfo().getProjectedView();
|
||||
transformStack.push();
|
||||
transformStack.translate( pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ() );
|
||||
|
||||
|
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
class MonitorTextureBufferShader
|
||||
{
|
||||
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
|
||||
|
||||
private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer( 16 );
|
||||
private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer( 16 * 3 );
|
||||
|
||||
private static int uniformMv;
|
||||
|
||||
private static int uniformFont;
|
||||
private static int uniformWidth;
|
||||
private static int uniformHeight;
|
||||
private static int uniformTbo;
|
||||
private static int uniformPalette;
|
||||
|
||||
private static boolean initialised;
|
||||
private static boolean ok;
|
||||
private static int program;
|
||||
|
||||
static void setupUniform( Matrix4f transform, int width, int height, Palette palette, boolean greyscale )
|
||||
{
|
||||
MATRIX_BUFFER.rewind();
|
||||
transform.write( MATRIX_BUFFER );
|
||||
MATRIX_BUFFER.rewind();
|
||||
RenderSystem.glUniformMatrix4( uniformMv, false, MATRIX_BUFFER );
|
||||
|
||||
RenderSystem.glUniform1i( uniformWidth, width );
|
||||
RenderSystem.glUniform1i( uniformHeight, height );
|
||||
|
||||
// TODO: Cache this? Maybe??
|
||||
PALETTE_BUFFER.rewind();
|
||||
for( int i = 0; i < 16; i++ )
|
||||
{
|
||||
double[] colour = palette.getColour( i );
|
||||
if( greyscale )
|
||||
{
|
||||
float f = FixedWidthFontRenderer.toGreyscale( colour );
|
||||
PALETTE_BUFFER.put( f ).put( f ).put( f );
|
||||
}
|
||||
else
|
||||
{
|
||||
PALETTE_BUFFER.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] );
|
||||
}
|
||||
}
|
||||
PALETTE_BUFFER.flip();
|
||||
RenderSystem.glUniform3( uniformPalette, PALETTE_BUFFER );
|
||||
}
|
||||
|
||||
static boolean use()
|
||||
{
|
||||
if( initialised )
|
||||
{
|
||||
if( ok ) GlStateManager.useProgram( program );
|
||||
return ok;
|
||||
}
|
||||
|
||||
if( ok = load() )
|
||||
{
|
||||
GL20.glUseProgram( program );
|
||||
RenderSystem.glUniform1i( uniformFont, 0 );
|
||||
RenderSystem.glUniform1i( uniformTbo, TEXTURE_INDEX - GL13.GL_TEXTURE0 );
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
private static boolean load()
|
||||
{
|
||||
initialised = true;
|
||||
|
||||
try
|
||||
{
|
||||
int vertexShader = loadShader( GL20.GL_VERTEX_SHADER, "assets/computercraft/shaders/monitor.vert" );
|
||||
int fragmentShader = loadShader( GL20.GL_FRAGMENT_SHADER, "assets/computercraft/shaders/monitor.frag" );
|
||||
|
||||
program = GlStateManager.createProgram();
|
||||
GlStateManager.attachShader( program, vertexShader );
|
||||
GlStateManager.attachShader( program, fragmentShader );
|
||||
GL20.glBindAttribLocation( program, 0, "v_pos" );
|
||||
|
||||
GlStateManager.linkProgram( program );
|
||||
boolean ok = GlStateManager.getProgram( program, GL20.GL_LINK_STATUS ) != 0;
|
||||
String log = GlStateManager.getProgramInfoLog( program, Short.MAX_VALUE ).trim();
|
||||
if( !Strings.isNullOrEmpty( log ) )
|
||||
{
|
||||
ComputerCraft.log.warn( "Problems when linking monitor shader: {}", log );
|
||||
}
|
||||
|
||||
GL20.glDetachShader( program, vertexShader );
|
||||
GL20.glDetachShader( program, fragmentShader );
|
||||
GlStateManager.deleteShader( vertexShader );
|
||||
GlStateManager.deleteShader( fragmentShader );
|
||||
|
||||
if( !ok ) return false;
|
||||
|
||||
uniformMv = getUniformLocation( program, "u_mv" );
|
||||
uniformFont = getUniformLocation( program, "u_font" );
|
||||
uniformWidth = getUniformLocation( program, "u_width" );
|
||||
uniformHeight = getUniformLocation( program, "u_height" );
|
||||
uniformTbo = getUniformLocation( program, "u_tbo" );
|
||||
uniformPalette = getUniformLocation( program, "u_palette" );
|
||||
|
||||
ComputerCraft.log.info( "Loaded monitor shader." );
|
||||
return true;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
ComputerCraft.log.error( "Cannot load monitor shaders", e );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int loadShader( int kind, String path )
|
||||
{
|
||||
InputStream stream = TileEntityMonitorRenderer.class.getClassLoader().getResourceAsStream( path );
|
||||
if( stream == null ) throw new IllegalArgumentException( "Cannot find " + path );
|
||||
String contents = TextureUtil.readResourceAsString( stream );
|
||||
|
||||
int shader = GlStateManager.createShader( kind );
|
||||
|
||||
GlStateManager.shaderSource( shader, contents );
|
||||
GlStateManager.compileShader( shader );
|
||||
|
||||
boolean ok = GlStateManager.getShader( shader, GL20.GL_COMPILE_STATUS ) != 0;
|
||||
String log = GlStateManager.getShaderInfoLog( shader, Short.MAX_VALUE ).trim();
|
||||
if( !Strings.isNullOrEmpty( log ) )
|
||||
{
|
||||
ComputerCraft.log.warn( "Problems when loading monitor shader {}: {}", path, log );
|
||||
}
|
||||
|
||||
if( !ok ) throw new IllegalStateException( "Cannot compile shader " + path );
|
||||
return shader;
|
||||
}
|
||||
|
||||
private static int getUniformLocation( int program, String name )
|
||||
{
|
||||
int uniform = GlStateManager.getUniformLocation( program, name );
|
||||
if( uniform == -1 ) throw new IllegalStateException( "Cannot find uniform " + name );
|
||||
return uniform;
|
||||
}
|
||||
}
|
@@ -10,11 +10,11 @@ import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.RenderState;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
|
@@ -6,22 +6,39 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import dan200.computercraft.client.FrameInfo;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GLAllocation;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
|
||||
|
||||
public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
|
||||
{
|
||||
@@ -30,6 +47,7 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
|
||||
* the monitor frame and contents.
|
||||
*/
|
||||
private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
|
||||
private static ByteBuffer tboContents;
|
||||
|
||||
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
|
||||
|
||||
@@ -90,50 +108,23 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
|
||||
Terminal terminal = originTerminal.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
boolean redraw = originTerminal.pollTerminalChanged();
|
||||
if( originTerminal.buffer == null )
|
||||
{
|
||||
originTerminal.createBuffer( MonitorRenderer.VBO );
|
||||
redraw = true;
|
||||
}
|
||||
VertexBuffer vbo = originTerminal.buffer;
|
||||
|
||||
// Draw a terminal
|
||||
double xScale = xSize / (terminal.getWidth() * FixedWidthFontRenderer.FONT_WIDTH);
|
||||
double yScale = ySize / (terminal.getHeight() * FixedWidthFontRenderer.FONT_HEIGHT);
|
||||
int width = terminal.getWidth(), height = terminal.getHeight();
|
||||
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
|
||||
double xScale = xSize / pixelWidth;
|
||||
double yScale = ySize / pixelHeight;
|
||||
transform.push();
|
||||
transform.scale( (float) xScale, (float) -yScale, 1.0f );
|
||||
|
||||
float xMargin = (float) (MARGIN / xScale);
|
||||
float yMargin = (float) (MARGIN / yScale);
|
||||
|
||||
Matrix4f matrix = transform.getLast().getMatrix();
|
||||
|
||||
if( redraw )
|
||||
{
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder builder = tessellator.getBuffer();
|
||||
builder.begin( FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat() );
|
||||
FixedWidthFontRenderer.drawTerminalWithoutCursor(
|
||||
IDENTITY, builder, 0, 0,
|
||||
terminal, !originTerminal.isColour(), yMargin, yMargin, xMargin, xMargin
|
||||
);
|
||||
|
||||
builder.finishDrawing();
|
||||
vbo.upload( builder );
|
||||
}
|
||||
|
||||
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
|
||||
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
|
||||
// for now.
|
||||
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
|
||||
FixedWidthFontRenderer.TYPE.setupRenderState();
|
||||
|
||||
vbo.bindBuffer();
|
||||
FixedWidthFontRenderer.TYPE.getVertexFormat().setupBufferState( 0L );
|
||||
vbo.draw( matrix, FixedWidthFontRenderer.TYPE.getDrawMode() );
|
||||
VertexBuffer.unbindBuffer();
|
||||
FixedWidthFontRenderer.TYPE.getVertexFormat().clearBufferState();
|
||||
renderTerminal( matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
|
||||
|
||||
// We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is
|
||||
// reasonable.
|
||||
@@ -158,4 +149,95 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
|
||||
|
||||
transform.pop();
|
||||
}
|
||||
|
||||
private static void renderTerminal( Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
|
||||
{
|
||||
Terminal terminal = monitor.getTerminal();
|
||||
|
||||
MonitorRenderer renderType = MonitorRenderer.current();
|
||||
boolean redraw = monitor.pollTerminalChanged();
|
||||
if( monitor.createBuffer( renderType ) ) redraw = true;
|
||||
|
||||
switch( renderType )
|
||||
{
|
||||
case TBO:
|
||||
{
|
||||
if( !MonitorTextureBufferShader.use() ) return;
|
||||
|
||||
int width = terminal.getWidth(), height = terminal.getHeight();
|
||||
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
|
||||
|
||||
if( redraw )
|
||||
{
|
||||
int size = width * height * 3;
|
||||
if( tboContents == null || tboContents.capacity() < size )
|
||||
{
|
||||
tboContents = GLAllocation.createDirectByteBuffer( size );
|
||||
}
|
||||
|
||||
ByteBuffer monitorBuffer = tboContents;
|
||||
monitorBuffer.clear();
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y );
|
||||
for( int x = 0; x < width; x++ )
|
||||
{
|
||||
monitorBuffer.put( (byte) (text.charAt( x ) & 0xFF) );
|
||||
monitorBuffer.put( (byte) getColour( textColour.charAt( x ), Colour.WHITE ) );
|
||||
monitorBuffer.put( (byte) getColour( background.charAt( x ), Colour.BLACK ) );
|
||||
}
|
||||
}
|
||||
monitorBuffer.flip();
|
||||
|
||||
GlStateManager.bindBuffer( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer );
|
||||
GlStateManager.bufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW );
|
||||
GlStateManager.bindBuffer( GL31.GL_TEXTURE_BUFFER, 0 );
|
||||
}
|
||||
|
||||
// Nobody knows what they're doing!
|
||||
GlStateManager.activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX );
|
||||
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture );
|
||||
GlStateManager.activeTexture( GL13.GL_TEXTURE0 );
|
||||
|
||||
MonitorTextureBufferShader.setupUniform( matrix, width, height, terminal.getPalette(), !monitor.isColour() );
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin( GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION );
|
||||
buffer.pos( -xMargin, -yMargin, 0 ).endVertex();
|
||||
buffer.pos( -xMargin, pixelHeight + yMargin, 0 ).endVertex();
|
||||
buffer.pos( pixelWidth + xMargin, -yMargin, 0 ).endVertex();
|
||||
buffer.pos( pixelWidth + xMargin, pixelHeight + yMargin, 0 ).endVertex();
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.useProgram( 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
case VBO:
|
||||
{
|
||||
VertexBuffer vbo = monitor.buffer;
|
||||
if( redraw )
|
||||
{
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder builder = tessellator.getBuffer();
|
||||
builder.begin( FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat() );
|
||||
FixedWidthFontRenderer.drawTerminalWithoutCursor(
|
||||
IDENTITY, builder, 0, 0,
|
||||
terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
|
||||
);
|
||||
|
||||
builder.finishDrawing();
|
||||
vbo.upload( builder );
|
||||
}
|
||||
|
||||
vbo.bindBuffer();
|
||||
FixedWidthFontRenderer.TYPE.getVertexFormat().setupBufferState( 0L );
|
||||
vbo.draw( matrix, FixedWidthFontRenderer.TYPE.getDrawMode() );
|
||||
VertexBuffer.unbindBuffer();
|
||||
FixedWidthFontRenderer.TYPE.getVertexFormat().clearBufferState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,6 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.Atlases;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.Vector3f;
|
||||
import net.minecraft.client.renderer.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.ModelManager;
|
||||
@@ -31,7 +29,9 @@ import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraftforge.client.model.data.EmptyModelData;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -99,7 +99,7 @@ public class TileEntityTurtleRenderer extends TileEntityRenderer<TileTurtle>
|
||||
transform.push();
|
||||
|
||||
// Setup the transform.
|
||||
Vec3d offset = turtle.getRenderOffset( partialTicks );
|
||||
Vector3d offset = turtle.getRenderOffset( partialTicks );
|
||||
float yaw = turtle.getRenderYaw( partialTicks );
|
||||
transform.translate( offset.x, offset.y, offset.z );
|
||||
|
||||
|
@@ -57,16 +57,16 @@ public final class TurtleModelLoader implements IModelLoader<TurtleModelLoader.T
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Material> getTextures( IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors )
|
||||
public Collection<RenderMaterial> getTextures( IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors )
|
||||
{
|
||||
Set<Material> materials = new HashSet<>();
|
||||
Set<RenderMaterial> materials = new HashSet<>();
|
||||
materials.addAll( modelGetter.apply( family ).getTextures( modelGetter, missingTextureErrors ) );
|
||||
materials.addAll( modelGetter.apply( COLOUR_TURTLE_MODEL ).getTextures( modelGetter, missingTextureErrors ) );
|
||||
return materials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBakedModel bake( IModelConfiguration owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, IModelTransform transform, ItemOverrideList overrides, ResourceLocation modelLocation )
|
||||
public IBakedModel bake( IModelConfiguration owner, ModelBakery bakery, Function<RenderMaterial, TextureAtlasSprite> spriteGetter, IModelTransform transform, ItemOverrideList overrides, ResourceLocation modelLocation )
|
||||
{
|
||||
return new TurtleSmartItemModel(
|
||||
bakery.getBakedModel( family, transform, spriteGetter ),
|
||||
|
@@ -7,12 +7,12 @@ package dan200.computercraft.client.render;
|
||||
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.TransformationMatrix;
|
||||
import net.minecraft.client.renderer.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
import net.minecraftforge.client.model.data.EmptyModelData;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
|
||||
|
@@ -7,7 +7,6 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import dan200.computercraft.client.gui.GuiComputer;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
@@ -27,7 +26,7 @@ public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer>
|
||||
@Override
|
||||
public ResourceLocation getEntityTexture( @Nonnull TurtlePlayer entity )
|
||||
{
|
||||
return GuiComputer.BACKGROUND_NORMAL;
|
||||
return ComputerBorderRenderer.BACKGROUND_NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -15,14 +15,14 @@ import dan200.computercraft.shared.util.Holiday;
|
||||
import dan200.computercraft.shared.util.HolidayUtil;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.TransformationMatrix;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -109,7 +109,7 @@ public class TurtleSmartItemModel implements IBakedModel
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public IBakedModel getModelWithOverrides( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable LivingEntity entity )
|
||||
public IBakedModel func_239290_a_( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity )
|
||||
{
|
||||
ItemTurtle turtle = (ItemTurtle) stack.getItem();
|
||||
int colour = turtle.getColour( stack );
|
||||
|
@@ -116,7 +116,7 @@ public abstract class ComputerAccess implements IComputerAccess
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
public void queueEvent( @Nonnull String event, Object... arguments )
|
||||
{
|
||||
Objects.requireNonNull( event, "event cannot be null" );
|
||||
m_environment.queueEvent( event, arguments );
|
||||
|
@@ -6,8 +6,8 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.BinaryWritableHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
||||
@@ -17,7 +17,6 @@ import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.filesystem.FileSystemWrapper;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
@@ -29,17 +28,19 @@ import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.getString;
|
||||
|
||||
/**
|
||||
* The FS API allows you to manipulate files and the filesystem.
|
||||
*
|
||||
* @cc.module fs
|
||||
*/
|
||||
public class FSAPI implements ILuaAPI
|
||||
{
|
||||
private IAPIEnvironment m_env;
|
||||
private FileSystem m_fileSystem;
|
||||
private final IAPIEnvironment environment;
|
||||
private FileSystem fileSystem = null;
|
||||
|
||||
public FSAPI( IAPIEnvironment env )
|
||||
{
|
||||
m_env = env;
|
||||
m_fileSystem = null;
|
||||
environment = env;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,329 +52,442 @@ public class FSAPI implements ILuaAPI
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
m_fileSystem = m_env.getFileSystem();
|
||||
fileSystem = environment.getFileSystem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
m_fileSystem = null;
|
||||
fileSystem = null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
/**
|
||||
* Returns a list of files in a directory.
|
||||
*
|
||||
* @param path The path to list.
|
||||
* @return A table with a list of files in the directory.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] list( String path ) throws LuaException
|
||||
{
|
||||
return new String[] {
|
||||
"list",
|
||||
"combine",
|
||||
"getName",
|
||||
"getSize",
|
||||
"exists",
|
||||
"isDir",
|
||||
"isReadOnly",
|
||||
"makeDir",
|
||||
"move",
|
||||
"copy",
|
||||
"delete",
|
||||
"open",
|
||||
"getDrive",
|
||||
"getFreeSpace",
|
||||
"find",
|
||||
"getDir",
|
||||
"getCapacity",
|
||||
"attributes",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
try
|
||||
{
|
||||
case 0:
|
||||
return fileSystem.list( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines two parts of a path into one full path, adding separators as
|
||||
* needed.
|
||||
*
|
||||
* @param pathA The first part of the path. For example, a parent directory path.
|
||||
* @param pathB The second part of the path. For example, a file name.
|
||||
* @return The new path, with separators added between parts as needed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String combine( String pathA, String pathB )
|
||||
{
|
||||
return fileSystem.combine( pathA, pathB );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name portion of a path.
|
||||
*
|
||||
* @param path The path to get the name from.
|
||||
* @return The final part of the path (the file name).
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String getName( String path )
|
||||
{
|
||||
return FileSystem.getName( path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent directory portion of a path.
|
||||
*
|
||||
* @param path The path to get the directory from.
|
||||
* @return The path with the final part removed (the parent directory).
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String getDir( String path )
|
||||
{
|
||||
return FileSystem.getDirectory( path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the specified file.
|
||||
*
|
||||
* @param path The file to get the file size of.
|
||||
* @return The size of the file, in bytes.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final long getSize( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileSystem.getSize( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified path exists.
|
||||
*
|
||||
* @param path The path to check the existence of.
|
||||
* @return Whether the path exists.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean exists( String path )
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileSystem.exists( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified path is a directory.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return Whether the path is a directory.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean isDir( String path )
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileSystem.isDir( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a path is read-only.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return Whether the path cannot be written to.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean isReadOnly( String path )
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileSystem.isReadOnly( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory, and any missing parents, at the specified path.
|
||||
*
|
||||
* @param path The path to the directory to create.
|
||||
* @throws LuaException If the directory couldn't be created.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void makeDir( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
fileSystem.makeDir( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a file or directory from one path to another.
|
||||
*
|
||||
* Any parent directories are created as needed.
|
||||
*
|
||||
* @param path The current file or directory to move from.
|
||||
* @param dest The destination path for the file or directory.
|
||||
* @throws LuaException If the file or directory couldn't be moved.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void move( String path, String dest ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
fileSystem.move( path, dest );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file or directory to a new path.
|
||||
*
|
||||
* Any parent directories are created as needed.
|
||||
*
|
||||
* @param path The file or directory to copy.
|
||||
* @param dest The path to the destination file or directory.
|
||||
* @throws LuaException If the file or directory couldn't be copied.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void copy( String path, String dest ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
fileSystem.copy( path, dest );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file or directory.
|
||||
*
|
||||
* If the path points to a directory, all of the enclosed files and
|
||||
* subdirectories are also deleted.
|
||||
*
|
||||
* @param path The path to the file or directory to delete.
|
||||
* @throws LuaException If the file or directory couldn't be deleted.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void delete( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
fileSystem.delete( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Add individual handle type documentation
|
||||
|
||||
/**
|
||||
* Opens a file for reading or writing at a path.
|
||||
*
|
||||
* The mode parameter can be {@code r} to read, {@code w} to write (deleting
|
||||
* all contents), or {@code a} to append (keeping contents). If {@code b} is
|
||||
* added to the end, the file will be opened in binary mode; otherwise, it's
|
||||
* opened in text mode.
|
||||
*
|
||||
* @param path The path to the file to open.
|
||||
* @param mode The mode to open the file with.
|
||||
* @return A file handle object for the file, or {@code nil} + an error message on error.
|
||||
* @throws LuaException If an invalid mode was specified.
|
||||
* @cc.treturn [1] table A file handle object for the file.
|
||||
* @cc.treturn [2] nil If the file does not exist, or cannot be opened.
|
||||
* @cc.treturn string|nil A message explaining why the file cannot be opened.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] open( String path, String mode ) throws LuaException
|
||||
{
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
try
|
||||
{
|
||||
switch( mode )
|
||||
{
|
||||
// list
|
||||
String path = getString( args, 0 );
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
try
|
||||
case "r":
|
||||
{
|
||||
return new Object[] { m_fileSystem.list( path ) };
|
||||
// Open the file for reading, then create a wrapper around the reader
|
||||
FileSystemWrapper<BufferedReader> reader = fileSystem.openForRead( path, EncodedReadableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedReadableHandle( reader.get(), reader ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
case "w":
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
// Open the file for writing, then create a wrapper around the writer
|
||||
FileSystemWrapper<BufferedWriter> writer = fileSystem.openForWrite( path, false, EncodedWritableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "a":
|
||||
{
|
||||
// Open the file for appending, then create a wrapper around the writer
|
||||
FileSystemWrapper<BufferedWriter> writer = fileSystem.openForWrite( path, true, EncodedWritableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "rb":
|
||||
{
|
||||
// Open the file for binary reading, then create a wrapper around the reader
|
||||
FileSystemWrapper<ReadableByteChannel> reader = fileSystem.openForRead( path, Function.identity() );
|
||||
return new Object[] { BinaryReadableHandle.of( reader.get(), reader ) };
|
||||
}
|
||||
case "wb":
|
||||
{
|
||||
// Open the file for binary writing, then create a wrapper around the writer
|
||||
FileSystemWrapper<WritableByteChannel> writer = fileSystem.openForWrite( path, false, Function.identity() );
|
||||
return new Object[] { BinaryWritableHandle.of( writer.get(), writer ) };
|
||||
}
|
||||
case "ab":
|
||||
{
|
||||
// Open the file for binary appending, then create a wrapper around the reader
|
||||
FileSystemWrapper<WritableByteChannel> writer = fileSystem.openForWrite( path, true, Function.identity() );
|
||||
return new Object[] { BinaryWritableHandle.of( writer.get(), writer ) };
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported mode" );
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// combine
|
||||
String pathA = getString( args, 0 );
|
||||
String pathB = getString( args, 1 );
|
||||
return new Object[] { m_fileSystem.combine( pathA, pathB ) };
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// getName
|
||||
String path = getString( args, 0 );
|
||||
return new Object[] { FileSystem.getName( path ) };
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// getSize
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
return new Object[] { m_fileSystem.getSize( path ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
// exists
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
return new Object[] { m_fileSystem.exists( path ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return new Object[] { false };
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
// isDir
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
return new Object[] { m_fileSystem.isDir( path ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return new Object[] { false };
|
||||
}
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
// isReadOnly
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
return new Object[] { m_fileSystem.isReadOnly( path ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return new Object[] { false };
|
||||
}
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
// makeDir
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.makeDir( path );
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
// move
|
||||
String path = getString( args, 0 );
|
||||
String dest = getString( args, 1 );
|
||||
try
|
||||
{
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.move( path, dest );
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
// copy
|
||||
String path = getString( args, 0 );
|
||||
String dest = getString( args, 1 );
|
||||
try
|
||||
{
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.copy( path, dest );
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 10:
|
||||
{
|
||||
// delete
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
m_fileSystem.delete( path );
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 11:
|
||||
{
|
||||
// open
|
||||
String path = getString( args, 0 );
|
||||
String mode = getString( args, 1 );
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
try
|
||||
{
|
||||
switch( mode )
|
||||
{
|
||||
case "r":
|
||||
{
|
||||
// Open the file for reading, then create a wrapper around the reader
|
||||
FileSystemWrapper<BufferedReader> reader = m_fileSystem.openForRead( path, EncodedReadableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedReadableHandle( reader.get(), reader ) };
|
||||
}
|
||||
case "w":
|
||||
{
|
||||
// Open the file for writing, then create a wrapper around the writer
|
||||
FileSystemWrapper<BufferedWriter> writer = m_fileSystem.openForWrite( path, false, EncodedWritableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "a":
|
||||
{
|
||||
// Open the file for appending, then create a wrapper around the writer
|
||||
FileSystemWrapper<BufferedWriter> writer = m_fileSystem.openForWrite( path, true, EncodedWritableHandle::openUtf8 );
|
||||
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "rb":
|
||||
{
|
||||
// Open the file for binary reading, then create a wrapper around the reader
|
||||
FileSystemWrapper<ReadableByteChannel> reader = m_fileSystem.openForRead( path, Function.identity() );
|
||||
return new Object[] { new BinaryReadableHandle( reader.get(), reader ) };
|
||||
}
|
||||
case "wb":
|
||||
{
|
||||
// Open the file for binary writing, then create a wrapper around the writer
|
||||
FileSystemWrapper<WritableByteChannel> writer = m_fileSystem.openForWrite( path, false, Function.identity() );
|
||||
return new Object[] { new BinaryWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
case "ab":
|
||||
{
|
||||
// Open the file for binary appending, then create a wrapper around the reader
|
||||
FileSystemWrapper<WritableByteChannel> writer = m_fileSystem.openForWrite( path, true, Function.identity() );
|
||||
return new Object[] { new BinaryWritableHandle( writer.get(), writer ) };
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported mode" );
|
||||
}
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return new Object[] { null, e.getMessage() };
|
||||
}
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
// getDrive
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
if( !m_fileSystem.exists( path ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new Object[] { m_fileSystem.getMountLabel( path ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 13:
|
||||
{
|
||||
// getFreeSpace
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
long freeSpace = m_fileSystem.getFreeSpace( path );
|
||||
if( freeSpace >= 0 )
|
||||
{
|
||||
return new Object[] { freeSpace };
|
||||
}
|
||||
return new Object[] { "unlimited" };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 14: // find
|
||||
{
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
m_env.addTrackingChange( TrackingField.FS_OPS );
|
||||
return new Object[] { m_fileSystem.find( path ) };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 15: // getDir
|
||||
{
|
||||
String path = getString( args, 0 );
|
||||
return new Object[] { FileSystem.getDirectory( path ) };
|
||||
}
|
||||
case 16: // getCapacity
|
||||
{
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
OptionalLong capacity = m_fileSystem.getCapacity( path );
|
||||
return new Object[] { capacity.isPresent() ? capacity.getAsLong() : null };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 17: // attributes
|
||||
{
|
||||
String path = getString( args, 0 );
|
||||
try
|
||||
{
|
||||
BasicFileAttributes attributes = m_fileSystem.getAttributes( path );
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put( "modification", getFileTime( attributes.lastModifiedTime() ) );
|
||||
result.put( "created", getFileTime( attributes.creationTime() ) );
|
||||
result.put( "size", attributes.isDirectory() ? 0 : attributes.size() );
|
||||
result.put( "isDir", attributes.isDirectory() );
|
||||
return new Object[] { result };
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
default:
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
return new Object[] { null, e.getMessage() };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the mount that the specified path is located on.
|
||||
*
|
||||
* @param path The path to get the drive of.
|
||||
* @return The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
* @cc.treturn string The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] getDrive( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileSystem.exists( path ) ? new Object[] { fileSystem.getMountLabel( path ) } : null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of free space available on the drive the path is
|
||||
* located on.
|
||||
*
|
||||
* @param path The path to check the free space for.
|
||||
* @return The amount of free space available, in bytes.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
* @cc.treturn number|"unlimited" The amount of free space available, in bytes, or "unlimited".
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object getFreeSpace( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
long freeSpace = fileSystem.getFreeSpace( path );
|
||||
return freeSpace >= 0 ? freeSpace : "unlimited";
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for files matching a string with wildcards.
|
||||
*
|
||||
* This string is formatted like a normal path string, but can include any
|
||||
* number of wildcards ({@code *}) to look for files matching anything.
|
||||
* For example, {@code rom/* /command*} will look for any path starting with
|
||||
* {@code command} inside any subdirectory of {@code /rom}.
|
||||
*
|
||||
* @param path The wildcard-qualified path to search for.
|
||||
* @return A list of paths that match the search string.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] find( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
||||
return fileSystem.find( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a path is mounted to the parent filesystem.
|
||||
*
|
||||
* The root filesystem "/" is considered a mount, along with disk folders and the rom folder. Other programs
|
||||
* (such as network shares) can extend this to make other mount types by correctly assigning their return value for
|
||||
* getDrive.
|
||||
*
|
||||
* @param path The path of the drive to get.
|
||||
* @return The drive's capacity.
|
||||
* @throws LuaException If the capacity cannot be determined.
|
||||
* @cc.treturn number|nil This drive's capacity. This will be nil for "read-only" drives, such as the ROM or
|
||||
* treasure disks.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object getCapacity( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
OptionalLong capacity = fileSystem.getCapacity( path );
|
||||
return capacity.isPresent() ? capacity.getAsLong() : null;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attributes about a specific file or folder.
|
||||
*
|
||||
* The returned attributes table contains information about the size of the file, whether it is a directory, and
|
||||
* when it was created and last modified.
|
||||
*
|
||||
* The creation and modification times are given as the number of milliseconds since the UNIX epoch. This may be
|
||||
* given to {@link OSAPI#date} in order to convert it to more usable form.
|
||||
*
|
||||
* @param path The path to get attributes for.
|
||||
* @return The resulting attributes.
|
||||
* @throws LuaException If the path does not exist.
|
||||
* @cc.treturn { size = number, isDir = boolean, created = number, modified = number } The resulting attributes.
|
||||
* @see #getSize If you only care about the file's size.
|
||||
* @see #isDir If you only care whether a path is a directory or not.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Map<String, Object> attributes( String path ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
BasicFileAttributes attributes = fileSystem.getAttributes( path );
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put( "modification", getFileTime( attributes.lastModifiedTime() ) );
|
||||
result.put( "modified", getFileTime( attributes.lastModifiedTime() ) );
|
||||
result.put( "created", getFileTime( attributes.creationTime() ) );
|
||||
result.put( "size", attributes.isDirectory() ? 0 : attributes.size() );
|
||||
result.put( "isDir", attributes.isDirectory() );
|
||||
return result;
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,9 +6,10 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.apis.http.*;
|
||||
import dan200.computercraft.core.apis.http.request.HttpRequest;
|
||||
import dan200.computercraft.core.apis.http.websocket.Websocket;
|
||||
@@ -21,10 +22,16 @@ import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.*;
|
||||
import static dan200.computercraft.core.apis.TableHelper.*;
|
||||
|
||||
/**
|
||||
* The http library allows communicating with web servers, sending and receiving data from them.
|
||||
*
|
||||
* @cc.module http
|
||||
* @hidden
|
||||
*/
|
||||
public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
private final IAPIEnvironment m_apiEnvironment;
|
||||
@@ -68,135 +75,106 @@ public class HTTPAPI implements ILuaAPI
|
||||
Resource.cleanup();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
@LuaFunction
|
||||
public final Object[] request( IArguments args ) throws LuaException
|
||||
{
|
||||
return new String[] {
|
||||
"request",
|
||||
"checkURL",
|
||||
"websocket",
|
||||
};
|
||||
String address, postString, requestMethod;
|
||||
Map<?, ?> headerTable;
|
||||
boolean binary, redirect;
|
||||
|
||||
if( args.get( 0 ) instanceof Map )
|
||||
{
|
||||
Map<?, ?> options = args.getTable( 0 );
|
||||
address = getStringField( options, "url" );
|
||||
postString = optStringField( options, "body", null );
|
||||
headerTable = optTableField( options, "headers", Collections.emptyMap() );
|
||||
binary = optBooleanField( options, "binary", false );
|
||||
requestMethod = optStringField( options, "method", null );
|
||||
redirect = optBooleanField( options, "redirect", true );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get URL and post information
|
||||
address = args.getString( 0 );
|
||||
postString = args.optString( 1, null );
|
||||
headerTable = args.optTable( 2, Collections.emptyMap() );
|
||||
binary = args.optBoolean( 3, false );
|
||||
requestMethod = null;
|
||||
redirect = true;
|
||||
}
|
||||
|
||||
HttpHeaders headers = getHeaders( headerTable );
|
||||
|
||||
HttpMethod httpMethod;
|
||||
if( requestMethod == null )
|
||||
{
|
||||
httpMethod = postString == null ? HttpMethod.GET : HttpMethod.POST;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpMethod = HttpMethod.valueOf( requestMethod.toUpperCase( Locale.ROOT ) );
|
||||
if( httpMethod == null || requestMethod.equalsIgnoreCase( "CONNECT" ) )
|
||||
{
|
||||
throw new LuaException( "Unsupported HTTP method" );
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
URI uri = HttpRequest.checkUri( address );
|
||||
HttpRequest request = new HttpRequest( requests, m_apiEnvironment, address, postString, headers, binary, redirect );
|
||||
|
||||
// Make the request
|
||||
request.queue( r -> r.request( uri, httpMethod ) );
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( "resource" )
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
@LuaFunction
|
||||
public final Object[] checkURL( String address )
|
||||
{
|
||||
switch( method )
|
||||
try
|
||||
{
|
||||
case 0: // request
|
||||
URI uri = HttpRequest.checkUri( address );
|
||||
new CheckUrl( checkUrls, m_apiEnvironment, address, uri ).queue( CheckUrl::run );
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final Object[] websocket( String address, Optional<Map<?, ?>> headerTbl ) throws LuaException
|
||||
{
|
||||
if( !ComputerCraft.httpWebsocketEnabled )
|
||||
{
|
||||
throw new LuaException( "Websocket connections are disabled" );
|
||||
}
|
||||
|
||||
HttpHeaders headers = getHeaders( headerTbl.orElse( Collections.emptyMap() ) );
|
||||
|
||||
try
|
||||
{
|
||||
URI uri = Websocket.checkUri( address );
|
||||
if( !new Websocket( websockets, m_apiEnvironment, uri, address, headers ).queue( Websocket::connect ) )
|
||||
{
|
||||
String address, postString, requestMethod;
|
||||
Map<?, ?> headerTable;
|
||||
boolean binary, redirect;
|
||||
|
||||
if( args.length >= 1 && args[0] instanceof Map )
|
||||
{
|
||||
Map<?, ?> options = (Map<?, ?>) args[0];
|
||||
address = getStringField( options, "url" );
|
||||
postString = optStringField( options, "body", null );
|
||||
headerTable = optTableField( options, "headers", Collections.emptyMap() );
|
||||
binary = optBooleanField( options, "binary", false );
|
||||
requestMethod = optStringField( options, "method", null );
|
||||
redirect = optBooleanField( options, "redirect", true );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get URL and post information
|
||||
address = getString( args, 0 );
|
||||
postString = optString( args, 1, null );
|
||||
headerTable = optTable( args, 2, Collections.emptyMap() );
|
||||
binary = optBoolean( args, 3, false );
|
||||
requestMethod = null;
|
||||
redirect = true;
|
||||
}
|
||||
|
||||
HttpHeaders headers = getHeaders( headerTable );
|
||||
|
||||
|
||||
HttpMethod httpMethod;
|
||||
if( requestMethod == null )
|
||||
{
|
||||
httpMethod = postString == null ? HttpMethod.GET : HttpMethod.POST;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpMethod = HttpMethod.valueOf( requestMethod.toUpperCase( Locale.ROOT ) );
|
||||
if( httpMethod == null || requestMethod.equalsIgnoreCase( "CONNECT" ) )
|
||||
{
|
||||
throw new LuaException( "Unsupported HTTP method" );
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
URI uri = HttpRequest.checkUri( address );
|
||||
HttpRequest request = new HttpRequest( requests, m_apiEnvironment, address, postString, headers, binary, redirect );
|
||||
|
||||
long requestBody = request.body().readableBytes() + HttpRequest.getHeaderSize( headers );
|
||||
if( ComputerCraft.httpMaxUpload != 0 && requestBody > ComputerCraft.httpMaxUpload )
|
||||
{
|
||||
throw new HTTPRequestException( "Request body is too large" );
|
||||
}
|
||||
|
||||
// Make the request
|
||||
request.queue( r -> r.request( uri, httpMethod ) );
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
throw new LuaException( "Too many websockets already open" );
|
||||
}
|
||||
case 1: // checkURL
|
||||
{
|
||||
String address = getString( args, 0 );
|
||||
|
||||
// Check URL
|
||||
try
|
||||
{
|
||||
URI uri = HttpRequest.checkUri( address );
|
||||
new CheckUrl( checkUrls, m_apiEnvironment, address, uri ).queue( CheckUrl::run );
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
case 2: // websocket
|
||||
{
|
||||
String address = getString( args, 0 );
|
||||
Map<?, ?> headerTbl = optTable( args, 1, Collections.emptyMap() );
|
||||
|
||||
if( !ComputerCraft.httpWebsocketEnabled )
|
||||
{
|
||||
throw new LuaException( "Websocket connections are disabled" );
|
||||
}
|
||||
|
||||
HttpHeaders headers = getHeaders( headerTbl );
|
||||
|
||||
try
|
||||
{
|
||||
URI uri = Websocket.checkUri( address );
|
||||
if( !new Websocket( websockets, m_apiEnvironment, uri, address, headers ).queue( Websocket::connect ) )
|
||||
{
|
||||
throw new LuaException( "Too many websockets already open" );
|
||||
}
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
return new Object[] { true };
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
return new Object[] { false, e.getMessage() };
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -43,7 +43,7 @@ public interface IAPIEnvironment
|
||||
|
||||
void reboot();
|
||||
|
||||
void queueEvent( String event, Object[] args );
|
||||
void queueEvent( String event, Object... args );
|
||||
|
||||
void setOutput( ComputerSide side, int output );
|
||||
|
||||
|
@@ -5,9 +5,10 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -20,11 +21,16 @@ import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.util.*;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.*;
|
||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
|
||||
/**
|
||||
* The {@link OSAPI} API allows interacting with the current computer.
|
||||
*
|
||||
* @cc.module os
|
||||
*/
|
||||
public class OSAPI implements ILuaAPI
|
||||
{
|
||||
private IAPIEnvironment m_apiEnvironment;
|
||||
private final IAPIEnvironment apiEnvironment;
|
||||
|
||||
private final Int2ObjectMap<Alarm> m_alarms = new Int2ObjectOpenHashMap<>();
|
||||
private int m_clock;
|
||||
@@ -55,11 +61,9 @@ public class OSAPI implements ILuaAPI
|
||||
|
||||
public OSAPI( IAPIEnvironment environment )
|
||||
{
|
||||
m_apiEnvironment = environment;
|
||||
apiEnvironment = environment;
|
||||
}
|
||||
|
||||
// ILuaAPI implementation
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
@@ -69,8 +73,8 @@ public class OSAPI implements ILuaAPI
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
m_time = m_apiEnvironment.getComputerEnvironment().getTimeOfDay();
|
||||
m_day = m_apiEnvironment.getComputerEnvironment().getDay();
|
||||
m_time = apiEnvironment.getComputerEnvironment().getTimeOfDay();
|
||||
m_day = apiEnvironment.getComputerEnvironment().getDay();
|
||||
m_clock = 0;
|
||||
|
||||
synchronized( m_alarms )
|
||||
@@ -89,8 +93,8 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
double previousTime = m_time;
|
||||
int previousDay = m_day;
|
||||
double time = m_apiEnvironment.getComputerEnvironment().getTimeOfDay();
|
||||
int day = m_apiEnvironment.getComputerEnvironment().getDay();
|
||||
double time = apiEnvironment.getComputerEnvironment().getTimeOfDay();
|
||||
int day = apiEnvironment.getComputerEnvironment().getDay();
|
||||
|
||||
if( time > previousTime || day > previousDay )
|
||||
{
|
||||
@@ -103,7 +107,7 @@ public class OSAPI implements ILuaAPI
|
||||
double t = alarm.m_day * 24.0 + alarm.m_time;
|
||||
if( now >= t )
|
||||
{
|
||||
queueLuaEvent( "alarm", new Object[] { entry.getIntKey() } );
|
||||
apiEnvironment.queueEvent( "alarm", entry.getIntKey() );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
@@ -123,31 +127,6 @@ public class OSAPI implements ILuaAPI
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
"queueEvent",
|
||||
"startTimer",
|
||||
"setAlarm",
|
||||
"shutdown",
|
||||
"reboot",
|
||||
"computerID",
|
||||
"getComputerID",
|
||||
"setComputerLabel",
|
||||
"computerLabel",
|
||||
"getComputerLabel",
|
||||
"clock",
|
||||
"time",
|
||||
"day",
|
||||
"cancelTimer",
|
||||
"cancelAlarm",
|
||||
"epoch",
|
||||
"date",
|
||||
};
|
||||
}
|
||||
|
||||
private static float getTimeForCalendar( Calendar c )
|
||||
{
|
||||
float time = c.get( Calendar.HOUR_OF_DAY );
|
||||
@@ -174,214 +153,310 @@ public class OSAPI implements ILuaAPI
|
||||
return c.getTime().getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
/**
|
||||
* Adds an event to the event queue. This event can later be pulled with
|
||||
* os.pullEvent.
|
||||
*
|
||||
* @param name The name of the event to queue.
|
||||
* @param args The parameters of the event.
|
||||
* @cc.tparam string name The name of the event to queue.
|
||||
* @cc.param ... The parameters of the event.
|
||||
* @cc.see os.pullEvent To pull the event queued
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void queueEvent( String name, IArguments args )
|
||||
{
|
||||
switch( method )
|
||||
apiEnvironment.queueEvent( name, args.drop( 1 ).getAll() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a timer that will run for the specified number of seconds. Once
|
||||
* the timer fires, a timer event will be added to the queue with the ID
|
||||
* returned from this function as the first parameter.
|
||||
*
|
||||
* @param timer The number of seconds until the timer fires.
|
||||
* @return The ID of the new timer.
|
||||
* @throws LuaException If the time is below zero.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int startTimer( double timer ) throws LuaException
|
||||
{
|
||||
return apiEnvironment.startTimer( Math.round( checkFinite( 0, timer ) / 0.05 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a timer previously started with startTimer. This will stop the
|
||||
* timer from firing.
|
||||
*
|
||||
* @param token The ID of the timer to cancel.
|
||||
* @see #startTimer To start a timer.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void cancelTimer( int token )
|
||||
{
|
||||
apiEnvironment.cancelTimer( token );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an alarm that will fire at the specified world time. When it fires,
|
||||
* an alarm event will be added to the event queue.
|
||||
*
|
||||
* @param time The time at which to fire the alarm, in the range [0.0, 24.0).
|
||||
* @return The ID of the alarm that was set.
|
||||
* @throws LuaException If the time is out of range.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int setAlarm( double time ) throws LuaException
|
||||
{
|
||||
checkFinite( 0, time );
|
||||
if( time < 0.0 || time >= 24.0 ) throw new LuaException( "Number out of range" );
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
case 0: // queueEvent
|
||||
queueLuaEvent( getString( args, 0 ), trimArray( args, 1 ) );
|
||||
return null;
|
||||
case 1:
|
||||
{
|
||||
// startTimer
|
||||
double timer = getFiniteDouble( args, 0 );
|
||||
int id = m_apiEnvironment.startTimer( Math.round( timer / 0.05 ) );
|
||||
return new Object[] { id };
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// setAlarm
|
||||
double time = getFiniteDouble( args, 0 );
|
||||
if( time < 0.0 || time >= 24.0 )
|
||||
{
|
||||
throw new LuaException( "Number out of range" );
|
||||
}
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
int day = time > m_time ? m_day : m_day + 1;
|
||||
m_alarms.put( m_nextAlarmToken, new Alarm( time, day ) );
|
||||
return new Object[] { m_nextAlarmToken++ };
|
||||
}
|
||||
}
|
||||
case 3: // shutdown
|
||||
m_apiEnvironment.shutdown();
|
||||
return null;
|
||||
case 4: // reboot
|
||||
m_apiEnvironment.reboot();
|
||||
return null;
|
||||
case 5:
|
||||
case 6: // computerID/getComputerID
|
||||
return new Object[] { getComputerID() };
|
||||
case 7:
|
||||
{
|
||||
// setComputerLabel
|
||||
String label = optString( args, 0, null );
|
||||
m_apiEnvironment.setLabel( StringUtil.normaliseLabel( label ) );
|
||||
return null;
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
{
|
||||
// computerLabel/getComputerLabel
|
||||
String label = m_apiEnvironment.getLabel();
|
||||
if( label != null )
|
||||
{
|
||||
return new Object[] { label };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 10: // clock
|
||||
return new Object[] { m_clock * 0.05 };
|
||||
case 11:
|
||||
{
|
||||
// time
|
||||
Object value = args.length > 0 ? args[0] : null;
|
||||
if( value instanceof Map ) return new Object[] { LuaDateTime.fromTable( (Map<?, ?>) value ) };
|
||||
|
||||
String param = optString( args, 0, "ingame" );
|
||||
switch( param.toLowerCase( Locale.ROOT ) )
|
||||
{
|
||||
case "utc":
|
||||
{
|
||||
// Get Hour of day (UTC)
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return new Object[] { getTimeForCalendar( c ) };
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get Hour of day (local time)
|
||||
Calendar c = Calendar.getInstance();
|
||||
return new Object[] { getTimeForCalendar( c ) };
|
||||
}
|
||||
case "ingame":
|
||||
// Get ingame hour
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return new Object[] { m_time };
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
}
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
// day
|
||||
String param = optString( args, 0, "ingame" );
|
||||
switch( param.toLowerCase( Locale.ROOT ) )
|
||||
{
|
||||
case "utc":
|
||||
{
|
||||
// Get numbers of days since 1970-01-01 (utc)
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return new Object[] { getDayForCalendar( c ) };
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get numbers of days since 1970-01-01 (local time)
|
||||
Calendar c = Calendar.getInstance();
|
||||
return new Object[] { getDayForCalendar( c ) };
|
||||
}
|
||||
case "ingame":
|
||||
// Get game day
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return new Object[] { m_day };
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
}
|
||||
}
|
||||
case 13:
|
||||
{
|
||||
// cancelTimer
|
||||
int token = getInt( args, 0 );
|
||||
m_apiEnvironment.cancelTimer( token );
|
||||
return null;
|
||||
}
|
||||
case 14:
|
||||
{
|
||||
// cancelAlarm
|
||||
int token = getInt( args, 0 );
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
m_alarms.remove( token );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 15: // epoch
|
||||
{
|
||||
String param = optString( args, 0, "ingame" );
|
||||
switch( param.toLowerCase( Locale.ROOT ) )
|
||||
{
|
||||
case "utc":
|
||||
{
|
||||
// Get utc epoch
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return new Object[] { getEpochForCalendar( c ) };
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get local epoch
|
||||
Calendar c = Calendar.getInstance();
|
||||
return new Object[] { getEpochForCalendar( c ) };
|
||||
}
|
||||
case "ingame":
|
||||
// Get in-game epoch
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return new Object[] { m_day * 86400000 + (int) (m_time * 3600000.0f) };
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
}
|
||||
}
|
||||
case 16: // date
|
||||
{
|
||||
String format = optString( args, 0, "%c" );
|
||||
long time = optLong( args, 1, Instant.now().getEpochSecond() );
|
||||
|
||||
Instant instant = Instant.ofEpochSecond( time );
|
||||
ZonedDateTime date;
|
||||
ZoneOffset offset;
|
||||
if( format.startsWith( "!" ) )
|
||||
{
|
||||
offset = ZoneOffset.UTC;
|
||||
date = ZonedDateTime.ofInstant( instant, offset );
|
||||
format = format.substring( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ZoneId id = ZoneId.systemDefault();
|
||||
offset = id.getRules().getOffset( instant );
|
||||
date = ZonedDateTime.ofInstant( instant, id );
|
||||
}
|
||||
|
||||
if( format.equals( "*t" ) ) return new Object[] { LuaDateTime.toTable( date, offset, instant ) };
|
||||
|
||||
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
|
||||
LuaDateTime.format( formatter, format, offset );
|
||||
return new Object[] { formatter.toFormatter( Locale.ROOT ).format( date ) };
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
int day = time > m_time ? m_day : m_day + 1;
|
||||
m_alarms.put( m_nextAlarmToken, new Alarm( time, day ) );
|
||||
return m_nextAlarmToken++;
|
||||
}
|
||||
}
|
||||
|
||||
// Private methods
|
||||
|
||||
private void queueLuaEvent( String event, Object[] args )
|
||||
/**
|
||||
* Cancels an alarm previously started with setAlarm. This will stop the
|
||||
* alarm from firing.
|
||||
*
|
||||
* @param token The ID of the alarm to cancel.
|
||||
* @see #setAlarm To set an alarm.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void cancelAlarm( int token )
|
||||
{
|
||||
m_apiEnvironment.queueEvent( event, args );
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
m_alarms.remove( token );
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] trimArray( Object[] array, int skip )
|
||||
/**
|
||||
* Shuts down the computer immediately.
|
||||
*/
|
||||
@LuaFunction( "shutdown" )
|
||||
public final void doShutdown()
|
||||
{
|
||||
return Arrays.copyOfRange( array, skip, array.length );
|
||||
apiEnvironment.shutdown();
|
||||
}
|
||||
|
||||
private int getComputerID()
|
||||
/**
|
||||
* Reboots the computer immediately.
|
||||
*/
|
||||
@LuaFunction( "reboot" )
|
||||
public final void doReboot()
|
||||
{
|
||||
return m_apiEnvironment.getComputerID();
|
||||
apiEnvironment.reboot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the computer.
|
||||
*
|
||||
* @return The ID of the computer.
|
||||
*/
|
||||
@LuaFunction( { "getComputerID", "computerID" } )
|
||||
public final int getComputerID()
|
||||
{
|
||||
return apiEnvironment.getComputerID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of the computer, or {@code nil} if none is set.
|
||||
*
|
||||
* @return The label of the computer.
|
||||
* @cc.treturn string The label of the computer.
|
||||
*/
|
||||
@LuaFunction( { "getComputerLabel", "computerLabel" } )
|
||||
public final Object[] getComputerLabel()
|
||||
{
|
||||
String label = apiEnvironment.getLabel();
|
||||
return label == null ? null : new Object[] { label };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the label of this computer.
|
||||
*
|
||||
* @param label The new label. May be {@code nil} in order to clear it.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void setComputerLabel( Optional<String> label )
|
||||
{
|
||||
apiEnvironment.setLabel( StringUtil.normaliseLabel( label.orElse( null ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of seconds that the computer has been running.
|
||||
*
|
||||
* @return The computer's uptime.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final double clock()
|
||||
{
|
||||
return m_clock * 0.05;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time depending on the string passed in. This will
|
||||
* always be in the range [0.0, 24.0).
|
||||
*
|
||||
* * If called with {@code ingame}, the current world time will be returned.
|
||||
* This is the default if nothing is passed.
|
||||
* * If called with {@code utc}, returns the hour of the day in UTC time.
|
||||
* * If called with {@code local}, returns the hour of the day in the
|
||||
* timezone the server is located in.
|
||||
*
|
||||
* This function can also be called with a table returned from {@link #date},
|
||||
* which will convert the date fields into a UNIX timestamp (number of
|
||||
* seconds since 1 January 1970).
|
||||
*
|
||||
* @param args The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code ingame} locale if not specified.
|
||||
* @return The hour of the selected locale, or a UNIX timestamp from the table, depending on the argument passed in.
|
||||
* @throws LuaException If an invalid locale is passed.
|
||||
* @cc.tparam [opt] string|table locale The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code ingame} locale if not specified.
|
||||
* @see #date To get a date table that can be converted with this function.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object time( IArguments args ) throws LuaException
|
||||
{
|
||||
Object value = args.get( 0 );
|
||||
if( value instanceof Map ) return LuaDateTime.fromTable( (Map<?, ?>) value );
|
||||
|
||||
String param = args.optString( 0, "ingame" );
|
||||
switch( param.toLowerCase( Locale.ROOT ) )
|
||||
{
|
||||
case "utc": // Get Hour of day (UTC)
|
||||
return getTimeForCalendar( Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ) );
|
||||
case "local": // Get Hour of day (local time)
|
||||
return getTimeForCalendar( Calendar.getInstance() );
|
||||
case "ingame": // Get in-game hour
|
||||
return m_time;
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the day depending on the locale specified.
|
||||
*
|
||||
* * If called with {@code ingame}, returns the number of days since the
|
||||
* world was created. This is the default.
|
||||
* * If called with {@code utc}, returns the number of days since 1 January
|
||||
* 1970 in the UTC timezone.
|
||||
* * If called with {@code local}, returns the number of days since 1
|
||||
* January 1970 in the server's local timezone.
|
||||
*
|
||||
* @param args The locale to get the day for. Defaults to {@code ingame} if not set.
|
||||
* @return The day depending on the selected locale.
|
||||
* @throws LuaException If an invalid locale is passed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int day( Optional<String> args ) throws LuaException
|
||||
{
|
||||
switch( args.orElse( "ingame" ).toLowerCase( Locale.ROOT ) )
|
||||
{
|
||||
case "utc": // Get numbers of days since 1970-01-01 (utc)
|
||||
return getDayForCalendar( Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ) );
|
||||
case "local": // Get numbers of days since 1970-01-01 (local time)
|
||||
return getDayForCalendar( Calendar.getInstance() );
|
||||
case "ingame":// Get game day
|
||||
return m_day;
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of seconds since an epoch depending on the locale.
|
||||
*
|
||||
* * If called with {@code ingame}, returns the number of seconds since the
|
||||
* world was created. This is the default.
|
||||
* * If called with {@code utc}, returns the number of seconds since 1
|
||||
* January 1970 in the UTC timezone.
|
||||
* * If called with {@code local}, returns the number of seconds since 1
|
||||
* January 1970 in the server's local timezone.
|
||||
*
|
||||
* @param args The locale to get the seconds for. Defaults to {@code ingame} if not set.
|
||||
* @return The seconds since the epoch depending on the selected locale.
|
||||
* @throws LuaException If an invalid locale is passed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final long epoch( Optional<String> args ) throws LuaException
|
||||
{
|
||||
switch( args.orElse( "ingame" ).toLowerCase( Locale.ROOT ) )
|
||||
{
|
||||
case "utc":
|
||||
{
|
||||
// Get utc epoch
|
||||
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||
return getEpochForCalendar( c );
|
||||
}
|
||||
case "local":
|
||||
{
|
||||
// Get local epoch
|
||||
Calendar c = Calendar.getInstance();
|
||||
return getEpochForCalendar( c );
|
||||
}
|
||||
case "ingame":
|
||||
// Get in-game epoch
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return m_day * 86400000 + (int) (m_time * 3600000.0f);
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date string (or table) using a specified format string and
|
||||
* optional time to format.
|
||||
*
|
||||
* The format string takes the same formats as C's {@code strftime} function
|
||||
* (http://www.cplusplus.com/reference/ctime/strftime/). In extension, it
|
||||
* can be prefixed with an exclamation mark ({@code !}) to use UTC time
|
||||
* instead of the server's local timezone.
|
||||
*
|
||||
* If the format is exactly {@code *t} (optionally prefixed with {@code !}), a
|
||||
* table will be returned instead. This table has fields for the year, month,
|
||||
* day, hour, minute, second, day of the week, day of the year, and whether
|
||||
* Daylight Savings Time is in effect. This table can be converted to a UNIX
|
||||
* timestamp (days since 1 January 1970) with {@link #date}.
|
||||
*
|
||||
* @param formatA The format of the string to return. This defaults to {@code %c}, which expands to a string similar to "Sat Dec 24 16:58:00 2011".
|
||||
* @param timeA The time to convert to a string. This defaults to the current time.
|
||||
* @return The resulting format string.
|
||||
* @throws LuaException If an invalid format is passed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object date( Optional<String> formatA, Optional<Long> timeA ) throws LuaException
|
||||
{
|
||||
String format = formatA.orElse( "%c" );
|
||||
long time = timeA.orElseGet( () -> Instant.now().getEpochSecond() );
|
||||
|
||||
Instant instant = Instant.ofEpochSecond( time );
|
||||
ZonedDateTime date;
|
||||
ZoneOffset offset;
|
||||
if( format.startsWith( "!" ) )
|
||||
{
|
||||
offset = ZoneOffset.UTC;
|
||||
date = ZonedDateTime.ofInstant( instant, offset );
|
||||
format = format.substring( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ZoneId id = ZoneId.systemDefault();
|
||||
offset = id.getRules().getOffset( instant );
|
||||
date = ZonedDateTime.ofInstant( instant, id );
|
||||
}
|
||||
|
||||
if( format.equals( "*t" ) ) return LuaDateTime.toTable( date, offset, instant );
|
||||
|
||||
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
|
||||
LuaDateTime.format( formatter, format, offset );
|
||||
return formatter.toFormatter( Locale.ROOT ).format( date );
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,88 +7,80 @@ package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||
import dan200.computercraft.api.peripheral.NotAttachedException;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.NamedMethod;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.getString;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* CC's "native" peripheral API. This is wrapped within CraftOS to provide a version which works with modems.
|
||||
*
|
||||
* @cc.module peripheral
|
||||
* @hidden
|
||||
*/
|
||||
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener
|
||||
{
|
||||
private class PeripheralWrapper extends ComputerAccess
|
||||
{
|
||||
private final String m_side;
|
||||
private final IPeripheral m_peripheral;
|
||||
private final String side;
|
||||
private final IPeripheral peripheral;
|
||||
|
||||
private String m_type;
|
||||
private String[] m_methods;
|
||||
private Map<String, Integer> m_methodMap;
|
||||
private boolean m_attached;
|
||||
private final String type;
|
||||
private final Map<String, PeripheralMethod> methodMap;
|
||||
private boolean attached;
|
||||
|
||||
PeripheralWrapper( IPeripheral peripheral, String side )
|
||||
{
|
||||
super( m_environment );
|
||||
m_side = side;
|
||||
m_peripheral = peripheral;
|
||||
m_attached = false;
|
||||
super( environment );
|
||||
this.side = side;
|
||||
this.peripheral = peripheral;
|
||||
attached = false;
|
||||
|
||||
m_type = peripheral.getType();
|
||||
m_methods = peripheral.getMethodNames();
|
||||
assert m_type != null;
|
||||
assert m_methods != null;
|
||||
type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be 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 );
|
||||
}
|
||||
}
|
||||
methodMap = PeripheralAPI.getMethods( peripheral );
|
||||
}
|
||||
|
||||
public IPeripheral getPeripheral()
|
||||
{
|
||||
return m_peripheral;
|
||||
return peripheral;
|
||||
}
|
||||
|
||||
public String getType()
|
||||
{
|
||||
return m_type;
|
||||
return type;
|
||||
}
|
||||
|
||||
public String[] getMethods()
|
||||
public Collection<String> getMethods()
|
||||
{
|
||||
return m_methods;
|
||||
return methodMap.keySet();
|
||||
}
|
||||
|
||||
public synchronized boolean isAttached()
|
||||
{
|
||||
return m_attached;
|
||||
return attached;
|
||||
}
|
||||
|
||||
public synchronized void attach()
|
||||
{
|
||||
m_attached = true;
|
||||
m_peripheral.attach( this );
|
||||
attached = true;
|
||||
peripheral.attach( this );
|
||||
}
|
||||
|
||||
public void detach()
|
||||
{
|
||||
// Call detach
|
||||
m_peripheral.detach( this );
|
||||
peripheral.detach( this );
|
||||
|
||||
synchronized( this )
|
||||
{
|
||||
@@ -96,63 +88,56 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
unmountAll();
|
||||
}
|
||||
|
||||
m_attached = false;
|
||||
attached = false;
|
||||
}
|
||||
|
||||
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
|
||||
public MethodResult call( ILuaContext context, String methodName, IArguments arguments ) throws LuaException
|
||||
{
|
||||
int method = -1;
|
||||
PeripheralMethod method;
|
||||
synchronized( this )
|
||||
{
|
||||
if( m_methodMap.containsKey( methodName ) )
|
||||
{
|
||||
method = m_methodMap.get( methodName );
|
||||
}
|
||||
}
|
||||
if( method >= 0 )
|
||||
{
|
||||
m_environment.addTrackingChange( TrackingField.PERIPHERAL_OPS );
|
||||
return m_peripheral.callMethod( this, context, method, arguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new LuaException( "No such method " + methodName );
|
||||
method = methodMap.get( methodName );
|
||||
}
|
||||
|
||||
if( method == null ) throw new LuaException( "No such method " + methodName );
|
||||
|
||||
environment.addTrackingChange( TrackingField.PERIPHERAL_OPS );
|
||||
return method.apply( peripheral, context, this, arguments );
|
||||
}
|
||||
|
||||
// IComputerAccess implementation
|
||||
@Override
|
||||
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName )
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
return super.mount( desiredLoc, mount, driveName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName )
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
return super.mountWritable( desiredLoc, mount, driveName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unmount( String location )
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
super.unmount( location );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID()
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
return super.getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueEvent( @Nonnull final String event, final Object[] arguments )
|
||||
public void queueEvent( @Nonnull String event, Object... arguments )
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
super.queueEvent( event, arguments );
|
||||
}
|
||||
|
||||
@@ -160,18 +145,18 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
@Override
|
||||
public String getAttachmentName()
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
return m_side;
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
return side;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<String, IPeripheral> getAvailablePeripherals()
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
|
||||
Map<String, IPeripheral> peripherals = new HashMap<>();
|
||||
for( PeripheralWrapper wrapper : m_peripherals )
|
||||
for( PeripheralWrapper wrapper : PeripheralAPI.this.peripherals )
|
||||
{
|
||||
if( wrapper != null && wrapper.isAttached() )
|
||||
{
|
||||
@@ -186,9 +171,9 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
@Override
|
||||
public IPeripheral getAvailablePeripheral( @Nonnull String name )
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
|
||||
for( PeripheralWrapper wrapper : m_peripherals )
|
||||
for( PeripheralWrapper wrapper : peripherals )
|
||||
{
|
||||
if( wrapper != null && wrapper.isAttached() && wrapper.getAttachmentName().equals( name ) )
|
||||
{
|
||||
@@ -202,27 +187,20 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
@Override
|
||||
public IWorkMonitor getMainThreadMonitor()
|
||||
{
|
||||
if( !m_attached ) throw new NotAttachedException();
|
||||
if( !attached ) throw new NotAttachedException();
|
||||
return super.getMainThreadMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
private final IAPIEnvironment m_environment;
|
||||
private final PeripheralWrapper[] m_peripherals;
|
||||
private boolean m_running;
|
||||
private final IAPIEnvironment environment;
|
||||
private final PeripheralWrapper[] peripherals = new PeripheralWrapper[6];
|
||||
private boolean running;
|
||||
|
||||
public PeripheralAPI( IAPIEnvironment environment )
|
||||
{
|
||||
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;
|
||||
this.environment = environment;
|
||||
this.environment.setPeripheralChangeListener( this );
|
||||
running = false;
|
||||
}
|
||||
|
||||
// IPeripheralChangeListener
|
||||
@@ -230,37 +208,35 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
@Override
|
||||
public void onPeripheralChanged( ComputerSide side, IPeripheral newPeripheral )
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
synchronized( peripherals )
|
||||
{
|
||||
int index = side.ordinal();
|
||||
if( m_peripherals[index] != null )
|
||||
if( peripherals[index] != null )
|
||||
{
|
||||
// Queue a detachment
|
||||
final PeripheralWrapper wrapper = m_peripherals[index];
|
||||
final PeripheralWrapper wrapper = peripherals[index];
|
||||
if( wrapper.isAttached() ) wrapper.detach();
|
||||
|
||||
// Queue a detachment event
|
||||
m_environment.queueEvent( "peripheral_detach", new Object[] { side.getName() } );
|
||||
environment.queueEvent( "peripheral_detach", side.getName() );
|
||||
}
|
||||
|
||||
// Assign the new peripheral
|
||||
m_peripherals[index] = newPeripheral == null ? null
|
||||
peripherals[index] = newPeripheral == null ? null
|
||||
: new PeripheralWrapper( newPeripheral, side.getName() );
|
||||
|
||||
if( m_peripherals[index] != null )
|
||||
if( peripherals[index] != null )
|
||||
{
|
||||
// Queue an attachment
|
||||
final PeripheralWrapper wrapper = m_peripherals[index];
|
||||
if( m_running && !wrapper.isAttached() ) wrapper.attach();
|
||||
final PeripheralWrapper wrapper = peripherals[index];
|
||||
if( running && !wrapper.isAttached() ) wrapper.attach();
|
||||
|
||||
// Queue an attachment event
|
||||
m_environment.queueEvent( "peripheral", new Object[] { side.getName() } );
|
||||
environment.queueEvent( "peripheral", side.getName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ILuaAPI implementation
|
||||
|
||||
@Override
|
||||
public String[] getNames()
|
||||
{
|
||||
@@ -270,12 +246,12 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
@Override
|
||||
public void startup()
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
synchronized( peripherals )
|
||||
{
|
||||
m_running = true;
|
||||
running = true;
|
||||
for( int i = 0; i < 6; i++ )
|
||||
{
|
||||
PeripheralWrapper wrapper = m_peripherals[i];
|
||||
PeripheralWrapper wrapper = peripherals[i];
|
||||
if( wrapper != null && !wrapper.isAttached() ) wrapper.attach();
|
||||
}
|
||||
}
|
||||
@@ -284,12 +260,12 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
synchronized( peripherals )
|
||||
{
|
||||
m_running = false;
|
||||
running = false;
|
||||
for( int i = 0; i < 6; i++ )
|
||||
{
|
||||
PeripheralWrapper wrapper = m_peripherals[i];
|
||||
PeripheralWrapper wrapper = peripherals[i];
|
||||
if( wrapper != null && wrapper.isAttached() )
|
||||
{
|
||||
wrapper.detach();
|
||||
@@ -298,98 +274,95 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
@LuaFunction
|
||||
public final boolean isPresent( String sideName )
|
||||
{
|
||||
return new String[] {
|
||||
"isPresent",
|
||||
"getType",
|
||||
"getMethods",
|
||||
"call",
|
||||
};
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( sideName );
|
||||
if( side != null )
|
||||
{
|
||||
synchronized( peripherals )
|
||||
{
|
||||
PeripheralWrapper p = peripherals[side.ordinal()];
|
||||
if( p != null ) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
|
||||
@LuaFunction
|
||||
public final Object[] getType( String sideName )
|
||||
{
|
||||
switch( method )
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( sideName );
|
||||
if( side == null ) return null;
|
||||
|
||||
synchronized( peripherals )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// isPresent
|
||||
boolean present = false;
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
|
||||
if( side != null )
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
{
|
||||
PeripheralWrapper p = m_peripherals[side.ordinal()];
|
||||
if( p != null ) present = true;
|
||||
}
|
||||
}
|
||||
return new Object[] { present };
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// getType
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
|
||||
if( side != null )
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
{
|
||||
PeripheralWrapper p = m_peripherals[side.ordinal()];
|
||||
if( p != null ) return new Object[] { p.getType() };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// getMethods
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
|
||||
if( side != null )
|
||||
{
|
||||
synchronized( m_peripherals )
|
||||
{
|
||||
PeripheralWrapper p = m_peripherals[side.ordinal()];
|
||||
if( p != null ) return new Object[] { p.getMethods() };
|
||||
}
|
||||
}
|
||||
PeripheralWrapper p = peripherals[side.ordinal()];
|
||||
if( p != null ) return new Object[] { p.getType() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// call
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
|
||||
String methodName = getString( args, 1 );
|
||||
Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length );
|
||||
@LuaFunction
|
||||
public final Object[] getMethods( String sideName )
|
||||
{
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( sideName );
|
||||
if( side == null ) return null;
|
||||
|
||||
if( side == null ) throw new LuaException( "No peripheral attached" );
|
||||
synchronized( peripherals )
|
||||
{
|
||||
PeripheralWrapper p = peripherals[side.ordinal()];
|
||||
if( p != null ) return new Object[] { p.getMethods() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PeripheralWrapper p;
|
||||
synchronized( m_peripherals )
|
||||
{
|
||||
p = m_peripherals[side.ordinal()];
|
||||
}
|
||||
if( p == null ) throw new LuaException( "No peripheral attached" );
|
||||
@LuaFunction
|
||||
public final MethodResult call( ILuaContext context, IArguments args ) throws LuaException
|
||||
{
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( args.getString( 0 ) );
|
||||
String methodName = args.getString( 1 );
|
||||
IArguments methodArgs = args.drop( 2 );
|
||||
|
||||
try
|
||||
{
|
||||
return p.call( context, methodName, methodArgs );
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
// We increase the error level by one in order to shift the error level to where peripheral.call was
|
||||
// invoked. It would be possible to do it in Lua code, but would add significantly more overhead.
|
||||
if( e.getLevel() > 0 ) throw new FastLuaException( e.getMessage(), e.getLevel() + 1 );
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
if( side == null ) throw new LuaException( "No peripheral attached" );
|
||||
|
||||
PeripheralWrapper p;
|
||||
synchronized( peripherals )
|
||||
{
|
||||
p = peripherals[side.ordinal()];
|
||||
}
|
||||
if( p == null ) throw new LuaException( "No peripheral attached" );
|
||||
|
||||
try
|
||||
{
|
||||
return p.call( context, methodName, methodArgs ).adjustError( 1 );
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
// We increase the error level by one in order to shift the error level to where peripheral.call was
|
||||
// invoked. It would be possible to do it in Lua code, but would add significantly more overhead.
|
||||
if( e.getLevel() > 0 ) throw new FastLuaException( e.getMessage(), e.getLevel() + 1 );
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, PeripheralMethod> getMethods( IPeripheral peripheral )
|
||||
{
|
||||
String[] dynamicMethods = peripheral instanceof IDynamicPeripheral
|
||||
? Objects.requireNonNull( ((IDynamicPeripheral) peripheral).getMethodNames(), "Peripheral methods cannot be null" )
|
||||
: LuaMethod.EMPTY_METHODS;
|
||||
|
||||
List<NamedMethod<PeripheralMethod>> methods = PeripheralMethod.GENERATOR.getMethods( peripheral.getClass() );
|
||||
|
||||
Map<String, PeripheralMethod> methodMap = new HashMap<>( methods.size() + dynamicMethods.length );
|
||||
for( int i = 0; i < dynamicMethods.length; i++ )
|
||||
{
|
||||
methodMap.put( dynamicMethods[i], PeripheralMethod.DYNAMIC.get( i ) );
|
||||
}
|
||||
for( NamedMethod<PeripheralMethod> method : methods )
|
||||
{
|
||||
methodMap.put( method.getName(), method.getMethod() );
|
||||
}
|
||||
return methodMap;
|
||||
}
|
||||
}
|
||||
|
@@ -6,21 +6,59 @@
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.*;
|
||||
|
||||
/**
|
||||
* Interact with redstone attached to this computer.
|
||||
*
|
||||
* The {@link RedstoneAPI} library exposes three "types" of redstone control:
|
||||
* - Binary input/output ({@link #setOutput}/{@link #getInput}): These simply check if a redstone wire has any input or
|
||||
* output. A signal strength of 1 and 15 are treated the same.
|
||||
* - Analogue input/output ({@link #setAnalogOutput}/{@link #getAnalogInput}): These work with the actual signal
|
||||
* strength of the redstone wired, from 0 to 15.
|
||||
* - Bundled cables ({@link #setBundledOutput}/{@link #getBundledInput}): These interact with "bundled" cables, such
|
||||
* as those from Project:Red. These allow you to send 16 separate on/off signals. Each channel corresponds to a
|
||||
* colour, with the first being @{colors.white} and the last @{colors.black}.
|
||||
*
|
||||
* Whenever a redstone input changes, a {@code redstone} event will be fired. This may be used instead of repeativly
|
||||
* polling.
|
||||
*
|
||||
* This module may also be referred to as {@code rs}. For example, one may call {@code rs.getSides()} instead of
|
||||
* {@link #getSides}.
|
||||
*
|
||||
* @cc.usage Toggle the redstone signal above the computer every 0.5 seconds.
|
||||
*
|
||||
* <pre>
|
||||
* while true do
|
||||
* redstone.setOutput("top", not redstone.getOutput("top"))
|
||||
* sleep(0.5)
|
||||
* end
|
||||
* </pre>
|
||||
* @cc.usage Mimic a redstone comparator in [subtraction mode][comparator].
|
||||
*
|
||||
* <pre>
|
||||
* while true do
|
||||
* local rear = rs.getAnalogueInput("back")
|
||||
* local sides = math.max(rs.getAnalogueInput("left"), rs.getAnalogueInput("right"))
|
||||
* rs.setAnalogueOutput("front", math.max(rear - sides, 0))
|
||||
*
|
||||
* os.pullEvent("redstone") -- Wait for a change to inputs.
|
||||
* end
|
||||
* </pre>
|
||||
*
|
||||
* [comparator]: https://minecraft.gamepedia.com/Redstone_Comparator#Subtract_signal_strength "Redstone Comparator on
|
||||
* the Minecraft wiki."
|
||||
* @cc.module redstone
|
||||
*/
|
||||
public class RedstoneAPI implements ILuaAPI
|
||||
{
|
||||
private IAPIEnvironment m_environment;
|
||||
private final IAPIEnvironment environment;
|
||||
|
||||
public RedstoneAPI( IAPIEnvironment environment )
|
||||
{
|
||||
m_environment = environment;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,95 +67,149 @@ public class RedstoneAPI implements ILuaAPI
|
||||
return new String[] { "rs", "redstone" };
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
/**
|
||||
* Returns a table containing the six sides of the computer. Namely, "top", "bottom", "left", "right", "front" and
|
||||
* "back".
|
||||
*
|
||||
* @return A table of valid sides.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] getSides()
|
||||
{
|
||||
return new String[] {
|
||||
"getSides",
|
||||
"setOutput",
|
||||
"getOutput",
|
||||
"getInput",
|
||||
"setBundledOutput",
|
||||
"getBundledOutput",
|
||||
"getBundledInput",
|
||||
"testBundledInput",
|
||||
"setAnalogOutput",
|
||||
"setAnalogueOutput",
|
||||
"getAnalogOutput",
|
||||
"getAnalogueOutput",
|
||||
"getAnalogInput",
|
||||
"getAnalogueInput",
|
||||
};
|
||||
return ComputerSide.NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
/**
|
||||
* Turn the redstone signal of a specific side on or off.
|
||||
*
|
||||
* @param side The side to set.
|
||||
* @param on Whether the redstone signal should be on or off. When on, a signal strength of 15 is emitted.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void setOutput( ComputerSide side, boolean on )
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0: // getSides
|
||||
return new Object[] { ComputerSide.NAMES };
|
||||
case 1:
|
||||
{
|
||||
// setOutput
|
||||
ComputerSide side = parseSide( args );
|
||||
boolean output = getBoolean( args, 1 );
|
||||
m_environment.setOutput( side, output ? 15 : 0 );
|
||||
return null;
|
||||
}
|
||||
case 2: // getOutput
|
||||
return new Object[] { m_environment.getOutput( parseSide( args ) ) > 0 };
|
||||
case 3: // getInput
|
||||
return new Object[] { m_environment.getInput( parseSide( args ) ) > 0 };
|
||||
case 4:
|
||||
{
|
||||
// setBundledOutput
|
||||
ComputerSide side = parseSide( args );
|
||||
int output = getInt( args, 1 );
|
||||
m_environment.setBundledOutput( side, output );
|
||||
return null;
|
||||
}
|
||||
case 5: // getBundledOutput
|
||||
return new Object[] { m_environment.getBundledOutput( parseSide( args ) ) };
|
||||
case 6: // getBundledInput
|
||||
return new Object[] { m_environment.getBundledInput( parseSide( args ) ) };
|
||||
case 7:
|
||||
{
|
||||
// testBundledInput
|
||||
ComputerSide side = parseSide( args );
|
||||
int mask = getInt( args, 1 );
|
||||
int input = m_environment.getBundledInput( side );
|
||||
return new Object[] { (input & mask) == mask };
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
{
|
||||
// setAnalogOutput/setAnalogueOutput
|
||||
ComputerSide side = parseSide( args );
|
||||
int output = getInt( args, 1 );
|
||||
if( output < 0 || output > 15 )
|
||||
{
|
||||
throw new LuaException( "Expected number in range 0-15" );
|
||||
}
|
||||
m_environment.setOutput( side, output );
|
||||
return null;
|
||||
}
|
||||
case 10:
|
||||
case 11: // getAnalogOutput/getAnalogueOutput
|
||||
return new Object[] { m_environment.getOutput( parseSide( args ) ) };
|
||||
case 12:
|
||||
case 13: // getAnalogInput/getAnalogueInput
|
||||
return new Object[] { m_environment.getInput( parseSide( args ) ) };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
environment.setOutput( side, on ? 15 : 0 );
|
||||
}
|
||||
|
||||
private static ComputerSide parseSide( Object[] args ) throws LuaException
|
||||
/**
|
||||
* Get the current redstone output of a specific side.
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return Whether the redstone output is on or off.
|
||||
* @see #setOutput
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean getOutput( ComputerSide side )
|
||||
{
|
||||
ComputerSide side = ComputerSide.valueOfInsensitive( getString( args, 0 ) );
|
||||
if( side == null ) throw new LuaException( "Invalid side." );
|
||||
return side;
|
||||
return environment.getOutput( side ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current redstone input of a specific side.
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return Whether the redstone input is on or off.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean getInput( ComputerSide side )
|
||||
{
|
||||
return environment.getInput( side ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the redstone signal strength for a specific side.
|
||||
*
|
||||
* @param side The side to set.
|
||||
* @param value The signal strength between 0 and 15.
|
||||
* @throws LuaException If {@code value} is not betwene 0 and 15.
|
||||
*/
|
||||
@LuaFunction( { "setAnalogOutput", "setAnalogueOutput" } )
|
||||
public final void setAnalogOutput( ComputerSide side, int value ) throws LuaException
|
||||
{
|
||||
if( value < 0 || value > 15 ) throw new LuaException( "Expected number in range 0-15" );
|
||||
environment.setOutput( side, value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the redstone output signal strength for a specific side.
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return The output signal strength, between 0 and 15.
|
||||
* @see #setAnalogOutput
|
||||
*/
|
||||
@LuaFunction( { "getAnalogOutput", "getAnalogueOutput" } )
|
||||
public final int getAnalogOutput( ComputerSide side )
|
||||
{
|
||||
return environment.getOutput( side );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the redstone input signal strength for a specific side.
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return The input signal strength, between 0 and 15.
|
||||
*/
|
||||
@LuaFunction( { "getAnalogInput", "getAnalogueInput" } )
|
||||
public final int getAnalogInput( ComputerSide side )
|
||||
{
|
||||
return environment.getInput( side );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bundled cable output for a specific side.
|
||||
*
|
||||
* @param side The side to set.
|
||||
* @param output The colour bitmask to set.
|
||||
* @cc.see colors.subtract For removing a colour from the bitmask.
|
||||
* @cc.see colors.combine For adding a color to the bitmask.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void setBundledOutput( ComputerSide side, int output )
|
||||
{
|
||||
environment.setBundledOutput( side, output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bundled cable output for a specific side.
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return The bundle cable's output.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int getBundledOutput( ComputerSide side )
|
||||
{
|
||||
return environment.getBundledOutput( side );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bundled cable input for a specific side.
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return The bundle cable's input.
|
||||
* @see #testBundledInput To determine if a specific colour is set.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int getBundledInput( ComputerSide side )
|
||||
{
|
||||
return environment.getBundledOutput( side );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a specific combination of colours are on for the given side.
|
||||
*
|
||||
* @param side The side to test.
|
||||
* @param mask The mask to test.
|
||||
* @return If the colours are on.
|
||||
* @cc.usage Check if @{colors.white} and @{colors.black} are on above the computer.
|
||||
* <pre>
|
||||
* print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
|
||||
* </pre>
|
||||
* @see #getBundledInput
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean testBundledInput( ComputerSide side, int mask )
|
||||
{
|
||||
int input = environment.getBundledInput( side );
|
||||
return (input & mask) == mask;
|
||||
}
|
||||
}
|
||||
|
@@ -5,14 +5,14 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.ArgumentHelper;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaValues;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.getNumericType;
|
||||
import static dan200.computercraft.api.lua.LuaValues.getNumericType;
|
||||
|
||||
/**
|
||||
* Various helpers for tables.
|
||||
@@ -27,7 +27,7 @@ public final class TableHelper
|
||||
@Nonnull
|
||||
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nullable Object actual )
|
||||
{
|
||||
return badKey( key, expected, ArgumentHelper.getType( actual ) );
|
||||
return badKey( key, expected, LuaValues.getType( actual ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -5,28 +5,30 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.*;
|
||||
|
||||
public class TermAPI implements ILuaAPI
|
||||
/**
|
||||
* The Terminal API provides functions for writing text to the terminal and monitors, and drawing ASCII graphics.
|
||||
*
|
||||
* @cc.module term
|
||||
*/
|
||||
public class TermAPI extends TermMethods implements ILuaAPI
|
||||
{
|
||||
private final Terminal m_terminal;
|
||||
private final IComputerEnvironment m_environment;
|
||||
private final Terminal terminal;
|
||||
private final IComputerEnvironment environment;
|
||||
|
||||
public TermAPI( IAPIEnvironment environment )
|
||||
{
|
||||
m_terminal = environment.getTerminal();
|
||||
m_environment = environment.getComputerEnvironment();
|
||||
terminal = environment.getTerminal();
|
||||
this.environment = environment.getComputerEnvironment();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,262 +37,40 @@ public class TermAPI implements ILuaAPI
|
||||
return new String[] { "term" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default palette value for a colour.
|
||||
*
|
||||
* @param colour The colour whose palette should be fetched.
|
||||
* @return The RGB values.
|
||||
* @throws LuaException When given an invalid colour.
|
||||
* @cc.treturn number The red channel, will be between 0 and 1.
|
||||
* @cc.treturn number The green channel, will be between 0 and 1.
|
||||
* @cc.treturn number The blue channel, will be between 0 and 1.
|
||||
* @see TermMethods#setPaletteColour(IArguments) To change the palette colour.
|
||||
*/
|
||||
@LuaFunction( { "nativePaletteColour", "nativePaletteColor" } )
|
||||
public final Object[] nativePaletteColour( int colour ) throws LuaException
|
||||
{
|
||||
int actualColour = 15 - parseColour( colour );
|
||||
Colour c = Colour.fromInt( actualColour );
|
||||
|
||||
float[] rgb = c.getRGB();
|
||||
|
||||
Object[] rgbObj = new Object[rgb.length];
|
||||
for( int i = 0; i < rgbObj.length; ++i ) rgbObj[i] = rgb[i];
|
||||
return rgbObj;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
public Terminal getTerminal()
|
||||
{
|
||||
return new String[] {
|
||||
"write",
|
||||
"scroll",
|
||||
"setCursorPos",
|
||||
"setCursorBlink",
|
||||
"getCursorPos",
|
||||
"getSize",
|
||||
"clear",
|
||||
"clearLine",
|
||||
"setTextColour",
|
||||
"setTextColor",
|
||||
"setBackgroundColour",
|
||||
"setBackgroundColor",
|
||||
"isColour",
|
||||
"isColor",
|
||||
"getTextColour",
|
||||
"getTextColor",
|
||||
"getBackgroundColour",
|
||||
"getBackgroundColor",
|
||||
"blit",
|
||||
"setPaletteColour",
|
||||
"setPaletteColor",
|
||||
"getPaletteColour",
|
||||
"getPaletteColor",
|
||||
"nativePaletteColour",
|
||||
"nativePaletteColor",
|
||||
"getCursorBlink",
|
||||
};
|
||||
}
|
||||
|
||||
public static int parseColour( Object[] args ) throws LuaException
|
||||
{
|
||||
int colour = getInt( args, 0 );
|
||||
if( colour <= 0 )
|
||||
{
|
||||
throw new LuaException( "Colour out of range" );
|
||||
}
|
||||
colour = getHighestBit( colour ) - 1;
|
||||
if( colour < 0 || colour > 15 )
|
||||
{
|
||||
throw new LuaException( "Colour out of range" );
|
||||
}
|
||||
return colour;
|
||||
}
|
||||
|
||||
public static Object[] encodeColour( int colour ) throws LuaException
|
||||
{
|
||||
return new Object[] { 1 << colour };
|
||||
}
|
||||
|
||||
public static void setColour( Terminal terminal, int colour, double r, double g, double b )
|
||||
{
|
||||
if( terminal.getPalette() != null )
|
||||
{
|
||||
terminal.getPalette().setColour( colour, r, g, b );
|
||||
terminal.setChanged();
|
||||
}
|
||||
return terminal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
public boolean isColour()
|
||||
{
|
||||
switch( method )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// write
|
||||
String text = args.length > 0 && args[0] != null ? args[0].toString() : "";
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.write( text );
|
||||
m_terminal.setCursorPos( m_terminal.getCursorX() + text.length(), m_terminal.getCursorY() );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// scroll
|
||||
int y = getInt( args, 0 );
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.scroll( y );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// setCursorPos
|
||||
int x = getInt( args, 0 ) - 1;
|
||||
int y = getInt( args, 1 ) - 1;
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.setCursorPos( x, y );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// setCursorBlink
|
||||
boolean b = getBoolean( args, 0 );
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.setCursorBlink( b );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
// getCursorPos
|
||||
int x, y;
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
x = m_terminal.getCursorX();
|
||||
y = m_terminal.getCursorY();
|
||||
}
|
||||
return new Object[] { x + 1, y + 1 };
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
// getSize
|
||||
int width, height;
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
width = m_terminal.getWidth();
|
||||
height = m_terminal.getHeight();
|
||||
}
|
||||
return new Object[] { width, height };
|
||||
}
|
||||
case 6: // clear
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.clear();
|
||||
}
|
||||
return null;
|
||||
case 7: // clearLine
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.clearLine();
|
||||
}
|
||||
return null;
|
||||
case 8:
|
||||
case 9:
|
||||
{
|
||||
// setTextColour/setTextColor
|
||||
int colour = parseColour( args );
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.setTextColour( colour );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 10:
|
||||
case 11:
|
||||
{
|
||||
// setBackgroundColour/setBackgroundColor
|
||||
int colour = parseColour( args );
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.setBackgroundColour( colour );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 12:
|
||||
case 13: // isColour/isColor
|
||||
return new Object[] { m_environment.isColour() };
|
||||
case 14:
|
||||
case 15: // getTextColour/getTextColor
|
||||
return encodeColour( m_terminal.getTextColour() );
|
||||
case 16:
|
||||
case 17: // getBackgroundColour/getBackgroundColor
|
||||
return encodeColour( m_terminal.getBackgroundColour() );
|
||||
case 18:
|
||||
{
|
||||
// blit
|
||||
String text = getString( args, 0 );
|
||||
String textColour = getString( args, 1 );
|
||||
String backgroundColour = getString( args, 2 );
|
||||
if( textColour.length() != text.length() || backgroundColour.length() != text.length() )
|
||||
{
|
||||
throw new LuaException( "Arguments must be the same length" );
|
||||
}
|
||||
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
m_terminal.blit( text, textColour, backgroundColour );
|
||||
m_terminal.setCursorPos( m_terminal.getCursorX() + text.length(), m_terminal.getCursorY() );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 19:
|
||||
case 20:
|
||||
{
|
||||
// setPaletteColour/setPaletteColor
|
||||
int colour = 15 - parseColour( args );
|
||||
if( args.length == 2 )
|
||||
{
|
||||
int hex = getInt( args, 1 );
|
||||
double[] rgb = Palette.decodeRGB8( hex );
|
||||
setColour( m_terminal, colour, rgb[0], rgb[1], rgb[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
double r = getFiniteDouble( args, 1 );
|
||||
double g = getFiniteDouble( args, 2 );
|
||||
double b = getFiniteDouble( args, 3 );
|
||||
setColour( m_terminal, colour, r, g, b );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 21:
|
||||
case 22:
|
||||
{
|
||||
// getPaletteColour/getPaletteColor
|
||||
int colour = 15 - parseColour( args );
|
||||
synchronized( m_terminal )
|
||||
{
|
||||
if( m_terminal.getPalette() != null )
|
||||
{
|
||||
return ArrayUtils.toObject( m_terminal.getPalette().getColour( colour ) );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 23:
|
||||
case 24:
|
||||
{
|
||||
// nativePaletteColour/nativePaletteColor
|
||||
int colour = 15 - parseColour( args );
|
||||
Colour c = Colour.fromInt( colour );
|
||||
|
||||
float[] rgb = c.getRGB();
|
||||
|
||||
Object[] rgbObj = new Object[rgb.length];
|
||||
for( int i = 0; i < rgbObj.length; ++i ) rgbObj[i] = rgb[i];
|
||||
return rgbObj;
|
||||
}
|
||||
case 25:
|
||||
// getCursorBlink
|
||||
return new Object[] { m_terminal.getCursorBlink() };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getHighestBit( int group )
|
||||
{
|
||||
int bit = 0;
|
||||
while( group > 0 )
|
||||
{
|
||||
group >>= 1;
|
||||
bit++;
|
||||
}
|
||||
return bit;
|
||||
return environment.isColour();
|
||||
}
|
||||
}
|
||||
|
383
src/main/java/dan200/computercraft/core/apis/TermMethods.java
Normal file
383
src/main/java/dan200/computercraft/core/apis/TermMethods.java
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.util.Palette;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A base class for all objects which interact with a terminal. Namely the {@link TermAPI} and monitors.
|
||||
*
|
||||
* @cc.module term.Redirect
|
||||
*/
|
||||
public abstract class TermMethods
|
||||
{
|
||||
private static int getHighestBit( int group )
|
||||
{
|
||||
int bit = 0;
|
||||
while( group > 0 )
|
||||
{
|
||||
group >>= 1;
|
||||
bit++;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public abstract Terminal getTerminal() throws LuaException;
|
||||
|
||||
public abstract boolean isColour() throws LuaException;
|
||||
|
||||
/**
|
||||
* Write {@code text} at the current cursor position, moving the cursor to the end of the text.
|
||||
*
|
||||
* Unlike functions like {@code write} and {@code print}, this does not wrap the text - it simply copies the
|
||||
* text to the current terminal line.
|
||||
*
|
||||
* @param arguments The text to write.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.param text The text to write.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void write( IArguments arguments ) throws LuaException
|
||||
{
|
||||
String text = StringUtil.toString( arguments.get( 0 ) );
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
terminal.write( text );
|
||||
terminal.setCursorPos( terminal.getCursorX() + text.length(), terminal.getCursorY() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all positions up (or down) by {@code y} pixels.
|
||||
*
|
||||
* Every pixel in the terminal will be replaced by the line {@code y} pixels below it. If {@code y} is negative, it
|
||||
* will copy pixels from above instead.
|
||||
*
|
||||
* @param y The number of lines to move up by. This may be a negative number.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void scroll( int y ) throws LuaException
|
||||
{
|
||||
getTerminal().scroll( y );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of the cursor.
|
||||
*
|
||||
* @return The cursor's position.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.treturn number The x position of the cursor.
|
||||
* @cc.treturn number The y position of the cursor.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] getCursorPos() throws LuaException
|
||||
{
|
||||
Terminal terminal = getTerminal();
|
||||
return new Object[] { terminal.getCursorX() + 1, terminal.getCursorY() + 1 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the cursor. {@link #write(IArguments) terminal writes} will begin from this position.
|
||||
*
|
||||
* @param x The new x position of the cursor.
|
||||
* @param y The new y position of the cursor.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void setCursorPos( int x, int y ) throws LuaException
|
||||
{
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
terminal.setCursorPos( x - 1, y - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cursor is currently blinking.
|
||||
*
|
||||
* @return If the cursor is blinking.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final boolean getCursorBlink() throws LuaException
|
||||
{
|
||||
return getTerminal().getCursorBlink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the cursor should be visible (and blinking) at the current {@link #getCursorPos() cursor position}.
|
||||
*
|
||||
* @param blink Whether the cursor should blink.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void setCursorBlink( boolean blink ) throws LuaException
|
||||
{
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
terminal.setCursorBlink( blink );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the terminal.
|
||||
*
|
||||
* @return The terminal's size.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.treturn number The terminal's width.
|
||||
* @cc.treturn number The terminal's height.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] getSize() throws LuaException
|
||||
{
|
||||
Terminal terminal = getTerminal();
|
||||
return new Object[] { terminal.getWidth(), terminal.getHeight() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the terminal, filling it with the {@link #getBackgroundColour() current background colour}.
|
||||
*
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void clear() throws LuaException
|
||||
{
|
||||
getTerminal().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the line the cursor is currently on, filling it with the {@link #getBackgroundColour() current background
|
||||
* colour}.
|
||||
*
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void clearLine() throws LuaException
|
||||
{
|
||||
getTerminal().clearLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the colour that new text will be written as.
|
||||
*
|
||||
* @return The current text colour.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.see colors For a list of colour constants, returned by this function.
|
||||
*/
|
||||
@LuaFunction( { "getTextColour", "getTextColor" } )
|
||||
public final int getTextColour() throws LuaException
|
||||
{
|
||||
return encodeColour( getTerminal().getTextColour() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colour that new text will be written as.
|
||||
*
|
||||
* @param colourArg The new text colour.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.see colors For a list of colour constants.
|
||||
*/
|
||||
@LuaFunction( { "setTextColour", "setTextColor" } )
|
||||
public final void setTextColour( int colourArg ) throws LuaException
|
||||
{
|
||||
int colour = parseColour( colourArg );
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
terminal.setTextColour( colour );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current background colour. This is used when {@link #write writing text} and {@link #clear clearing}
|
||||
* the terminal.
|
||||
*
|
||||
* @return The current background colour.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.see colors For a list of colour constants, returned by this function.
|
||||
*/
|
||||
@LuaFunction( { "getBackgroundColour", "getBackgroundColor" } )
|
||||
public final int getBackgroundColour() throws LuaException
|
||||
{
|
||||
return encodeColour( getTerminal().getBackgroundColour() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current background colour. This is used when {@link #write writing text} and {@link #clear clearing} the
|
||||
* terminal.
|
||||
*
|
||||
* @param colourArg The new background colour.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.see colors For a list of colour constants.
|
||||
*/
|
||||
@LuaFunction( { "setBackgroundColour", "setBackgroundColor" } )
|
||||
public final void setBackgroundColour( int colourArg ) throws LuaException
|
||||
{
|
||||
int colour = parseColour( colourArg );
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
terminal.setBackgroundColour( colour );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this terminal supports colour.
|
||||
*
|
||||
* Terminals which do not support colour will still allow writing coloured text/backgrounds, but it will be
|
||||
* displayed in greyscale.
|
||||
*
|
||||
* @return Whether this terminal supports colour.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction( { "isColour", "isColor" } )
|
||||
public final boolean getIsColour() throws LuaException
|
||||
{
|
||||
return isColour();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code text} to the terminal with the specific foreground and background characters.
|
||||
*
|
||||
* As with {@link #write(IArguments)}, the text will be written at the current cursor location, with the cursor
|
||||
* moving to the end of the text.
|
||||
*
|
||||
* {@code textColour} and {@code backgroundColour} must both be strings the same length as {@code text}. All
|
||||
* characters represent a single hexadecimal digit, which is converted to one of CC's colours. For instance,
|
||||
* {@code "a"} corresponds to purple.
|
||||
*
|
||||
* @param text The text to write.
|
||||
* @param textColour The corresponding text colours.
|
||||
* @param backgroundColour The corresponding background colours.
|
||||
* @throws LuaException If the three inputs are not the same length.
|
||||
* @cc.see colors For a list of colour constants, and their hexadecimal values.
|
||||
* @cc.usage Prints "Hello, world!" in rainbow text.
|
||||
* <pre>{@code
|
||||
* term.blit("Hello, world!","01234456789ab","0000000000000")
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void blit( String text, String textColour, String backgroundColour ) throws LuaException
|
||||
{
|
||||
if( textColour.length() != text.length() || backgroundColour.length() != text.length() )
|
||||
{
|
||||
throw new LuaException( "Arguments must be the same length" );
|
||||
}
|
||||
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
terminal.blit( text, textColour, backgroundColour );
|
||||
terminal.setCursorPos( terminal.getCursorX() + text.length(), terminal.getCursorY() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the palette for a specific colour.
|
||||
*
|
||||
* ComputerCraft's palette system allows you to change how a specific colour should be displayed. For instance, you
|
||||
* can make @{colors.red} <em>more red</em> by setting its palette to #FF0000. This does now allow you to draw more
|
||||
* colours - you are still limited to 16 on the screen at one time - but you can change <em>which</em> colours are
|
||||
* used.
|
||||
*
|
||||
* @param args The new palette values.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.tparam [1] number index The colour whose palette should be changed.
|
||||
* @cc.tparam number colour A 24-bit integer representing the RGB value of the colour. For instance the integer
|
||||
* `0xFF0000` corresponds to the colour #FF0000.
|
||||
* @cc.tparam [2] number index The colour whose palette should be changed.
|
||||
* @cc.tparam number r The intensity of the red channel, between 0 and 1.
|
||||
* @cc.tparam number g The intensity of the green channel, between 0 and 1.
|
||||
* @cc.tparam number b The intensity of the blue channel, between 0 and 1.
|
||||
* @cc.usage Change the @{colors.red|red colour} from the default #CC4C4C to #FF0000.
|
||||
* <pre>{@code
|
||||
* term.setPaletteColour(colors.red, 0xFF0000)
|
||||
* term.setTextColour(colors.red)
|
||||
* print("Hello, world!")
|
||||
* }</pre>
|
||||
* @cc.usage As above, but specifying each colour channel separately.
|
||||
* <pre>{@code
|
||||
* term.setPaletteColour(colors.red, 1, 0, 0)
|
||||
* term.setTextColour(colors.red)
|
||||
* print("Hello, world!")
|
||||
* }</pre>
|
||||
* @cc.see colors.unpackRGB To convert from the 24-bit format to three separate channels.
|
||||
* @cc.see colors.packRGB To convert from three separate channels to the 24-bit format.
|
||||
*/
|
||||
@LuaFunction( { "setPaletteColour", "setPaletteColor" } )
|
||||
public final void setPaletteColour( IArguments args ) throws LuaException
|
||||
{
|
||||
int colour = 15 - parseColour( args.getInt( 0 ) );
|
||||
if( args.count() == 2 )
|
||||
{
|
||||
int hex = args.getInt( 1 );
|
||||
double[] rgb = Palette.decodeRGB8( hex );
|
||||
setColour( getTerminal(), colour, rgb[0], rgb[1], rgb[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
double r = args.getFiniteDouble( 1 );
|
||||
double g = args.getFiniteDouble( 2 );
|
||||
double b = args.getFiniteDouble( 3 );
|
||||
setColour( getTerminal(), colour, r, g, b );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current palette for a specific colour.
|
||||
*
|
||||
* @param colourArg The colour whose palette should be fetched.
|
||||
* @return The resulting colour.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.treturn number The red channel, will be between 0 and 1.
|
||||
* @cc.treturn number The green channel, will be between 0 and 1.
|
||||
* @cc.treturn number The blue channel, will be between 0 and 1.
|
||||
*/
|
||||
@LuaFunction( { "getPaletteColour", "getPaletteColor" } )
|
||||
public final Object[] getPaletteColour( int colourArg ) throws LuaException
|
||||
{
|
||||
int colour = 15 - parseColour( colourArg );
|
||||
Terminal terminal = getTerminal();
|
||||
synchronized( terminal )
|
||||
{
|
||||
return ArrayUtils.toObject( terminal.getPalette().getColour( colour ) );
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseColour( int colour ) throws LuaException
|
||||
{
|
||||
if( colour <= 0 ) throw new LuaException( "Colour out of range" );
|
||||
colour = getHighestBit( colour ) - 1;
|
||||
if( colour < 0 || colour > 15 ) throw new LuaException( "Colour out of range" );
|
||||
return colour;
|
||||
}
|
||||
|
||||
|
||||
public static int encodeColour( int colour )
|
||||
{
|
||||
return 1 << colour;
|
||||
}
|
||||
|
||||
public static void setColour( Terminal terminal, int colour, double r, double g, double b )
|
||||
{
|
||||
terminal.getPalette().setColour( colour, r, g, b );
|
||||
terminal.setChanged();
|
||||
}
|
||||
}
|
@@ -5,11 +5,9 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@@ -17,208 +15,256 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.getInt;
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.optBoolean;
|
||||
|
||||
/**
|
||||
* A file handle opened with {@link dan200.computercraft.core.apis.FSAPI#open(String, String)} with the {@code "rb"}
|
||||
* mode.
|
||||
*
|
||||
* @cc.module fs.BinaryReadHandle
|
||||
*/
|
||||
public class BinaryReadableHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private static final String[] METHOD_NAMES = new String[] { "read", "readAll", "readLine", "close" };
|
||||
private static final String[] METHOD_SEEK_NAMES = ObjectArrays.concat( METHOD_NAMES, new String[] { "seek" }, String.class );
|
||||
|
||||
private final ReadableByteChannel m_reader;
|
||||
private final SeekableByteChannel m_seekable;
|
||||
private final ReadableByteChannel reader;
|
||||
final SeekableByteChannel seekable;
|
||||
private final ByteBuffer single = ByteBuffer.allocate( 1 );
|
||||
|
||||
public BinaryReadableHandle( ReadableByteChannel channel, Closeable closeable )
|
||||
BinaryReadableHandle( ReadableByteChannel reader, SeekableByteChannel seekable, Closeable closeable )
|
||||
{
|
||||
super( closeable );
|
||||
m_reader = channel;
|
||||
m_seekable = asSeekable( channel );
|
||||
this.reader = reader;
|
||||
this.seekable = seekable;
|
||||
}
|
||||
|
||||
public BinaryReadableHandle( ReadableByteChannel channel )
|
||||
public static BinaryReadableHandle of( ReadableByteChannel channel, Closeable closeable )
|
||||
{
|
||||
this( channel, channel );
|
||||
SeekableByteChannel seekable = asSeekable( channel );
|
||||
return seekable == null ? new BinaryReadableHandle( channel, null, closeable ) : new Seekable( seekable, closeable );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
public static BinaryReadableHandle of( ReadableByteChannel channel )
|
||||
{
|
||||
return m_seekable == null ? METHOD_NAMES : METHOD_SEEK_NAMES;
|
||||
return of( channel, channel );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
/**
|
||||
* Read a number of bytes from this file.
|
||||
*
|
||||
* @param countArg The number of bytes to read. When absent, a single byte will be read <em>as a number</em>. This
|
||||
* may be 0 to determine we are at the end of the file.
|
||||
* @return The read bytes.
|
||||
* @throws LuaException When trying to read a negative number of bytes.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn [1] nil If we are at the end of the file.
|
||||
* @cc.treturn [2] number The value of the byte read. This is returned when the {@code count} is absent.
|
||||
* @cc.treturn [3] string The bytes read as a string. This is returned when the {@code count} is given.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] read( Optional<Integer> countArg ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
case 0: // read
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
if( args.length > 0 && args[0] != null )
|
||||
{
|
||||
int count = getInt( args, 0 );
|
||||
if( count < 0 )
|
||||
{
|
||||
throw new LuaException( "Cannot read a negative number of bytes" );
|
||||
}
|
||||
else if( count == 0 && m_seekable != null )
|
||||
{
|
||||
return m_seekable.position() >= m_seekable.size() ? null : new Object[] { "" };
|
||||
}
|
||||
|
||||
if( count <= BUFFER_SIZE )
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocate( count );
|
||||
|
||||
int read = m_reader.read( buffer );
|
||||
if( read < 0 ) return null;
|
||||
return new Object[] { read < count ? Arrays.copyOf( buffer.array(), read ) : buffer.array() };
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the initial set of characters, failing if none are read.
|
||||
ByteBuffer buffer = ByteBuffer.allocate( BUFFER_SIZE );
|
||||
int read = m_reader.read( buffer );
|
||||
if( read < 0 ) return null;
|
||||
|
||||
// If we failed to read "enough" here, let's just abort
|
||||
if( read >= count || read < BUFFER_SIZE )
|
||||
{
|
||||
return new Object[] { Arrays.copyOf( buffer.array(), read ) };
|
||||
}
|
||||
|
||||
// Build up an array of ByteBuffers. Hopefully this means we can perform less allocation
|
||||
// than doubling up the buffer each time.
|
||||
int totalRead = read;
|
||||
List<ByteBuffer> parts = new ArrayList<>( 4 );
|
||||
parts.add( buffer );
|
||||
while( read >= BUFFER_SIZE && totalRead < count )
|
||||
{
|
||||
buffer = ByteBuffer.allocate( Math.min( BUFFER_SIZE, count - totalRead ) );
|
||||
read = m_reader.read( buffer );
|
||||
if( read < 0 ) break;
|
||||
|
||||
totalRead += read;
|
||||
parts.add( buffer );
|
||||
}
|
||||
|
||||
// Now just copy all the bytes across!
|
||||
byte[] bytes = new byte[totalRead];
|
||||
int pos = 0;
|
||||
for( ByteBuffer part : parts )
|
||||
{
|
||||
System.arraycopy( part.array(), 0, bytes, pos, part.position() );
|
||||
pos += part.position();
|
||||
}
|
||||
return new Object[] { bytes };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
single.clear();
|
||||
int b = m_reader.read( single );
|
||||
return b == -1 ? null : new Object[] { single.get( 0 ) & 0xFF };
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 1: // readAll
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
int expected = 32;
|
||||
if( m_seekable != null )
|
||||
{
|
||||
expected = Math.max( expected, (int) (m_seekable.size() - m_seekable.position()) );
|
||||
}
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream( expected );
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocate( 8192 );
|
||||
boolean readAnything = false;
|
||||
while( true )
|
||||
{
|
||||
buf.clear();
|
||||
int r = m_reader.read( buf );
|
||||
if( r == -1 ) break;
|
||||
|
||||
readAnything = true;
|
||||
stream.write( buf.array(), 0, r );
|
||||
}
|
||||
return readAnything ? new Object[] { stream.toByteArray() } : null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 2: // readLine
|
||||
if( countArg.isPresent() )
|
||||
{
|
||||
checkOpen();
|
||||
boolean withTrailing = optBoolean( args, 0, false );
|
||||
try
|
||||
int count = countArg.get();
|
||||
if( count < 0 ) throw new LuaException( "Cannot read a negative number of bytes" );
|
||||
if( count == 0 && seekable != null )
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
boolean readAnything = false, readRc = false;
|
||||
while( true )
|
||||
{
|
||||
single.clear();
|
||||
int read = m_reader.read( single );
|
||||
if( read <= 0 )
|
||||
{
|
||||
// Nothing else to read, and we saw no \n. Return the array. If we saw a \r, then add it
|
||||
// back.
|
||||
if( readRc ) stream.write( '\r' );
|
||||
return readAnything ? new Object[] { stream.toByteArray() } : null;
|
||||
}
|
||||
|
||||
readAnything = true;
|
||||
|
||||
byte chr = single.get( 0 );
|
||||
if( chr == '\n' )
|
||||
{
|
||||
if( withTrailing )
|
||||
{
|
||||
if( readRc ) stream.write( '\r' );
|
||||
stream.write( chr );
|
||||
}
|
||||
return new Object[] { stream.toByteArray() };
|
||||
}
|
||||
else
|
||||
{
|
||||
// We want to skip \r\n, but obviously need to include cases where \r is not followed by \n.
|
||||
// Note, this behaviour is non-standard compliant (strictly speaking we should have no
|
||||
// special logic for \r), but we preserve compatibility with EncodedReadableHandle and
|
||||
// previous behaviour of the io library.
|
||||
if( readRc ) stream.write( '\r' );
|
||||
readRc = chr == '\r';
|
||||
if( !readRc ) stream.write( chr );
|
||||
}
|
||||
}
|
||||
return seekable.position() >= seekable.size() ? null : new Object[] { "" };
|
||||
}
|
||||
catch( IOException e )
|
||||
|
||||
if( count <= BUFFER_SIZE )
|
||||
{
|
||||
return null;
|
||||
ByteBuffer buffer = ByteBuffer.allocate( count );
|
||||
|
||||
int read = reader.read( buffer );
|
||||
if( read < 0 ) return null;
|
||||
buffer.flip();
|
||||
return new Object[] { buffer };
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the initial set of characters, failing if none are read.
|
||||
ByteBuffer buffer = ByteBuffer.allocate( BUFFER_SIZE );
|
||||
int read = reader.read( buffer );
|
||||
if( read < 0 ) return null;
|
||||
|
||||
// If we failed to read "enough" here, let's just abort
|
||||
if( read >= count || read < BUFFER_SIZE )
|
||||
{
|
||||
buffer.flip();
|
||||
return new Object[] { buffer };
|
||||
}
|
||||
|
||||
// Build up an array of ByteBuffers. Hopefully this means we can perform less allocation
|
||||
// than doubling up the buffer each time.
|
||||
int totalRead = read;
|
||||
List<ByteBuffer> parts = new ArrayList<>( 4 );
|
||||
parts.add( buffer );
|
||||
while( read >= BUFFER_SIZE && totalRead < count )
|
||||
{
|
||||
buffer = ByteBuffer.allocate( Math.min( BUFFER_SIZE, count - totalRead ) );
|
||||
read = reader.read( buffer );
|
||||
if( read < 0 ) break;
|
||||
|
||||
totalRead += read;
|
||||
parts.add( buffer );
|
||||
}
|
||||
|
||||
// Now just copy all the bytes across!
|
||||
byte[] bytes = new byte[totalRead];
|
||||
int pos = 0;
|
||||
for( ByteBuffer part : parts )
|
||||
{
|
||||
System.arraycopy( part.array(), 0, bytes, pos, part.position() );
|
||||
pos += part.position();
|
||||
}
|
||||
return new Object[] { bytes };
|
||||
}
|
||||
}
|
||||
case 3: // close
|
||||
checkOpen();
|
||||
close();
|
||||
return null;
|
||||
case 4: // seek
|
||||
checkOpen();
|
||||
return handleSeek( m_seekable, args );
|
||||
default:
|
||||
return null;
|
||||
else
|
||||
{
|
||||
single.clear();
|
||||
int b = reader.read( single );
|
||||
return b == -1 ? null : new Object[] { single.get( 0 ) & 0xFF };
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the remainder of the file.
|
||||
*
|
||||
* @return The file, or {@code null} if at the end of it.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] readAll() throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
int expected = 32;
|
||||
if( seekable != null ) expected = Math.max( expected, (int) (seekable.size() - seekable.position()) );
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream( expected );
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocate( 8192 );
|
||||
boolean readAnything = false;
|
||||
while( true )
|
||||
{
|
||||
buf.clear();
|
||||
int r = reader.read( buf );
|
||||
if( r == -1 ) break;
|
||||
|
||||
readAnything = true;
|
||||
stream.write( buf.array(), 0, r );
|
||||
}
|
||||
return readAnything ? new Object[] { stream.toByteArray() } : null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a line from the file.
|
||||
*
|
||||
* @param withTrailingArg Whether to include the newline characters with the returned string. Defaults to {@code false}.
|
||||
* @return The read string.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] readLine( Optional<Boolean> withTrailingArg ) throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
boolean withTrailing = withTrailingArg.orElse( false );
|
||||
try
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
boolean readAnything = false, readRc = false;
|
||||
while( true )
|
||||
{
|
||||
single.clear();
|
||||
int read = reader.read( single );
|
||||
if( read <= 0 )
|
||||
{
|
||||
// Nothing else to read, and we saw no \n. Return the array. If we saw a \r, then add it
|
||||
// back.
|
||||
if( readRc ) stream.write( '\r' );
|
||||
return readAnything ? new Object[] { stream.toByteArray() } : null;
|
||||
}
|
||||
|
||||
readAnything = true;
|
||||
|
||||
byte chr = single.get( 0 );
|
||||
if( chr == '\n' )
|
||||
{
|
||||
if( withTrailing )
|
||||
{
|
||||
if( readRc ) stream.write( '\r' );
|
||||
stream.write( chr );
|
||||
}
|
||||
return new Object[] { stream.toByteArray() };
|
||||
}
|
||||
else
|
||||
{
|
||||
// We want to skip \r\n, but obviously need to include cases where \r is not followed by \n.
|
||||
// Note, this behaviour is non-standard compliant (strictly speaking we should have no
|
||||
// special logic for \r), but we preserve compatibility with EncodedReadableHandle and
|
||||
// previous behaviour of the io library.
|
||||
if( readRc ) stream.write( '\r' );
|
||||
readRc = chr == '\r';
|
||||
if( !readRc ) stream.write( chr );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Seekable extends BinaryReadableHandle
|
||||
{
|
||||
Seekable( SeekableByteChannel seekable, Closeable closeable )
|
||||
{
|
||||
super( seekable, seekable, closeable );
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a new position within the file, changing where bytes are written to. The new position is an offset
|
||||
* given by {@code offset}, relative to a start position determined by {@code whence}:
|
||||
*
|
||||
* - {@code "set"}: {@code offset} is relative to the beginning of the file.
|
||||
* - {@code "cur"}: Relative to the current position. This is the default.
|
||||
* - {@code "end"}: Relative to the end of the file.
|
||||
*
|
||||
* In case of success, {@code seek} returns the new file position from the beginning of the file.
|
||||
*
|
||||
* @param whence Where the offset is relative to.
|
||||
* @param offset The offset to seek to.
|
||||
* @return The new position.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn [1] number The new position.
|
||||
* @cc.treturn [2] nil If seeking failed.
|
||||
* @cc.treturn string The reason seeking failed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] seek( Optional<String> whence, Optional<Long> offset ) throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
return handleSeek( seekable, whence, offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,103 +5,137 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import dan200.computercraft.api.lua.ArgumentHelper;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.LuaValues;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A file handle opened by {@link dan200.computercraft.core.apis.FSAPI#open} using the {@code "wb"} or {@code "ab"}
|
||||
* modes.
|
||||
*
|
||||
* @cc.module fs.BinaryWriteHandle
|
||||
*/
|
||||
public class BinaryWritableHandle extends HandleGeneric
|
||||
{
|
||||
private static final String[] METHOD_NAMES = new String[] { "write", "flush", "close" };
|
||||
private static final String[] METHOD_SEEK_NAMES = ObjectArrays.concat( METHOD_NAMES, new String[] { "seek" }, String.class );
|
||||
|
||||
private final WritableByteChannel m_writer;
|
||||
private final SeekableByteChannel m_seekable;
|
||||
private final WritableByteChannel writer;
|
||||
final SeekableByteChannel seekable;
|
||||
private final ByteBuffer single = ByteBuffer.allocate( 1 );
|
||||
|
||||
public BinaryWritableHandle( WritableByteChannel channel, Closeable closeable )
|
||||
protected BinaryWritableHandle( WritableByteChannel writer, SeekableByteChannel seekable, Closeable closeable )
|
||||
{
|
||||
super( closeable );
|
||||
m_writer = channel;
|
||||
m_seekable = asSeekable( channel );
|
||||
this.writer = writer;
|
||||
this.seekable = seekable;
|
||||
}
|
||||
|
||||
public BinaryWritableHandle( WritableByteChannel channel )
|
||||
public static BinaryWritableHandle of( WritableByteChannel channel, Closeable closeable )
|
||||
{
|
||||
this( channel, channel );
|
||||
SeekableByteChannel seekable = asSeekable( channel );
|
||||
return seekable == null ? new BinaryWritableHandle( channel, null, closeable ) : new Seekable( seekable, closeable );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
public static BinaryWritableHandle of( WritableByteChannel channel )
|
||||
{
|
||||
return m_seekable == null ? METHOD_NAMES : METHOD_SEEK_NAMES;
|
||||
return of( channel, channel );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
/**
|
||||
* Write a string or byte to the file.
|
||||
*
|
||||
* @param arguments The value to write.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.tparam [1] number The byte to write.
|
||||
* @cc.tparam [2] string The string to write.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void write( IArguments arguments ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
case 0: // write
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
if( args.length > 0 && args[0] instanceof Number )
|
||||
{
|
||||
int number = ((Number) args[0]).intValue();
|
||||
single.clear();
|
||||
single.put( (byte) number );
|
||||
single.flip();
|
||||
Object arg = arguments.get( 0 );
|
||||
if( arg instanceof Number )
|
||||
{
|
||||
int number = ((Number) arg).intValue();
|
||||
single.clear();
|
||||
single.put( (byte) number );
|
||||
single.flip();
|
||||
|
||||
m_writer.write( single );
|
||||
}
|
||||
else if( args.length > 0 && args[0] instanceof String )
|
||||
{
|
||||
String value = (String) args[0];
|
||||
m_writer.write( ByteBuffer.wrap( StringUtil.encodeString( value ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ArgumentHelper.badArgumentOf( 0, "string or number", args.length > 0 ? args[0] : null );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
case 1: // flush
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
// Technically this is not needed
|
||||
if( m_writer instanceof FileChannel ) ((FileChannel) m_writer).force( false );
|
||||
writer.write( single );
|
||||
}
|
||||
else if( arg instanceof String )
|
||||
{
|
||||
writer.write( arguments.getBytes( 0 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw LuaValues.badArgumentOf( 0, "string or number", arg );
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 2: // close
|
||||
checkOpen();
|
||||
close();
|
||||
return null;
|
||||
case 3: // seek
|
||||
checkOpen();
|
||||
return handleSeek( m_seekable, args );
|
||||
default:
|
||||
return null;
|
||||
/**
|
||||
* Save the current file without closing it.
|
||||
*
|
||||
* @throws LuaException If the file has been closed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void flush() throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
// Technically this is not needed
|
||||
if( writer instanceof FileChannel ) ((FileChannel) writer).force( false );
|
||||
}
|
||||
catch( IOException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class Seekable extends BinaryWritableHandle
|
||||
{
|
||||
public Seekable( SeekableByteChannel seekable, Closeable closeable )
|
||||
{
|
||||
super( seekable, seekable, closeable );
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a new position within the file, changing where bytes are written to. The new position is an offset
|
||||
* given by {@code offset}, relative to a start position determined by {@code whence}:
|
||||
*
|
||||
* - {@code "set"}: {@code offset} is relative to the beginning of the file.
|
||||
* - {@code "cur"}: Relative to the current position. This is the default.
|
||||
* - {@code "end"}: Relative to the end of the file.
|
||||
*
|
||||
* In case of success, {@code seek} returns the new file position from the beginning of the file.
|
||||
*
|
||||
* @param whence Where the offset is relative to.
|
||||
* @param offset The offset to seek to.
|
||||
* @return The new position.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn [1] number The new position.
|
||||
* @cc.treturn [2] nil If seeking failed.
|
||||
* @cc.treturn string The reason seeking failed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] seek( Optional<String> whence, Optional<Long> offset ) throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
return handleSeek( seekable, whence, offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.BufferedReader;
|
||||
@@ -18,20 +18,24 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.optBoolean;
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.optInt;
|
||||
|
||||
/**
|
||||
* A file handle opened with {@link dan200.computercraft.core.apis.FSAPI#open(String, String)} with the {@code "r"}
|
||||
* mode.
|
||||
*
|
||||
* @cc.module fs.ReadHandle
|
||||
*/
|
||||
public class EncodedReadableHandle extends HandleGeneric
|
||||
{
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
private BufferedReader m_reader;
|
||||
private final BufferedReader reader;
|
||||
|
||||
public EncodedReadableHandle( @Nonnull BufferedReader reader, @Nonnull Closeable closable )
|
||||
{
|
||||
super( closable );
|
||||
m_reader = reader;
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public EncodedReadableHandle( @Nonnull BufferedReader reader )
|
||||
@@ -39,123 +43,131 @@ public class EncodedReadableHandle extends HandleGeneric
|
||||
this( reader, reader );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
/**
|
||||
* Read a line from the file.
|
||||
*
|
||||
* @param withTrailingArg Whether to include the newline characters with the returned string. Defaults to {@code false}.
|
||||
* @return The read string.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] readLine( Optional<Boolean> withTrailingArg ) throws LuaException
|
||||
{
|
||||
return new String[] {
|
||||
"readLine",
|
||||
"readAll",
|
||||
"read",
|
||||
"close",
|
||||
};
|
||||
checkOpen();
|
||||
boolean withTrailing = withTrailingArg.orElse( false );
|
||||
try
|
||||
{
|
||||
String line = reader.readLine();
|
||||
if( line != null )
|
||||
{
|
||||
// While this is technically inaccurate, it's better than nothing
|
||||
if( withTrailing ) line += "\n";
|
||||
return new Object[] { line };
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
/**
|
||||
* Read the remainder of the file.
|
||||
*
|
||||
* @return The file, or {@code null} if at the end of it.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn nil|string The remaining contents of the file, or {@code nil} if we are at the end.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] readAll() throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
case 0: // readLine
|
||||
StringBuilder result = new StringBuilder();
|
||||
String line = reader.readLine();
|
||||
while( line != null )
|
||||
{
|
||||
checkOpen();
|
||||
boolean withTrailing = optBoolean( args, 0, false );
|
||||
try
|
||||
result.append( line );
|
||||
line = reader.readLine();
|
||||
if( line != null )
|
||||
{
|
||||
String line = m_reader.readLine();
|
||||
if( line != null )
|
||||
{
|
||||
// While this is technically inaccurate, it's better than nothing
|
||||
if( withTrailing ) line += "\n";
|
||||
return new Object[] { line };
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
result.append( "\n" );
|
||||
}
|
||||
}
|
||||
case 1: // readAll
|
||||
checkOpen();
|
||||
try
|
||||
return new Object[] { result.toString() };
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a number of characters from this file.
|
||||
*
|
||||
* @param countA The number of characters to read, defaulting to 1.
|
||||
* @return The read characters.
|
||||
* @throws LuaException When trying to read a negative number of characters.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.treturn string|nil The read characters, or {@code nil} if at the of the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object[] read( Optional<Integer> countA ) throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
int count = countA.orElse( 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 = reader.read( chars );
|
||||
|
||||
return read < 0 ? null : new Object[] { 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 = reader.read( buffer, 0, Math.min( buffer.length, count ) );
|
||||
if( read < 0 ) return null;
|
||||
|
||||
StringBuilder out = new StringBuilder( read );
|
||||
int totalRead = 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 && totalRead < count )
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
String line = m_reader.readLine();
|
||||
while( line != null )
|
||||
{
|
||||
result.append( line );
|
||||
line = m_reader.readLine();
|
||||
if( line != null )
|
||||
{
|
||||
result.append( "\n" );
|
||||
}
|
||||
}
|
||||
return new Object[] { result.toString() };
|
||||
read = reader.read( buffer, 0, Math.min( BUFFER_SIZE, count - totalRead ) );
|
||||
if( read < 0 ) break;
|
||||
|
||||
totalRead += read;
|
||||
out.append( buffer, 0, read );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 2: // 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 ? null : new Object[] { 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 < 0 ) return null;
|
||||
|
||||
StringBuilder out = new StringBuilder( read );
|
||||
int totalRead = 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 && totalRead < count )
|
||||
{
|
||||
read = m_reader.read( buffer, 0, Math.min( BUFFER_SIZE, count - totalRead ) );
|
||||
if( read < 0 ) break;
|
||||
|
||||
totalRead += read;
|
||||
out.append( buffer, 0, read );
|
||||
}
|
||||
|
||||
return new Object[] { out.toString() };
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 3: // close
|
||||
checkOpen();
|
||||
close();
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
return new Object[] { out.toString() };
|
||||
}
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.shared.util.StringUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.BufferedWriter;
|
||||
@@ -19,87 +21,83 @@ import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* A file handle opened by {@link dan200.computercraft.core.apis.FSAPI#open} using the {@code "w"} or {@code "a"} modes.
|
||||
*
|
||||
* @cc.module fs.WriteHandle
|
||||
*/
|
||||
public class EncodedWritableHandle extends HandleGeneric
|
||||
{
|
||||
private BufferedWriter m_writer;
|
||||
private final BufferedWriter writer;
|
||||
|
||||
public EncodedWritableHandle( @Nonnull BufferedWriter writer, @Nonnull Closeable closable )
|
||||
{
|
||||
super( closable );
|
||||
m_writer = writer;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public EncodedWritableHandle( @Nonnull BufferedWriter writer )
|
||||
/**
|
||||
* Write a string of characters to the file.
|
||||
*
|
||||
* @param args The value to write.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.param The value to write to the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void write( IArguments args ) throws LuaException
|
||||
{
|
||||
this( writer, writer );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
return new String[] {
|
||||
"write",
|
||||
"writeLine",
|
||||
"flush",
|
||||
"close",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
|
||||
{
|
||||
switch( method )
|
||||
checkOpen();
|
||||
String text = StringUtil.toString( args.get( 0 ) );
|
||||
try
|
||||
{
|
||||
case 0: // write
|
||||
{
|
||||
checkOpen();
|
||||
String text = args.length > 0 && args[0] != null ? args[0].toString() : "";
|
||||
try
|
||||
{
|
||||
m_writer.write( text, 0, text.length() );
|
||||
return null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 1: // writeLine
|
||||
{
|
||||
checkOpen();
|
||||
String text = args.length > 0 && args[0] != null ? args[0].toString() : "";
|
||||
try
|
||||
{
|
||||
m_writer.write( text, 0, text.length() );
|
||||
m_writer.newLine();
|
||||
return null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
case 2: // flush
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
m_writer.flush();
|
||||
return null;
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case 3: // close
|
||||
checkOpen();
|
||||
close();
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
writer.write( text, 0, text.length() );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a string of characters to the file, follwing them with a new line character.
|
||||
*
|
||||
* @param args The value to write.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.param The value to write to the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void writeLine( IArguments args ) throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
String text = StringUtil.toString( args.get( 0 ) );
|
||||
try
|
||||
{
|
||||
writer.write( text, 0, text.length() );
|
||||
writer.newLine();
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new LuaException( e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current file without closing it.
|
||||
*
|
||||
* @throws LuaException If the file has been closed.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void flush() throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
try
|
||||
{
|
||||
writer.flush();
|
||||
}
|
||||
catch( IOException ignored )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferedWriter openUtf8( WritableByteChannel channel )
|
||||
{
|
||||
|
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.shared.util.IoUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -14,62 +14,71 @@ import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.optLong;
|
||||
import static dan200.computercraft.api.lua.ArgumentHelper.optString;
|
||||
|
||||
public abstract class HandleGeneric implements ILuaObject
|
||||
public abstract class HandleGeneric
|
||||
{
|
||||
private Closeable m_closable;
|
||||
private boolean m_open = true;
|
||||
private Closeable closable;
|
||||
private boolean open = true;
|
||||
|
||||
protected HandleGeneric( @Nonnull Closeable closable )
|
||||
{
|
||||
m_closable = closable;
|
||||
this.closable = closable;
|
||||
}
|
||||
|
||||
protected void checkOpen() throws LuaException
|
||||
{
|
||||
if( !m_open ) throw new LuaException( "attempt to use a closed file" );
|
||||
if( !open ) throw new LuaException( "attempt to use a closed file" );
|
||||
}
|
||||
|
||||
protected final void close()
|
||||
{
|
||||
m_open = false;
|
||||
open = false;
|
||||
|
||||
Closeable closeable = m_closable;
|
||||
if( closeable != null )
|
||||
{
|
||||
IoUtil.closeQuietly( closeable );
|
||||
m_closable = null;
|
||||
}
|
||||
IoUtil.closeQuietly( closable );
|
||||
closable = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this file, freeing any resources it uses.
|
||||
*
|
||||
* Once a file is closed it may no longer be read or written to.
|
||||
*
|
||||
* @throws LuaException If the file has already been closed.
|
||||
*/
|
||||
@LuaFunction( "close" )
|
||||
public final void doClose() throws LuaException
|
||||
{
|
||||
checkOpen();
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shared implementation for various file handle types.
|
||||
*
|
||||
* @param channel The channel to seek in
|
||||
* @param args The Lua arguments to process, like Lua's {@code file:seek}.
|
||||
* @param whence The seeking mode.
|
||||
* @param offset The offset to seek to.
|
||||
* @return The new position of the file, or null if some error occurred.
|
||||
* @throws LuaException If the arguments were invalid
|
||||
* @see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:seek">{@code file:seek} in the Lua manual.</a>
|
||||
*/
|
||||
protected static Object[] handleSeek( SeekableByteChannel channel, Object[] args ) throws LuaException
|
||||
protected static Object[] handleSeek( SeekableByteChannel channel, Optional<String> whence, Optional<Long> offset ) throws LuaException
|
||||
{
|
||||
long actualOffset = offset.orElse( 0L );
|
||||
try
|
||||
{
|
||||
String whence = optString( args, 0, "cur" );
|
||||
long offset = optLong( args, 1, 0 );
|
||||
switch( whence )
|
||||
switch( whence.orElse( "cur" ) )
|
||||
{
|
||||
case "set":
|
||||
channel.position( offset );
|
||||
channel.position( actualOffset );
|
||||
break;
|
||||
case "cur":
|
||||
channel.position( channel.position() + offset );
|
||||
channel.position( channel.position() + actualOffset );
|
||||
break;
|
||||
case "end":
|
||||
channel.position( channel.size() + offset );
|
||||
channel.position( channel.size() + actualOffset );
|
||||
break;
|
||||
default:
|
||||
throw new LuaException( "bad argument #1 to 'seek' (invalid option '" + whence + "'" );
|
||||
@@ -79,7 +88,7 @@ public abstract class HandleGeneric implements ILuaObject
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
return new Object[] { false, "Position is negative" };
|
||||
return new Object[] { null, "Position is negative" };
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http;
|
||||
|
||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@@ -23,14 +24,14 @@ public class CheckUrl extends Resource<CheckUrl>
|
||||
|
||||
private final IAPIEnvironment environment;
|
||||
private final String address;
|
||||
private final String host;
|
||||
private final URI uri;
|
||||
|
||||
public CheckUrl( ResourceGroup<CheckUrl> limiter, IAPIEnvironment environment, String address, URI uri )
|
||||
{
|
||||
super( limiter );
|
||||
this.environment = environment;
|
||||
this.address = address;
|
||||
host = uri.getHost();
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void run()
|
||||
@@ -46,12 +47,15 @@ public class CheckUrl extends Resource<CheckUrl>
|
||||
|
||||
try
|
||||
{
|
||||
NetworkUtils.getAddress( host, 80, false );
|
||||
if( tryClose() ) environment.queueEvent( EVENT, new Object[] { address, true } );
|
||||
boolean ssl = uri.getScheme().equalsIgnoreCase( "https" );
|
||||
InetSocketAddress netAddress = NetworkUtils.getAddress( uri, ssl );
|
||||
NetworkUtils.getOptions( uri.getHost(), netAddress );
|
||||
|
||||
if( tryClose() ) environment.queueEvent( EVENT, address, true );
|
||||
}
|
||||
catch( HTTPRequestException e )
|
||||
{
|
||||
if( tryClose() ) environment.queueEvent( EVENT, new Object[] { address, false, e.getMessage() } );
|
||||
if( tryClose() ) environment.queueEvent( EVENT, address, false, e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,9 @@
|
||||
package dan200.computercraft.core.apis.http;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.apis.http.options.Action;
|
||||
import dan200.computercraft.core.apis.http.options.AddressRule;
|
||||
import dan200.computercraft.core.apis.http.options.Options;
|
||||
import dan200.computercraft.shared.util.ThreadUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
@@ -15,8 +18,8 @@ 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.security.KeyStore;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
@@ -97,6 +100,21 @@ public final class NetworkUtils
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link InetSocketAddress} from a {@link java.net.URI}.
|
||||
*
|
||||
* Note, this may require a DNS lookup, and so should not be executed on the main CC thread.
|
||||
*
|
||||
* @param uri The URI to fetch.
|
||||
* @param ssl Whether to connect with SSL. This is used to find the default port if not otherwise specified.
|
||||
* @return The resolved address.
|
||||
* @throws HTTPRequestException If the host is not malformed.
|
||||
*/
|
||||
public static InetSocketAddress getAddress( URI uri, boolean ssl ) throws HTTPRequestException
|
||||
{
|
||||
return getAddress( uri.getHost(), uri.getPort(), ssl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link InetSocketAddress} from the resolved {@code host} and port.
|
||||
*
|
||||
@@ -106,24 +124,31 @@ public final class NetworkUtils
|
||||
* @param port The port, or -1 if not defined.
|
||||
* @param ssl Whether to connect with SSL. This is used to find the default port if not otherwise specified.
|
||||
* @return The resolved address.
|
||||
* @throws HTTPRequestException If the host is not permitted.
|
||||
* @throws HTTPRequestException If the host is not malformed.
|
||||
*/
|
||||
public static InetSocketAddress getAddress( String host, int port, boolean ssl ) throws HTTPRequestException
|
||||
{
|
||||
if( port < 0 ) port = ssl ? 443 : 80;
|
||||
|
||||
InetSocketAddress socketAddress = new InetSocketAddress( host, port );
|
||||
if( socketAddress.isUnresolved() ) throw new HTTPRequestException( "Unknown host" );
|
||||
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
if( AddressRule.apply( ComputerCraft.httpRules, host, address ) == AddressRule.Action.DENY )
|
||||
{
|
||||
throw new HTTPRequestException( "Domain not permitted" );
|
||||
}
|
||||
|
||||
return socketAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options for a specific domain.
|
||||
*
|
||||
* @param host The host to resolve.
|
||||
* @param address The address, resolved by {@link #getAddress(String, int, boolean)}.
|
||||
* @return The options for this host.
|
||||
* @throws HTTPRequestException If the host is not permitted
|
||||
*/
|
||||
public static Options getOptions( String host, InetSocketAddress address ) throws HTTPRequestException
|
||||
{
|
||||
Options options = AddressRule.apply( ComputerCraft.httpRules, host, address );
|
||||
if( options.action == Action.DENY ) throw new HTTPRequestException( "Domain not permitted" );
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a {@link ByteBuf} into a byte array.
|
||||
*
|
||||
|
@@ -106,7 +106,7 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
|
||||
|
||||
protected static <T extends Closeable> T closeCloseable( T closeable )
|
||||
{
|
||||
if( closeable != null ) IoUtil.closeQuietly( closeable );
|
||||
IoUtil.closeQuietly( closeable );
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis.http.options;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public enum Action
|
||||
{
|
||||
ALLOW,
|
||||
DENY;
|
||||
|
||||
private final PartialOptions partial = new PartialOptions( this, null, null, null, null );
|
||||
|
||||
@Nonnull
|
||||
public PartialOptions toPartial()
|
||||
{
|
||||
return partial;
|
||||
}
|
||||
}
|
@@ -3,14 +3,16 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.core.apis.http;
|
||||
package dan200.computercraft.core.apis.http.options;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -18,6 +20,11 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public final class AddressRule
|
||||
{
|
||||
public static final long MAX_DOWNLOAD = 16 * 1024 * 1024;
|
||||
public static final long MAX_UPLOAD = 4 * 1024 * 1024;
|
||||
public static final int TIMEOUT = 30_000;
|
||||
public static final int WEBSOCKET_MESSAGE = 128 * 1024;
|
||||
|
||||
private static final class HostRange
|
||||
{
|
||||
private final byte[] min;
|
||||
@@ -44,25 +51,25 @@ public final class AddressRule
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action
|
||||
{
|
||||
ALLOW,
|
||||
DENY,
|
||||
}
|
||||
|
||||
private final HostRange ip;
|
||||
private final Pattern domainPattern;
|
||||
private final Action action;
|
||||
private final Integer port;
|
||||
private final PartialOptions partial;
|
||||
|
||||
private AddressRule( HostRange ip, Pattern domainPattern, Action action )
|
||||
private AddressRule(
|
||||
@Nullable HostRange ip,
|
||||
@Nullable Pattern domainPattern,
|
||||
@Nullable Integer port,
|
||||
@Nonnull PartialOptions partial )
|
||||
{
|
||||
this.ip = ip;
|
||||
this.domainPattern = domainPattern;
|
||||
this.action = action;
|
||||
this.partial = partial;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AddressRule parse( String filter, Action action )
|
||||
public static AddressRule parse( String filter, @Nullable Integer port, @Nonnull PartialOptions partial )
|
||||
{
|
||||
int cidr = filter.indexOf( '/' );
|
||||
if( cidr >= 0 )
|
||||
@@ -117,24 +124,27 @@ public final class AddressRule
|
||||
size -= 8;
|
||||
}
|
||||
|
||||
return new AddressRule( new HostRange( minBytes, maxBytes ), null, action );
|
||||
return new AddressRule( new HostRange( minBytes, maxBytes ), null, port, partial );
|
||||
}
|
||||
else
|
||||
{
|
||||
Pattern pattern = Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" );
|
||||
return new AddressRule( null, pattern, action );
|
||||
return new AddressRule( null, pattern, port, partial );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given address matches a series of patterns.
|
||||
*
|
||||
* @param domain The domain to match
|
||||
* @param address The address to check.
|
||||
* @param domain The domain to match
|
||||
* @param socketAddress The address to check.
|
||||
* @return Whether it matches any of these patterns.
|
||||
*/
|
||||
public boolean matches( String domain, InetAddress address )
|
||||
private boolean matches( String domain, InetSocketAddress socketAddress )
|
||||
{
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
if( port != null && port != socketAddress.getPort() ) return false;
|
||||
|
||||
if( domainPattern != null )
|
||||
{
|
||||
if( domainPattern.matcher( domain ).matches() ) return true;
|
||||
@@ -155,13 +165,32 @@ public final class AddressRule
|
||||
return ip != null && ip.contains( address );
|
||||
}
|
||||
|
||||
public static Action apply( Iterable<? extends AddressRule> rules, String domain, InetAddress address )
|
||||
public static Options apply( Iterable<? extends AddressRule> rules, String domain, InetSocketAddress address )
|
||||
{
|
||||
PartialOptions options = null;
|
||||
boolean hasMany = false;
|
||||
|
||||
for( AddressRule rule : rules )
|
||||
{
|
||||
if( rule.matches( domain, address ) ) return rule.action;
|
||||
if( !rule.matches( domain, address ) ) continue;
|
||||
|
||||
if( options == null )
|
||||
{
|
||||
options = rule.partial;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if( !hasMany )
|
||||
{
|
||||
options = options.copy();
|
||||
hasMany = true;
|
||||
}
|
||||
|
||||
options.merge( rule.partial );
|
||||
}
|
||||
}
|
||||
|
||||
return Action.DENY;
|
||||
return (options == null ? PartialOptions.DEFAULT : options).toOptions();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user