1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-15 14:07:38 +00:00

Compare commits

...

141 Commits

Author SHA1 Message Date
Merith-TK
19273b3696 CC:R 1.92.0 2021-02-22 01:42:34 -08:00
Merith-TK
4643641d51 Update Patchwork, Handle Tabs when Parsing Json 2021-02-22 01:23:39 -08:00
Merith-TK
63cd9c5bc7 Update Patchwork.md with format 2021-02-22 01:20:51 -08:00
Merith-TK
e9c11ff325 Translations for Vienamese 2021-02-22 01:13:56 -08:00
Merith-TK
452464aa01 add changelog + vienamese support 2021-02-22 01:11:29 -08:00
Merith-TK
8885462175 Don't use entity.captureDrops at all (removed line) 2021-02-22 01:05:22 -08:00
Merith-TK
b8bd64913b Add date-specific MOTDs (like Minecraft) (CCT#533) 2021-02-22 01:02:56 -08:00
Devan-Kerman
88722d484f Merge pull request #29 from davidqueneau/fabric
Porting GenericPeripherals from upstream CC: Tweaked
2021-02-03 10:44:02 -06:00
David Queneau
6d103e2114 Item movement methods now respect inventory slot rules (i.e. no pickaxes in the fuel slot of a furnace). 2021-02-01 23:21:25 -08:00
David Queneau
42f23d56ae Double chest inventories are now handled correctly. 2021-02-01 19:04:02 -08:00
David Queneau
89d5211bd7 Fixed bad assumption about empty ItemStacks being reset to ItemStack.EMPTY. Items no longer transfer into an inventory as a different item than they began. 2021-02-01 00:02:28 -08:00
David Queneau
83e70377f7 Fixed off by one error. Commented not very nice inventory code. 2021-01-31 20:13:59 -08:00
David Queneau
f6a26f75c3 Reverted change to how GenericPeripherals report type. Instead, added generic lua function that returns the name of Nameable targets. Might not be at home in InventoryMethods since it could apply to other types of targets. 2021-01-30 20:45:08 -08:00
David Queneau
664df62d5d Cable modems can be placed against all blocks, including chests. 2021-01-30 17:12:09 -08:00
David Queneau
1348ee0588 Ported the generic peripheral feature from upstream forge version along with initial implementation of generic inventory peripherals. 2021-01-30 15:28:11 -08:00
Jacob Farley
145dce7653 Merge pull request #27 from techninja1008/fix-http-config
Fix http config
2021-01-21 20:09:23 -06:00
Jacob Farley
7f2651c23e Merge pull request #28 from techninja1008/fix-turtle-breaking-computer
Change turtle block breaking to call onBreak
2021-01-20 15:06:17 -06:00
Danny Wensley
05464107a8 Update ComputerCraft.httpRules on config sync.
Also removes a bit of http config related legacy code.
2021-01-20 20:03:00 +00:00
Danny Wensley
86705787f0 Change turtle block breaking to call onBreak
Fixes #25
2021-01-20 14:30:11 +00:00
Devan-Kerman
b34d8387d9 Update README.md 2020-12-29 15:17:00 -06:00
Devan-Kerman
4d00969ef0 fix #10
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-15 11:52:23 -05:00
Devan-Kerman
01d3d12992 update commits
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-15 11:38:16 -05:00
Devan-Kerman
5e31dcde83 fix CCE
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-12 20:20:38 -05:00
Devan-Kerman
5184883af1 fix potential bug
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-09 10:07:38 -05:00
Devan-Kerman
0c45112262 fix npe 2020-09-09 09:36:47 -05:00
Jacob Farley
0bf1672f45 Update README.md 2020-09-08 12:31:17 -05:00
Devan-Kerman
e1b8ac1f84 does this work? I don't know, it probably does 2020-09-07 13:19:14 -06:00
Jacob Farley
deea552d99 Merge remote-tracking branch 'origin/fabric' into fabric 2020-09-07 10:54:52 -05:00
Jacob Farley
54229c2ce1 I suppose Netherite Pick turtle recipe is important. 2020-09-07 10:54:46 -05:00
Devan-Kerman
1346a26179 remove all filthy access wideners 2020-09-06 20:04:23 -05:00
Jacob Farley
14df44f09d Fix crash from inspecting sign text 2020-09-06 18:45:30 -05:00
Jacob Farley
1a21529499 Merge remote-tracking branch 'origin/fabric' into fabric 2020-09-06 16:50:36 -05:00
Jacob Farley
2546990f41 Fix playNote not working for Speaker peripheral. 2020-09-06 11:21:20 -05:00
Devan-Kerman
b39ca02464 Merge pull request #5 from immibis/fabric
Turtles can read sign text.
2020-09-06 08:47:09 -06:00
immibis
628618105c Turtles can read sign text. 2020-09-06 12:14:41 +02:00
Jacob Farley
075ba03f5d Merge pull request #2 from Zundrel/fix_ids
Fix IDs
2020-09-05 18:57:34 -05:00
Mary
10bf84b631 Update ViewComputerContainerData.java 2020-09-05 20:58:05 +02:00
Jacob Farley
c346e22a45 Fix creative tab localization. 2020-09-05 10:10:07 -05:00
Jacob Farley
418e1335b1 Netherite pick turtle. 2020-09-05 10:08:53 -05:00
Jacob Farley
d52372df31 Fix Turtle tool rendering. 2020-09-05 10:02:41 -05:00
Jacob Farley
cc72e1c2bd Fix turtle digging. 2020-09-04 21:49:07 -05:00
Jacob Farley
d28afcc6a9 Update Gradle and, you know, actually give it RAM. 2020-09-04 20:33:43 -05:00
Jacob Farley
220ed21e6e Oops forgot this. 2020-09-04 20:15:43 -05:00
Jacob Farley
09d465774d Bumped version to 1.91.1 (same as 1.16.2 Forge version) 2020-09-04 20:12:14 -05:00
Jacob Farley
dfc8f48f12 Disable TurtleTool rendering temporarily. 2020-09-04 20:08:53 -05:00
Jacob Farley
abe2ec4686 Fixed pocket and turtle upgrades. (Turtle tools render weird in inventories) 2020-09-04 19:55:15 -05:00
Devan-Kerman
5333cda44e the final sin, I hope 2020-09-04 18:18:10 -05:00
Devan-Kerman
dc88fbeb12 Merge remote-tracking branch 'origin/fabric' into fabric
# Conflicts:
#	src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java
2020-09-04 18:09:36 -05:00
Devan-Kerman
c5eb7a9501 reformat 2020-09-04 18:07:48 -05:00
Jacob Farley
bc969db2be Merge remote-tracking branch 'origin/fabric' into fabric 2020-09-04 18:06:03 -05:00
Jacob Farley
50b7646178 Fix block setting more and fix computer break noise and particles. 2020-09-04 18:05:53 -05:00
Devan-Kerman
3fa6b5bc9d god forgive me, for I have sinned 2 2020-09-04 17:49:41 -05:00
Devan-Kerman
33e65e39e3 god forgive me, for I have sinned 2020-09-04 17:29:46 -05:00
Jacob Farley
0380e60590 Fixed block settings. 2020-09-04 14:14:14 -05:00
Jacob Farley
2918892ee2 Oops 2020-09-04 14:12:23 -05:00
Jacob Farley
e43dd9f7c6 Fix cables and turtle rendering. 2020-09-04 14:05:56 -05:00
Jacob Farley
244fd95034 Fix computer and monitor syncing. 2020-09-04 12:35:55 -05:00
Jacob Farley
14e98e2fcb Add AW to fabric.mod.json 2020-09-04 10:25:09 -05:00
Jacob Farley
dcf5d59109 fix peripherals 2020-09-01 11:44:08 -05:00
shedaniel
b95083c77e make gui work 2020-09-01 23:41:50 +08:00
shedaniel
c54c8c3ea6 computers block themselves render 2020-09-01 22:37:43 +08:00
Devan-Kerman
f79c67e243 CRAB GAME IS LAUNCH CRAB 2020-08-31 20:03:40 -05:00
Jacob Farley
6ec7ebe439 Yes hello and hey 0.5 Loom 2020-08-31 19:43:11 -05:00
Jacob Farley
76fe33760d It now compiles. 2020-08-31 19:09:22 -05:00
Devan-Kerman
cb549d8f43 help 2020-08-31 13:44:05 -05:00
Martmists
7b400fdcdd Merge branch 'fabric' of github.com:Zundrel/cc-tweaked-fabric into fabric 2020-08-30 15:47:01 +02:00
Martmists
ab70d918b5 yeef 2020-08-30 15:46:57 +02:00
Devan-Kerman
c64644b9ec time to make a diff of the fabric branch with ours to see what we are miss 2020-08-30 08:35:44 -05:00
Jacob Farley
afb12eb342 Fix FrameInfo 2020-08-29 22:16:06 -05:00
Jacob Farley
64f5ca02b3 Auto stash before merge of "fabric" and "origin/fabric" 2020-08-29 22:14:32 -05:00
Devan-Kerman
84bca21b0c show true error count 2020-08-29 22:10:14 -05:00
Devan-Kerman
b6757c416f ClientRegistry 2020-08-29 22:06:04 -05:00
Devan-Kerman
dc9d3f2d15 TurtlePlayer 2020-08-29 21:55:11 -05:00
Devan-Kerman
807825d74e registry is gone 2020-08-29 21:42:17 -05:00
Jacob Farley
14c17676c6 Some basic fixes, will work on this some other time. 2020-08-29 20:13:35 -05:00
Devan-Kerman
324519575c remap with yarrnforge 2020-08-29 19:38:26 -05:00
Devan-Kerman
fbcf26bdc9 remap with yarrnforge 2020-08-29 19:32:53 -05:00
Devan-Kerman
30ab6bd045 Merge remote-tracking branch 'origin/fabric' into fabric 2020-08-29 18:47:57 -05:00
Devan-Kerman
2ea816b78b printout renderer, terminal, tile printer, fake player, guicomputer, fixed width font renderer 2020-08-29 18:47:47 -05:00
Jacob Farley
56dcc57755 Fix some issues, add AW. 2020-08-29 18:44:19 -05:00
Devan-Kerman
a4830aff86 cloth config 2020-08-29 18:01:13 -05:00
Devan-Kerman
621bc526be remap 2020-08-29 18:01:01 -05:00
Alex Evelyn
605e1f6b9b Started work on upgrading to 1.16.1. Not in a compilable state yet 2020-07-07 13:27:13 -04:00
parly
cb66ef7e30 v1.14.4-1.83.2+build.9 2019-12-02 00:19:02 +09:00
parly
fd2f6a38c1 Update dependencies 2019-12-02 00:17:49 +09:00
parly
229821d398 Organize imports 2019-12-02 00:00:36 +09:00
hugeblank
1f2e0c444d Initial Implementation of Feature
Adds the ability for the command computer to get block information from other dimensions by passing the dimension ID as the last argument in both getBlockInfo and getBlockInfos
2019-12-01 23:55:54 +09:00
parly
429baa350c Fix issues with building with Java 9+, take 2
Fix another potential issues when using Java 9+ at build time and Java
8 at runtime.
2019-10-24 01:11:25 +09:00
parly
7fe62485fa v1.14.4-1.83.2+build.8 2019-10-20 00:40:48 +09:00
parly
fe10c68099 Update dependencies 2019-10-20 00:29:50 +09:00
parly
06092cfddd Fix issues with building with Java 9+
Fix for NoSuchMethodError when using Java 9+ at build time and Java 8
at runtime.

Fixes #14
2019-10-19 23:58:34 +09:00
parly
9967dc5740 Cleanup build.gradle
Based on #18 (thanks to hugeblank and Fuyukai), made the following
changes:

* Shade Cobalt
It seems that NoClassDefFoundError occurs on the CC thread if the
library Cobalt is not shaded.

* More cleanup
Removed more unnecessary parts.
2019-10-19 23:38:25 +09:00
hugeblank
b920e04c59 oneline fix command parsing
(Merged from #17)
2019-10-18 19:14:38 -07:00
parly
baa1b5a5c9 v1.14.4-1.83.2+build.7 2019-10-01 16:07:19 +09:00
parly
e84663a5c5 Update ClothConfig 2019-10-01 15:41:36 +09:00
parly
1c46220c42 Update Fabric toolchain 2019-10-01 15:41:16 +09:00
parly
889b445855 Add time unit conversion
Task execution times are stored in the config file in milliseconds, but
they're stored internally in nanoseconds. This commit adds time unit
conversions from milliseconds to nanoseconds.

Fixes #9
2019-10-01 15:21:37 +09:00
parly
19553a981e v1.14.4-1.83.2+build.5 2019-09-28 00:17:28 +09:00
parly
15f4dbd061 Update ClothConfig 2019-09-28 00:06:23 +09:00
parly
98e12c7c3e Quick patch to fix rendering issues with OptiFine
OptiFine seems to cause NullPointerException when it cannot find loader.spriteAtlas::getSprite method in ModelItemPropertyOverrideList constructor.

Fixes #1
2019-09-27 23:36:03 +09:00
svitoos
dfad319864 Fix build (jankson version in 'include') 2019-09-23 02:33:33 +09:00
parly
111b58f533 v1.14.4-1.83.2+build.4 2019-09-22 23:59:26 +09:00
parly
9345652808 Change repository url 2019-09-22 23:50:07 +09:00
svitoos
d40163b409 Fix mod metadata 2019-09-22 23:26:12 +09:00
svitoos
96400966d7 - Add config (json5, jankson)
- Add config gui (Cloth Config API and Mod Menu)
2019-09-22 23:26:12 +09:00
parly
db83bd4f64 v1.14.4-1.83.2+build.3 2019-09-16 22:52:10 +09:00
parly
579a38d366 Format code 2019-09-16 22:45:32 +09:00
parly
0e6797c7da Update fabric toolchain 2019-09-16 22:34:25 +09:00
parly
c935577768 Update gradle wrapper to 5.5.1 2019-09-16 22:23:41 +09:00
parly
c2b4077aa1 Update group 2019-09-16 22:22:19 +09:00
svitoos
ac020859f3 code cleanup 2019-09-16 22:19:16 +09:00
svitoos
238be8955b Fix turtle can't place water in waterloggable blocks 2019-09-16 22:19:16 +09:00
svitoos
f997b02b8f Fix the use of the item on the entity 2019-09-16 22:19:16 +09:00
svitoos
799bb77847 Fix turtle place bucket in the wrong block 2019-09-16 22:19:16 +09:00
parly
2f66792a0d v1.14.4-1.83.2+build.2 2019-08-19 21:38:50 +09:00
doomy64
6cae8e211e Update ServerComputer.java
Fix os.time and os.day

(Merged from #4)
2019-08-18 16:31:15 +09:00
parly
48b1b2f01d v1.14.4-1.83.2 2019-08-09 22:26:46 +09:00
parly
8c45fd362a Update to Minecraft 1.14.4 2019-08-09 22:26:46 +09:00
parly
07b13dd2b4 Prepare for cc-tweaked-fabric 2019-08-09 22:26:46 +09:00
SquidDev
45e84e1ede Merge branch 'mc-1.13.x' into mc-1.14-fabric 2019-06-02 18:41:53 +01:00
SquidDev
725dfa764f Add highlight rendering for monitors and cables 2019-06-02 18:24:08 +01:00
SquidDev
c221502ec9 Correctly offset touch position on a monitor
Fixes #223
2019-06-02 18:06:24 +01:00
SquidDev
234f18c228 Merge branch 'mc-1.13.x' into mc-1.14-fabric 2019-06-02 17:54:00 +01:00
SquidDev
006ad109cb Update to 1.14.2
Ughr, mapping changes. Though this time they weren't too bad I guess.
2019-06-02 17:42:07 +01:00
SquidDev
0db080154c Fix incorrect inventory check
Closes #209
2019-05-26 16:29:28 +01:00
SquidDev
200311033f Small bodgey patch to fix NPEs when turtles place tiles 2019-05-14 17:17:34 +01:00
SquidDev
3192dc81ac Bump to 1.14.1 2019-05-14 16:35:40 +01:00
SquidDev
b11d4bb209 Bump to 1.14.1pr1
Which means bumping mappings version. Oh boy, this was not a bundle of
laughs...
2019-05-07 20:40:55 +01:00
SquidDev
2a716244e9 Don't use the TileTurtle as our property source
We probably need to do this for some other inventories too - to
investigate.

Fixes #198
2019-05-06 22:59:44 +01:00
SquidDev
19b7ed538a Hopefuly stub out all the network methods
Fixes #197
2019-05-06 22:46:44 +01:00
SquidDev
b0d9dc0b88 Merge branch 'mc-1.13.x' into mc-1.14-fabric 2019-05-06 21:53:59 +01:00
SquidDev
e6094a59fa Get us booting on a dedicated server
It doesn't appear that blocks are syncing, so we'll need to look into
that. Hopefully fixes #193
2019-04-29 08:04:24 +01:00
SquidDev
e8d7e6a562 Fix a whole bunch of bugs 2019-04-25 11:31:40 +01:00
SquidDev
536c2d9b2d Merge branch 'mc-1.13.x' into mc-1.14-fabric 2019-04-25 08:39:32 +01:00
SquidDev
f15a278f3b Bump versions 2019-04-24 11:54:03 +01:00
SquidDev
26b73c2ff3 Merge branch 'mc-1.13.x' into mc-1.14-fabric 2019-04-24 10:53:28 +01:00
SquidDev
c1e08fc3c7 Fix computer block drops not being handles
This is still not 100% perfect - we don't drop them when in creative.
However, it's a notable improvement on what it was.

Closes #174
2019-04-12 19:54:08 +01:00
SquidDev
b9ec6f236d Update to 1.14pr2 2019-04-12 19:49:39 +01:00
SquidDev
b1fff97bff Bump to 1.14pr1 2019-04-11 09:40:32 +01:00
SquidDev
c81bc70475 Merge branch 'mc-1.13.x' into mc-1.14-fabric 2019-04-11 08:57:04 +01:00
SquidDev
55a7ee4acf Initial update to Fabric 2019-04-03 23:27:10 +01:00
845 changed files with 47348 additions and 37043 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@
.idea
.gradle
*.DS_Store
.project

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}

View File

@@ -1,75 +1,9 @@
# ![CC: Tweaked](logo.png)
[![Current build status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked "Current build status") [![Download CC: Tweaked on CurseForge](https://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge")
# CC:Restitched Patchwork
# This is a Work In Progress Port
*it runs and works-ish*
CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
turtles and more to Minecraft.
## Reached Parity with CC:T 1.92.0
## What?
ComputerCraft has always held a fond place in my heart: it's the mod which really got me into Minecraft, and it's the
mod which has kept me playing it for many years. However, development of the original mod has slowed, as the original
developers have had less time to work on the mod, and moved onto other projects and commitments.
THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T
CC: Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor
to CC, nor do I want to take it in a vastly different direction to the original mod. Instead, CC:T focuses on making the
ComputerCraft experience as _solid_ as possible, ironing out any wrinkles that may have developed over time.
## Features
CC: Tweaked contains all the features of the latest version of ComputerCraft, as well as numerous fixes, performance
improvements and several nifty additions. I'd recommend checking out [the releases page](https://github.com/SquidDev-CC/CC-Tweaked/releases)
to see the full set of changes, but here's a couple of the more interesting additions:
- Improvements to the `http` library, including websockets, support for other HTTP methods (`PUT`, `DELETE`, etc...)
and configurable limits on HTTP usage.
- Full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is
installed).
- Pocket computers can be held like maps, allowing you to view the screen without entering a GUI.
- Printed pages and books can be placed in item frames and held like maps.
- Several profiling and administration tools for server owners, via the `/computercraft` command. This allows operators
to track which computers are hogging resources, turn on and shutdown multiple computers at once and interact with
computers remotely.
- Closer emulation of standard Lua, adding the `debug` and `io` libraries. This also enables seeking within binary
files, meaning you don't need to read large files into memory.
- Allow running multiple computers on multiple threads, reducing latency on worlds with many computers.
## Relation to CCTweaks?
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said,
several features have been included, such as full block modems, the Cobalt runtime and map-like rendering for pocket
computers.
## Contributing
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. In order to start helping
develop CC:T, you'll need to follow these steps:
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
- **Setup Forge:** `./gradlew setupDecompWorkspace`
- **Test your changes:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
## Community
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.gg/H2UyJXe)!
There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=#computercraft), if
that's more your cup of tea.
I'd generally recommend you don't contact me directly (email, DM, etc...) unless absolutely necessary (i.e. in order to
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.
```groovy
dependencies {
maven { url 'https://squiddev.cc/maven/' }
}
dependencies {
implementation "org.squiddev:cc-tweaked-${mc_version}:${cct_version}"
}
```
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
subject to change at any point. If you depend on functionality outside the API, file an issue, and we can look into
exposing more features.
The changelog is located at [PatchWork.md](patchwork.md)

View File

@@ -1,417 +1,120 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven {
name = "forge"
url = "http://files.minecraftforge.net/maven"
}
}
dependencies {
classpath 'com.google.code.gson:gson:2.8.1'
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.117'
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
}
}
plugins {
id 'com.matthewprenger.cursegradle' version '1.2.0'
id "com.github.breadmoirai.github-release" version "2.2.4"
id 'fabric-loom' version '0.5-SNAPSHOT'
id 'maven-publish'
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.ajoberstar.grgit'
apply plugin: 'maven-publish'
apply plugin: 'maven'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
version = mod_version
group = "org.squiddev"
archivesBaseName = "cc-tweaked-${mc_version}"
minecraft {
runs {
client {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
mods {
computercraft {
source sourceSets.main
}
}
}
server {
workingDirectory project.file('run')
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
property 'forge.logging.console.level', 'debug'
mods {
computercraft {
source sourceSets.main
}
}
}
}
mappings channel: 'snapshot', version: "${mappings_version}".toString()
accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg')
}
group = "dan200.computercraft"
archivesBaseName = "cc-tweaked-fabric-${mc_version}"
repositories {
maven {
name "JEI"
url "http://dvs1.progwml6.com/files/maven"
}
mavenCentral()
maven {
name "SquidDev"
url "https://squiddev.cc/maven"
}
ivy {
name "Charset"
artifactPattern "https://asie.pl/files/mods/Charset/LibOnly/[module]-[revision](-[classifier]).[ext]"
}
maven {
name "Amadornes"
url "http://maven.amadornes.com/"
}
}
configurations {
shade
compile.extendsFrom shade
deployerJars
}
dependencies {
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
minecraft "com.mojang:minecraft:${mc_version}"
mappings "net.fabricmc:yarn:${mc_version}+build.${mappings_version}:v2"
modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}"
compileOnly fg.deobf("mezz.jei:jei-1.13.2:5.0.0.20:api")
// deobfProvided "pl.asie:Charset-Lib:0.5.4.6"
// deobfProvided "MCMultiPart2:MCMultiPart:2.5.3"
modImplementation "me.shedaniel.cloth:config-2:${cloth_config_version}"
modImplementation "io.github.prospector:modmenu:${modmenu_version}"
runtimeOnly fg.deobf("mezz.jei:jei-1.13.2:5.0.0.20")
modApi "me.shedaniel.cloth.api:cloth-utils-v1:${project.cloth_api_version}"
include "me.shedaniel.cloth.api:cloth-utils-v1:${project.cloth_api_version}"
shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT'
implementation "blue.endless:jankson:${jankson_version}"
implementation 'com.google.code.findbugs:jsr305:3.0.2'
compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
include "me.shedaniel.cloth:config-2:${cloth_config_version}"
include "blue.endless:jankson:${jankson_version}"
include 'javax.vecmath:vecmath:1.5.2'
compile 'javax.vecmath:vecmath:1.5.2'
shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.0'
deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0"
modRuntime "me.shedaniel:RoughlyEnoughItems-api:5.2.10"
modRuntime "me.shedaniel:RoughlyEnoughItems:5.2.10"
}
sourceSets {
main {
java {
exclude 'dan200/computercraft/shared/integration/mcmp'
exclude 'dan200/computercraft/shared/integration/charset'
exclude 'dan200/computercraft/shared/integration'
}
}
}
javadoc {
include "dan200/computercraft/api/**/*.java"
processResources {
inputs.property "version", project.version
from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json"
expand "version": project.version
}
from(sourceSets.main.resources.srcDirs) {
exclude "fabric.mod.json"
}
}
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
}
jar {
dependsOn javadoc
manifest {
attributes(["Specification-Title": "computercraft",
"Specification-Vendor": "SquidDev",
"Specification-Version": "25.0",
"Implementation-Title": "CC: Tweaked",
"Implementation-Version": "${mod_version}",
"Implementation-Vendor" :"SquidDev",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")])
}
from (sourceSets.main.allSource) {
include "dan200/computercraft/api/**/*.java"
}
from "LICENSE"
from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
}
import java.nio.charset.StandardCharsets
import java.nio.file.*
import java.util.zip.*
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import org.ajoberstar.grgit.Grgit
import proguard.gradle.ProGuardTask
task proguard(type: ProGuardTask, dependsOn: jar) {
description "Removes unused shadowed classes from the jar"
group "compact"
injars jar.archivePath
outjars "${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar"
// Add the main runtime jar and all non-shadowed dependencies
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
doFirst {
sourceSets.main.compileClasspath
.filter { !it.name.contains("Cobalt") }
.each { libraryjars it }
}
// We want to avoid as much obfuscation as possible. We're only doing this to shrink code size.
dontobfuscate; dontoptimize; keepattributes; keepparameternames
// Proguard will remove directories by default, but that breaks JarMount.
keepdirectories 'data/computercraft/lua**'
// Preserve ComputerCraft classes - we only want to strip shadowed files.
keep 'class dan200.computercraft.** { *; }'
// Preserve the constructors in Cobalt library class, as we init them via reflection
keepclassmembers 'class org.squiddev.cobalt.lib.** { <init>(...); }'
// LWJGL and Apache bundle Java 9 versions, which is great, but rather breaks Proguard
dontwarn 'module-info'
dontwarn 'org.apache.**,org.lwjgl.**'
}
task proguardMove(dependsOn: proguard) {
description "Replace the original jar with the minified version"
group "compact"
doLast {
Files.move(
file("${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar").toPath(),
file(jar.archivePath).toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
processResources {
inputs.property "version", mod_version
inputs.property "mcversion", mc_version
def hash = 'none'
Set<String> contributors = []
try {
def grgit = Grgit.open(dir: '.')
hash = grgit.head().id
def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe']
grgit.log().each {
if (!blacklist.contains(it.author.name)) contributors.add(it.author.name)
if (!blacklist.contains(it.committer.name)) contributors.add(it.committer.name)
}
} catch(Exception ignored) { }
inputs.property "commithash", hash
from(sourceSets.main.resources.srcDirs) {
include 'META-INF/mods.toml'
include 'data/computercraft/lua/rom/help/credits.txt'
expand 'version': mod_version,
'mcversion': mc_version,
'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n')
}
from(sourceSets.main.resources.srcDirs) {
exclude 'META-INF/mods.toml'
exclude 'data/computercraft/lua/rom/help/credits.txt'
}
}
task compressJson(dependsOn: jar) {
group "compact"
description "Minifies all JSON files, stripping whitespace"
def jarPath = file(jar.archivePath)
def tempPath = File.createTempFile("input", ".jar", temporaryDir)
tempPath.deleteOnExit()
def gson = new GsonBuilder().create()
doLast {
// Copy over all files in the current jar to the new one, running json files from GSON. As pretty printing
// is turned off, they should be minified.
new ZipFile(jarPath).withCloseable { inJar ->
new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar ->
inJar.entries().each { entry ->
if(entry.directory) {
outJar.putNextEntry(entry)
} else if(!entry.name.endsWith(".json")) {
outJar.putNextEntry(entry)
inJar.getInputStream(entry).withCloseable { outJar << it }
} else {
ZipEntry newEntry = new ZipEntry(entry.name)
newEntry.setTime(entry.time)
outJar.putNextEntry(newEntry)
def element = inJar.getInputStream(entry).withCloseable { gson.fromJson(it.newReader("UTF8"), JsonElement.class) }
outJar.write(gson.toJson(element).getBytes(StandardCharsets.UTF_8))
}
}
}
}
// And replace the original jar again
Files.move(tempPath.toPath(), jarPath.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
assemble.dependsOn compressJson
task checkRelease {
group "upload"
description "Verifies that everything is ready for a release"
inputs.property "version", mod_version
inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.txt")
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
doLast {
def ok = true
// Check we're targetting the current version
def whatsnew = new File("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt").readLines()
if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
ok = false
project.logger.error("Expected `whatsnew.txt' to target $mod_version.")
}
// Check "read more" exists and trim it
def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' }
if (idx == -1) {
ok = false
project.logger.error("Must mention the changelog in whatsnew.txt")
} else {
whatsnew = whatsnew.getAt(0 ..< idx)
}
// Check whatsnew and changelog match.
def versionChangelog = "# " + whatsnew.join("\n")
def changelog = new File("src/main/resources/data/computercraft/lua/rom/help/changelog.txt").getText()
if (!changelog.startsWith(versionChangelog)) {
ok = false
project.logger.error("whatsnew and changelog are not in sync")
}
if (!ok) throw new IllegalStateException("Could not check release")
}
}
curseforge {
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
project {
id = '282001'
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 {
incompatible "computercraft"
}
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
// artifact sourceJar
}
}
}
uploadArchives {
repositories {
if(project.hasProperty('mavenUploadUrl')) {
mavenDeployer {
configuration = configurations.deployerJars
repository(url: project.property('mavenUploadUrl')) {
authentication(
userName: project.property('mavenUploadUser'),
privateKey: project.property('mavenUploadKey'))
}
pom.project {
name 'CC: Tweaked'
packaging 'jar'
description 'CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.'
url 'https://github.com/SquidDev-CC/CC-Tweaked'
scm {
url 'https://github.com/SquidDev-CC/CC-Tweaked.git'
}
issueManagement {
system 'github'
url 'https://github.com/SquidDev-CC/CC-Tweaked/issues'
}
licenses {
license {
name 'ComputerCraft Public License, Version 1.0'
url 'https://github.com/SquidDev-CC/CC-Tweaked/blob/master/LICENSE'
distribution 'repo'
}
}
}
pom.whenConfigured { pom ->
pom.dependencies.clear()
}
// add all the jars that should be included when publishing to maven
artifact(remapJar) {
builtBy remapJar
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
}
githubRelease {
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
owner 'SquidDev-CC'
repo 'CC-Tweaked'
targetCommitish { Grgit.open(dir: '.').branch.current().name }
tagName "v${mc_version}-${mod_version}"
releaseName "[${mc_version}] ${mod_version}"
body {
"## " + new File("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
.readLines()
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
.join("\n").trim()
}
prerelease false
}
def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"]
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }
task uploadAll(dependsOn: uploadTasks) {
group "upload"
description "Uploads to all repositories (Maven, Curse, GitHub release)"
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
// select the repositories you want to publish to
repositories {
// uncomment to publish to the local maven
// mavenLocal()
}
}
gradle.projectsEvaluated {
reobfJar.dependsOn proguardMove
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint" << "-Xlint:-processing" // Causes Forge build to fail << "-Werror"
}
}

View File

@@ -1,7 +1,17 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Mod properties
mod_version=1.83.1
mod_version=1.92.0
# Minecraft properties
mc_version=1.13.2
forge_version=25.0.219
mappings_version=20190530-1.13.2
mc_version=1.16.3
mappings_version=31
# Dependencies
cloth_config_version=4.8.1
fabric_api_version=0.19.0+build.398-1.16
fabric_loader_version=0.9.2+build.206
jankson_version=1.2.0
modmenu_version=1.14.6+
cloth_api_version=1.4.5

Binary file not shown.

View File

@@ -1,5 +1,6 @@
#Tue Jul 07 13:15:43 EDT 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

48
patchwork.md Normal file
View File

@@ -0,0 +1,48 @@
# Just my list of things I have ported over
Format for the changelog of ported stuff
```
commit // Shows commit from CC:T
commit2 // Shows a commit that is the same thing, just a clean up, only if right after
Title // Commit Title
SubScript // Desc of commit
```
If a edit that is present in CC:T is not needed, I will skip over it.
Any and all references to an issue number, are to be found on CC:T's repo. not this oen
```md
5155e18de279a193c558aa029963486fd1294769
Added translation for Vietnamese
Co-authored-by: Boom <boom@flyingpackets.net>
```
```
7e121ff72f2b1504cd6af47b57500876682bac45
ae6124d1f477487abab1858abde8c4ec49dfee3c
Translations for Vienamese
Co-authored-by: Boom <boom@flyingpackets.net>
```
```
59de21eae29849988e77fad6bc335f5ce78dfec7
Handle tabs when parsing JSON
Fixes #539
```
```
748ebbe66bf0a4239bde34f557e4b4b75d61d990
Bump to 1.92.0
A tiny release, but there's new features so it's technically a minor
bump.
```
Cherry Picked because this update was partially related to forge updates rather than mod updates
```
8b4a01df27ff7f6fa9ffd9c2188c6e3166edd515
Update to Minecraft 1.16.3
I hope the Fabric folks now realise this is gonna be a race of who can
update first :p. Either way, this was a very easy update - only changes
were due to unrelated Forge changes.
```

View File

@@ -1 +1,12 @@
rootProject.name = "cc-tweaked-${mc_version}"
pluginManagement {
repositories {
jcenter()
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
}
}
rootProject.name = "cc-tweaked-fabric-${mc_version}"

View File

@@ -1,67 +1,93 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft;
import dan200.computercraft.api.filesystem.IMount;
import static dan200.computercraft.shared.ComputerCraftRegistry.ModBlocks;
import static dan200.computercraft.shared.ComputerCraftRegistry.init;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRule;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.shared.Config;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.core.asm.GenericSource;
import dan200.computercraft.shared.common.ColourableRecipe;
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.printer.BlockPrinter;
import dan200.computercraft.shared.peripheral.speaker.BlockSpeaker;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
import dan200.computercraft.shared.media.recipes.DiskRecipe;
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
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 net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
import dan200.computercraft.shared.turtle.upgrades.TurtleAxe;
import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable;
import dan200.computercraft.shared.turtle.upgrades.TurtleHoe;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import dan200.computercraft.shared.turtle.upgrades.TurtleShovel;
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
import dan200.computercraft.shared.turtle.upgrades.TurtleSword;
import dan200.computercraft.shared.turtle.upgrades.TurtleTool;
import dan200.computercraft.shared.util.Config;
import dan200.computercraft.shared.util.ImpostorRecipe;
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
import dan200.computercraft.shared.util.ServiceUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import java.util.concurrent.TimeUnit;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
@Mod( ComputerCraft.MOD_ID )
public final class ComputerCraft
{
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.loader.api.FabricLoader;
public final class ComputerCraft implements ModInitializer {
public static final String MOD_ID = "computercraft";
public static final int DATAFIXER_VERSION = 0;
// Configuration options
public static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
public static final String[] DEFAULT_HTTP_WHITELIST = new String[] {"*"};
public static final String[] DEFAULT_HTTP_BLACKLIST = new String[] {
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"fd00::/8",
};
"0.0.0.0/8"
};
public static final int terminalWidth_computer = 51;
public static final int terminalHeight_computer = 19;
public static final int terminalWidth_turtle = 39;
public static final int terminalHeight_turtle = 13;
public static final int terminalWidth_pocketComputer = 26;
public static final int terminalHeight_pocketComputer = 20;
// Registries
public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry();
public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry();
// Logging
public static final Logger log = LogManager.getLogger(MOD_ID);
public static ItemGroup MAIN_GROUP = FabricItemGroupBuilder.build(new Identifier(MOD_ID, "main"), () -> new ItemStack(ModBlocks.COMPUTER_NORMAL));
public static boolean commandRequireCreative = false;
public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST;
public static int computerSpaceLimit = 1000 * 1000;
public static int floppySpaceLimit = 125 * 1000;
public static int maximumFilesOpen = 128;
@@ -69,148 +95,68 @@ public final class ComputerCraft
public static String default_computer_settings = "";
public static boolean debug_enable = true;
public static boolean logPeripheralErrors = false;
public static int computer_threads = 1;
public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( 10 );
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( 5 );
public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos(10);
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos(5);
public static boolean http_enable = true;
public static boolean http_websocket_enable = true;
public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
public static AddressPredicate http_blacklist = new AddressPredicate( DEFAULT_HTTP_BLACKLIST );
public static 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 = Websocket.MAX_MESSAGE_SIZE;
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 maxNotesPerTick = 8;
public static boolean turtlesNeedFuel = true;
public static int turtleFuelLimit = 20000;
public static int advancedTurtleFuelLimit = 100000;
public static boolean turtlesObeyBlockProtection = true;
public static boolean turtlesCanPush = true;
public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf( TurtleAction.class );
public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf(TurtleAction.class);
public static int monitorWidth = 8;
public static int monitorHeight = 6;
public static double monitorDistanceSq = 4096;
public static final int terminalWidth_computer = 51;
public static final int terminalHeight_computer = 19;
public static List<AddressRule> httpRules = buildHttpRulesFromConfig(DEFAULT_HTTP_BLACKLIST, DEFAULT_HTTP_WHITELIST);
public static final int terminalWidth_turtle = 39;
public static final int terminalHeight_turtle = 13;
public static final int terminalWidth_pocketComputer = 26;
public static final int terminalHeight_pocketComputer = 20;
// Blocks and Items
public static final class Blocks
{
public static BlockComputer computerNormal;
public static BlockComputer computerAdvanced;
public static BlockComputer computerCommand;
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 List<AddressRule> buildHttpRulesFromConfig(String[] blacklist, String[] whitelist) {
return Stream.concat(Stream.of(blacklist)
.map(x -> AddressRule.parse(x, Action.DENY.toPartial()))
.filter(Objects::nonNull),
Stream.of(whitelist)
.map(x -> AddressRule.parse(x, Action.ALLOW.toPartial()))
.filter(Objects::nonNull))
.collect(Collectors.toList());
}
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;
@Override
public void onInitialize() {
Config.load(Paths.get(FabricLoader.getInstance()
.getConfigDir()
.toFile()
.getPath(), MOD_ID + ".json5"));
ComputerCraftProxyCommon.init();
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "colour"), ColourableRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "computer_upgrade"), ComputerUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER,
new Identifier(ComputerCraft.MOD_ID, "pocket_computer_upgrade"),
PocketComputerUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "disk"), DiskRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "printout"), PrintoutRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "turtle"), TurtleRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "turtle_upgrade"), TurtleUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "impostor_shaped"), ImpostorRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "impostor_shapeless"), ImpostorShapelessRecipe.SERIALIZER);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "block_named"), BlockNamedEntityLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "player_creative"), PlayerCreativeLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "has_id"), HasComputerIdLootCondition.TYPE);
init();
GenericSource.setup( () -> ServiceUtil.loadServices( GenericSource.class ));
}
public static final class TurtleUpgrades
{
public static TurtleModem wirelessModemNormal;
public static TurtleModem wirelessModemAdvanced;
public static TurtleSpeaker speaker;
public static TurtleCraftingTable craftingTable;
public static TurtleSword diamondSword;
public static TurtleShovel diamondShovel;
public static TurtleTool diamondPickaxe;
public static TurtleAxe diamondAxe;
public static TurtleHoe diamondHoe;
}
public static final class PocketUpgrades
{
public static PocketModem wirelessModemNormal;
public static PocketModem wirelessModemAdvanced;
public static PocketSpeaker speaker;
}
// Registries
public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry();
public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry();
// Logging
public static final Logger log = LogManager.getLogger( MOD_ID );
public ComputerCraft()
{
Config.load();
}
public static String getVersion()
{
return "${version}";
}
static IMount createResourceMount( String domain, String subPath )
{
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();
ResourceMount mount = new ResourceMount( domain, subPath, manager );
return mount.exists( "" ) ? mount : null;
}
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;
}
}
}

View File

@@ -1,11 +1,18 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
@@ -20,122 +27,148 @@ import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.ApiFactories;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.shared.*;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.mixin.MinecraftServerAccess;
import dan200.computercraft.shared.BundledRedstone;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.Peripherals;
import dan200.computercraft.shared.PocketUpgrades;
import dan200.computercraft.shared.TurtleUpgrades;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.peripheral.modem.wired.TileWiredModemFull;
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.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.resource.ReloadableResourceManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import java.io.File;
import net.fabricmc.loader.api.FabricLoader;
public final class ComputerCraftAPIImpl implements IComputerCraftAPI
{
public final class ComputerCraftAPIImpl implements IComputerCraftAPI {
public static final ComputerCraftAPIImpl INSTANCE = new ComputerCraftAPIImpl();
private ComputerCraftAPIImpl()
{
private String version;
private ComputerCraftAPIImpl() {
}
public static InputStream getResourceFile(String domain, String subPath) {
MinecraftServer server = GameInstanceUtils.getServer();
if (server != null) {
ReloadableResourceManager manager = (ReloadableResourceManager) ((MinecraftServerAccess)server).getServerResourceManager().getResourceManager();
try {
return manager.getResource(new Identifier(domain, subPath))
.getInputStream();
} catch (IOException ignored) {
return null;
}
}
return null;
}
@Nonnull
@Override
public String getInstalledVersion()
{
return "${version}";
}
@Override
public int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath )
{
return IDAssigner.getNextId( parentSubPath );
}
@Override
public IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity )
{
try
{
return new FileMount( new File( IDAssigner.getDir(), subPath ), capacity );
public String getInstalledVersion() {
if (this.version != null) {
return this.version;
}
catch( Exception e )
{
return this.version = FabricLoader.getInstance()
.getModContainer(ComputerCraft.MOD_ID)
.map(x -> x.getMetadata()
.getVersion()
.toString())
.orElse("unknown");
}
@Override
public int createUniqueNumberedSaveDir(@Nonnull World world, @Nonnull String parentSubPath) {
return IDAssigner.getNextId(parentSubPath);
}
@Override
public IWritableMount createSaveDirMount(@Nonnull World world, @Nonnull String subPath, long capacity) {
try {
return new FileMount(new File(IDAssigner.getDir(), subPath), capacity);
} catch (Exception e) {
return null;
}
}
@Override
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath )
{
return ComputerCraft.createResourceMount( domain, subPath );
public IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath) {
MinecraftServer server = GameInstanceUtils.getServer();
if (server != null) {
ReloadableResourceManager manager = (ReloadableResourceManager) ((MinecraftServerAccess)server).getServerResourceManager().getResourceManager();
ResourceMount mount = ResourceMount.get(domain, subPath, manager);
return mount.exists("") ? mount : null;
}
return null;
}
@Override
public void registerPeripheralProvider( @Nonnull IPeripheralProvider provider )
{
Peripherals.register( provider );
public void registerPeripheralProvider(@Nonnull IPeripheralProvider provider) {
Peripherals.register(provider);
}
@Override
public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade )
{
TurtleUpgrades.register( upgrade );
public void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade) {
TurtleUpgrades.register(upgrade);
}
@Override
public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider )
{
BundledRedstone.register( provider );
public void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider) {
BundledRedstone.register(provider);
}
@Override
public int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
{
return BundledRedstone.getDefaultOutput( world, pos, side );
public int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side) {
return BundledRedstone.getDefaultOutput(world, pos, side);
}
@Override
public void registerMediaProvider( @Nonnull IMediaProvider provider )
{
MediaProviders.register( provider );
public void registerMediaProvider(@Nonnull IMediaProvider provider) {
MediaProviders.register(provider);
}
@Override
public void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade )
{
PocketUpgrades.register( upgrade );
public void registerPocketUpgrade(@Nonnull IPocketUpgrade upgrade) {
PocketUpgrades.register(upgrade);
}
@Nonnull
@Override
public IPacketNetwork getWirelessNetwork()
{
public IPacketNetwork getWirelessNetwork() {
return WirelessNetwork.getUniversal();
}
@Override
public void registerAPIFactory( @Nonnull ILuaAPIFactory factory )
{
ApiFactories.register( factory );
public void registerAPIFactory(@Nonnull ILuaAPIFactory factory) {
ApiFactories.register(factory);
}
@Nonnull
@Override
public IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element )
{
return new WiredNode( element );
public IWiredNode createWiredNodeForElement(@Nonnull IWiredElement element) {
return new WiredNode(element);
}
@Nonnull
@Nullable
@Override
public LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
{
TileEntity tile = world.getTileEntity( pos );
return tile == null ? LazyOptional.empty() : tile.getCapability( CapabilityWiredElement.CAPABILITY, side );
public IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side) {
BlockEntity tile = world.getBlockEntity(pos);
if (tile instanceof TileCable) {
return ((TileCable) tile).getElement(side);
} else if (tile instanceof TileWiredModemFull) {
return ((TileWiredModemFull) tile).getElement();
}
return null;
}
}

View File

@@ -1,80 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.api;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IItemProvider;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Util;
import javax.annotation.Nonnull;
/**
* A base class for {@link ITurtleUpgrade}s.
*
* One does not have to use this, but it does provide a convenient template.
*/
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
{
private final ResourceLocation id;
private final TurtleUpgradeType type;
private final String adjective;
private final ItemStack stack;
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack )
{
this.id = id;
this.type = type;
this.adjective = adjective;
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 )
{
this( id, type, Util.makeTranslationKey( "upgrade", id ) + ".adjective", stack );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, IItemProvider item )
{
this( id, type, new ItemStack( item ) );
}
@Nonnull
@Override
public final ResourceLocation getUpgradeID()
{
return id;
}
@Nonnull
@Override
public final String getUnlocalisedAdjective()
{
return adjective;
}
@Nonnull
@Override
public final TurtleUpgradeType getType()
{
return type;
}
@Nonnull
@Override
public final ItemStack getCraftingItem()
{
return stack;
}
}

View File

@@ -1,11 +1,14 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaAPIFactory;
@@ -20,32 +23,44 @@ import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
/**
* The static entry point to the ComputerCraft API.
* Members in this class must be called after mod_ComputerCraft has been initialised,
* but may be called before it is fully loaded.
*
* Members in this class must be called after mod_ComputerCraft has been initialised, but may be called before it is fully loaded.
*/
public final class ComputerCraftAPI
{
public final class ComputerCraftAPI {
private static IComputerCraftAPI instance;
@Nonnull
public static String getInstalledVersion()
{
@Deprecated
public static String getAPIVersion() {
return getInstalledVersion();
}
@Nonnull
public static String getInstalledVersion() {
return getInstance().getInstalledVersion();
}
@Nonnull
public static String getAPIVersion()
{
return "${version}";
private static IComputerCraftAPI getInstance() {
if (instance != null) {
return instance;
}
try {
return instance = (IComputerCraftAPI) Class.forName("dan200.computercraft.ComputerCraftAPIImpl")
.getField("INSTANCE")
.get(null);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Cannot find ComputerCraft API", e);
}
}
/**
@@ -53,31 +68,29 @@ public final class ComputerCraftAPI
*
* Use in conjunction with createSaveDirMount() to create a unique place for your peripherals or media items to store files.
*
* @param world The world for which the save dir should be created. This should be the server side world object.
* @param world The world for which the save dir should be created. This should be the server side world object.
* @param parentSubPath The folder path within the save directory where the new directory should be created. eg: "computercraft/disk"
* @return The numerical value of the name of the new folder, or -1 if the folder could not be created for some reason.
*
* eg: if createUniqueNumberedSaveDir( world, "computer/disk" ) was called returns 42, then "computer/disk/42" is now
* available for writing.
* eg: if createUniqueNumberedSaveDir( world, "computer/disk" ) was called returns 42, then "computer/disk/42" is now available for writing.
* @see #createSaveDirMount(World, String, long)
*/
public static int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath )
{
return getInstance().createUniqueNumberedSaveDir( world, parentSubPath );
public static int createUniqueNumberedSaveDir(@Nonnull World world, @Nonnull String parentSubPath) {
return getInstance().createUniqueNumberedSaveDir(world, parentSubPath);
}
/**
* Creates a file system mount that maps to a subfolder of the save directory for a given world, and returns it.
*
* Use in conjunction with IComputerAccess.mount() or IComputerAccess.mountWritable() to mount a folder from the
* users save directory onto a computers file system.
* Use in conjunction with IComputerAccess.mount() or IComputerAccess.mountWritable() to mount a folder from the users save directory onto a computers
* file system.
*
* @param world The world for which the save dir can be found. This should be the server side world object.
* @param subPath The folder path within the save directory that the mount should map to. eg: "computer/disk/42".
* Use createUniqueNumberedSaveDir() to create a new numbered folder to use.
* @param world The world for which the save dir can be found. This should be the server side world object.
* @param subPath The folder path within the save directory that the mount should map to. eg: "computer/disk/42". Use createUniqueNumberedSaveDir()
* to create a new numbered folder to use.
* @param capacity The amount of data that can be stored in the directory before it fills up, in bytes.
* @return The mount, or null if it could be created for some reason. Use IComputerAccess.mount() or IComputerAccess.mountWritable()
* to mount this on a Computers' file system.
* @return The mount, or null if it could be created for some reason. Use IComputerAccess.mount() or IComputerAccess.mountWritable() to mount this on a
* Computers' file system.
* @see #createUniqueNumberedSaveDir(World, String)
* @see IComputerAccess#mount(String, IMount)
* @see IComputerAccess#mountWritable(String, IWritableMount)
@@ -85,21 +98,19 @@ public final class ComputerCraftAPI
* @see IWritableMount
*/
@Nullable
public static IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity )
{
return getInstance().createSaveDirMount( world, subPath, capacity );
public static IWritableMount createSaveDirMount(@Nonnull World world, @Nonnull String subPath, long capacity) {
return getInstance().createSaveDirMount(world, subPath, capacity);
}
/**
* Creates a file system mount to a resource folder, and returns it.
*
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a
* resource folder onto a computer's file system.
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a resource folder onto a computer's file
* system.
*
* The files in this mount will be a combination of files in all mod jar, and data packs that contain
* resources with the same domain and path.
* The files in this mount will be a combination of files in all mod jar, and data packs that contain resources with the same domain and path.
*
* @param domain The domain under which to look for resources. eg: "mymod".
* @param domain The domain under which to look for resources. eg: "mymod".
* @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
* @return The mount, or {@code null} if it could be created for some reason.
* @see IComputerAccess#mount(String, IMount)
@@ -107,34 +118,8 @@ public final class ComputerCraftAPI
* @see IMount
*/
@Nullable
public static IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath )
{
return getInstance().createResourceMount( domain, subPath );
}
/**
* Creates a file system mount to a resource folder, and returns it.
*
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a
* resource folder onto a computer's file system.
*
* The files in this mount will be a combination of files in all mod jar, and data packs that contain
* resources with the same domain and path.
*
* @param klass The mod class to which the files belong.
* @param domain The domain under which to look for resources. eg: "mymod".
* @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
* @return The mount, or {@code null} if it could be created for some reason.
* @see IComputerAccess#mount(String, IMount)
* @see IComputerAccess#mountWritable(String, IWritableMount)
* @see IMount
* @deprecated Use {@link #createResourceMount(String, String)} instead.
*/
@Nullable
@Deprecated
public static IMount createResourceMount( Class<?> klass, @Nonnull String domain, @Nonnull String subPath )
{
return getInstance().createResourceMount( domain, subPath );
public static IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath) {
return getInstance().createResourceMount(domain, subPath);
}
/**
@@ -144,22 +129,19 @@ public final class ComputerCraftAPI
* @see IPeripheral
* @see IPeripheralProvider
*/
public static void registerPeripheralProvider( @Nonnull IPeripheralProvider provider )
{
getInstance().registerPeripheralProvider( provider );
public static void registerPeripheralProvider(@Nonnull IPeripheralProvider provider) {
getInstance().registerPeripheralProvider(provider);
}
/**
* Registers a new turtle turtle for use in ComputerCraft. After calling this,
* users should be able to craft Turtles with your new turtle. It is recommended to call
* this during the load() method of your mod.
* Registers a new turtle turtle for use in ComputerCraft. After calling this, users should be able to craft Turtles with your new turtle. It is
* recommended to call this during the load() method of your mod.
*
* @param upgrade The turtle upgrade to register.
* @see ITurtleUpgrade
*/
public static void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade )
{
getInstance().registerTurtleUpgrade( upgrade );
public static void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade) {
getInstance().registerTurtleUpgrade(upgrade);
}
/**
@@ -168,40 +150,36 @@ public final class ComputerCraftAPI
* @param provider The bundled redstone provider to register.
* @see IBundledRedstoneProvider
*/
public static void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider )
{
getInstance().registerBundledRedstoneProvider( provider );
public static void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider) {
getInstance().registerBundledRedstoneProvider(provider);
}
/**
* If there is a Computer or Turtle at a certain position in the world, get it's bundled redstone output.
*
* @param world The world this block is in.
* @param pos The position this block is at.
* @param side The side to extract the bundled redstone output from.
* @return If there is a block capable of emitting bundled redstone at the location, it's signal (0-65535) will be returned.
* If there is no block capable of emitting bundled redstone at the location, -1 will be returned.
* @param pos The position this block is at.
* @param side The side to extract the bundled redstone output from.
* @return If there is a block capable of emitting bundled redstone at the location, it's signal (0-65535) will be returned. If there is no block
* capable of emitting bundled redstone at the location, -1 will be returned.
* @see IBundledRedstoneProvider
*/
public static int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
{
return getInstance().getBundledRedstoneOutput( world, pos, side );
public static int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side) {
return getInstance().getBundledRedstoneOutput(world, pos, side);
}
/**
* Registers a media provider to provide {@link IMedia} implementations for Items
* Registers a media provider to provide {@link IMedia} implementations for Items.
*
* @param provider The media provider to register.
* @see IMediaProvider
*/
public static void registerMediaProvider( @Nonnull IMediaProvider provider )
{
getInstance().registerMediaProvider( provider );
public static void registerMediaProvider(@Nonnull IMediaProvider provider) {
getInstance().registerMediaProvider(provider);
}
public static void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade )
{
getInstance().registerPocketUpgrade( upgrade );
public static void registerPocketUpgrade(@Nonnull IPocketUpgrade upgrade) {
getInstance().registerPocketUpgrade(upgrade);
}
/**
@@ -209,96 +187,73 @@ public final class ComputerCraftAPI
*
* @return The global wireless network, or {@code null} if it could not be fetched.
*/
public static IPacketNetwork getWirelessNetwork()
{
public static IPacketNetwork getWirelessNetwork() {
return getInstance().getWirelessNetwork();
}
public static void registerAPIFactory( @Nonnull ILuaAPIFactory factory )
{
getInstance().registerAPIFactory( factory );
public static void registerAPIFactory(@Nonnull ILuaAPIFactory factory) {
getInstance().registerAPIFactory(factory);
}
/**
* Construct a new wired node for a given wired element
* Construct a new wired node for a given wired element.
*
* @param element The element to construct it for
* @return The element's node
* @see IWiredElement#getNode()
*/
@Nonnull
public static IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element )
{
return getInstance().createWiredNodeForElement( element );
public static IWiredNode createWiredNodeForElement(@Nonnull IWiredElement element) {
return getInstance().createWiredNodeForElement(element);
}
/**
* Get the wired network element for a block in world
* Get the wired network element for a block in world.
*
* @param world The world the block exists in
* @param pos The position the block exists in
* @param side The side to extract the network element from
* @param pos The position the block exists in
* @param side The side to extract the network element from
* @return The element's node
* @see IWiredElement#getNode()
*/
@Nonnull
public static LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
{
return getInstance().getWiredElementAt( world, pos, side );
@Nullable
public static IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side) {
return getInstance().getWiredElementAt(world, pos, side);
}
private static IComputerCraftAPI instance;
@Nonnull
private static IComputerCraftAPI getInstance()
{
if( instance != null ) return instance;
try
{
return instance = (IComputerCraftAPI) Class.forName( "dan200.computercraft.ComputerCraftAPIImpl" )
.getField( "INSTANCE" ).get( null );
}
catch( ReflectiveOperationException e )
{
throw new IllegalStateException( "Cannot find ComputerCraft API", e );
}
}
public interface IComputerCraftAPI
{
public interface IComputerCraftAPI {
@Nonnull
String getInstalledVersion();
int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath );
int createUniqueNumberedSaveDir(@Nonnull World world, @Nonnull String parentSubPath);
@Nullable
IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity );
IWritableMount createSaveDirMount(@Nonnull World world, @Nonnull String subPath, long capacity);
@Nullable
IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath );
IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath);
void registerPeripheralProvider( @Nonnull IPeripheralProvider provider );
void registerPeripheralProvider(@Nonnull IPeripheralProvider provider);
void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade );
void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade);
void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider );
void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider);
int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side );
int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side);
void registerMediaProvider( @Nonnull IMediaProvider provider );
void registerMediaProvider(@Nonnull IMediaProvider provider);
void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade );
void registerPocketUpgrade(@Nonnull IPocketUpgrade upgrade);
@Nonnull
IPacketNetwork getWirelessNetwork();
void registerAPIFactory( @Nonnull ILuaAPIFactory factory );
void registerAPIFactory(@Nonnull ILuaAPIFactory factory);
@Nonnull
IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element );
IWiredNode createWiredNodeForElement(@Nonnull IWiredElement element);
@Nonnull
LazyOptional<IWiredElement> getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull EnumFacing side );
@Nullable
IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side);
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.client;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.mixin.AffineTransformationAccess;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.client.util.math.AffineTransformation;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/**
* A model to render, combined with a transformation matrix to apply.
*/
@Environment (EnvType.CLIENT)
public final class TransformedModel {
private final BakedModel model;
private final AffineTransformation matrix;
public TransformedModel(@Nonnull BakedModel model, @Nonnull AffineTransformation matrix) {
this.model = Objects.requireNonNull(model);
this.matrix = Objects.requireNonNull(matrix);
}
public TransformedModel(@Nonnull BakedModel model) {
this.model = Objects.requireNonNull(model);
this.matrix = AffineTransformation.identity();
}
public static TransformedModel of(@Nonnull ModelIdentifier location) {
BakedModelManager modelManager = MinecraftClient.getInstance()
.getBakedModelManager();
return new TransformedModel(modelManager.getModel(location));
}
public static TransformedModel of(@Nonnull ItemStack item, @Nonnull AffineTransformation transform) {
BakedModel model = MinecraftClient.getInstance()
.getItemRenderer()
.getModels()
.getModel(item);
return new TransformedModel(model, transform);
}
@Nonnull
public BakedModel getModel() {
return this.model;
}
@Nonnull
public AffineTransformation getMatrix() {
return this.matrix;
}
public void push(MatrixStack matrixStack) {
matrixStack.push();
AffineTransformationAccess access = (AffineTransformationAccess) (Object) this.matrix;
if (access.getTranslation() != null)
matrixStack.translate(access.getTranslation().getX(), access.getTranslation().getY(), access.getTranslation().getZ());
matrixStack.multiply(this.matrix.getRotation2());
if (access.getScale() != null)
matrixStack.scale(access.getScale().getX(), access.getScale().getY(), access.getScale().getZ());
if (access.getRotation1() != null)
matrixStack.multiply(access.getRotation1());
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.filesystem;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
/**
* A simple version of {@link BasicFileAttributes}, which provides what information a {@link IMount} already exposes.
*/
final class FileAttributes implements BasicFileAttributes {
private static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
private final boolean isDirectory;
private final long size;
FileAttributes(boolean isDirectory, long size) {
this.isDirectory = isDirectory;
this.size = size;
}
@Override
public FileTime lastModifiedTime() {
return EPOCH;
}
@Override
public FileTime lastAccessTime() {
return EPOCH;
}
@Override
public FileTime creationTime() {
return EPOCH;
}
@Override
public boolean isRegularFile() {
return !this.isDirectory;
}
@Override
public boolean isDirectory() {
return this.isDirectory;
}
@Override
public boolean isSymbolicLink() {
return false;
}
@Override
public boolean isOther() {
return false;
}
@Override
public long size() {
return this.size;
}
@Override
public Object fileKey() {
return null;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.filesystem;
import java.io.IOException;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* An {@link IOException} which occurred on a specific file.
*
* This may be thrown from a {@link IMount} or {@link IWritableMount} to give more information about a failure.
*/
public class FileOperationException extends IOException {
private static final long serialVersionUID = -8809108200853029849L;
private final String filename;
public FileOperationException(@Nullable String filename, @Nonnull String message) {
super(Objects.requireNonNull(message, "message cannot be null"));
this.filename = filename;
}
public FileOperationException(@Nonnull String message) {
super(Objects.requireNonNull(message, "message cannot be null"));
this.filename = null;
}
@Nullable
public String getFilename() {
return this.filename;
}
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -13,32 +13,31 @@ import java.io.IOException;
*
* This exists for use by various APIs - one should not attempt to mount it.
*/
public interface IFileSystem extends IWritableMount
{
public interface IFileSystem extends IWritableMount {
/**
* Combine two paths together, reducing them into a normalised form.
*
* @param path The main path.
* @param path The main path.
* @param child The path to append.
* @return The combined, normalised path.
*/
String combine( String path, String child );
String combine(String path, String child);
/**
* Copy files from one location to another.
*
* @param from The location to copy from.
* @param to The location to copy to. This should not exist.
* @param to The location to copy to. This should not exist.
* @throws IOException If the copy failed.
*/
void copy( String from, String to ) throws IOException;
void copy(String from, String to) throws IOException;
/**
* Move files from one location to another.
*
* @param from The location to move from.
* @param to The location to move to. This should not exist.
* @param to The location to move to. This should not exist.
* @throws IOException If the move failed.
*/
void move( String from, String to ) throws IOException;
void move(String from, String to) throws IOException;
}

View File

@@ -1,37 +1,70 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.filesystem;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import javax.annotation.Nonnull;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
/**
* Represents a read only part of a virtual filesystem that can be mounted onto a computer using
* {@link IComputerAccess#mount(String, IMount)}
* Represents a read only part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)}.
*
* Ready made implementations of this interface can be created using
* {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or
* {@link ComputerCraftAPI#createResourceMount(Class, String, String)}, or you're free to implement it yourselves!
* Ready made implementations of this interface can be created using {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or {@link
* ComputerCraftAPI#createResourceMount(String, String)}, or you're free to implement it yourselves!
*
* @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see ComputerCraftAPI#createResourceMount(String, String)
* @see IComputerAccess#mount(String, IMount)
* @see IWritableMount
*/
public interface IMount
{
public interface IMount {
/**
* Returns the file names of all the files in a directory.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprograms".
* @param contents A list of strings. Add all the file names to this list.
* @throws IOException If the file was not a directory, or could not be listed.
*/
void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException;
/**
* Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A channel representing the contents of the file. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one will be able to
* seek to arbitrary positions when using binary mode.
* @throws IOException If the file does not exist, or could not be opened.
*/
@Nonnull
ReadableByteChannel openForRead(@Nonnull String path) throws IOException;
/**
* Get attributes about the given file.
*
* @param path The path to query.
* @return File attributes for the given file.
* @throws IOException If the file does not exist, or attributes could not be fetched.
*/
@Nonnull
default BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
if (!this.exists(path)) {
throw new FileOperationException(path, "No such file");
}
return new FileAttributes(this.isDirectory(path), this.getSize(path));
}
/**
* Returns whether a file with a given path exists or not.
*
@@ -39,7 +72,7 @@ public interface IMount
* @return If the file exists.
* @throws IOException If an error occurs when checking the existence of the file.
*/
boolean exists( @Nonnull String path ) throws IOException;
boolean exists(@Nonnull String path) throws IOException;
/**
* Returns whether a file with a given path is a directory or not.
@@ -48,51 +81,14 @@ public interface IMount
* @return If the file exists and is a directory
* @throws IOException If an error occurs when checking whether the file is a directory.
*/
boolean isDirectory( @Nonnull String path ) throws IOException;
boolean isDirectory(@Nonnull String path) throws IOException;
/**
* Returns the file names of all the files in a directory.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprograms".
* @param contents A list of strings. Add all the file names to this list.
* @throws IOException If the file was not a directory, or could not be listed.
*/
void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException;
/**
* Returns the size of a file with a given path, in bytes
* Returns the size of a file with a given path, in bytes.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return The size of the file, in bytes.
* @throws IOException If the file does not exist, or its size could not be determined.
*/
long getSize( @Nonnull String path ) throws IOException;
/**
* Opens a file with a given path, and returns an {@link InputStream} representing its contents.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream representing the contents of the file.
* @throws IOException If the file does not exist, or could not be opened.
* @deprecated Use {@link #openChannelForRead(String)} instead
*/
@Nonnull
@Deprecated
InputStream openForRead( @Nonnull String path ) throws IOException;
/**
* Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A channel representing the contents of the file. If the channel implements
* {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary positions when using binary
* mode.
* @throws IOException If the file does not exist, or could not be opened.
*/
@Nonnull
@SuppressWarnings( "deprecation" )
default ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForRead( path ) );
}
long getSize(@Nonnull String path) throws IOException;
}

View File

@@ -1,42 +1,43 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.filesystem;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.OptionalLong;
import javax.annotation.Nonnull;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
/**
* Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)}
* or {@link IComputerAccess#mountWritable(String, IWritableMount)}, that can also be written to.
* Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)} or {@link
* IComputerAccess#mountWritable(String, IWritableMount)}, that can also be written to.
*
* Ready made implementations of this interface can be created using
* {@link ComputerCraftAPI#createSaveDirMount(World, String, long)}, or you're free to implement it yourselves!
* Ready made implementations of this interface can be created using {@link ComputerCraftAPI#createSaveDirMount(World, String, long)}, or you're free to
* implement it yourselves!
*
* @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see IComputerAccess#mount(String, IMount)
* @see IComputerAccess#mountWritable(String, IWritableMount)
* @see IMount
*/
public interface IWritableMount extends IMount
{
public interface IWritableMount extends IMount {
/**
* Creates a directory at a given path inside the virtual file system.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/mynewprograms".
* @throws IOException If the directory already exists or could not be created.
*/
void makeDirectory( @Nonnull String path ) throws IOException;
void makeDirectory(@Nonnull String path) throws IOException;
/**
* Deletes a directory at a given path inside the virtual file system.
@@ -44,68 +45,46 @@ public interface IWritableMount extends IMount
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myoldprograms".
* @throws IOException If the file does not exist or could not be deleted.
*/
void delete( @Nonnull String path ) throws IOException;
void delete(@Nonnull String path) throws IOException;
/**
* Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to
* @throws IOException If the file could not be opened for writing.
* @deprecated Use {@link #openChannelForWrite(String)} instead.
*/
@Nonnull
@Deprecated
OutputStream openForWrite( @Nonnull String path ) throws IOException;
/**
* Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one
* will be able to seek to arbitrary positions when using binary mode.
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary
* positions when using binary mode.
* @throws IOException If the file could not be opened for writing.
*/
@Nonnull
@SuppressWarnings( "deprecation" )
default WritableByteChannel openChannelForWrite( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForWrite( path ) );
}
WritableByteChannel openForWrite(@Nonnull String path) throws IOException;
/**
* Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to.
* @throws IOException If the file could not be opened for writing.
* @deprecated Use {@link #openChannelForAppend(String)} instead.
*/
@Nonnull
@Deprecated
OutputStream openForAppend( @Nonnull String path ) throws IOException;
/**
* Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one
* will be able to seek to arbitrary positions when using binary mode.
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary
* positions when using binary mode.
* @throws IOException If the file could not be opened for writing.
*/
@Nonnull
@SuppressWarnings( "deprecation" )
default WritableByteChannel openChannelForAppend( @Nonnull String path ) throws IOException
{
return Channels.newChannel( openForAppend( path ) );
}
WritableByteChannel openForAppend(@Nonnull String path) throws IOException;
/**
* Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the
* mount, and write operations should fail once it reaches zero.
* Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the mount, and write operations should fail
* once it reaches zero.
*
* @return The amount of free space, in bytes.
* @throws IOException If the remaining space could not be computed.
*/
long getRemainingSpace() throws IOException;
/**
* Get the capacity of this mount. This should be equal to the size of all files/directories on this mount, minus the {@link #getRemainingSpace()}.
*
* @return The capacity of this mount, in bytes.
*/
@Nonnull
default OptionalLong getCapacity() {
return OptionalLong.empty();
}
}

View File

@@ -0,0 +1,418 @@
/*
* 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 static dan200.computercraft.api.lua.LuaValues.checkFinite;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* The arguments passed to a function.
*/
public interface IArguments {
/**
* 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[this.count()];
for (int i = 0; i < result.length; i++) {
result[i] = this.get(i);
}
return result;
}
/**
* 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);
/**
* 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) this.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 = this.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, this.getDouble(index));
}
/**
* 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 = this.get(index);
if (!(value instanceof Number)) {
throw LuaValues.badArgumentOf(index, "number", value);
}
return ((Number) value).doubleValue();
}
/**
* 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 = this.get(index);
if (!(value instanceof Boolean)) {
throw LuaValues.badArgumentOf(index, "boolean", value);
}
return (Boolean) 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(this.getString(index));
}
/**
* 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 = this.get(index);
if (!(value instanceof String)) {
throw LuaValues.badArgumentOf(index, "string", value);
}
return (String) value;
}
/**
* 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, this.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 = this.get(index);
if (!(value instanceof Map)) {
throw LuaValues.badArgumentOf(index, "table", value);
}
return (Map<?, ?>) 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 this.optString(index).map(LuaValues::encode);
}
/**
* 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 = this.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 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 = this.optString(index);
return str.isPresent() ? Optional.of(LuaValues.checkEnum(index, klass, str.get())) : Optional.empty();
}
/**
* 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 this.optDouble(index).orElse(def);
}
/**
* 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 = this.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.
* @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 this.optInt(index).orElse(def);
}
/**
* 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 this.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 = this.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 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 this.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 this.optFiniteDouble(index).orElse(def);
}
/**
* 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 = this.optDouble(index);
if (value.isPresent()) {
LuaValues.checkFiniteNum(index, value.get());
}
return value;
}
/**
* 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 this.optBoolean(index).orElse(def);
}
/**
* 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 = this.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.
* @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 this.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 this.optTable(index).orElse(def);
}
/**
* 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 = this.get(index);
if (value == null) {
return Optional.empty();
}
if (!(value instanceof Map)) {
throw LuaValues.badArgumentOf(index, "map", value);
}
return Optional.of((Map<?, ?>) value);
}
}

View File

@@ -1,22 +1,20 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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 dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.peripheral.IComputerAccess;
import javax.annotation.Nullable;
/**
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
* about a computer.
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information about a computer.
*/
public interface IComputerSystem extends IComputerAccess
{
public interface IComputerSystem extends IComputerAccess {
/**
* Get the file system for this computer.
*
@@ -26,7 +24,7 @@ public interface IComputerSystem extends IComputerAccess
IFileSystem getFileSystem();
/**
* Get the label for this computer
* Get the label for this computer.
*
* @return This computer's label, or {@code null} if it is not set.
*/

View File

@@ -0,0 +1,40 @@
/*
* 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 dan200.computercraft.api.peripheral.IDynamicPeripheral;
/**
* 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;
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -9,16 +9,16 @@ 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.
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred to use peripherals to provide
* functionality to users.
*
* @see ILuaAPIFactory
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/
public interface ILuaAPI extends ILuaObject
{
public interface ILuaAPI {
/**
* Get the globals this API will be assigned to. This will override any other global, so you should
*
@@ -31,15 +31,13 @@ public interface ILuaAPI extends ILuaObject
*
* One should only interact with the file system.
*/
default void startup()
{
default void startup() {
}
/**
* Called every time the computer is ticked. This can be used to process various.
*/
default void update()
{
default void update() {
}
/**
@@ -47,7 +45,6 @@ public interface ILuaAPI extends ILuaObject
*
* This should reset the state of the object, disposing any remaining file handles, or other resources.
*/
default void shutdown()
{
default void shutdown() {
}
}

View File

@@ -1,16 +1,16 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.ComputerCraftAPI;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI;
/**
* Construct an {@link ILuaAPI} for a specific computer.
*
@@ -18,8 +18,7 @@ import javax.annotation.Nullable;
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/
@FunctionalInterface
public interface ILuaAPIFactory
{
public interface ILuaAPIFactory {
/**
* Create a new API instance for a given computer.
*
@@ -27,5 +26,5 @@ public interface ILuaAPIFactory
* @return The created API, or {@code null} if one should not be injected.
*/
@Nullable
ILuaAPI create( @Nonnull IComputerSystem computer );
ILuaAPI create(@Nonnull IComputerSystem computer);
}

View File

@@ -0,0 +1,26 @@
/*
* 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;
}

View File

@@ -1,105 +1,29 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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;
/**
* 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
{
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.
* 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.
*
* @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)}.
* 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.
*
* @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;
long issueMainThreadTask(@Nonnull ILuaTask task) throws LuaException;
}

View 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;
}

View File

@@ -6,51 +6,13 @@
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()
*/
public interface ILuaObject {
@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;
Object[] callMethod(@Nonnull ILuaContext context, int method, @Nonnull Object[] arguments) throws LuaException, InterruptedException;
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -9,24 +9,19 @@ 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
* next tick.
* 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
public interface ILuaTask
{
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)}.
* @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.
* @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.
*/
@Nullable
Object[] execute() throws LuaException;

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -11,35 +11,38 @@ import javax.annotation.Nullable;
/**
* An exception representing an error in Lua, like that raised by the {@code error()} function.
*/
public class LuaException extends Exception
{
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) {
super(message);
this.hasLevel = false;
this.level = 1;
}
public LuaException( @Nullable String message )
{
this( message, 1 );
}
public LuaException( @Nullable String message, int level )
{
super( message );
public LuaException(@Nullable String message, int level) {
super(message);
this.hasLevel = true;
this.level = level;
}
/**
* The level this error is raised at. Level 1 is the function's caller, level 2 is that function's caller, and so
* on.
* Whether a level was explicitly specified when constructing. This is used to determine
*
* @return Whether this has an explicit level.
*/
public boolean hasLevel() {
return this.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.
*
* @return The level to raise the error at.
*/
public int getLevel()
{
return level;
public int getLevel() {
return this.level;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import java.util.Optional;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
/**
* 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;
}

View File

@@ -0,0 +1,163 @@
/*
* 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 java.nio.ByteBuffer;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* 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();
}
/**
* 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 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";
}
/**
* 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;
}
/**
* 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";
}
/**
* 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 + ")");
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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 java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.peripheral.IComputerAccess;
/**
* 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 this.result;
}
@Nullable
public ILuaCallback getCallback() {
return this.callback;
}
public int getErrorAdjust() {
return this.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 || this.callback == null) {
return this;
}
return new MethodResult(this.result, this.callback, this.adjust + adjust);
}
}

View File

@@ -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 java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* 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 IArguments drop(int count) {
if (count < 0) {
throw new IllegalStateException("count cannot be negative");
}
if (count == 0) {
return this;
}
if (count >= this.args.size()) {
return EMPTY;
}
return new ObjectArguments(this.args.subList(count, this.args.size()));
}
@Override
public Object[] getAll() {
return this.args.toArray();
}
@Override
public int count() {
return this.args.size();
}
@Nullable
@Override
public Object get(int index) {
return index >= this.args.size() ? null : this.args.get(index);
}
}

View File

@@ -1,28 +1,27 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.media;
import dan200.computercraft.api.filesystem.IMount;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.SoundEvent;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.filesystem.IMount;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvent;
import net.minecraft.world.World;
/**
* Represents an item that can be placed in a disk drive and used by a Computer.
*
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register
* a {@link IMediaProvider}.
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register a {@link IMediaProvider}.
*/
public interface IMedia
{
public interface IMedia {
/**
* Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua.
*
@@ -30,7 +29,7 @@ public interface IMedia
* @return The label. ie: "Dan's Programs".
*/
@Nullable
String getLabel( @Nonnull ItemStack stack );
String getLabel(@Nonnull ItemStack stack);
/**
* Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua.
@@ -39,21 +38,18 @@ public interface IMedia
* @param label The string to set the label to.
* @return true if the label was updated, false if the label may not be modified.
*/
default boolean setLabel( @Nonnull ItemStack stack, @Nullable String label )
{
default boolean setLabel(@Nonnull ItemStack stack, @Nullable String label) {
return false;
}
/**
* If this disk represents an item with audio (like a record), get the readable name of the audio track. ie:
* "Jonathan Coulton - Still Alive"
* If this disk represents an item with audio (like a record), get the readable name of the audio track. ie: "Jonathan Coulton - Still Alive"
*
* @param stack The {@link ItemStack} to modify.
* @return The name, or null if this item does not represent an item with audio.
*/
@Nullable
default String getAudioTitle( @Nonnull ItemStack stack )
{
default String getAudioTitle(@Nonnull ItemStack stack) {
return null;
}
@@ -64,27 +60,25 @@ public interface IMedia
* @return The name, or null if this item does not represent an item with audio.
*/
@Nullable
default SoundEvent getAudio( @Nonnull ItemStack stack )
{
default SoundEvent getAudio(@Nonnull ItemStack stack) {
return null;
}
/**
* If this disk represents an item with data (like a floppy disk), get a mount representing it's contents. This will
* be mounted onto the filesystem of the computer while the media is in the disk drive.
* If this disk represents an item with data (like a floppy disk), get a mount representing it's contents. This will be mounted onto the filesystem of
* the computer while the media is in the disk drive.
*
* @param stack The {@link ItemStack} to modify.
* @param world The world in which the item and disk drive reside.
* @return The mount, or null if this item does not represent an item with data. If the mount returned also
* implements {@link dan200.computercraft.api.filesystem.IWritableMount}, it will mounted using mountWritable()
* @return The mount, or null if this item does not represent an item with data. If the mount returned also implements {@link
* dan200.computercraft.api.filesystem.IWritableMount}, it will mounted using mountWritable()
* @see IMount
* @see dan200.computercraft.api.filesystem.IWritableMount
* @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(Class, String, String)
* @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String)
*/
@Nullable
default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
{
default IMount createDataMount(@Nonnull ItemStack stack, @Nonnull World world) {
return null;
}
}

View File

@@ -1,24 +1,23 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.media;
import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.item.ItemStack;
/**
* This interface is used to provide {@link IMedia} implementations for {@link ItemStack}.
*
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider)
*/
@FunctionalInterface
public interface IMediaProvider
{
public interface IMediaProvider {
/**
* Produce an IMedia implementation from an ItemStack.
*
@@ -27,5 +26,5 @@ public interface IMediaProvider
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider)
*/
@Nullable
IMedia getMedia( @Nonnull ItemStack stack );
IMedia getMedia(@Nonnull ItemStack stack);
}

View File

@@ -1,8 +1,9 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network;
import javax.annotation.Nonnull;
@@ -13,21 +14,20 @@ import javax.annotation.Nonnull;
* @see Packet
* @see IPacketReceiver
*/
public interface IPacketNetwork
{
public interface IPacketNetwork {
/**
* Add a receiver to the network.
*
* @param receiver The receiver to register to the network.
*/
void addReceiver( @Nonnull IPacketReceiver receiver );
void addReceiver(@Nonnull IPacketReceiver receiver);
/**
* Remove a receiver from the network.
*
* @param receiver The device to remove from the network.
*/
void removeReceiver( @Nonnull IPacketReceiver receiver );
void removeReceiver(@Nonnull IPacketReceiver receiver);
/**
* Determine whether this network is wireless.
@@ -37,23 +37,23 @@ public interface IPacketNetwork
boolean isWireless();
/**
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it
* to all receivers within range (or any interdimensional ones).
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it to all receivers within range (or any
* interdimensional ones).
*
* @param packet The packet to send.
* @param range The maximum distance this packet will be sent.
* @param range The maximum distance this packet will be sent.
* @see #transmitInterdimensional(Packet)
* @see IPacketReceiver#receiveSameDimension(Packet, double)
*/
void transmitSameDimension( @Nonnull Packet packet, double range );
void transmitSameDimension(@Nonnull Packet packet, double range);
/**
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it
* to all receivers across all dimensions.
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it to all receivers across all
* dimensions.
*
* @param packet The packet to send.
* @see #transmitSameDimension(Packet, double)
* @see IPacketReceiver#receiveDifferentDimension(Packet)
*/
void transmitInterdimensional( @Nonnull Packet packet );
void transmitInterdimensional(@Nonnull Packet packet);
}

View File

@@ -1,20 +1,20 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network;
import javax.annotation.Nonnull;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
/**
* An object on an {@link IPacketNetwork}, capable of receiving packets.
*/
public interface IPacketReceiver
{
public interface IPacketReceiver {
/**
* Get the world in which this packet receiver exists.
*
@@ -34,9 +34,8 @@ public interface IPacketReceiver
/**
* Get the maximum distance this receiver can send and receive messages.
*
* When determining whether a receiver can receive a message, the largest distance of the packet and receiver is
* used - ensuring it is within range. If the packet or receiver is inter-dimensional, then the packet will always
* be received.
* When determining whether a receiver can receive a message, the largest distance of the packet and receiver is used - ensuring it is within range. If
* the packet or receiver is inter-dimensional, then the packet will always be received.
*
* @return The maximum distance this device can send and receive messages.
* @see #isInterdimensional()
@@ -60,25 +59,25 @@ public interface IPacketReceiver
/**
* Receive a network packet from the same dimension.
*
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and,
* if so, queue the appropriate modem event.
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and, if so, queue the appropriate
* modem event.
* @param distance The distance this packet has travelled from the source.
* @see Packet
* @see #getRange()
* @see IPacketNetwork#transmitSameDimension(Packet, double)
* @see IPacketNetwork#transmitInterdimensional(Packet)
*/
void receiveSameDimension( @Nonnull Packet packet, double distance );
void receiveSameDimension(@Nonnull Packet packet, double distance);
/**
* Receive a network packet from a different dimension.
*
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and,
* if so, queue the appropriate modem event.
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and, if so, queue the appropriate
* modem event.
* @see Packet
* @see IPacketNetwork#transmitInterdimensional(Packet)
* @see IPacketNetwork#transmitSameDimension(Packet, double)
* @see #isInterdimensional()
*/
void receiveDifferentDimension( @Nonnull Packet packet );
void receiveDifferentDimension(@Nonnull Packet packet);
}

View File

@@ -1,20 +1,20 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network;
import javax.annotation.Nonnull;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
/**
* An object on a {@link IPacketNetwork}, capable of sending packets.
*/
public interface IPacketSender
{
public interface IPacketSender {
/**
* Get the world in which this packet sender exists.
*
@@ -32,8 +32,8 @@ public interface IPacketSender
Vec3d getPosition();
/**
* Get some sort of identification string for this sender. This does not strictly need to be unique, but you
* should be able to extract some identifiable information from it.
* Get some sort of identification string for this sender. This does not strictly need to be unique, but you should be able to extract some identifiable
* information from it.
*
* @return This device's id.
*/

View File

@@ -1,13 +1,15 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
/**
* Represents a packet which may be sent across a {@link IPacketNetwork}.
@@ -18,8 +20,7 @@ import java.util.Objects;
* @see IPacketReceiver#receiveDifferentDimension(Packet)
* @see IPacketReceiver#receiveSameDimension(Packet, double)
*/
public class Packet
{
public class Packet {
private final int channel;
private final int replyChannel;
private final Object payload;
@@ -29,16 +30,14 @@ public class Packet
/**
* Create a new packet, ready for transmitting across the network.
*
* @param channel The channel to send the packet along. Receiving devices should only process packets from on
* channels they are listening to.
* @param channel The channel to send the packet along. Receiving devices should only process packets from on channels they are listening to.
* @param replyChannel The channel to reply on.
* @param payload The contents of this packet. This should be a "valid" Lua object, safe for queuing as an
* event or returning from a peripheral call.
* @param sender The object which sent this packet.
* @param payload The contents of this packet. This should be a "valid" Lua object, safe for queuing as an event or returning from a peripheral
* call.
* @param sender The object which sent this packet.
*/
public Packet( int channel, int replyChannel, @Nullable Object payload, @Nonnull IPacketSender sender )
{
Objects.requireNonNull( sender, "sender cannot be null" );
public Packet(int channel, int replyChannel, @Nullable Object payload, @Nonnull IPacketSender sender) {
Objects.requireNonNull(sender, "sender cannot be null");
this.channel = channel;
this.replyChannel = replyChannel;
@@ -47,14 +46,12 @@ public class Packet
}
/**
* Get the channel this packet is sent along. Receivers should generally only process packets from on channels they
* are listening to.
* Get the channel this packet is sent along. Receivers should generally only process packets from on channels they are listening to.
*
* @return This packet's channel.
*/
public int getChannel()
{
return channel;
public int getChannel() {
return this.channel;
}
/**
@@ -62,21 +59,18 @@ public class Packet
*
* @return This channel to reply on.
*/
public int getReplyChannel()
{
return replyChannel;
public int getReplyChannel() {
return this.replyChannel;
}
/**
* The actual data of this packet. This should be a "valid" Lua object, safe for queuing as an
* event or returning from a peripheral call.
* The actual data of this packet. This should be a "valid" Lua object, safe for queuing as an event or returning from a peripheral call.
*
* @return The packet's payload
*/
@Nullable
public Object getPayload()
{
return payload;
public Object getPayload() {
return this.payload;
}
/**
@@ -85,33 +79,40 @@ public class Packet
* @return The sending object.
*/
@Nonnull
public IPacketSender getSender()
{
return sender;
public IPacketSender getSender() {
return this.sender;
}
@Override
public boolean equals( Object o )
{
if( this == o ) return true;
if( o == null || getClass() != o.getClass() ) return false;
public int hashCode() {
int result;
result = this.channel;
result = 31 * result + this.replyChannel;
result = 31 * result + (this.payload != null ? this.payload.hashCode() : 0);
result = 31 * result + this.sender.hashCode();
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
Packet packet = (Packet) o;
if( channel != packet.channel ) return false;
if( replyChannel != packet.replyChannel ) return false;
if( !Objects.equals( payload, packet.payload ) ) return false;
return sender.equals( packet.sender );
}
@Override
public int hashCode()
{
int result;
result = channel;
result = 31 * result + replyChannel;
result = 31 * result + (payload != null ? payload.hashCode() : 0);
result = 31 * result + sender.hashCode();
return result;
if (this.channel != packet.channel) {
return false;
}
if (this.replyChannel != packet.replyChannel) {
return false;
}
if (!Objects.equals(this.payload, packet.payload)) {
return false;
}
return this.sender.equals(packet.sender);
}
}

View File

@@ -1,35 +1,31 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network.wired;
import dan200.computercraft.api.ComputerCraftAPI;
import javax.annotation.Nonnull;
import dan200.computercraft.api.ComputerCraftAPI;
/**
* An object which may be part of a wired network.
*
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
* for its lifespan.
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts as a proxy for all network objects.
* Whilst the node may change networks, an element's node should remain constant for its lifespan.
*
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
* {@link IWiredElement} capability for the appropriate sides.
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the {@link IWiredElement} capability for the
* appropriate sides.
*/
public interface IWiredElement extends IWiredSender
{
public interface IWiredElement extends IWiredSender {
/**
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
* peripherals change.
* Called when objects on the network change. This may occur when network nodes are added or removed, or when peripherals change.
*
* @param change The change which occurred.
* @see IWiredNetworkChange
*/
default void networkChanged( @Nonnull IWiredNetworkChange change )
{
default void networkChanged(@Nonnull IWiredNetworkChange change) {
}
}

View File

@@ -1,53 +1,51 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network.wired;
import dan200.computercraft.api.peripheral.IPeripheral;
import javax.annotation.Nonnull;
import java.util.Map;
import javax.annotation.Nonnull;
import dan200.computercraft.api.peripheral.IPeripheral;
/**
* A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series
* of peripherals.
* A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series of peripherals.
*
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
* there is some path between two nodes then they must be on the same network. {@link IWiredNetwork} will automatically
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
* change.
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if there is some path between two nodes
* then they must be on the same network. {@link IWiredNetwork} will automatically handle the merging and splitting of networks (and thus changing of
* available nodes and peripherals) as connections change.
*
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
* it is generally preferred to use the methods provided by {@link IWiredNode}.
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently, it is generally preferred to use the
* methods provided by {@link IWiredNode}.
*
* @see IWiredNode#getNetwork()
*/
public interface IWiredNetwork
{
public interface IWiredNetwork {
/**
* Create a connection between two nodes.
*
* This should only be used on the server thread.
*
* @param left The first node to connect
* @param left The first node to connect
* @param right The second node to connect
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
* @throws IllegalStateException If neither node is on the network.
* @throws IllegalStateException If neither node is on the network.
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
* @see IWiredNode#connectTo(IWiredNode)
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
*/
boolean connect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
boolean connect(@Nonnull IWiredNode left, @Nonnull IWiredNode right);
/**
* Destroy a connection between this node and another.
*
* This should only be used on the server thread.
*
* @param left The first node in the connection.
* @param left The first node in the connection.
* @param right The second node in the connection.
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
* @throws IllegalArgumentException If either node is not on the network.
@@ -55,32 +53,29 @@ public interface IWiredNetwork
* @see IWiredNode#disconnectFrom(IWiredNode)
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
*/
boolean disconnect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
boolean disconnect(@Nonnull IWiredNode left, @Nonnull IWiredNode right);
/**
* Sever all connections this node has, removing it from this network.
*
* This should only be used on the server thread. You should only call this on nodes
* that your network element owns.
* This should only be used on the server thread. You should only call this on nodes that your network element owns.
*
* @param node The node to remove
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
* only element.
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the only element.
* @throws IllegalArgumentException If the node is not in the network.
* @see IWiredNode#remove()
*/
boolean remove( @Nonnull IWiredNode node );
boolean remove(@Nonnull IWiredNode node);
/**
* Update the peripherals a node provides.
*
* This should only be used on the server thread. You should only call this on nodes
* that your network element owns.
* This should only be used on the server thread. You should only call this on nodes that your network element owns.
*
* @param node The node to attach peripherals for.
* @param node The node to attach peripherals for.
* @param peripherals The new peripherals for this node.
* @throws IllegalArgumentException If the node is not in the network.
* @see IWiredNode#updatePeripherals(Map)
*/
void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals );
void updatePeripherals(@Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals);
}

View File

@@ -1,26 +1,26 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network.wired;
import dan200.computercraft.api.peripheral.IPeripheral;
import java.util.Map;
import javax.annotation.Nonnull;
import java.util.Map;
import dan200.computercraft.api.peripheral.IPeripheral;
/**
* Represents a change to the objects on a wired network.
*
* @see IWiredElement#networkChanged(IWiredNetworkChange)
*/
public interface IWiredNetworkChange
{
public interface IWiredNetworkChange {
/**
* A set of peripherals which have been removed. Note that there may be entries with the same name
* in the added and removed set, but with a different peripheral.
* A set of peripherals which have been removed. Note that there may be entries with the same name in the added and removed set, but with a different
* peripheral.
*
* @return The set of removed peripherals.
*/
@@ -28,8 +28,8 @@ public interface IWiredNetworkChange
Map<String, IPeripheral> peripheralsRemoved();
/**
* A set of peripherals which have been added. Note that there may be entries with the same name
* in the added and removed set, but with a different peripheral.
* A set of peripherals which have been added. Note that there may be entries with the same name in the added and removed set, but with a different
* peripheral.
*
* @return The set of added peripherals.
*/

View File

@@ -1,32 +1,31 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network.wired;
import dan200.computercraft.api.network.IPacketNetwork;
import dan200.computercraft.api.peripheral.IPeripheral;
import java.util.Map;
import javax.annotation.Nonnull;
import java.util.Map;
import dan200.computercraft.api.network.IPacketNetwork;
import dan200.computercraft.api.peripheral.IPeripheral;
/**
* Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s.
*
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
* methods may be safely used on any thread.
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These methods may be safely used on any
* thread.
*
* When sending a packet, the system will attempt to find the shortest path between the two nodes based on their
* element's position. Note that packet senders and receivers can have different locations from their associated
* element: the distance between the two will be added to the total packet's distance.
* When sending a packet, the system will attempt to find the shortest path between the two nodes based on their element's position. Note that packet
* senders and receivers can have different locations from their associated element: the distance between the two will be added to the total packet's
* distance.
*
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
* be used on the main server thread.
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever be used on the main server thread.
*/
public interface IWiredNode extends IPacketNetwork
{
public interface IWiredNode extends IPacketNetwork {
/**
* The associated element for this network node.
*
@@ -35,17 +34,6 @@ public interface IWiredNode extends IPacketNetwork
@Nonnull
IWiredElement getElement();
/**
* The network this node is currently connected to. Note that this may change
* after any network operation, so it should not be cached.
*
* This should only be used on the server thread.
*
* @return This node's network.
*/
@Nonnull
IWiredNetwork getNetwork();
/**
* Create a connection from this node to another.
*
@@ -56,11 +44,20 @@ public interface IWiredNode extends IPacketNetwork
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
* @see IWiredNode#disconnectFrom(IWiredNode)
*/
default boolean connectTo( @Nonnull IWiredNode node )
{
return getNetwork().connect( this, node );
default boolean connectTo(@Nonnull IWiredNode node) {
return this.getNetwork().connect(this, node);
}
/**
* The network this node is currently connected to. Note that this may change after any network operation, so it should not be cached.
*
* This should only be used on the server thread.
*
* @return This node's network.
*/
@Nonnull
IWiredNetwork getNetwork();
/**
* Destroy a connection between this node and another.
*
@@ -72,38 +69,32 @@ public interface IWiredNode extends IPacketNetwork
* @see IWiredNetwork#disconnect(IWiredNode, IWiredNode)
* @see IWiredNode#connectTo(IWiredNode)
*/
default boolean disconnectFrom( @Nonnull IWiredNode node )
{
return getNetwork().disconnect( this, node );
default boolean disconnectFrom(@Nonnull IWiredNode node) {
return this.getNetwork().disconnect(this, node);
}
/**
* Sever all connections this node has, removing it from this network.
*
* This should only be used on the server thread. You should only call this on nodes
* that your network element owns.
* This should only be used on the server thread. You should only call this on nodes that your network element owns.
*
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
* only element.
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the only element.
* @throws IllegalArgumentException If the node is not in the network.
* @see IWiredNetwork#remove(IWiredNode)
*/
default boolean remove()
{
return getNetwork().remove( this );
default boolean remove() {
return this.getNetwork().remove(this);
}
/**
* Mark this node's peripherals as having changed.
*
* This should only be used on the server thread. You should only call this on nodes
* that your network element owns.
* This should only be used on the server thread. You should only call this on nodes that your network element owns.
*
* @param peripherals The new peripherals for this node.
* @see IWiredNetwork#updatePeripherals(IWiredNode, Map)
*/
default void updatePeripherals( @Nonnull Map<String, IPeripheral> peripherals )
{
getNetwork().updatePeripherals( this, peripherals );
default void updatePeripherals(@Nonnull Map<String, IPeripheral> peripherals) {
this.getNetwork().updatePeripherals(this, peripherals);
}
}

View File

@@ -1,28 +1,25 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.network.wired;
import dan200.computercraft.api.network.IPacketSender;
import javax.annotation.Nonnull;
import dan200.computercraft.api.network.IPacketSender;
/**
* An object on a {@link IWiredNetwork} capable of sending packets.
*
* Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to
* to send the packet from.
* Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to to send the packet from.
*/
public interface IWiredSender extends IPacketSender
{
public interface IWiredSender extends IPacketSender {
/**
* The node in the network representing this object.
*
* This should be used as a proxy for the main network. One should send packets
* and register receivers through this object.
* This should be used as a proxy for the main network. One should send packets and register receivers through this object.
*
* @return The node for this element.
*/

View File

@@ -1,169 +1,160 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask;
import net.minecraft.world.World;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map;
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;
/**
* The interface passed to peripherals by computers or turtles, providing methods
* that they can call. This should not be implemented by your classes. Do not interact
* with computers except via this interface.
* The interface passed to peripherals by computers or turtles, providing methods that they can call. This should not be implemented by your classes. Do not
* interact with computers except via this interface.
*/
public interface IComputerAccess
{
public interface IComputerAccess {
/**
* Mount a mount onto the computer's file system in a read only mode.
*
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later.
* @throws RuntimeException If the peripheral has been detached.
* @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a file in the desired location.
* Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see ComputerCraftAPI#createResourceMount(String, String)
* @see #mount(String, IMount, String)
* @see #mountWritable(String, IWritableMount)
* @see #unmount(String)
* @see IMount
*/
@Nullable
default String mount( @Nonnull String desiredLocation, @Nonnull IMount mount )
{
return mount( desiredLocation, mount, getAttachmentName() );
default String mount(@Nonnull String desiredLocation, @Nonnull IMount mount) {
return this.mount(desiredLocation, mount, this.getAttachmentName());
}
/**
* Mount a mount onto the computer's file system in a read only mode.
*
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer.
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later.
* @throws RuntimeException If the peripheral has been detached.
* @param mount The mount object to mount on the computer.
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a file in the desired location.
* Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see ComputerCraftAPI#createResourceMount(String, String)
* @see #mount(String, IMount)
* @see #mountWritable(String, IWritableMount)
* @see #unmount(String)
* @see IMount
*/
@Nullable
String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName );
String mount(@Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName);
/**
* Get a string, unique to the computer, by which the computer refers to this peripheral. For directly attached peripherals this will be
* "left","right","front","back",etc, but for peripherals attached remotely it will be different. It is good practice to supply this string when raising
* events to the computer, so that the computer knows from which peripheral the event came.
*
* @return A string unique to the computer, but not globally.
* @throws NotAttachedException If the peripheral has been detached.
*/
@Nonnull
String getAttachmentName();
/**
* Mount a mount onto the computer's file system in a writable mode.
*
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later.
* @throws RuntimeException If the peripheral has been detached.
* @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a file in the desired location. Store
* this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see ComputerCraftAPI#createResourceMount(String, String)
* @see #mount(String, IMount)
* @see #unmount(String)
* @see IMount
*/
@Nullable
default String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount )
{
return mountWritable( desiredLocation, mount, getAttachmentName() );
default String mountWritable(@Nonnull String desiredLocation, @Nonnull IWritableMount mount) {
return this.mountWritable(desiredLocation, mount, this.getAttachmentName());
}
/**
* Mount a mount onto the computer's file system in a writable mode.
*
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer.
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a
* file in the desired location. Store this value if you wish to unmount the mount later.
* @throws RuntimeException If the peripheral has been detached.
* @param mount The mount object to mount on the computer.
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a file in the desired location. Store
* this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(Class, String, String)
* @see ComputerCraftAPI#createResourceMount(String, String)
* @see #mount(String, IMount)
* @see #unmount(String)
* @see IMount
*/
String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName );
String mountWritable(@Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName);
/**
* Unmounts a directory previously mounted onto the computers file system by {@link #mount(String, IMount)}
* or {@link #mountWritable(String, IWritableMount)}.
* Unmounts a directory previously mounted onto the computers file system by {@link #mount(String, IMount)} or {@link #mountWritable(String,
* IWritableMount)}.
*
* When a directory is unmounted, it will disappear from the computers file system, and the user will no longer be
* able to access it. All directories mounted by a mount or mountWritable are automatically unmounted when the
* peripheral is attached if they have not been explicitly unmounted.
* When a directory is unmounted, it will disappear from the computers file system, and the user will no longer be able to access it. All directories
* mounted by a mount or mountWritable are automatically unmounted when the peripheral is attached if they have not been explicitly unmounted.
*
* Note that you cannot unmount another peripheral's mounts.
*
* @param location The desired location in the computers file system of the directory to unmount.
* This must be the location of a directory previously mounted by {@link #mount(String, IMount)} or
* {@link #mountWritable(String, IWritableMount)}, as indicated by their return value.
* @throws RuntimeException If the peripheral has been detached.
* @throws RuntimeException If the mount does not exist, or was mounted by another peripheral.
* @param location The desired location in the computers file system of the directory to unmount. This must be the location of a directory
* previously mounted by {@link #mount(String, IMount)} or {@link #mountWritable(String, IWritableMount)}, as indicated by their return value.
* @throws NotAttachedException If the peripheral has been detached.
* @throws IllegalStateException If the mount does not exist, or was mounted by another peripheral.
* @see #mount(String, IMount)
* @see #mountWritable(String, IWritableMount)
*/
void unmount( @Nullable String location );
void unmount(@Nullable String location);
/**
* Returns the numerical ID of this computer.
*
* This is the same number obtained by calling {@code os.getComputerID()} or running the "id" program from lua,
* and is guaranteed unique. This number will be positive.
* This is the same number obtained by calling {@code os.getComputerID()} or running the "id" program from lua, and is guaranteed unique. This number
* will be positive.
*
* @return The identifier.
*/
int getID();
/**
* Causes an event to be raised on this computer, which the computer can respond to by calling
* {@code os.pullEvent()}. This can be used to notify the computer when things happen in the world or to
* this peripheral.
* Causes an event to be raised on this computer, which the computer can respond to by calling {@code os.pullEvent()}. This can be used to notify the
* computer when things happen in the world or to this peripheral.
*
* @param event A string identifying the type of event that has occurred, this will be
* returned as the first value from {@code os.pullEvent()}. It is recommended that you
* you choose a name that is unique, and recognisable as originating from your
* peripheral. eg: If your peripheral type is "button", a suitable event would be
* "button_pressed".
* @param arguments In addition to a name, you may pass an array of extra arguments to the event, that will
* be supplied as extra return values to os.pullEvent(). Objects in the array will be converted
* to lua data types in the same fashion as the return values of IPeripheral.callMethod().
* @param event A string identifying the type of event that has occurred, this will be returned as the first value from {@code os.pullEvent()}. It
* is recommended that you you choose a name that is unique, and recognisable as originating from your peripheral. eg: If your peripheral type is
* "button", a suitable event would be "button_pressed".
* @param arguments In addition to a name, you may pass an array of extra arguments to the event, that will be supplied as extra return values to
* os.pullEvent(). Objects in the array will be converted to lua data types in the same fashion as the return values of IPeripheral.callMethod().
*
* You may supply {@code null} to indicate that no arguments are to be supplied.
* @throws RuntimeException If the peripheral has been detached.
* @see IPeripheral#callMethod
* You may supply {@code null} to indicate that no arguments are to be supplied.
* @throws NotAttachedException If the peripheral has been detached.
* @see MethodResult#pullEvent(String, ILuaCallback)
*/
void queueEvent( @Nonnull String event, @Nullable Object[] arguments );
/**
* Get a string, unique to the computer, by which the computer refers to this peripheral.
* For directly attached peripherals this will be "left","right","front","back",etc, but
* for peripherals attached remotely it will be different. It is good practice to supply
* this string when raising events to the computer, so that the computer knows from
* which peripheral the event came.
*
* @return A string unique to the computer, but not globally.
* @throws RuntimeException If the peripheral has been detached.
*/
@Nonnull
String getAttachmentName();
void queueEvent(@Nonnull String event, @Nullable Object... arguments);
/**
* Get a set of peripherals that this computer access can "see", along with their attachment name.
@@ -171,45 +162,36 @@ public interface IComputerAccess
* This may include other peripherals on the wired network or peripherals on other sides of the computer.
*
* @return All reachable peripherals
* @throws NotAttachedException If the peripheral has been detached.
* @see #getAttachmentName()
* @see #getAvailablePeripheral(String)
*/
@Nonnull
default Map<String, IPeripheral> getAvailablePeripherals()
{
return Collections.emptyMap();
}
Map<String, IPeripheral> getAvailablePeripherals();
/**
* Get a reachable peripheral with the given attachment name. This is a equivalent to
* {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more efficient.
* Get a reachable peripheral with the given attachment name. This is a equivalent to {@link #getAvailablePeripherals()}{@code .get(name)}, though may
* be more efficient.
*
* @param name The peripheral's attached name
* @return The reachable peripheral, or {@code null} if none can be found.
* @see #getAvailablePeripherals()
*/
@Nullable
default IPeripheral getAvailablePeripheral( @Nonnull String name )
{
return null;
}
IPeripheral getAvailablePeripheral(@Nonnull String name);
/**
* Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread.
*
* This should be used to ensure your peripheral integrates with ComputerCraft's monitoring and limiting of how much
* server time each computer consumes. You should not need to use this if you use
* {@link ILuaContext#issueMainThreadTask(ILuaTask)} - this is intended for mods with their own system for running
* work on the main thread.
* This should be used to ensure your peripheral integrates with ComputerCraft's monitoring and limiting of how much server time each computer consumes.
* You should not need to use this if you use {@link ILuaContext#issueMainThreadTask(ILuaTask)} - this is intended for mods with their own system for
* running work on the main thread.
*
* Please note that the returned implementation is <em>not</em> thread-safe, and should only be used from the main
* thread.
* Please note that the returned implementation is <em>not</em> thread-safe, and should only be used from the main thread.
*
* @return The work monitor for the main thread, or {@code null} if this computer does not have one.
* @throws NotAttachedException If the peripheral has been detached.
*/
@Nullable
default IWorkMonitor getMainThreadMonitor()
{
return null;
}
@Nonnull
IWorkMonitor getMainThreadMonitor();
}

View File

@@ -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 javax.annotation.Nonnull;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.IDynamicLuaObject;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.lua.MethodResult;
/**
* 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;
}

View File

@@ -1,142 +1,84 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.lua.LuaFunction;
/**
* 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 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
{
public interface IPeripheral {
/**
* Should return a string that uniquely identifies this type of peripheral.
* This can be queried from lua by calling {@code peripheral.getType()}
* Should return a string that uniquely identifies this type of peripheral. This can be queried from lua by calling {@code peripheral.getType()}
*
* @return A string identifying the type of peripheral.
*/
@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.
* @return An array of objects, representing values you wish to return to the lua program. Integers, Doubles, Floats,
* Strings, Booleans, Maps and 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
*/
@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.
*
* This will occur when a peripheral is placed next to an active computer, when a computer is turned on next to a
* peripheral, when a turtle travels into a square next to a peripheral, or when a wired modem adjacent to this
* peripheral is does any of the above.
* This will occur when a peripheral is placed next to an active computer, when a computer is turned on next to a peripheral, when a turtle travels into
* a square next to a peripheral, or when a wired modem adjacent to this peripheral is does any of the above.
*
* Between calls to attach and {@link #detach}, the attached computer can make method calls on the peripheral using
* {@code peripheral.call()}. This method can be used to keep track of which computers are attached to the
* peripheral, or to take action when attachment occurs.
* Between calls to attach and {@link #detach}, the attached computer can make method calls on the peripheral using {@code peripheral.call()}. This
* method can be used to keep track of which computers are attached to the peripheral, or to take action when attachment occurs.
*
* Be aware that will be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe
* and reentrant.
* Be aware that will be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe and reentrant.
*
* @param computer The interface to the computer that is being attached. Remember that multiple computers can be
* attached to a peripheral at once.
* @param computer The interface to the computer that is being attached. Remember that multiple computers can be attached to a peripheral at once.
* @see #detach
*/
default void attach( @Nonnull IComputerAccess computer )
{
default void attach(@Nonnull IComputerAccess computer) {
}
/**
* Called when a computer is detaching from the peripheral.
*
* This will occur when a computer shuts down, when the peripheral is removed while attached to computers, when a
* turtle moves away from a block attached to a peripheral, or when a wired modem adjacent to this peripheral is
* detached.
* This will occur when a computer shuts down, when the peripheral is removed while attached to computers, when a turtle moves away from a block
* attached to a peripheral, or when a wired modem adjacent to this peripheral is detached.
*
* This method can be used to keep track of which computers are attached to the peripheral, or to take action when
* detachment occurs.
* This method can be used to keep track of which computers are attached to the peripheral, or to take action when detachment occurs.
*
* Be aware that this will be called from both the server and ComputerCraft Lua thread, and must be thread-safe
* and reentrant.
* Be aware that this will be called from both the server and ComputerCraft Lua thread, and must be thread-safe and reentrant.
*
* @param computer The interface to the computer that is being detached. Remember that multiple computers can be
* attached to a peripheral at once.
* @param computer The interface to the computer that is being detached. Remember that multiple computers can be attached to a peripheral at once.
* @see #attach
*/
default void detach( @Nonnull IComputerAccess computer )
{
default void detach(@Nonnull IComputerAccess computer) {
}
/**
* Get the object that this peripheral provides methods for. This will generally be the tile entity
* or block, but may be an inventory, entity, etc...
* Get the object that this peripheral provides methods for. This will generally be the tile entity or block, but may be an inventory, entity, etc...
*
* @return The object this peripheral targets
*/
@Nonnull
default Object getTarget()
{
return this;
@Nullable
default Object getTarget() {
return null;
}
/**
* Determine whether this peripheral is equivalent to another one.
*
* The minimal example should at least check whether they are the same object. However, you may wish to check if
* they point to the same block or tile entity.
* The minimal example should at least check whether they are the same object. However, you may wish to check if they point to the same block or tile
* entity.
*
* @param other The peripheral to compare against. This may be {@code null}.
* @return Whether these peripherals are equivalent.
*/
boolean equals( @Nullable IPeripheral other );
boolean equals(@Nullable IPeripheral other);
}

View File

@@ -1,38 +1,38 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
/**
* 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 BlockEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral} capability.
*
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
*/
@FunctionalInterface
public interface IPeripheralProvider
{
public interface IPeripheralProvider {
/**
* Produce an peripheral implementation from a block location.
*
* @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.
* @param pos The position the block is at.
* @param side The side to get the peripheral from.
* @return A peripheral, or {@link Optional#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 EnumFacing side );
@Nonnull
IPeripheral getPeripheral(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side);
}

View File

@@ -3,30 +3,29 @@
* Copyright Daniel Ratcliffe, 2011-2019. 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.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
package dan200.computercraft.api.peripheral;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
/**
* A {@link net.minecraft.tileentity.TileEntity} which may act as a peripheral.
* A {@link net.minecraft.block.entity.BlockEntity} 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}.
* If you need more complex capabilities (such as handling TEs not belonging to your mod), you should use {@link IPeripheralProvider}.
*/
public interface IPeripheralTile
{
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, EnumFacing)
* @see IPeripheralProvider#getPeripheral(World, BlockPos, Direction)
*/
@Nullable
IPeripheral getPeripheral( @Nonnull EnumFacing side );
IPeripheral getPeripheral(@Nonnull Direction side);
}

View File

@@ -1,32 +1,63 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* 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 javax.annotation.Nonnull;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
/**
* Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every
* computer receives a fair share of any processing time.
* Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every computer receives a fair share of any
* processing time.
*
* This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but
* could be used for other purposes (such as complex computations done on another thread).
* This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but could be used for other purposes (such
* as complex computations done on another thread).
*
* Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to
* execute work. If that returns true, you should execute the task and use {@link #trackWork(long, TimeUnit)} to inform
* the monitor how long that task took.
* Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to execute work. If that returns true, you
* should execute the task and use {@link #trackWork(long, TimeUnit)} to inform the monitor how long that task took.
*
* Alternatively, use {@link #runWork(Runnable)} to run and keep track of work.
*
* @see IComputerAccess#getMainThreadMonitor()
*/
public interface IWorkMonitor
{
public interface IWorkMonitor {
/**
* If the owning computer is currently allowed to execute work, and has ample time to do so.
*
* This is effectively a more restrictive form of {@link #canWork()}. One should use that in order to determine if you may do an initial piece of work,
* and shouldWork to determine if any additional task may be performed.
*
* @return If we should execute work right now.
*/
boolean shouldWork();
/**
* Run a task if possible, and inform the monitor of how long it took.
*
* @param runnable The task to run.
* @return If the task was actually run (namely, {@link #canWork()} returned {@code true}).
*/
default boolean runWork(@Nonnull Runnable runnable) {
Objects.requireNonNull(runnable, "runnable should not be null");
if (!this.canWork()) {
return false;
}
long start = System.nanoTime();
try {
runnable.run();
} finally {
this.trackWork(System.nanoTime() - start, TimeUnit.NANOSECONDS);
}
return true;
}
/**
* If the owning computer is currently allowed to execute work.
*
@@ -34,45 +65,11 @@ public interface IWorkMonitor
*/
boolean canWork();
/**
* If the owning computer is currently allowed to execute work, and has ample time to do so.
*
* This is effectively a more restrictive form of {@link #canWork()}. One should use that in order to determine if
* you may do an initial piece of work, and shouldWork to determine if any additional task may be performed.
*
* @return If we should execute work right now.
*/
boolean shouldWork();
/**
* Inform the monitor how long some piece of work took to execute.
*
* @param time The time some task took to run
* @param unit The unit that {@code time} was measured in.
*/
void trackWork( long time, @Nonnull TimeUnit unit );
/**
* Run a task if possible, and inform the monitor of how long it took.
*
* @param runnable The task to run.
* @return If the task was actually run (namely, {@link #canWork()} returned {@code true}).
*/
default boolean runWork( @Nonnull Runnable runnable )
{
Objects.requireNonNull( runnable, "runnable should not be null" );
if( !canWork() ) return false;
long start = System.nanoTime();
try
{
runnable.run();
}
finally
{
trackWork( System.nanoTime() - start, TimeUnit.NANOSECONDS );
}
return true;
}
void trackWork(long time, @Nonnull TimeUnit unit);
}

View File

@@ -0,0 +1,22 @@
/*
* 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;
/**
* Thrown when performing operations on {@link IComputerAccess} when the current peripheral is no longer attached to the computer.
*/
public class NotAttachedException extends IllegalStateException {
private static final long serialVersionUID = 1221244785535553536L;
public NotAttachedException() {
super("You are not attached to this computer");
}
public NotAttachedException(String s) {
super(s);
}
}

View File

@@ -1,64 +1,60 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* 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.pocket;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IItemProvider;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Util;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
/**
* A base class for {@link IPocketUpgrade}s.
*
* One does not have to use this, but it does provide a convenient template.
*/
public abstract class AbstractPocketUpgrade implements IPocketUpgrade
{
private final ResourceLocation id;
public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
private final Identifier id;
private final String adjective;
private final ItemStack stack;
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
{
protected AbstractPocketUpgrade(Identifier id, ItemConvertible item) {
this(id, Util.createTranslationKey("upgrade", id) + ".adjective", item);
}
protected AbstractPocketUpgrade(Identifier id, String adjective, ItemConvertible item) {
this.id = id;
this.adjective = adjective;
this.stack = new ItemStack(item);
}
protected AbstractPocketUpgrade(Identifier id, String adjective, ItemStack stack) {
this.id = id;
this.adjective = adjective;
this.stack = stack;
}
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, IItemProvider item )
{
this( id, adjective, new ItemStack( item ) );
}
protected AbstractPocketUpgrade( ResourceLocation id, IItemProvider item )
{
this( id, Util.makeTranslationKey( "upgrade", id ) + ".adjective", new ItemStack( item ) );
@Nonnull
@Override
public final Identifier getUpgradeID() {
return this.id;
}
@Nonnull
@Override
public final ResourceLocation getUpgradeID()
{
return id;
public final String getUnlocalisedAdjective() {
return this.adjective;
}
@Nonnull
@Override
public final String getUnlocalisedAdjective()
{
return adjective;
}
@Nonnull
@Override
public final ItemStack getCraftingItem()
{
return stack;
public final ItemStack getCraftingItem() {
return this.stack;
}
}

View File

@@ -1,25 +1,26 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.pocket;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Identifier;
/**
* Wrapper class for pocket computers
* Wrapper class for pocket computers.
*/
public interface IPocketAccess
{
public interface IPocketAccess {
/**
* Gets the entity holding this item.
*
@@ -33,8 +34,7 @@ public interface IPocketAccess
/**
* Get the colour of this pocket computer as a RGB number.
*
* @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and
* {@code 0xFFFFFF} or -1 if it has no colour.
* @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 if it has no colour.
* @see #setColour(int)
*/
int getColour();
@@ -42,17 +42,16 @@ public interface IPocketAccess
/**
* Set the colour of the pocket computer to a RGB number.
*
* @param colour The colour this pocket computer should be changed to. This should be a RGB colour between
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
* @param colour The colour this pocket computer should be changed to. This should be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or
* -1 to reset to the default colour.
* @see #getColour()
*/
void setColour( int colour );
void setColour(int colour);
/**
* Get the colour of this pocket computer's light as a RGB number.
*
* @return The colour this light is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or
* -1 if it has no colour.
* @return The colour this light is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 if it has no colour.
* @see #setLight(int)
*/
int getLight();
@@ -60,11 +59,11 @@ public interface IPocketAccess
/**
* Set the colour of the pocket computer's light to a RGB number.
*
* @param colour The colour this modem's light will be changed to. This should be a RGB colour between
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
* @param colour The colour this modem's light will be changed to. This should be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1
* to reset to the default colour.
* @see #getLight()
*/
void setLight( int colour );
void setLight(int colour);
/**
* Get the upgrade-specific NBT.
@@ -75,7 +74,7 @@ public interface IPocketAccess
* @see #updateUpgradeNBTData()
*/
@Nonnull
NBTTagCompound getUpgradeNBTData();
CompoundTag getUpgradeNBTData();
/**
* Mark the upgrade-specific NBT as dirty.
@@ -95,5 +94,5 @@ public interface IPocketAccess
* @return A collection of all upgrade names.
*/
@Nonnull
Map<ResourceLocation, IPeripheral> getUpgrades();
Map<Identifier, IPeripheral> getUpgrades();
}

View File

@@ -1,42 +1,41 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.pocket;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
/**
* Additional peripherals for pocket computers.
*
* This is similar to {@link ITurtleUpgrade}.
*/
public interface IPocketUpgrade
{
public interface IPocketUpgrade {
/**
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or
* "my_mod:my_upgrade".
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade".
*
* You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail
* registration if an already used ID is specified.
* You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail registration if an already used ID is
* specified.
*
* @return The upgrade's id.
* @see IPocketUpgrade#getUpgradeID()
* @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade)
*/
@Nonnull
ResourceLocation getUpgradeID();
Identifier getUpgradeID();
/**
* Return an unlocalised string to describe the type of pocket computer this upgrade provides.
@@ -50,12 +49,11 @@ public interface IPocketUpgrade
String getUnlocalisedAdjective();
/**
* Return an item stack representing the type of item that a pocket computer must be crafted with to create a
* pocket computer which holds this upgrade. This item stack is also used to determine the upgrade given by
* {@code pocket.equip()}/{@code pocket.unequip()}.
* Return an item stack representing the type of item that a pocket computer must be crafted with to create a pocket computer which holds this upgrade.
* This item stack is also used to determine the upgrade given by {@code pocket.equip()}/{@code pocket.unequip()}.
*
* Ideally this should be constant over a session. It is recommended that you cache
* the item too, in order to prevent constructing it every time the method is called.
* Ideally this should be constant over a session. It is recommended that you cache the item too, in order to prevent constructing it every time the
* method is called.
*
* @return The item stack used for crafting. This can be {@link ItemStack#EMPTY} if crafting is disabled.
*/
@@ -65,41 +63,37 @@ public interface IPocketUpgrade
/**
* Creates a peripheral for the pocket computer.
*
* The peripheral created will be stored for the lifetime of the upgrade, will be passed an argument to
* {@link #update(IPocketAccess, IPeripheral)} and will be attached, detached and have methods called in the same
* manner as an ordinary peripheral.
* The peripheral created will be stored for the lifetime of the upgrade, will be passed an argument to {@link #update(IPocketAccess, IPeripheral)} and
* will be attached, detached and have methods called in the same manner as an ordinary peripheral.
*
* @param access The access object for the pocket item stack.
* @return The newly created peripheral.
* @see #update(IPocketAccess, IPeripheral)
*/
@Nullable
IPeripheral createPeripheral( @Nonnull IPocketAccess access );
IPeripheral createPeripheral(@Nonnull IPocketAccess access);
/**
* Called when the pocket computer item stack updates.
*
* @param access The access object for the pocket item stack.
* @param access The access object for the pocket item stack.
* @param peripheral The peripheral for this upgrade.
* @see #createPeripheral(IPocketAccess)
*/
default void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral )
{
default void update(@Nonnull IPocketAccess access, @Nullable IPeripheral peripheral) {
}
/**
* Called when the pocket computer is right clicked.
*
* @param world The world the computer is in.
* @param access The access object for the pocket item stack.
* @param world The world the computer is in.
* @param access The access object for the pocket item stack.
* @param peripheral The peripheral for this upgrade.
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path
* which returns {@code false}, such as requiring the player to be sneaking - otherwise they will be unable to
* access the GUI.
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path which returns {@code false}, such as
* requiring the player to be sneaking - otherwise they will be unable to access the GUI.
* @see #createPeripheral(IPocketAccess)
*/
default boolean onRightClick( @Nonnull World world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral )
{
default boolean onRightClick(@Nonnull World world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral) {
return false;
}
}

View File

@@ -1,34 +1,32 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.redstone;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
/**
* This interface is used to provide bundled redstone output for blocks.
*
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
*/
@FunctionalInterface
public interface IBundledRedstoneProvider
{
public interface IBundledRedstoneProvider {
/**
* Produce an bundled redstone output from a block location.
*
* @param world The world this block is in.
* @param pos The position this block is at.
* @param side The side to extract the bundled redstone output from.
* @return A number in the range 0-65535 to indicate this block is providing output, or -1 if you do not wish to
* handle this block.
* @param pos The position this block is at.
* @param side The side to extract the bundled redstone output from.
* @return A number in the range 0-65535 to indicate this block is providing output, or -1 if you do not wish to handle this block.
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
*/
int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side );
int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side);
}

View File

@@ -0,0 +1,70 @@
/*
* 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.turtle;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
/**
* A base class for {@link ITurtleUpgrade}s.
*
* One does not have to use this, but it does provide a convenient template.
*/
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade {
private final Identifier id;
private final TurtleUpgradeType type;
private final String adjective;
private final ItemStack stack;
protected AbstractTurtleUpgrade(Identifier id, TurtleUpgradeType type, String adjective, ItemConvertible item) {
this(id, type, adjective, new ItemStack(item));
}
protected AbstractTurtleUpgrade(Identifier id, TurtleUpgradeType type, String adjective, ItemStack stack) {
this.id = id;
this.type = type;
this.adjective = adjective;
this.stack = stack;
}
protected AbstractTurtleUpgrade(Identifier id, TurtleUpgradeType type, ItemConvertible item) {
this(id, type, new ItemStack(item));
}
protected AbstractTurtleUpgrade(Identifier id, TurtleUpgradeType type, ItemStack stack) {
this(id, type, Util.createTranslationKey("upgrade", id) + ".adjective", stack);
}
@Nonnull
@Override
public final Identifier getUpgradeID() {
return this.id;
}
@Nonnull
@Override
public final String getUnlocalisedAdjective() {
return this.adjective;
}
@Nonnull
@Override
public final TurtleUpgradeType getType() {
return this.type;
}
@Nonnull
@Override
public final ItemStack getCraftingItem() {
return this.stack;
}
}

View File

@@ -0,0 +1,266 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.api.turtle;
import java.util.Collection;
import java.util.OptionalInt;
import java.util.UUID;
import javax.annotation.Nullable;
import javax.crypto.SecretKey;
import com.mojang.authlib.GameProfile;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.block.entity.CommandBlockBlockEntity;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.command.argument.EntityAnchorArgumentType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.passive.HorseBaseEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.MessageType;
import net.minecraft.network.NetworkSide;
import net.minecraft.network.NetworkState;
import net.minecraft.network.Packet;
import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket;
import net.minecraft.network.packet.c2s.play.VehicleMoveC2SPacket;
import net.minecraft.recipe.Recipe;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.ServerPlayerInteractionManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.village.TraderOfferList;
import net.minecraft.world.GameMode;
/**
* A wrapper for {@link ServerPlayerEntity} which denotes a "fake" player.
*
* Please note that this does not implement any of the traditional fake player behaviour. It simply exists to prevent me passing in normal players.
*/
public class FakePlayer extends ServerPlayerEntity {
public FakePlayer(ServerWorld world, GameProfile gameProfile) {
super(world.getServer(), world, gameProfile, new ServerPlayerInteractionManager(world));
this.networkHandler = new FakeNetHandler(this);
}
// region Direct networkHandler access
@Override
public void enterCombat() { }
@Override
public void endCombat() { }
@Override
public void tick() { }
@Override
public void playerTick() { }
@Override
public void onDeath(DamageSource damage) { }
@Override
public Entity moveToWorld(ServerWorld destination) {
return this;
}
@Override
public void wakeUp(boolean bl, boolean updateSleepingPlayers) {
}
@Override
public boolean startRiding(Entity entity, boolean flag) {
return false;
}
@Override
public void stopRiding() { }
@Override
public void openEditSignScreen(SignBlockEntity tile) { }
@Override
public OptionalInt openHandledScreen(@Nullable NamedScreenHandlerFactory container) {
return OptionalInt.empty();
}
@Override
public void sendTradeOffers(int id, TraderOfferList list, int level, int experience, boolean levelled, boolean refreshable) { }
@Override
public void openHorseInventory(HorseBaseEntity horse, Inventory inventory) { }
@Override
public void openEditBookScreen(ItemStack stack, Hand hand) { }
@Override
public void openCommandBlockScreen(CommandBlockBlockEntity block) { }
@Override
public void onSlotUpdate(ScreenHandler container, int slot, ItemStack stack) { }
@Override
public void onHandlerRegistered(ScreenHandler container, DefaultedList<ItemStack> defaultedList) { }
@Override
public void onPropertyUpdate(ScreenHandler container, int key, int value) { }
@Override
public void closeHandledScreen() { }
@Override
public void updateCursorStack() { }
@Override
public int unlockRecipes(Collection<Recipe<?>> recipes) {
return 0;
}
// Indirect
@Override
public int lockRecipes(Collection<Recipe<?>> recipes) {
return 0;
}
@Override
public void sendMessage(Text textComponent, boolean status) { }
@Override
protected void consumeItem() { }
@Override
public void lookAt(EntityAnchorArgumentType.EntityAnchor anchor, Vec3d vec3d) {}
@Override
public void method_14222(EntityAnchorArgumentType.EntityAnchor self, Entity entity, EntityAnchorArgumentType.EntityAnchor target) { }
@Override
protected void onStatusEffectApplied(StatusEffectInstance statusEffectInstance) { }
@Override
protected void onStatusEffectUpgraded(StatusEffectInstance statusEffectInstance, boolean particles) { }
@Override
protected void onStatusEffectRemoved(StatusEffectInstance statusEffectInstance) { }
@Override
public void requestTeleport(double x, double y, double z) { }
@Override
public void setGameMode(GameMode gameMode) { }
@Override
public void sendMessage(Text message, MessageType type, UUID senderUuid) {
}
@Override
public String getIp() {
return "[Fake Player]";
}
@Override
public void sendResourcePackUrl(String url, String hash) { }
@Override
public void onStoppedTracking(Entity entity) { }
@Override
public void setCameraEntity(Entity entity) { }
@Override
public void teleport(ServerWorld serverWorld, double x, double y, double z, float pitch, float yaw) { }
@Override
public void sendInitialChunkPackets(ChunkPos chunkPos, Packet<?> packet, Packet<?> packet2) { }
@Override
public void sendUnloadChunkPacket(ChunkPos chunkPos) { }
@Override
public void playSound(SoundEvent soundEvent, SoundCategory soundCategory, float volume, float pitch) { }
private static class FakeNetHandler extends ServerPlayNetworkHandler {
FakeNetHandler(ServerPlayerEntity player) {
super(player.server, new FakeConnection(), player);
}
@Override
public void disconnect(Text message) { }
@Override
public void onVehicleMove(VehicleMoveC2SPacket move) { }
@Override
public void onRequestCommandCompletions(RequestCommandCompletionsC2SPacket packet) { }
@Override
public void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> listener) { }
}
private static class FakeConnection extends ClientConnection {
FakeConnection() {
super(NetworkSide.CLIENTBOUND);
}
@Override
public void channelActive(ChannelHandlerContext active) {
}
@Override
public void setState(NetworkState state) {
}
@Override
public void exceptionCaught(ChannelHandlerContext context, Throwable err) {
}
@Override
protected void channelRead0(ChannelHandlerContext context, Packet<?> packet) {
}
@Override
public void send(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> listener) {
}
@Override
public void tick() {
}
@Override
public void disconnect(Text message) {
}
@Override
public void setupEncryption(SecretKey key) {
}
@Override
public void disableAutoRead() {
}
@Override
public void setCompressionThreshold(int size) {
}
}
}

View File

@@ -1,34 +1,33 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle;
import com.mojang.authlib.GameProfile;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.inventory.IInventory;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandlerModifiable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.authlib.GameProfile;
import dan200.computercraft.api.lua.ILuaCallback;
import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.util.ItemStorage;
import net.minecraft.inventory.Inventory;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
/**
* The interface passed to turtle by turtles, providing methods that they can call.
*
* This should not be implemented by your classes. Do not interact with turtles except via this interface and
* {@link ITurtleUpgrade}.
* This should not be implemented by your classes. Do not interact with turtles except via this interface and {@link ITurtleUpgrade}.
*/
public interface ITurtleAccess
{
public interface ITurtleAccess {
/**
* Returns the world in which the turtle resides.
*
@@ -48,27 +47,25 @@ public interface ITurtleAccess
/**
* Attempt to move this turtle to a new position.
*
* This will preserve the turtle's internal state, such as it's inventory, computer and upgrades. It should
* be used before playing a movement animation using {@link #playAnimation(TurtleAnimation)}.
* This will preserve the turtle's internal state, such as it's inventory, computer and upgrades. It should be used before playing a movement animation
* using {@link #playAnimation(TurtleAnimation)}.
*
* @param world The new world to move it to
* @param pos The new position to move it to.
* @return Whether the movement was successful. It may fail if the block was not loaded or the block placement
* was cancelled.
* @param pos The new position to move it to.
* @return Whether the movement was successful. It may fail if the block was not loaded or the block placement was cancelled.
* @throws UnsupportedOperationException When attempting to teleport on the client side.
*/
boolean teleportTo( @Nonnull World world, @Nonnull BlockPos pos );
boolean teleportTo(@Nonnull World world, @Nonnull BlockPos pos);
/**
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered.
* This will shift when the turtle is moving.
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered. This will shift when the turtle is moving.
*
* @param f The subframe fraction.
* @return A vector containing the floating point co-ordinates at which the turtle resides.
* @see #getVisualYaw(float)
*/
@Nonnull
Vec3d getVisualPosition( float f );
Vec3d getVisualPosition(float f);
/**
* Returns the yaw the turtle is facing when it is rendered.
@@ -77,25 +74,25 @@ public interface ITurtleAccess
* @return The yaw the turtle is facing.
* @see #getVisualPosition(float)
*/
float getVisualYaw( float f );
float getVisualYaw(float f);
/**
* Returns the world direction the turtle is currently facing.
*
* @return The world direction the turtle is currently facing.
* @see #setDirection(EnumFacing)
* @see #setDirection(Direction)
*/
@Nonnull
EnumFacing getDirection();
Direction getDirection();
/**
* Set the direction the turtle is facing. Note that this will not play a rotation animation, you will also need to
* call {@link #playAnimation(TurtleAnimation)} to do so.
* Set the direction the turtle is facing. Note that this will not play a rotation animation, you will also need to call {@link
* #playAnimation(TurtleAnimation)} to do so.
*
* @param dir The new direction to set. This should be on either the x or z axis (so north, south, east or west).
* @see #getDirection()
*/
void setDirection( @Nonnull EnumFacing dir );
void setDirection(@Nonnull Direction dir);
/**
* Get the currently selected slot in the turtle's inventory.
@@ -109,32 +106,30 @@ public interface ITurtleAccess
/**
* Set the currently selected slot in the turtle's inventory.
*
* @param slot The slot to set. This must be greater or equal to 0 and less than the inventory size. Otherwise no
* action will be taken.
* @param slot The slot to set. This must be greater or equal to 0 and less than the inventory size. Otherwise no action will be taken.
* @throws UnsupportedOperationException When attempting to change the slot on the client side.
* @see #getInventory()
* @see #getSelectedSlot()
*/
void setSelectedSlot( int slot );
/**
* Set the colour of the turtle to a RGB number.
*
* @param colour The colour this turtle should be changed to. This should be a RGB colour between {@code 0x000000}
* and {@code 0xFFFFFF} or -1 to reset to the default colour.
* @see #getColour()
*/
void setColour( int colour );
void setSelectedSlot(int slot);
/**
* Get the colour of this turtle as a RGB number.
*
* @return The colour this turtle is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or
* -1 if it has no colour.
* @return The colour this turtle is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 if it has no colour.
* @see #setColour(int)
*/
int getColour();
/**
* Set the colour of the turtle to a RGB number.
*
* @param colour The colour this turtle should be changed to. This should be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 to
* reset to the default colour.
* @see #getColour()
*/
void setColour(int colour);
/**
* Get the player who owns this turtle, namely whoever placed it.
*
@@ -143,26 +138,6 @@ public interface ITurtleAccess
@Nonnull
GameProfile getOwningPlayer();
/**
* Get the inventory of this turtle
*
* @return This turtle's inventory
* @see #getItemHandler()
*/
@Nonnull
IInventory getInventory();
/**
* Get the inventory of this turtle as an {@link IItemHandlerModifiable}.
*
* @return This turtle's inventory
* @see #getInventory()
* @see IItemHandlerModifiable
* @see net.minecraftforge.items.CapabilityItemHandler#ITEM_HANDLER_CAPABILITY
*/
@Nonnull
IItemHandlerModifiable getItemHandler();
/**
* Determine whether this turtle will require fuel when performing actions.
*
@@ -182,8 +157,7 @@ public interface ITurtleAccess
int getFuelLevel();
/**
* Set the fuel level to a new value. It is generally preferred to use {@link #consumeFuel(int)}} or {@link #addFuel(int)}
* instead.
* Set the fuel level to a new value. It is generally preferred to use {@link #consumeFuel(int)}} or {@link #addFuel(int)} instead.
*
* @param fuel The new amount of fuel. This must be between 0 and the fuel limit.
* @see #getFuelLevel()
@@ -191,7 +165,7 @@ public interface ITurtleAccess
* @see #addFuel(int)
* @see #consumeFuel(int)
*/
void setFuelLevel( int fuel );
void setFuelLevel(int fuel);
/**
* Get the maximum amount of fuel a turtle can hold.
@@ -204,11 +178,11 @@ public interface ITurtleAccess
* Removes some fuel from the turtles fuel supply. Negative numbers can be passed in to INCREASE the fuel level of the turtle.
*
* @param fuel The amount of fuel to consume.
* @return Whether the turtle was able to consume the amount of fuel specified. Will return false if you supply a number
* greater than the current fuel level of the turtle. No fuel will be consumed if {@code false} is returned.
* @return Whether the turtle was able to consume the amount of fuel specified. Will return false if you supply a number greater than the current fuel
* level of the turtle. No fuel will be consumed if {@code false} is returned.
* @throws UnsupportedOperationException When attempting to consume fuel on the client side.
*/
boolean consumeFuel( int fuel );
boolean consumeFuel(int fuel);
/**
* Increase the turtle's fuel level by the given amount.
@@ -216,40 +190,31 @@ public interface ITurtleAccess
* @param fuel The amount to refuel with.
* @throws UnsupportedOperationException When attempting to refuel on the client side.
*/
void addFuel( int fuel );
void addFuel(int fuel);
/**
* Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed
* on the main thread, so are guaranteed to be able to access Minecraft objects safely, and will be queued up
* with the turtles standard movement and tool commands. An issued command will return an unique integer, which will
* be supplied as a parameter to a "turtle_response" event issued to the turtle after the command has completed. Look at the
* lua source code for "rom/apis/turtle" for how to build a lua wrapper around this functionality.
* Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed on the main thread, so are
* guaranteed to be able to access Minecraft objects safely, and will be queued up with the turtles standard movement and tool commands. An issued
* command will return an unique integer, which will be supplied as a parameter to a "turtle_response" event issued to the turtle after the command has
* completed. Look at the lua source code for "rom/apis/turtle" for how to build a lua wrapper around this functionality.
*
* @param 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.
* @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
* it is finished.
* Start playing a specific animation. This will prevent other turtle commands from executing until it is finished.
*
* @param animation The animation to play.
* @throws UnsupportedOperationException When attempting to execute play an animation on the client side.
* @see TurtleAnimation
*/
void playAnimation( @Nonnull TurtleAnimation animation );
void playAnimation(@Nonnull TurtleAnimation animation);
/**
* Returns the turtle on the specified side of the turtle, if there is one.
@@ -259,16 +224,16 @@ public interface ITurtleAccess
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
*/
@Nullable
ITurtleUpgrade getUpgrade( @Nonnull TurtleSide side );
ITurtleUpgrade getUpgrade(@Nonnull TurtleSide side);
/**
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
*
* @param side The side to set the upgrade on.
* @param side The side to set the upgrade on.
* @param upgrade The upgrade to set, may be {@code null} to clear.
* @see #getUpgrade(TurtleSide)
*/
void setUpgrade( @Nonnull TurtleSide side, @Nullable ITurtleUpgrade upgrade );
void setUpgrade(@Nonnull TurtleSide side, @Nullable ITurtleUpgrade upgrade);
/**
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
@@ -277,27 +242,40 @@ public interface ITurtleAccess
* @return The peripheral created by the upgrade on the specified side of the turtle, {@code null} if none exists.
*/
@Nullable
IPeripheral getPeripheral( @Nonnull TurtleSide side );
IPeripheral getPeripheral(@Nonnull TurtleSide side);
/**
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
*
* This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You must
* call {@link #updateUpgradeNBTData(TurtleSide)} after modifying it.
* This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You must call {@link
* #updateUpgradeNBTData(TurtleSide)} after modifying it.
*
* @param side The side to get the upgrade data for.
* @return The upgrade-specific data.
* @see #updateUpgradeNBTData(TurtleSide)
*/
@Nonnull
NBTTagCompound getUpgradeNBTData( @Nullable TurtleSide side );
CompoundTag getUpgradeNBTData(@Nullable TurtleSide side);
/**
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the
* client and persisted.
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the client and persisted.
*
* @param side The side to mark dirty.
* @see #updateUpgradeNBTData(TurtleSide)
*/
void updateUpgradeNBTData( @Nonnull TurtleSide side );
void updateUpgradeNBTData(@Nonnull TurtleSide side);
default ItemStorage getItemHandler() {
return ItemStorage.wrap(this.getInventory());
}
/**
* Get the inventory of this turtle.
*
* Note: this inventory should only be accessed and modified on the server thread.
*
* @return This turtle's inventory
*/
@Nonnull
Inventory getInventory();
}

View File

@@ -1,36 +1,33 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.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
{
public interface ITurtleCommand {
/**
* Will be called by the turtle on the main thread when it is time to execute the custom command.
*
* The handler should either perform the work of the command, and return success, or return
* failure with an error message to indicate the command cannot be executed at this time.
* The handler should either perform the work of the command, and return success, or return failure with an error message to indicate the command cannot
* be executed at this time.
*
* @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
*/
@Nonnull
TurtleCommandResult execute( @Nonnull ITurtleAccess turtle );
TurtleCommandResult execute(@Nonnull ITurtleAccess turtle);
}

View File

@@ -1,48 +1,40 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.world.BlockEvent;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/**
* The primary interface for defining an update for Turtles. A turtle update
* can either be a new tool, or a new peripheral.
* The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new peripheral.
*
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
*/
public interface ITurtleUpgrade
{
public interface ITurtleUpgrade {
/**
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade".
* You should use a unique resource domain to ensure this upgrade is uniquely identified.
* The turtle will fail registration if an already used ID is specified.
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade". You should use a unique
* resource domain to ensure this upgrade is uniquely identified. The turtle will fail registration if an already used ID is specified.
*
* @return The unique ID for this upgrade.
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
*/
@Nonnull
ResourceLocation getUpgradeID();
Identifier getUpgradeID();
/**
* Return an unlocalised string to describe this type of turtle in turtle item names.
@@ -64,12 +56,11 @@ public interface ITurtleUpgrade
TurtleUpgradeType getType();
/**
* Return an item stack representing the type of item that a turtle must be crafted
* with to create a turtle which holds this upgrade. This item stack is also used
* to determine the upgrade given by {@code turtle.equip()}
* Return an item stack representing the type of item that a turtle must be crafted with to create a turtle which holds this upgrade. This item stack is
* also used to determine the upgrade given by {@code turtle.equip()}
*
* Ideally this should be constant over a session. It is recommended that you cache
* the item too, in order to prevent constructing it every time the method is called.
* Ideally this should be constant over a session. It is recommended that you cache the item too, in order to prevent constructing it every time the
* method is called.
*
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
*/
@@ -79,68 +70,52 @@ public interface ITurtleUpgrade
/**
* Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
*
* The peripheral created will be stored for the lifetime of the upgrade and will be passed as an argument to
* {@link #update(ITurtleAccess, TurtleSide)}. It will be attached, detached and have methods called in the same
* manner as a Computer peripheral.
* The peripheral created will be stored for the lifetime of the upgrade and will be passed as an argument to {@link #update(ITurtleAccess,
* TurtleSide)}. It will be attached, detached and have methods called in the same manner as a Computer peripheral.
*
* @param turtle Access to the turtle that the peripheral is being created for.
* @param side Which side of the turtle (left or right) that the upgrade resides on.
* @return The newly created peripheral. You may return {@code null} if this upgrade is a Tool
* and this method is not expected to be called.
* @param side Which side of the turtle (left or right) that the upgrade resides on.
* @return The newly created peripheral. You may return {@code null} if this upgrade is a Tool and this method is not expected to be called.
*/
@Nullable
default IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side )
{
default IPeripheral createPeripheral(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side) {
return null;
}
/**
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called
* by the turtle, and the tool is required to do some work.
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called by the turtle, and the tool is required to do some work.
*
* Conforming implementations should fire {@link BlockEvent.BreakEvent} and {@link TurtleBlockEvent.Dig}for digging,
* {@link AttackEntityEvent} and {@link TurtleAttackEvent} for attacking.
*
* @param turtle Access to the turtle that the tool resides on.
* @param side Which side of the turtle (left or right) the tool resides on.
* @param verb Which action (dig or attack) the turtle is being called on to perform.
* @param direction Which world direction the action should be performed in, relative to the turtles
* position. This will either be up, down, or the direction the turtle is facing, depending on
* whether dig, digUp or digDown was called.
* @return Whether the turtle was able to perform the action, and hence whether the {@code turtle.dig()}
* or {@code turtle.attack()} lua method should return true. If true is returned, the tool will perform
* a swinging animation. You may return {@code null} if this turtle is a Peripheral and this method is not expected
* to be called.
* @param turtle Access to the turtle that the tool resides on.
* @param side Which side of the turtle (left or right) the tool resides on.
* @param verb Which action (dig or attack) the turtle is being called on to perform.
* @param direction Which world direction the action should be performed in, relative to the turtles position. This will either be up, down, or the
* direction the turtle is facing, depending on whether dig, digUp or digDown was called.
* @return Whether the turtle was able to perform the action, and hence whether the {@code turtle.dig()} or {@code turtle.attack()} lua method should
* return true. If true is returned, the tool will perform a swinging animation. You may return {@code null} if this turtle is a Peripheral and
* this method is not expected to be called.
*/
@Nonnull
default TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull EnumFacing direction )
{
default TurtleCommandResult useTool(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction) {
return TurtleCommandResult.failure();
}
/**
* Called to obtain the model to be used when rendering a turtle peripheral.
*
* This can be obtained from {@link net.minecraft.client.renderer.ItemModelMesher#getItemModel(ItemStack)},
* {@link net.minecraft.client.renderer.model.ModelManager#getModel(ModelResourceLocation)} or any other
* source.
*
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
* @param side Which side of the turtle (left or right) the upgrade resides on.
* @return The model that you wish to be used to render your upgrade, and a transformation to apply to it. Returning
* a transformation of {@code null} has the same effect as the identify matrix.
* @param side Which side of the turtle (left or right) the upgrade resides on.
* @return The model that you wish to be used to render your upgrade.
*/
@Nonnull
@OnlyIn( Dist.CLIENT )
Pair<IBakedModel, Matrix4f> getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
@Environment (EnvType.CLIENT)
TransformedModel getModel(@Nullable ITurtleAccess turtle, @Nonnull TurtleSide side);
/**
* Called once per tick for each turtle which has the upgrade equipped.
*
* @param turtle Access to the turtle that the upgrade resides on.
* @param side Which side of the turtle (left or right) the upgrade resides on.
* @param side Which side of the turtle (left or right) the upgrade resides on.
*/
default void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side )
{
default void update(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side) {
}
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -13,75 +13,70 @@ package dan200.computercraft.api.turtle;
*
* @see ITurtleAccess#playAnimation(TurtleAnimation)
*/
public enum TurtleAnimation
{
public enum TurtleAnimation {
/**
* An animation which does nothing. This takes no time to complete.
*
* @see #Wait
* @see #ShortWait
* @see #WAIT
* @see #SHORT_WAIT
*/
None,
NONE,
/**
* Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and
* moves into this one.
* Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and moves into this one.
*/
MoveForward,
MOVE_FORWARD,
/**
* Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and
* moves into this one.
* Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and moves into this one.
*/
MoveBack,
MOVE_BACK,
/**
* Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and
* moves into this one.
* Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and moves into this one.
*/
MoveUp,
MOVE_UP,
/**
* Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and
* moves into this one.
* Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and moves into this one.
*/
MoveDown,
MOVE_DOWN,
/**
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and
* the turtle turns to face in the current direction.
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and the turtle turns to face in the current
* direction.
*/
TurnLeft,
TURN_LEFT,
/**
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and
* the turtle turns to face in the current direction.
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and the turtle turns to face in the current
* direction.
*/
TurnRight,
TURN_RIGHT,
/**
* Swing the tool on the left.
*/
SwingLeftTool,
SWING_LEFT_TOOL,
/**
* Swing the tool on the right.
*/
SwingRightTool,
SWING_RIGHT_TOOL,
/**
* Wait until the animation has finished, performing no movement.
*
* @see #ShortWait
* @see #None
* @see #SHORT_WAIT
* @see #NONE
*/
Wait,
WAIT,
/**
* Wait until the animation has finished, performing no movement. This takes 4 ticks to complete.
*
* @see #Wait
* @see #None
* @see #WAIT
* @see #NONE
*/
ShortWait,
SHORT_WAIT,
}

View File

@@ -1,13 +1,11 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle;
import net.minecraft.util.EnumFacing;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -15,12 +13,20 @@ import javax.annotation.Nullable;
* Used to indicate the result of executing a turtle command.
*
* @see ITurtleCommand#execute(ITurtleAccess)
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
*/
public final class TurtleCommandResult
{
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult( true, null, null );
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult( false, null, null );
public final class TurtleCommandResult {
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult(true, null, null);
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult(false, null, null);
private final boolean success;
private final String errorMessage;
private final Object[] results;
private TurtleCommandResult(boolean success, String errorMessage, Object[] results) {
this.success = success;
this.errorMessage = errorMessage;
this.results = results;
}
/**
* Create a successful command result with no result.
@@ -28,8 +34,7 @@ public final class TurtleCommandResult
* @return A successful command result with no values.
*/
@Nonnull
public static TurtleCommandResult success()
{
public static TurtleCommandResult success() {
return EMPTY_SUCCESS;
}
@@ -40,10 +45,11 @@ public final class TurtleCommandResult
* @return A successful command result with the given values.
*/
@Nonnull
public static TurtleCommandResult success( @Nullable Object[] results )
{
if( results == null || results.length == 0 ) return EMPTY_SUCCESS;
return new TurtleCommandResult( true, null, results );
public static TurtleCommandResult success(@Nullable Object[] results) {
if (results == null || results.length == 0) {
return EMPTY_SUCCESS;
}
return new TurtleCommandResult(true, null, results);
}
/**
@@ -52,8 +58,7 @@ public final class TurtleCommandResult
* @return A failed command result with no message.
*/
@Nonnull
public static TurtleCommandResult failure()
{
public static TurtleCommandResult failure() {
return EMPTY_FAILURE;
}
@@ -64,21 +69,11 @@ public final class TurtleCommandResult
* @return A failed command result with a message.
*/
@Nonnull
public static TurtleCommandResult failure( @Nullable String errorMessage )
{
if( errorMessage == null ) return EMPTY_FAILURE;
return new TurtleCommandResult( false, errorMessage, null );
}
private final boolean success;
private final String errorMessage;
private final Object[] results;
private TurtleCommandResult( boolean success, String errorMessage, Object[] results )
{
this.success = success;
this.errorMessage = errorMessage;
this.results = results;
public static TurtleCommandResult failure(@Nullable String errorMessage) {
if (errorMessage == null) {
return EMPTY_FAILURE;
}
return new TurtleCommandResult(false, errorMessage, null);
}
/**
@@ -86,9 +81,8 @@ public final class TurtleCommandResult
*
* @return If the command was successful.
*/
public boolean isSuccess()
{
return success;
public boolean isSuccess() {
return this.success;
}
/**
@@ -97,9 +91,8 @@ public final class TurtleCommandResult
* @return The command's error message, or {@code null} if it was a success.
*/
@Nullable
public String getErrorMessage()
{
return errorMessage;
public String getErrorMessage() {
return this.errorMessage;
}
/**
@@ -108,8 +101,7 @@ public final class TurtleCommandResult
* @return The command's result, or {@code null} if it was a failure.
*/
@Nullable
public Object[] getResults()
{
return results;
public Object[] getResults() {
return this.results;
}
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -9,15 +9,14 @@ package dan200.computercraft.api.turtle;
/**
* An enum representing the two sides of the turtle that a turtle turtle might reside.
*/
public enum TurtleSide
{
public enum TurtleSide {
/**
* The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle)
* The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle).
*/
Left,
LEFT,
/**
* The turtle's right side (where the modem usually is on a Wireless Mining Turtle)
* The turtle's right side (where the modem usually is on a Wireless Mining Turtle).
*/
Right,
RIGHT,
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -11,34 +11,30 @@ package dan200.computercraft.api.turtle;
*
* @see ITurtleUpgrade#getType()
*/
public enum TurtleUpgradeType
{
public enum TurtleUpgradeType {
/**
* A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()}
* and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles).
* A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()} and {@code turtle.attack()} methods (Such as
* pickaxe or sword on Mining and Melee turtles).
*/
Tool,
TOOL,
/**
* A peripheral adds a special peripheral which is attached to the side of the turtle,
* and can be interacted with the {@code peripheral} API (Such as the modem on Wireless Turtles).
* A peripheral adds a special peripheral which is attached to the side of the turtle, and can be interacted with the {@code peripheral} API (Such as
* the modem on Wireless Turtles).
*/
Peripheral,
PERIPHERAL,
/**
* An upgrade which provides both a tool and a peripheral. This can be used when you wish
* your upgrade to also provide methods. For example, a pickaxe could provide methods
* determining whether it can break the given block or not.
* An upgrade which provides both a tool and a peripheral. This can be used when you wish your upgrade to also provide methods. For example, a pickaxe
* could provide methods determining whether it can break the given block or not.
*/
Both;
BOTH;
public boolean isTool()
{
return this == Tool || this == Both;
public boolean isTool() {
return this == TOOL || this == BOTH;
}
public boolean isPeripheral()
{
return this == Peripheral || this == Both;
public boolean isPeripheral() {
return this == PERIPHERAL || this == BOTH;
}
}

View File

@@ -1,29 +1,25 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle;
import net.minecraft.util.EnumFacing;
/**
* An enum representing the different actions that an {@link ITurtleUpgrade} of type Tool may be called on to perform by
* a turtle.
* An enum representing the different actions that an {@link ITurtleUpgrade} of type Tool may be called on to perform by a turtle.
*
* @see ITurtleUpgrade#getType()
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
*/
public enum TurtleVerb
{
public enum TurtleVerb {
/**
* The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}
* The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}.
*/
Dig,
DIG,
/**
* The turtle called {@code turtle.attack()}, {@code turtle.attackUp()} or {@code turtle.attackDown()}
* The turtle called {@code turtle.attack()}, {@code turtle.attackUp()} or {@code turtle.attackDown()}.
*/
Attack,
ATTACK,
}

View File

@@ -1,6 +1,6 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.
*/
@@ -11,8 +11,7 @@ package dan200.computercraft.api.turtle.event;
*
* @see TurtleActionEvent
*/
public enum TurtleAction
{
public enum TurtleAction {
/**
* A turtle moves to a new position.
*
@@ -71,7 +70,7 @@ public enum TurtleAction
EQUIP,
/**
* Inspect a block in world
* Inspect a block in world.
*
* @see TurtleBlockEvent.Inspect
*/

View File

@@ -1,39 +1,36 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import net.minecraftforge.eventbus.api.Cancelable;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommandResult;
/**
* An event fired when a turtle is performing a known action.
*/
@Cancelable
public class TurtleActionEvent extends TurtleEvent
{
public class TurtleActionEvent extends TurtleEvent {
private final TurtleAction action;
private String failureMessage;
private boolean cancelled = false;
public TurtleActionEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action )
{
super( turtle );
public TurtleActionEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action) {
super(turtle);
Objects.requireNonNull( action, "action cannot be null" );
Objects.requireNonNull(action, "action cannot be null");
this.action = action;
}
public TurtleAction getAction()
{
return action;
public TurtleAction getAction() {
return this.action;
}
/**
@@ -45,11 +42,9 @@ public class TurtleActionEvent extends TurtleEvent
* @see TurtleCommandResult#failure()
* @deprecated Use {@link #setCanceled(boolean, String)} instead.
*/
@Override
@Deprecated
public void setCanceled( boolean cancel )
{
setCanceled( cancel, null );
public void setCanceled(boolean cancel) {
this.setCanceled(cancel, null);
}
/**
@@ -57,13 +52,12 @@ public class TurtleActionEvent extends TurtleEvent
*
* If {@code cancel} is {@code true}, this action will not be carried out.
*
* @param cancel The new canceled value.
* @param cancel The new canceled value.
* @param failureMessage The message to return to the user explaining the failure.
* @see TurtleCommandResult#failure(String)
*/
public void setCanceled( boolean cancel, @Nullable String failureMessage )
{
super.setCanceled( cancel );
public void setCanceled(boolean cancel, @Nullable String failureMessage) {
this.cancelled = true;
this.failureMessage = cancel ? failureMessage : null;
}
@@ -75,8 +69,11 @@ public class TurtleActionEvent extends TurtleEvent
* @see #setCanceled(boolean, String)
*/
@Nullable
public String getFailureMessage()
{
return failureMessage;
public String getFailureMessage() {
return this.failureMessage;
}
public boolean isCancelled() {
return this.cancelled;
}
}

View File

@@ -1,45 +1,38 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle.event;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleVerb;
import net.minecraft.entity.Entity;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import javax.annotation.Nonnull;
import java.util.Objects;
import net.minecraft.entity.Entity;
/**
* Fired when a turtle attempts to attack an entity.
*
* This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)},
* as the base {@code turtle.attack()} command does not fire it.
*
* Note that such commands should also fire {@link AttackEntityEvent}, so you do not need to listen to both.
*
* @see TurtleAction#ATTACK
*/
public class TurtleAttackEvent extends TurtlePlayerEvent
{
public class TurtleAttackEvent extends TurtlePlayerEvent {
private final Entity target;
private final ITurtleUpgrade upgrade;
private final TurtleSide side;
public TurtleAttackEvent( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side )
{
super( turtle, TurtleAction.ATTACK, player );
Objects.requireNonNull( target, "target cannot be null" );
Objects.requireNonNull( upgrade, "upgrade cannot be null" );
Objects.requireNonNull( side, "side cannot be null" );
public TurtleAttackEvent(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade,
@Nonnull TurtleSide side) {
super(turtle, TurtleAction.ATTACK, player);
Objects.requireNonNull(target, "target cannot be null");
Objects.requireNonNull(upgrade, "upgrade cannot be null");
Objects.requireNonNull(side, "side cannot be null");
this.target = target;
this.upgrade = upgrade;
this.side = side;
@@ -51,9 +44,8 @@ public class TurtleAttackEvent extends TurtlePlayerEvent
* @return The entity being attacked.
*/
@Nonnull
public Entity getTarget()
{
return target;
public Entity getTarget() {
return this.target;
}
/**
@@ -62,9 +54,8 @@ public class TurtleAttackEvent extends TurtlePlayerEvent
* @return The upgrade responsible for attacking.
*/
@Nonnull
public ITurtleUpgrade getUpgrade()
{
return upgrade;
public ITurtleUpgrade getUpgrade() {
return this.upgrade;
}
/**
@@ -73,8 +64,7 @@ public class TurtleAttackEvent extends TurtlePlayerEvent
* @return The upgrade's side.
*/
@Nonnull
public TurtleSide getSide()
{
return side;
public TurtleSide getSide() {
return this.side;
}
}

View File

@@ -1,52 +1,46 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle.event;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.peripheral.IComputerAccess;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleVerb;
import net.minecraft.block.state.IBlockState;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.world.BlockEvent;
import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Objects;
/**
* A general event for when a turtle interacts with a block or region.
*
* You should generally listen to one of the sub-events instead, cancelling them where
* appropriate.
* You should generally listen to one of the sub-events instead, cancelling them where appropriate.
*
* Note that you are not guaranteed to receive this event, if it has been cancelled by other
* mechanisms, such as block protection systems.
* Note that you are not guaranteed to receive this event, if it has been cancelled by other mechanisms, such as block protection systems.
*
* Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact
* with a block, simply objects within that block space.
* Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact with a block, simply objects within that block space.
*/
public abstract class TurtleBlockEvent extends TurtlePlayerEvent
{
public abstract class TurtleBlockEvent extends TurtlePlayerEvent {
private final World world;
private final BlockPos pos;
protected TurtleBlockEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos )
{
super( turtle, action, player );
protected TurtleBlockEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world,
@Nonnull BlockPos pos) {
super(turtle, action, player);
Objects.requireNonNull( world, "world cannot be null" );
Objects.requireNonNull( pos, "pos cannot be null" );
Objects.requireNonNull(world, "world cannot be null");
Objects.requireNonNull(pos, "pos cannot be null");
this.world = world;
this.pos = pos;
}
@@ -56,45 +50,36 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
*
* @return The world the turtle is interacting in.
*/
public World getWorld()
{
return world;
public World getWorld() {
return this.world;
}
/**
* Get the position the turtle is interacting with. Note that this is different
* to {@link ITurtleAccess#getPosition()}.
* Get the position the turtle is interacting with. Note that this is different to {@link ITurtleAccess#getPosition()}.
*
* @return The position the turtle is interacting with.
*/
public BlockPos getPos()
{
return pos;
public BlockPos getPos() {
return this.pos;
}
/**
* Fired when a turtle attempts to dig a block.
*
* This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)},
* as the base {@code turtle.dig()} command does not fire it.
*
* Note that such commands should also fire {@link BlockEvent.BreakEvent}, so you do not need to listen to both.
*
* @see TurtleAction#DIG
*/
public static class Dig extends TurtleBlockEvent
{
private final IBlockState block;
public static class Dig extends TurtleBlockEvent {
private final BlockState block;
private final ITurtleUpgrade upgrade;
private final TurtleSide side;
public Dig( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState block, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side )
{
super( turtle, TurtleAction.DIG, player, world, pos );
public Dig(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState block,
@Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side) {
super(turtle, TurtleAction.DIG, player, world, pos);
Objects.requireNonNull( block, "block cannot be null" );
Objects.requireNonNull( upgrade, "upgrade cannot be null" );
Objects.requireNonNull( side, "side cannot be null" );
Objects.requireNonNull(block, "block cannot be null");
Objects.requireNonNull(upgrade, "upgrade cannot be null");
Objects.requireNonNull(side, "side cannot be null");
this.block = block;
this.upgrade = upgrade;
this.side = side;
@@ -106,20 +91,18 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The block which is going to be broken.
*/
@Nonnull
public IBlockState getBlock()
{
return block;
public BlockState getBlock() {
return this.block;
}
/**
* Get the upgrade doing the digging
* Get the upgrade doing the digging.
*
* @return The upgrade doing the digging.
*/
@Nonnull
public ITurtleUpgrade getUpgrade()
{
return upgrade;
public ITurtleUpgrade getUpgrade() {
return this.upgrade;
}
/**
@@ -128,9 +111,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The upgrade's side.
*/
@Nonnull
public TurtleSide getSide()
{
return side;
public TurtleSide getSide() {
return this.side;
}
}
@@ -139,11 +121,9 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
*
* @see TurtleAction#MOVE
*/
public static class Move extends TurtleBlockEvent
{
public Move( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos )
{
super( turtle, TurtleAction.MOVE, player, world, pos );
public static class Move extends TurtleBlockEvent {
public Move(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos) {
super(turtle, TurtleAction.MOVE, player, world, pos);
}
}
@@ -152,15 +132,13 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
*
* @see TurtleAction#PLACE
*/
public static class Place extends TurtleBlockEvent
{
public static class Place extends TurtleBlockEvent {
private final ItemStack stack;
public Place( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack )
{
super( turtle, TurtleAction.PLACE, player, world, pos );
public Place(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack) {
super(turtle, TurtleAction.PLACE, player, world, pos);
Objects.requireNonNull( stack, "stack cannot be null" );
Objects.requireNonNull(stack, "stack cannot be null");
this.stack = stack;
}
@@ -170,9 +148,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The item stack to be placed.
*/
@Nonnull
public ItemStack getStack()
{
return stack;
public ItemStack getStack() {
return this.stack;
}
}
@@ -183,17 +160,16 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
*
* @see TurtleAction#INSPECT
*/
public static class Inspect extends TurtleBlockEvent
{
private final IBlockState state;
public static class Inspect extends TurtleBlockEvent {
private final BlockState state;
private final Map<String, Object> data;
public Inspect( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Map<String, Object> data )
{
super( turtle, TurtleAction.INSPECT, player, world, pos );
public Inspect(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState state,
@Nonnull Map<String, Object> data) {
super(turtle, TurtleAction.INSPECT, player, world, pos);
Objects.requireNonNull( state, "state cannot be null" );
Objects.requireNonNull( data, "data cannot be null" );
Objects.requireNonNull(state, "state cannot be null");
Objects.requireNonNull(data, "data cannot be null");
this.data = data;
this.state = state;
}
@@ -204,9 +180,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The inspected block state.
*/
@Nonnull
public IBlockState getState()
{
return state;
public BlockState getState() {
return this.state;
}
/**
@@ -215,21 +190,18 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return This block's inspection data.
*/
@Nonnull
public Map<String, Object> getData()
{
return data;
public Map<String, Object> getData() {
return this.data;
}
/**
* Add new information to the inspection result. Note this will override fields with the same name.
*
* @param newData The data to add. Note all values should be convertible to Lua (see
* {@link dan200.computercraft.api.peripheral.IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}).
* @param newData The data to add. Note all values should be convertible to Lua (see {@link MethodResult#of(Object)}).
*/
public void addData( @Nonnull Map<String, ?> newData )
{
Objects.requireNonNull( newData, "newData cannot be null" );
data.putAll( newData );
public void addData(@Nonnull Map<String, ?> newData) {
Objects.requireNonNull(newData, "newData cannot be null");
this.data.putAll(newData);
}
}
}

View File

@@ -1,43 +1,49 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraftforge.eventbus.api.Event;
import javax.annotation.Nonnull;
import java.util.Objects;
import javax.annotation.Nonnull;
import com.google.common.eventbus.EventBus;
import dan200.computercraft.api.turtle.ITurtleAccess;
/**
* A base class for all events concerning a turtle. This will only ever constructed and fired on the server side,
* so sever specific methods on {@link ITurtleAccess} are safe to use.
* A base class for all events concerning a turtle. This will only ever constructed and fired on the server side, so sever specific methods on {@link
* ITurtleAccess} are safe to use.
*
* You should generally not need to subscribe to this event, preferring one of the more specific classes.
*
* @see TurtleActionEvent
*/
public abstract class TurtleEvent extends Event
{
public abstract class TurtleEvent {
public static final EventBus EVENT_BUS = new EventBus();
private final ITurtleAccess turtle;
protected TurtleEvent( @Nonnull ITurtleAccess turtle )
{
Objects.requireNonNull( turtle, "turtle cannot be null" );
protected TurtleEvent(@Nonnull ITurtleAccess turtle) {
Objects.requireNonNull(turtle, "turtle cannot be null");
this.turtle = turtle;
}
public static boolean post(TurtleActionEvent event) {
EVENT_BUS.post(event);
return event.isCancelled();
}
/**
* Get the turtle which is performing this action.
*
* @return The access for this turtle.
*/
@Nonnull
public ITurtleAccess getTurtle()
{
return turtle;
public ITurtleAccess getTurtle() {
return this.turtle;
}
}

View File

@@ -1,41 +1,47 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* 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.turtle.event;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
/**
* 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
*/
public class TurtleInspectItemEvent extends TurtleActionEvent
{
public class TurtleInspectItemEvent extends TurtleActionEvent {
private final ItemStack stack;
private final Map<String, Object> data;
private final boolean mainThread;
public TurtleInspectItemEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data )
{
super( turtle, TurtleAction.INSPECT_ITEM );
@Deprecated
public TurtleInspectItemEvent(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data) {
this(turtle, stack, data, false);
}
Objects.requireNonNull( stack, "stack cannot be null" );
Objects.requireNonNull( data, "data cannot be null" );
public TurtleInspectItemEvent(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data, boolean mainThread) {
super(turtle, TurtleAction.INSPECT_ITEM);
Objects.requireNonNull(stack, "stack cannot be null");
Objects.requireNonNull(data, "data cannot be null");
this.stack = stack;
this.data = data;
this.mainThread = mainThread;
}
/**
@@ -44,9 +50,8 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
* @return The item stack which is being inspected. This should <b>not</b> be modified.
*/
@Nonnull
public ItemStack getStack()
{
return stack;
public ItemStack getStack() {
return this.stack;
}
/**
@@ -55,20 +60,26 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
* @return This items's inspection data.
*/
@Nonnull
public Map<String, Object> getData()
{
return data;
public Map<String, Object> getData() {
return this.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 this.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[])}).
* @param newData The data to add. Note all values should be convertible to Lua (see {@link MethodResult#of(Object)}).
*/
public void addData( @Nonnull Map<String, ?> newData )
{
Objects.requireNonNull( newData, "newData cannot be null" );
data.putAll( newData );
public void addData(@Nonnull Map<String, ?> newData) {
Objects.requireNonNull(newData, "newData cannot be null");
this.data.putAll(newData);
}
}

View File

@@ -1,44 +1,44 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.items.IItemHandler;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
/**
* Fired when a turtle attempts to interact with an inventory.
*/
public abstract class TurtleInventoryEvent extends TurtleBlockEvent
{
private final IItemHandler handler;
public abstract class TurtleInventoryEvent extends TurtleBlockEvent {
private final Inventory handler;
protected TurtleInventoryEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler )
{
super( turtle, action, player, world, pos );
protected TurtleInventoryEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world,
@Nonnull BlockPos pos, @Nullable Inventory handler) {
super(turtle, action, player, world, pos);
this.handler = handler;
}
/**
* Get the inventory being interacted with
* Get the inventory being interacted with.
*
* @return The inventory being interacted with, {@code null} if the item will be dropped to/sucked from the world.
*/
@Nullable
public IItemHandler getItemHandler()
{
return handler;
public Inventory getItemHandler() {
return this.handler;
}
/**
@@ -46,11 +46,9 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
*
* @see TurtleAction#SUCK
*/
public static class Suck extends TurtleInventoryEvent
{
public Suck( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler )
{
super( turtle, TurtleAction.SUCK, player, world, pos, handler );
public static class Suck extends TurtleInventoryEvent {
public Suck(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler) {
super(turtle, TurtleAction.SUCK, player, world, pos, handler);
}
}
@@ -59,15 +57,14 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
*
* @see TurtleAction#DROP
*/
public static class Drop extends TurtleInventoryEvent
{
public static class Drop extends TurtleInventoryEvent {
private final ItemStack stack;
public Drop( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler, @Nonnull ItemStack stack )
{
super( turtle, TurtleAction.DROP, player, world, pos, handler );
public Drop(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler,
@Nonnull ItemStack stack) {
super(turtle, TurtleAction.DROP, player, world, pos, handler);
Objects.requireNonNull( stack, "stack cannot be null" );
Objects.requireNonNull(stack, "stack cannot be null");
this.stack = stack;
}
@@ -77,9 +74,8 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
* @return The item stack which will be dropped. This should <b>not</b> be modified.
*/
@Nonnull
public ItemStack getStack()
{
return stack;
public ItemStack getStack() {
return this.stack;
}
}
}

View File

@@ -1,31 +1,30 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. This API may be redistributed unmodified and in full only.
* 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.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraftforge.common.util.FakePlayer;
import java.util.Objects;
import javax.annotation.Nonnull;
import java.util.Objects;
import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
/**
* An action done by a turtle which is normally done by a player.
*
* {@link #getPlayer()} may be used to modify the player's attributes or perform permission checks.
*/
public abstract class TurtlePlayerEvent extends TurtleActionEvent
{
public abstract class TurtlePlayerEvent extends TurtleActionEvent {
private final FakePlayer player;
protected TurtlePlayerEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player )
{
super( turtle, action );
protected TurtlePlayerEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player) {
super(turtle, action);
Objects.requireNonNull( player, "player cannot be null" );
Objects.requireNonNull(player, "player cannot be null");
this.player = player;
}
@@ -37,8 +36,7 @@ public abstract class TurtlePlayerEvent extends TurtleActionEvent
* @return A {@link FakePlayer} representing this turtle.
*/
@Nonnull
public FakePlayer getPlayer()
{
return player;
public FakePlayer getPlayer() {
return this.player;
}
}

View File

@@ -1,34 +1,34 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* 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.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
/**
* Fired when a turtle attempts to refuel from an item.
*
* One may use {@link #setCanceled(boolean, String)} to prevent refueling from this specific item. Additionally, you
* may use {@link #setHandler(Handler)} to register a custom fuel provider.
* One may use {@link #setCanceled(boolean, String)} to prevent refueling from this specific item. Additionally, you may use {@link #setHandler(Handler)} to
* register a custom fuel provider.
*/
public class TurtleRefuelEvent extends TurtleActionEvent
{
public class TurtleRefuelEvent extends TurtleActionEvent {
private final ItemStack stack;
private Handler handler;
public TurtleRefuelEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack )
{
super( turtle, TurtleAction.REFUEL );
public TurtleRefuelEvent(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack) {
super(turtle, TurtleAction.REFUEL);
Objects.requireNonNull( turtle, "turtle cannot be null" );
Objects.requireNonNull(turtle, "turtle cannot be null");
this.stack = stack;
}
@@ -39,9 +39,8 @@ public class TurtleRefuelEvent extends TurtleActionEvent
*
* @return The stack to refuel from.
*/
public ItemStack getStack()
{
return stack;
public ItemStack getStack() {
return this.stack;
}
/**
@@ -51,22 +50,19 @@ public class TurtleRefuelEvent extends TurtleActionEvent
* @see #setHandler(Handler)
*/
@Nullable
public Handler getHandler()
{
return handler;
public Handler getHandler() {
return this.handler;
}
/**
* Set the refuel handler for this stack.
*
* You should call this if you can actually refuel from this item, and ideally only if there are no existing
* handlers.
* You should call this if you can actually refuel from this item, and ideally only if there are no existing handlers.
*
* @param handler The new refuel handler.
* @see #getHandler()
*/
public void setHandler( @Nullable Handler handler )
{
public void setHandler(@Nullable Handler handler) {
this.handler = handler;
}
@@ -74,18 +70,16 @@ public class TurtleRefuelEvent extends TurtleActionEvent
* Handles refuelling a turtle from a specific item.
*/
@FunctionalInterface
public interface Handler
{
public interface Handler {
/**
* Refuel a turtle using an item.
*
* @param turtle The turtle to refuel.
* @param stack The stack to refuel with.
* @param slot The slot the stack resides within. This may be used to modify the inventory afterwards.
* @param limit The maximum number of refuel operations to perform. This will often correspond to the number of
* items to consume.
* @param stack The stack to refuel with.
* @param slot The slot the stack resides within. This may be used to modify the inventory afterwards.
* @param limit The maximum number of refuel operations to perform. This will often correspond to the number of items to consume.
* @return The amount of fuel gained.
*/
int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, int slot, int limit );
int refuel(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, int slot, int limit);
}
}

View File

@@ -1,44 +1,43 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import java.util.HashSet;
import java.util.function.Consumer;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.shared.ComputerCraftRegistry;
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.Minecraft;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IUnbakedModel;
import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.client.renderer.model.ModelRotation;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.HashSet;
import java.util.Map;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.render.model.ModelRotation;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
/**
* Registers textures and models for items.
*/
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class ClientRegistry
{
@SuppressWarnings ({
"MethodCallSideOnly",
"LocalVariableDeclarationSideOnly"
})
public final class ClientRegistry {
private static final String[] EXTRA_MODELS = new String[] {
"turtle_modem_normal_off_left",
"turtle_modem_normal_on_left",
@@ -57,112 +56,68 @@ public final class ClientRegistry
"turtle_colour",
"turtle_elf_overlay",
};
};
private static final String[] EXTRA_TEXTURES = new String[] {
// TODO: Gather these automatically from the model. I'm unable to get this working with Forge's current
// model loading code.
// TODO: Gather these automatically from the model. Sadly the model loader isn't available
// when stitching textures.
"block/turtle_colour",
"block/turtle_elf_overlay",
"block/turtle_crafty_face",
"block/turtle_speaker_face",
};
};
private ClientRegistry() {}
@SubscribeEvent
public static void registerModels( ModelRegistryEvent event )
{
ModelLoaderRegistry.registerLoader( TurtleModelLoader.INSTANCE );
}
@SubscribeEvent
public static void onTextureStitchEvent( TextureStitchEvent.Pre event )
{
IResourceManager manager = Minecraft.getInstance().getResourceManager();
for( String extra : EXTRA_TEXTURES )
{
event.getMap().registerSprite( manager, new ResourceLocation( ComputerCraft.MOD_ID, extra ) );
public static void onTextureStitchEvent(SpriteAtlasTexture atlasTexture, ClientSpriteRegistryCallback.Registry registry) {
for (String extra : EXTRA_TEXTURES) {
registry.register(new Identifier(ComputerCraft.MOD_ID, extra));
}
}
@SubscribeEvent
public static void onModelBakeEvent( ModelBakeEvent event )
{
// Load all extra models
ModelLoader loader = event.getModelLoader();
Map<ModelResourceLocation, IBakedModel> registry = event.getModelRegistry();
@SuppressWarnings ("NewExpressionSideOnly")
public static void onModelBakeEvent(ResourceManager manager, Consumer<ModelIdentifier> out) {
for (String model : EXTRA_MODELS) {
out.accept(new ModelIdentifier(new Identifier(ComputerCraft.MOD_ID, model), "inventory"));
}
}
for( String model : EXTRA_MODELS )
{
IBakedModel bakedModel = bake( loader, loader.getUnbakedModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/" + model ) ) );
public static void onItemColours() {
ColorProviderRegistry.ITEM.register((stack, layer) -> {
return layer == 1 ? ((ItemDisk) stack.getItem()).getColour(stack) : 0xFFFFFF;
}, ComputerCraftRegistry.ModItems.DISK);
if( bakedModel != null )
ColorProviderRegistry.ITEM.register((stack, layer) -> layer == 1 ? ItemTreasureDisk.getColour(stack) : 0xFFFFFF,
ComputerCraftRegistry.ModItems.TREASURE_DISK);
ColorProviderRegistry.ITEM.register((stack, layer) -> {
switch (layer) {
case 0:
default:
return 0xFFFFFF;
case 1: // Frame colour
return IColouredItem.getColourBasic(stack);
case 2: // Light colour
{
registry.put(
new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ),
bakedModel
);
int light = ItemPocketComputer.getLightState(stack);
return light == -1 ? Colour.BLACK.getHex() : light;
}
}
// And load the custom turtle models in too.
registry.put(
new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_normal" ), "inventory" ),
bake( loader, TurtleModelLoader.INSTANCE.loadModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/turtle_normal" ) ) )
);
registry.put(
new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_advanced" ), "inventory" ),
bake( loader, TurtleModelLoader.INSTANCE.loadModel( new ResourceLocation( ComputerCraft.MOD_ID, "item/turtle_advanced" ) ) )
);
}
@SubscribeEvent
public static void onItemColours( ColorHandlerEvent.Item event )
{
if( ComputerCraft.Items.disk == null || ComputerCraft.Blocks.turtleNormal == null )
{
ComputerCraft.log.warn( "Block/item registration has failed. Skipping registration of item colours." );
return;
}
event.getItemColors().register(
( stack, layer ) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour( stack ) : 0xFFFFFF,
ComputerCraft.Items.disk
);
event.getItemColors().register( ( stack, layer ) -> {
switch( layer )
{
case 0:
default:
return 0xFFFFFF;
case 1: // Frame colour
return IColouredItem.getColourBasic( stack );
case 2: // Light colour
{
int light = ItemPocketComputer.getLightState( stack );
return light == -1 ? Colour.Black.getHex() : light;
}
}
}, ComputerCraft.Items.pocketComputerNormal, ComputerCraft.Items.pocketComputerAdvanced );
}, ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL, ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED);
// Setup turtle colours
event.getItemColors().register(
( stack, tintIndex ) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour( stack ) : 0xFFFFFF,
ComputerCraft.Blocks.turtleNormal, ComputerCraft.Blocks.turtleAdvanced
);
ColorProviderRegistry.ITEM.register((stack, tintIndex) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF,
ComputerCraftRegistry.ModBlocks.TURTLE_NORMAL,
ComputerCraftRegistry.ModBlocks.TURTLE_ADVANCED);
}
private static IBakedModel bake( ModelLoader loader, IUnbakedModel model )
{
model.getTextures( loader::getUnbakedModel, new HashSet<>() );
return model.bake(
loader::getUnbakedModel,
ModelLoader.defaultTextureGetter(),
ModelRotation.X0_Y0, false, DefaultVertexFormats.BLOCK
);
private static BakedModel bake(ModelLoader loader, UnbakedModel model, Identifier identifier) {
model.getTextureDependencies(loader::getOrLoadModel, new HashSet<>());
return model.bake(loader,
spriteIdentifier -> MinecraftClient.getInstance()
.getSpriteAtlas(spriteIdentifier.getAtlasId())
.apply(spriteIdentifier.getTextureId()),
ModelRotation.X0_Y0,
identifier);
}
}

View File

@@ -1,89 +1,91 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import javax.annotation.Nullable;
import dan200.computercraft.mixin.ChatHudAccess;
import dan200.computercraft.shared.command.text.ChatHelpers;
import dan200.computercraft.shared.command.text.TableBuilder;
import dan200.computercraft.shared.command.text.TableFormatter;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiNewChat;
import net.minecraft.client.gui.GuiUtilRenderComponents;
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;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.MathHelper;
public class ClientTableFormatter implements TableFormatter
{
@SuppressWarnings ({
"MethodCallSideOnly",
"LocalVariableDeclarationSideOnly"
})
public class ClientTableFormatter implements TableFormatter {
public static final ClientTableFormatter INSTANCE = new ClientTableFormatter();
private static Int2IntOpenHashMap lastHeights = new Int2IntOpenHashMap();
private static FontRenderer renderer()
{
return Minecraft.getInstance().fontRenderer;
}
@Override
@Nullable
public ITextComponent getPadding( ITextComponent component, int width )
{
int extraWidth = width - getWidth( component );
if( extraWidth <= 0 ) return null;
public Text getPadding(Text component, int width) {
int extraWidth = width - this.getWidth(component);
if (extraWidth <= 0) {
return null;
}
FontRenderer renderer = renderer();
TextRenderer renderer = renderer();
float spaceWidth = renderer.getStringWidth( " " );
int spaces = MathHelper.floor( extraWidth / spaceWidth );
float spaceWidth = renderer.getWidth(" ");
int spaces = MathHelper.floor(extraWidth / spaceWidth);
int extra = extraWidth - (int) (spaces * spaceWidth);
return ChatHelpers.coloured( StringUtils.repeat( ' ', spaces ) + StringUtils.repeat( (char) 712, extra ), TextFormatting.GRAY );
return ChatHelpers.coloured(StringUtils.repeat(' ', spaces) + StringUtils.repeat((char) 712, extra), Formatting.GRAY);
}
private static TextRenderer renderer() {
return MinecraftClient.getInstance().textRenderer;
}
@Override
public int getColumnPadding()
{
public int getColumnPadding() {
return 3;
}
@Override
public int getWidth( ITextComponent component )
{
return renderer().getStringWidth( component.getFormattedText() );
public int getWidth(Text component) {
return renderer().getWidth(component);
}
@Override
public void writeLine( int id, ITextComponent component )
{
Minecraft mc = Minecraft.getInstance();
GuiNewChat chat = mc.ingameGUI.getChatGUI();
public void writeLine(int id, Text component) {
MinecraftClient mc = MinecraftClient.getInstance();
ChatHud chat = mc.inGameHud.getChatHud();
// Trim the text if it goes over the allowed length
int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() );
List<ITextComponent> list = GuiUtilRenderComponents.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 );
((ChatHudAccess)chat).callAddMessage(component, id);
}
@Override
public int display( TableBuilder table )
{
GuiNewChat chat = Minecraft.getInstance().ingameGUI.getChatGUI();
public int display(TableBuilder table) {
ChatHud chat = MinecraftClient.getInstance().inGameHud.getChatHud();
int lastHeight = lastHeights.get( table.getId() );
int lastHeight = lastHeights.get(table.getId());
int height = TableFormatter.super.display( table );
lastHeights.put( table.getId(), height );
int height = TableFormatter.super.display(table);
lastHeights.put(table.getId(), height);
for( int i = height; i < lastHeight; i++ ) chat.deleteChatLine( i + table.getId() );
for (int i = height; i < lastHeight; i++) {
((ChatHudAccess)chat).callRemoveMessage(i + table.getId());
}
return height;
}
}

View File

@@ -1,46 +1,45 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class FrameInfo
{
public final class FrameInfo {
private static int tick;
private static long renderFrame;
private FrameInfo()
{
static {
}
public static boolean getGlobalCursorBlink()
{
private FrameInfo() {
}
public static void init() {
ClientTickEvents.START_CLIENT_TICK.register(m -> {
tick++;
});
}
public static boolean getGlobalCursorBlink() {
return (tick / 8) % 2 == 0;
}
public static long getRenderFrame()
{
public static long getRenderFrame() {
return renderFrame;
}
@SubscribeEvent
public static void onTick( TickEvent.ClientTickEvent event )
{
if( event.phase == TickEvent.Phase.START ) tick++;
// TODO Call this in a callback
public static void onTick() {
tick++;
}
@SubscribeEvent
public static void onRenderTick( TickEvent.RenderTickEvent event )
{
if( event.phase == TickEvent.Phase.START ) renderFrame++;
// TODO Call this in a callback
public static void onRenderFrame() {
renderFrame++;
}
}

View File

@@ -1,200 +1,384 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.core.terminal.Terminal;
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.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import java.util.Arrays;
public final class FixedWidthFontRenderer
{
private static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
public static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/term_background.png" );
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderPhase;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.AffineTransformation;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Matrix4f;
public final class FixedWidthFontRenderer {
public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6;
public static final float WIDTH = 256.0f;
public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final Matrix4f IDENTITY = AffineTransformation.identity()
.getMatrix();
private static final Identifier FONT = new Identifier("computercraft", "textures/gui/term_font.png");
public static final RenderLayer TYPE = Type.MAIN;
private static FixedWidthFontRenderer instance;
public static FixedWidthFontRenderer instance()
{
if( instance != null ) return instance;
return instance = new FixedWidthFontRenderer();
private FixedWidthFontRenderer() {
}
private final TextureManager m_textureManager;
public static void drawString(float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize) {
bindFont();
private FixedWidthFontRenderer()
{
m_textureManager = Minecraft.getInstance().getTextureManager();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
.getBufferBuilders()
.getEntityVertexConsumers();
drawString(IDENTITY,
((VertexConsumerProvider) renderer).getBuffer(TYPE),
x,
y,
text,
textColour,
backgroundColour,
palette,
greyscale,
leftMarginSize,
rightMarginSize);
renderer.draw();
}
private static void greyscaleify( double[] rgb )
{
Arrays.fill( rgb, (rgb[0] + rgb[1] + rgb[2]) / 3.0f );
private static void bindFont() {
MinecraftClient.getInstance()
.getTextureManager()
.bindTexture(FONT);
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
}
private void drawChar( BufferBuilder renderer, double x, double y, int index, int color, Palette p, boolean greyscale )
{
public static void drawString(@Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y, @Nonnull TextBuffer text,
@Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize) {
if (backgroundColour != null) {
drawBackground(transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT);
}
for (int i = 0; i < text.length(); i++) {
double[] colour = palette.getColour(getColour(textColour.charAt(i), Colour.BLACK));
float r, g, b;
if (greyscale) {
r = g = b = toGreyscale(colour);
} else {
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
// Draw char
int index = text.charAt(i);
if (index > 255) {
index = '?';
}
drawChar(transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b);
}
}
private static void drawBackground(@Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, float leftMarginSize,
float rightMarginSize, float height) {
if (leftMarginSize > 0) {
drawQuad(transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt(0));
}
if (rightMarginSize > 0) {
drawQuad(transform,
renderer,
x + backgroundColour.length() * FONT_WIDTH,
y,
rightMarginSize,
height,
palette,
greyscale,
backgroundColour.charAt(backgroundColour.length() - 1));
}
// Batch together runs of identical background cells.
int blockStart = 0;
char blockColour = '\0';
for (int i = 0; i < backgroundColour.length(); i++) {
char colourIndex = backgroundColour.charAt(i);
if (colourIndex == blockColour) {
continue;
}
if (blockColour != '\0') {
drawQuad(transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour);
}
blockColour = colourIndex;
blockStart = i;
}
if (blockColour != '\0') {
drawQuad(transform,
renderer,
x + blockStart * FONT_WIDTH,
y,
FONT_WIDTH * (backgroundColour.length() - blockStart),
height,
palette,
greyscale,
blockColour);
}
}
public static int getColour(char c, Colour def) {
return 15 - Terminal.getColour(c, def);
}
public static float toGreyscale(double[] rgb) {
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
}
private static void drawChar(Matrix4f transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b) {
// Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') {
return;
}
int column = index % 16;
int row = index / 16;
double[] colour = p.getColour( 15 - color );
if( greyscale )
{
greyscaleify( colour );
}
float r = (float) colour[0];
float g = (float) colour[1];
float b = (float) colour[2];
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
renderer.pos( x, y, 0.0 ).tex( xStart / 256.0, yStart / 256.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x, y + FONT_HEIGHT, 0.0 ).tex( xStart / 256.0, (yStart + FONT_HEIGHT) / 256.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x + FONT_WIDTH, y, 0.0 ).tex( (xStart + FONT_WIDTH) / 256.0, yStart / 256.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x + FONT_WIDTH, y, 0.0 ).tex( (xStart + FONT_WIDTH) / 256.0, yStart / 256.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x, y + FONT_HEIGHT, 0.0 ).tex( xStart / 256.0, (yStart + FONT_HEIGHT) / 256.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x + FONT_WIDTH, y + FONT_HEIGHT, 0.0 ).tex( (xStart + FONT_WIDTH) / 256.0, (yStart + FONT_HEIGHT) / 256.0 ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex(transform, x, y, 0f)
.color(r, g, b, 1.0f)
.texture(xStart / WIDTH, yStart / WIDTH)
.next();
buffer.vertex(transform, x, y + FONT_HEIGHT, 0f)
.color(r, g, b, 1.0f)
.texture(xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH)
.next();
buffer.vertex(transform, x + FONT_WIDTH, y, 0f)
.color(r, g, b, 1.0f)
.texture((xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH)
.next();
buffer.vertex(transform, x + FONT_WIDTH, y, 0f)
.color(r, g, b, 1.0f)
.texture((xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH)
.next();
buffer.vertex(transform, x, y + FONT_HEIGHT, 0f)
.color(r, g, b, 1.0f)
.texture(xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH)
.next();
buffer.vertex(transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f)
.color(r, g, b, 1.0f)
.texture((xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH)
.next();
}
private void drawQuad( BufferBuilder renderer, double x, double y, int color, double width, Palette p, boolean greyscale )
{
double[] colour = p.getColour( 15 - color );
if( greyscale )
{
greyscaleify( colour );
private static void drawQuad(Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, Palette palette,
boolean greyscale, char colourIndex) {
double[] colour = palette.getColour(getColour(colourIndex, Colour.BLACK));
float r, g, b;
if (greyscale) {
r = g = b = toGreyscale(colour);
} else {
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
float r = (float) colour[0];
float g = (float) colour[1];
float b = (float) colour[2];
renderer.pos( x, y, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x, y + FONT_HEIGHT, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x + width, y, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x + width, y, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x, y + FONT_HEIGHT, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( x + width, y + FONT_HEIGHT, 0.0 ).color( r, g, b, 1.0f ).endVertex();
drawQuad(transform, buffer, x, y, width, height, r, g, b);
}
private boolean isGreyScale( int colour )
{
return colour == 0 || colour == 15 || colour == 7 || colour == 8;
private static void drawQuad(Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b) {
buffer.vertex(transform, x, y, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_START, BACKGROUND_START)
.next();
buffer.vertex(transform, x, y + height, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_START, BACKGROUND_END)
.next();
buffer.vertex(transform, x + width, y, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_END, BACKGROUND_START)
.next();
buffer.vertex(transform, x + width, y, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_END, BACKGROUND_START)
.next();
buffer.vertex(transform, x, y + height, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_START, BACKGROUND_END)
.next();
buffer.vertex(transform, x + width, y + height, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_END, BACKGROUND_END)
.next();
}
public void drawStringBackgroundPart( int x, int y, TextBuffer backgroundColour, double leftMarginSize, double rightMarginSize, boolean greyScale, Palette p )
{
// Draw the quads
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder renderer = tessellator.getBuffer();
renderer.begin( GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION_COLOR );
if( leftMarginSize > 0.0 )
{
int colour1 = "0123456789abcdef".indexOf( backgroundColour.charAt( 0 ) );
if( colour1 < 0 || (greyScale && !isGreyScale( colour1 )) )
{
colour1 = 15;
}
drawQuad( renderer, x - leftMarginSize, y, colour1, leftMarginSize, p, greyScale );
public static void drawTerminalWithoutCursor(@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize,
float leftMarginSize, float rightMarginSize) {
Palette palette = terminal.getPalette();
int height = terminal.getHeight();
// Top and bottom margins
drawBackground(transform,
buffer,
x,
y - topMarginSize,
terminal.getBackgroundColourLine(0),
palette,
greyscale,
leftMarginSize,
rightMarginSize,
topMarginSize);
drawBackground(transform,
buffer,
x,
y + height * FONT_HEIGHT,
terminal.getBackgroundColourLine(height - 1),
palette,
greyscale,
leftMarginSize,
rightMarginSize,
bottomMarginSize);
// The main text
for (int i = 0; i < height; i++) {
drawString(transform,
buffer,
x,
y + FixedWidthFontRenderer.FONT_HEIGHT * i,
terminal.getLine(i),
terminal.getTextColourLine(i),
terminal.getBackgroundColourLine(i),
palette,
greyscale,
leftMarginSize,
rightMarginSize);
}
if( rightMarginSize > 0.0 )
{
int colour2 = "0123456789abcdef".indexOf( backgroundColour.charAt( backgroundColour.length() - 1 ) );
if( colour2 < 0 || (greyScale && !isGreyScale( colour2 )) )
{
colour2 = 15;
}
drawQuad( renderer, x + backgroundColour.length() * FONT_WIDTH, y, colour2, rightMarginSize, p, greyScale );
}
for( int i = 0; i < backgroundColour.length(); i++ )
{
int colour = "0123456789abcdef".indexOf( backgroundColour.charAt( i ) );
if( colour < 0 || (greyScale && !isGreyScale( colour )) )
{
colour = 15;
}
drawQuad( renderer, x + i * FONT_WIDTH, y, colour, FONT_WIDTH, p, greyScale );
}
GlStateManager.disableTexture2D();
tessellator.draw();
GlStateManager.enableTexture2D();
}
public void drawStringTextPart( int x, int y, TextBuffer s, TextBuffer textColour, boolean greyScale, Palette p )
{
// Draw the quads
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder renderer = tessellator.getBuffer();
renderer.begin( GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION_TEX_COLOR );
for( int i = 0; i < s.length(); i++ )
{
// Switch colour
int colour = "0123456789abcdef".indexOf( textColour.charAt( i ) );
if( colour < 0 || (greyScale && !isGreyScale( colour )) )
{
colour = 0;
public static void drawCursor(@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, @Nonnull Terminal terminal,
boolean greyscale) {
Palette palette = terminal.getPalette();
int width = terminal.getWidth();
int height = terminal.getHeight();
int cursorX = terminal.getCursorX();
int cursorY = terminal.getCursorY();
if (terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink()) {
double[] colour = palette.getColour(15 - terminal.getTextColour());
float r, g, b;
if (greyscale) {
r = g = b = toGreyscale(colour);
} else {
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
// Draw char
int index = s.charAt( i );
if( index < 0 || index > 255 )
{
index = '?';
}
drawChar( renderer, x + i * FONT_WIDTH, y, index, colour, p, greyScale );
}
tessellator.draw();
}
public void drawString( TextBuffer s, int x, int y, TextBuffer textColour, TextBuffer backgroundColour, double leftMarginSize, double rightMarginSize, boolean greyScale, Palette p )
{
// Draw background
if( backgroundColour != null )
{
// Bind the background texture
m_textureManager.bindTexture( BACKGROUND );
// Draw the quads
drawStringBackgroundPart( x, y, backgroundColour, leftMarginSize, rightMarginSize, greyScale, p );
}
// Draw text
if( s != null && textColour != null )
{
// Bind the font texture
bindFont();
// Draw the quads
drawStringTextPart( x, y, s, textColour, greyScale, p );
drawChar(transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b);
}
}
public int getStringWidth( String s )
{
if( s == null )
{
return 0;
}
return s.length() * FONT_WIDTH;
public static void drawTerminal(@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, @Nonnull Terminal terminal,
boolean greyscale, float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize) {
drawTerminalWithoutCursor(transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize);
drawCursor(transform, buffer, x, y, terminal, greyscale);
}
public void bindFont()
{
m_textureManager.bindTexture( FONT );
GlStateManager.texParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
public static void drawTerminal(@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize,
float bottomMarginSize, float leftMarginSize, float rightMarginSize) {
bindFont();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
.getBufferBuilders()
.getEntityVertexConsumers();
VertexConsumer buffer = renderer.getBuffer(TYPE);
drawTerminal(transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize);
renderer.draw(TYPE);
}
public static void drawTerminal(float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize,
float leftMarginSize, float rightMarginSize) {
drawTerminal(IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize);
}
public static void drawEmptyTerminal(float x, float y, float width, float height) {
drawEmptyTerminal(IDENTITY, x, y, width, height);
}
public static void drawEmptyTerminal(@Nonnull Matrix4f transform, float x, float y, float width, float height) {
bindFont();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
.getBufferBuilders()
.getEntityVertexConsumers();
drawEmptyTerminal(transform, renderer, x, y, width, height);
renderer.draw();
}
public static void drawEmptyTerminal(@Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width,
float height) {
Colour colour = Colour.BLACK;
drawQuad(transform, renderer.getBuffer(TYPE), x, y, width, height, colour.getR(), colour.getG(), colour.getB());
}
public static void drawBlocker(@Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width, float height) {
Colour colour = Colour.BLACK;
drawQuad(transform, renderer.getBuffer(Type.BLOCKER), x, y, width, height, colour.getR(), colour.getG(), colour.getB());
}
private static final class Type extends RenderPhase {
private static final int GL_MODE = GL11.GL_TRIANGLES;
private static final VertexFormat FORMAT = VertexFormats.POSITION_COLOR_TEXTURE;
static final RenderLayer MAIN = RenderLayer.of("terminal_font", FORMAT, GL_MODE, 1024, false, false, // useDelegate, needsSorting
RenderLayer.MultiPhaseParameters.builder()
.texture(new RenderPhase.Texture(FONT,
false,
false)) // blur, minimap
.alpha(ONE_TENTH_ALPHA)
.lightmap(DISABLE_LIGHTMAP)
.writeMaskState(COLOR_MASK)
.build(false));
static final RenderLayer BLOCKER = RenderLayer.of("terminal_blocker", FORMAT, GL_MODE, 256, false, false, // useDelegate, needsSorting
RenderLayer.MultiPhaseParameters.builder()
.texture(new RenderPhase.Texture(FONT,
false,
false)) // blur, minimap
.alpha(ONE_TENTH_ALPHA)
.writeMaskState(DEPTH_MASK)
.lightmap(DISABLE_LIGHTMAP)
.build(false));
private Type(String name, Runnable setup, Runnable destroy) {
super(name, setup, destroy);
}
}
}

View File

@@ -1,154 +1,134 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
import javax.annotation.Nonnull;
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.shared.computer.blocks.TileComputer;
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;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.inventory.Container;
import net.minecraft.util.ResourceLocation;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import org.lwjgl.glfw.GLFW;
public class GuiComputer extends GuiContainer
{
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" );
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
private final ComputerFamily m_family;
private final ClientComputer m_computer;
private final int m_termWidth;
private final int m_termHeight;
public final class GuiComputer<T extends ContainerComputerBase> extends HandledScreen<T> {
private final ComputerFamily family;
private final ClientComputer computer;
private final int termWidth;
private final int termHeight;
private WidgetTerminal terminal;
private WidgetWrapper terminalWrapper;
public GuiComputer( Container container, ComputerFamily family, ClientComputer computer, int termWidth, int termHeight )
{
super( container );
m_family = family;
m_computer = computer;
m_termWidth = termWidth;
m_termHeight = termHeight;
terminal = null;
private GuiComputer(T container, PlayerInventory player, Text title, int termWidth, int termHeight) {
super(container, player, title);
this.family = container.getFamily();
this.computer = (ClientComputer) container.getComputer();
this.termWidth = termWidth;
this.termHeight = termHeight;
this.terminal = null;
}
public GuiComputer( TileComputer computer )
{
this(
new ContainerComputer( computer ),
computer.getFamily(),
computer.createClientComputer(),
ComputerCraft.terminalWidth_computer,
ComputerCraft.terminalHeight_computer
);
public static GuiComputer<ContainerComputer> create(ContainerComputer container, PlayerInventory inventory, Text component) {
return new GuiComputer<>(container, inventory, component, ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer);
}
public static GuiComputer<ContainerPocketComputer> createPocket(ContainerPocketComputer container, PlayerInventory inventory, Text component) {
return new GuiComputer<>(container, inventory, component, ComputerCraft.terminalWidth_pocketComputer, ComputerCraft.terminalHeight_pocketComputer);
}
public static GuiComputer<ContainerViewComputer> createView(ContainerViewComputer container, PlayerInventory inventory, Text component) {
return new GuiComputer<>(container, inventory, component, container.getWidth(), container.getHeight());
}
@Override
protected void init() {
this.client.keyboard.setRepeatEvents(true);
int termPxWidth = this.termWidth * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = this.termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
this.backgroundWidth = termPxWidth + MARGIN * 2 + BORDER * 2;
this.backgroundHeight = termPxHeight + MARGIN * 2 + BORDER * 2;
super.init();
this.terminal = new WidgetTerminal(this.client, () -> this.computer, this.termWidth, this.termHeight, MARGIN, MARGIN, MARGIN, MARGIN);
this.terminalWrapper = new WidgetWrapper(this.terminal, MARGIN + BORDER + this.x, MARGIN + BORDER + this.y, termPxWidth, termPxHeight);
this.children.add(this.terminalWrapper);
this.setFocused(this.terminalWrapper);
}
@Override
protected void initGui()
{
mc.keyboardListener.enableRepeatEvents( true );
int termPxWidth = m_termWidth * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = m_termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
xSize = termPxWidth + 4 + 24;
ySize = termPxHeight + 4 + 24;
super.initGui();
terminal = new WidgetTerminal( mc, () -> m_computer, m_termWidth, m_termHeight, 2, 2, 2, 2 );
terminalWrapper = new WidgetWrapper( terminal, 2 + 12 + guiLeft, 2 + 12 + guiTop, termPxWidth, termPxHeight );
children.add( terminalWrapper );
setFocused( terminalWrapper );
public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
super.render(stack, mouseX, mouseY, partialTicks);
this.drawMouseoverTooltip(stack, mouseX, mouseY);
}
@Override
public void onGuiClosed()
{
super.onGuiClosed();
children.remove( terminal );
terminal = null;
mc.keyboardListener.enableRepeatEvents( false );
protected void drawForeground(@Nonnull MatrixStack transform, int mouseX, int mouseY) {
// Skip rendering labels.
}
@Override
public void tick()
{
super.tick();
terminal.update();
}
@Override
public void drawGuiContainerBackgroundLayer( 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;
public void drawBackground(@Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY) {
// Draw terminal
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
this.terminal.draw(this.terminalWrapper.getX(), this.terminalWrapper.getY());
// Draw a border around the terminal
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
switch( m_family )
{
case Normal:
default:
mc.getTextureManager().bindTexture( BACKGROUND_NORMAL );
break;
case Advanced:
mc.getTextureManager().bindTexture( BACKGROUND_ADVANCED );
break;
case Command:
mc.getTextureManager().bindTexture( BACKGROUND_COMMAND );
break;
RenderSystem.color4f(1, 1, 1, 1);
this.client.getTextureManager()
.bindTexture(ComputerBorderRenderer.getTexture(this.family));
ComputerBorderRenderer.render(this.terminalWrapper.getX() - MARGIN, this.terminalWrapper.getY() - MARGIN,
this.getZOffset(), this.terminalWrapper.getWidth() + MARGIN * 2, this.terminalWrapper.getHeight() + MARGIN * 2);
}
@Override
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
return (this.getFocused() != null && this.getFocused().mouseDragged(x, y, button, deltaX, deltaY)) || super.mouseDragged(x, y, button, deltaX, deltaY);
}
@Override
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 && this.getFocused() != null && this.getFocused() == this.terminalWrapper) {
return this.getFocused().keyPressed(key, scancode, modifiers);
}
drawTexturedModalRect( startX - 12, startY - 12, 12, 28, 12, 12 );
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 12 );
drawTexturedModalRect( endX, startY - 12, 24, 28, 12, 12 );
drawTexturedModalRect( endX, endY, 24, 40, 12, 12 );
drawTexturedModalRect( startX, startY - 12, 0, 0, endX - startX, 12 );
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 12 );
drawTexturedModalRect( startX - 12, startY, 0, 28, 12, endY - startY );
drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY );
return super.keyPressed(key, scancode, modifiers);
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
{
drawDefaultBackground();
super.render( mouseX, mouseY, partialTicks );
renderHoveredToolTip( mouseX, mouseY );
public void removed() {
super.removed();
this.children.remove(this.terminal);
this.terminal = null;
this.client.keyboard.setRepeatEvents(false);
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|| super.mouseDragged( x, y, button, deltaX, deltaY );
}
@Override
public boolean mouseReleased( double x, double y, int button )
{
return (getFocused() != null && getFocused().mouseReleased( x, y, button ))
|| super.mouseReleased( x, y, button );
public void tick() {
super.tick();
this.terminal.update();
}
}

View File

@@ -1,50 +1,41 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
public class GuiDiskDrive extends GuiContainer
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/disk_drive.png" );
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
private final ContainerDiskDrive m_container;
public class GuiDiskDrive extends HandledScreen<ContainerDiskDrive> {
private static final Identifier BACKGROUND = new Identifier("computercraft", "textures/gui/disk_drive.png");
public GuiDiskDrive( ContainerDiskDrive container )
{
super( container );
m_container = container;
public GuiDiskDrive(ContainerDiskDrive container, PlayerInventory player, Text title) {
super(container, player, title);
}
@Override
protected void drawGuiContainerForegroundLayer( int mouseX, int mouseY )
{
String title = m_container.getDiskDrive().getDisplayName().getString();
fontRenderer.drawString( title, (xSize - fontRenderer.getStringWidth( title )) / 2.0f, 6, 0x404040 );
fontRenderer.drawString( I18n.format( "container.inventory" ), 8, ySize - 96 + 2, 0x404040 );
public void render(@Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(transform);
super.render(transform, mouseX, mouseY, partialTicks);
this.drawMouseoverTooltip(transform, mouseX, mouseY);
}
@Override
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
mc.getTextureManager().bindTexture( BACKGROUND );
drawTexturedModalRect( guiLeft, guiTop, 0, 0, xSize, ySize );
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
{
drawDefaultBackground();
super.render( mouseX, mouseY, partialTicks );
renderHoveredToolTip( mouseX, mouseY );
protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
this.client.getTextureManager()
.bindTexture(BACKGROUND);
this.drawTexture(transform, this.x, this.y, 0, 0, this.backgroundWidth, this.backgroundHeight);
}
}

View File

@@ -1,34 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
public class GuiPocketComputer extends GuiComputer
{
public GuiPocketComputer( ContainerPocketComputer container )
{
super(
container,
getFamily( container.getStack() ),
ItemPocketComputer.createClientComputer( container.getStack() ),
ComputerCraft.terminalWidth_pocketComputer,
ComputerCraft.terminalHeight_pocketComputer
);
}
private static ComputerFamily getFamily( ItemStack stack )
{
Item item = stack.getItem();
return item instanceof ItemPocketComputer ? ((ItemPocketComputer) item).getFamily() : ComputerFamily.Normal;
}
}

View File

@@ -1,52 +1,53 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
public class GuiPrinter extends GuiContainer
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/printer.png" );
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
private final ContainerPrinter container;
public class GuiPrinter extends HandledScreen<ContainerPrinter> {
private static final Identifier BACKGROUND = new Identifier("computercraft", "textures/gui/printer.png");
public GuiPrinter( ContainerPrinter container )
{
super( container );
this.container = container;
public GuiPrinter(ContainerPrinter container, PlayerInventory player, Text title) {
super(container, player, title);
}
@Override
/*@Override
protected void drawGuiContainerForegroundLayer( int mouseX, int mouseY )
{
String title = container.getPrinter().getDisplayName().getString();
fontRenderer.drawString( title, (xSize - fontRenderer.getStringWidth( title )) / 2.0f, 6, 0x404040 );
fontRenderer.drawString( I18n.format( "container.inventory" ), 8, ySize - 96 + 2, 0x404040 );
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
public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(stack);
super.render(stack, mouseX, mouseY, partialTicks);
this.drawMouseoverTooltip(stack, mouseX, mouseY);
}
@Override
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
mc.getTextureManager().bindTexture( BACKGROUND );
drawTexturedModalRect( guiLeft, guiTop, 0, 0, xSize, ySize );
protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
this.client.getTextureManager()
.bindTexture(BACKGROUND);
this.drawTexture(transform, this.x, this.y, 0, 0, this.backgroundWidth, this.backgroundHeight);
if( container.isPrinting() ) drawTexturedModalRect( guiLeft + 34, guiTop + 21, 176, 0, 25, 45 );
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
{
drawDefaultBackground();
super.render( mouseX, mouseY, partialTicks );
renderHoveredToolTip( mouseX, mouseY );
if (this.getScreenHandler().isPrinting()) {
this.drawTexture(transform, this.x + 34, this.y + 21, 176, 0, 25, 45);
}
}
}

View File

@@ -1,82 +1,81 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import static dan200.computercraft.client.render.PrintoutRenderer.X_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.Y_SIZE;
import static dan200.computercraft.client.render.PrintoutRenderer.Y_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.drawBorder;
import static dan200.computercraft.client.render.PrintoutRenderer.drawText;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import org.lwjgl.glfw.GLFW;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
import net.minecraft.util.math.Matrix4f;
public class GuiPrintout extends GuiContainer
{
public class GuiPrintout extends HandledScreen<ContainerHeldItem> {
private final boolean m_book;
private final int m_pages;
private final TextBuffer[] m_text;
private final TextBuffer[] m_colours;
private int m_page;
public GuiPrintout( ContainerHeldItem container )
{
super( container );
public GuiPrintout(ContainerHeldItem container, PlayerInventory player, Text title) {
super(container, player, title);
ySize = Y_SIZE;
this.backgroundHeight = Y_SIZE;
String[] text = ItemPrintout.getText( container.getStack() );
m_text = new TextBuffer[text.length];
for( int i = 0; i < m_text.length; i++ ) m_text[i] = new TextBuffer( text[i] );
String[] text = ItemPrintout.getText(container.getStack());
this.m_text = new TextBuffer[text.length];
for (int i = 0; i < this.m_text.length; i++) {
this.m_text[i] = new TextBuffer(text[i]);
}
String[] colours = ItemPrintout.getColours( container.getStack() );
m_colours = new TextBuffer[colours.length];
for( int i = 0; i < m_colours.length; i++ ) m_colours[i] = new TextBuffer( colours[i] );
String[] colours = ItemPrintout.getColours(container.getStack());
this.m_colours = new TextBuffer[colours.length];
for (int i = 0; i < this.m_colours.length; i++) {
this.m_colours[i] = new TextBuffer(colours[i]);
}
m_page = 0;
m_pages = Math.max( m_text.length / ItemPrintout.LINES_PER_PAGE, 1 );
m_book = ((ItemPrintout) container.getStack().getItem()).getType() == ItemPrintout.Type.BOOK;
this.m_page = 0;
this.m_pages = Math.max(this.m_text.length / ItemPrintout.LINES_PER_PAGE, 1);
this.m_book = ((ItemPrintout) container.getStack()
.getItem()).getType() == ItemPrintout.Type.BOOK;
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
if( super.keyPressed( key, scancode, modifiers ) ) return true;
if( key == GLFW.GLFW_KEY_RIGHT )
{
if( m_page < m_pages - 1 ) m_page++;
public boolean mouseScrolled(double x, double y, double delta) {
if (super.mouseScrolled(x, y, delta)) {
return true;
}
if( key == GLFW.GLFW_KEY_LEFT )
{
if( m_page > 0 ) m_page--;
return true;
}
return false;
}
@Override
public boolean mouseScrolled( double delta )
{
if( super.mouseScrolled( delta ) ) return true;
if( delta < 0 )
{
if (delta < 0) {
// Scroll up goes to the next page
if( m_page < m_pages - 1 ) m_page++;
if (this.m_page < this.m_pages - 1) {
this.m_page++;
}
return true;
}
if( delta > 0 )
{
if (delta > 0) {
// Scroll down goes to the previous page
if( m_page > 0 ) m_page--;
if (this.m_page > 0) {
this.m_page--;
}
return true;
}
@@ -84,25 +83,56 @@ public class GuiPrintout extends GuiContainer
}
@Override
public void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{
// Draw the printout
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
GlStateManager.enableDepthTest();
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.
this.setZOffset(this.getZOffset() - 1);
this.renderBackground(stack);
this.setZOffset(this.getZOffset() + 1);
drawBorder( guiLeft, guiTop, zLevel, m_page, m_pages, m_book );
drawText( guiLeft + X_TEXT_MARGIN, guiTop + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours );
super.render(stack, mouseX, mouseY, partialTicks);
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
{
// We must take the background further back in order to not overlap with our printed pages.
zLevel--;
drawDefaultBackground();
zLevel++;
protected void drawForeground(@Nonnull MatrixStack transform, int mouseX, int mouseY) {
// Skip rendering labels.
}
super.render( mouseX, mouseY, partialTicks );
renderHoveredToolTip( mouseX, mouseY );
@Override
protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
// Draw the printout
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
RenderSystem.enableDepthTest();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
.getBufferBuilders()
.getEntityVertexConsumers();
Matrix4f matrix = transform.peek()
.getModel();
drawBorder(matrix, renderer, this.x, this.y, this.getZOffset(), this.m_page, this.m_pages, this.m_book);
drawText(matrix, renderer, this.x + X_TEXT_MARGIN, this.y + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * this.m_page, this.m_text, this.m_colours);
renderer.draw();
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
if (super.keyPressed(key, scancode, modifiers)) {
return true;
}
if (key == GLFW.GLFW_KEY_RIGHT) {
if (this.m_page < this.m_pages - 1) {
this.m_page++;
}
return true;
}
if (key == GLFW.GLFW_KEY_LEFT) {
if (this.m_page > 0) {
this.m_page--;
}
return true;
}
return false;
}
}

View File

@@ -1,117 +1,126 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
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.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.glfw.GLFW;
public class GuiTurtle extends GuiContainer
{
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( "computercraft", "textures/gui/turtle_normal.png" );
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( "computercraft", "textures/gui/turtle_advanced.png" );
private ContainerTurtle m_container;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class GuiTurtle extends HandledScreen<ContainerTurtle> {
private static final Identifier BACKGROUND_NORMAL = new Identifier("computercraft", "textures/gui/turtle_normal.png");
private static final Identifier BACKGROUND_ADVANCED = new Identifier("computercraft", "textures/gui/turtle_advanced.png");
private final ComputerFamily m_family;
private final ClientComputer m_computer;
private ContainerTurtle m_container;
private WidgetTerminal terminal;
private WidgetWrapper terminalWrapper;
public GuiTurtle( TileTurtle turtle, ContainerTurtle container )
{
super( container );
public GuiTurtle(ContainerTurtle container, PlayerInventory player, Text title) {
super(container, player, title);
m_container = container;
m_family = turtle.getFamily();
m_computer = turtle.getClientComputer();
this.m_container = container;
this.m_family = container.getFamily();
this.m_computer = (ClientComputer) container.getComputer();
xSize = 254;
ySize = 217;
this.backgroundWidth = 254;
this.backgroundHeight = 217;
}
@Override
protected void initGui()
{
super.initGui();
mc.keyboardListener.enableRepeatEvents( true );
protected void init() {
super.init();
this.client.keyboard.setRepeatEvents(true);
int termPxWidth = ComputerCraft.terminalWidth_turtle * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = ComputerCraft.terminalHeight_turtle * FixedWidthFontRenderer.FONT_HEIGHT;
terminal = new WidgetTerminal(
mc, () -> m_computer,
ComputerCraft.terminalWidth_turtle,
ComputerCraft.terminalHeight_turtle,
2, 2, 2, 2
);
terminalWrapper = new WidgetWrapper( terminal, 2 + 8 + guiLeft, 2 + 8 + guiTop, termPxWidth, termPxHeight );
this.terminal = new WidgetTerminal(this.client, () -> this.m_computer, ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle, 2, 2, 2, 2);
this.terminalWrapper = new WidgetWrapper(this.terminal, 2 + 8 + this.x, 2 + 8 + this.y, termPxWidth, termPxHeight);
children.add( terminalWrapper );
setFocused( terminalWrapper );
this.children.add(this.terminalWrapper);
this.setFocused(this.terminalWrapper);
}
@Override
public void onGuiClosed()
{
children.remove( terminal );
terminal = null;
mc.keyboardListener.enableRepeatEvents( false );
public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(stack);
super.render(stack, mouseX, mouseY, partialTicks);
this.drawMouseoverTooltip(stack, mouseX, mouseY);
}
@Override
public void tick()
{
super.tick();
terminal.update();
protected void drawForeground(@Nonnull MatrixStack transform, int mouseX, int mouseY) {
// Skip rendering labels.
}
private void drawSelectionSlot( boolean advanced )
{
@Override
protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
// Draw term
Identifier texture = this.m_family == ComputerFamily.ADVANCED ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
this.terminal.draw(this.terminalWrapper.getX(), this.terminalWrapper.getY());
// Draw border/inventory
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
this.client.getTextureManager()
.bindTexture(texture);
this.drawTexture(transform, this.x, this.y, 0, 0, this.backgroundWidth, this.backgroundHeight);
// Draw selection slot
int slot = m_container.getSelectedSlot();
if( slot >= 0 )
{
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
int slot = this.m_container.getSelectedSlot();
if (slot >= 0) {
int slotX = slot % 4;
int slotY = slot / 4;
mc.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
drawTexturedModalRect( guiLeft + m_container.m_turtleInvStartX - 2 + slotX * 18, guiTop + m_container.m_playerInvStartY - 2 + slotY * 18, 0, 217, 24, 24 );
this.drawTexture(transform, this.x + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18, this.y + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18,
0,
217,
24,
24);
}
}
@Override
protected void drawGuiContainerBackgroundLayer( float partialTicks, int mouseX, int mouseY )
{
// Draw term
boolean advanced = m_family == ComputerFamily.Advanced;
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw border/inventory
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
mc.getTextureManager().bindTexture( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
drawTexturedModalRect( guiLeft, guiTop, 0, 0, xSize, ySize );
drawSelectionSlot( advanced );
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
return (this.getFocused() != null && this.getFocused().mouseDragged(x, y, button, deltaX, deltaY)) || super.mouseDragged(x, y, button, deltaX, deltaY);
}
@Override
public void render( int mouseX, int mouseY, float partialTicks )
{
drawDefaultBackground();
super.render( mouseX, mouseY, partialTicks );
renderHoveredToolTip( mouseX, mouseY );
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 && this.getFocused() != null && this.getFocused() == this.terminalWrapper) {
return this.getFocused().keyPressed(key, scancode, modifiers);
}
return super.keyPressed(key, scancode, modifiers);
}
@Override
public void removed() {
super.removed();
this.children.remove(this.terminal);
this.terminal = null;
this.client.keyboard.setRepeatEvents(false);
}
@Override
public void tick() {
super.tick();
this.terminal.update();
}
}

View File

@@ -1,61 +1,49 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui.widgets;
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.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.IGuiEventListener;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.SharedConstants;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import java.util.BitSet;
import java.util.function.Supplier;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.BACKGROUND;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.IComputer;
import org.lwjgl.glfw.GLFW;
public class WidgetTerminal implements IGuiEventListener
{
import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element;
public class WidgetTerminal implements Element {
private static final float TERMINATE_TIME = 0.5f;
private final Minecraft client;
private final MinecraftClient client;
private final Supplier<ClientComputer> computer;
private final int termWidth;
private final int termHeight;
private float terminateTimer = -1;
private float rebootTimer = -1;
private float shutdownTimer = -1;
private int lastMouseButton = -1;
private int lastMouseX = -1;
private int lastMouseY = -1;
private final int leftMargin;
private final int rightMargin;
private final int topMargin;
private final int bottomMargin;
private final BitSet keysDown = new BitSet(256);
private boolean focused;
private float terminateTimer = -1;
private float rebootTimer = -1;
private float shutdownTimer = -1;
private int lastMouseButton = -1;
private int lastMouseX = -1;
private int lastMouseY = -1;
private final BitSet keysDown = new BitSet( 256 );
public WidgetTerminal( Minecraft client, Supplier<ClientComputer> computer, int termWidth, int termHeight, int leftMargin, int rightMargin, int topMargin, int bottomMargin )
{
public WidgetTerminal(MinecraftClient client, Supplier<ClientComputer> computer, int termWidth, int termHeight, int leftMargin, int rightMargin,
int topMargin, int bottomMargin) {
this.client = client;
this.computer = computer;
this.termWidth = termWidth;
@@ -67,351 +55,287 @@ public class WidgetTerminal implements IGuiEventListener
}
@Override
public boolean charTyped( char ch, int modifiers )
{
if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range
public boolean mouseClicked(double mouseX, double mouseY, int button) {
ClientComputer computer = this.computer.get();
if (computer == null || !computer.isColour() || button < 0 || button > 2) {
return false;
}
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
computer.mouseClick(button + 1, charX + 1, charY + 1);
this.lastMouseButton = button;
this.lastMouseX = charX;
this.lastMouseY = charY;
}
return true;
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
ClientComputer computer = this.computer.get();
if (computer == null || !computer.isColour() || button < 0 || button > 2) {
return false;
}
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
if (this.lastMouseButton == button) {
computer.mouseUp(this.lastMouseButton + 1, charX + 1, charY + 1);
this.lastMouseButton = -1;
}
this.lastMouseX = charX;
this.lastMouseY = charY;
}
return false;
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double v2, double v3) {
ClientComputer computer = this.computer.get();
if (computer == null || !computer.isColour() || button < 0 || button > 2) {
return false;
}
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
if (button == this.lastMouseButton && (charX != this.lastMouseX || charY != this.lastMouseY)) {
computer.mouseDrag(button + 1, charX + 1, charY + 1);
this.lastMouseX = charX;
this.lastMouseY = charY;
}
}
return false;
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
ClientComputer computer = this.computer.get();
if (computer == null || !computer.isColour() || delta == 0) {
return false;
}
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
computer.mouseScroll(delta < 0 ? 1 : -1, charX + 1, charY + 1);
this.lastMouseX = charX;
this.lastMouseY = charY;
}
return true;
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
if (key == GLFW.GLFW_KEY_ESCAPE) {
return false;
}
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
switch (key) {
case GLFW.GLFW_KEY_T:
if (this.terminateTimer < 0) {
this.terminateTimer = 0;
}
return true;
case GLFW.GLFW_KEY_S:
if (this.shutdownTimer < 0) {
this.shutdownTimer = 0;
}
return true;
case GLFW.GLFW_KEY_R:
if (this.rebootTimer < 0) {
this.rebootTimer = 0;
}
return true;
case GLFW.GLFW_KEY_V:
// Ctrl+V for paste
String clipboard = this.client.keyboard.getClipboard();
if (clipboard != null) {
// Clip to the first occurrence of \r or \n
int newLineIndex1 = clipboard.indexOf("\r");
int newLineIndex2 = clipboard.indexOf("\n");
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
} else if (newLineIndex1 >= 0) {
clipboard = clipboard.substring(0, newLineIndex1);
} else if (newLineIndex2 >= 0) {
clipboard = clipboard.substring(0, newLineIndex2);
}
// Filter the string
clipboard = SharedConstants.stripInvalidChars(clipboard);
if (!clipboard.isEmpty()) {
// Clip to 512 characters and queue the event
if (clipboard.length() > 512) {
clipboard = clipboard.substring(0, 512);
}
this.queueEvent("paste", clipboard);
}
return true;
}
}
}
if (key >= 0 && this.terminateTimer < 0 && this.rebootTimer < 0 && this.shutdownTimer < 0) {
// Queue the "key" event and add to the down set
boolean repeat = this.keysDown.get(key);
this.keysDown.set(key);
IComputer computer = this.computer.get();
if (computer != null) {
computer.keyDown(key, repeat);
}
}
return true;
}
@Override
public boolean keyReleased(int key, int scancode, int modifiers) {
// Queue the "key_up" event and remove from the down set
if (key >= 0 && this.keysDown.get(key)) {
this.keysDown.set(key, false);
IComputer computer = this.computer.get();
if (computer != null) {
computer.keyUp(key);
}
}
switch (key) {
case GLFW.GLFW_KEY_T:
this.terminateTimer = -1;
break;
case GLFW.GLFW_KEY_R:
this.rebootTimer = -1;
break;
case GLFW.GLFW_KEY_S:
this.shutdownTimer = -1;
break;
case GLFW.GLFW_KEY_LEFT_CONTROL:
case GLFW.GLFW_KEY_RIGHT_CONTROL:
this.terminateTimer = this.rebootTimer = this.shutdownTimer = -1;
break;
}
return true;
}
@Override
public boolean charTyped(char ch, int modifiers) {
if (ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255) // printable chars in byte range
{
// Queue the "char" event
queueEvent( "char", Character.toString( ch ) );
this.queueEvent("char", Character.toString(ch));
}
return true;
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
if( key == GLFW.GLFW_KEY_ESCAPE ) return false;
if( (modifiers & GLFW.GLFW_MOD_CONTROL) != 0 )
{
switch( key )
{
case GLFW.GLFW_KEY_T:
if( terminateTimer < 0 ) terminateTimer = 0;
return true;
case GLFW.GLFW_KEY_S:
if( shutdownTimer < 0 ) shutdownTimer = 0;
return true;
case GLFW.GLFW_KEY_R:
if( rebootTimer < 0 ) rebootTimer = 0;
return true;
case GLFW.GLFW_KEY_V:
// Ctrl+V for paste
String clipboard = client.keyboardListener.getClipboardString();
if( clipboard != null )
{
// Clip to the first occurrence of \r or \n
int newLineIndex1 = clipboard.indexOf( "\r" );
int newLineIndex2 = clipboard.indexOf( "\n" );
if( newLineIndex1 >= 0 && newLineIndex2 >= 0 )
{
clipboard = clipboard.substring( 0, Math.min( newLineIndex1, newLineIndex2 ) );
}
else if( newLineIndex1 >= 0 )
{
clipboard = clipboard.substring( 0, newLineIndex1 );
}
else if( newLineIndex2 >= 0 )
{
clipboard = clipboard.substring( 0, newLineIndex2 );
}
// Filter the string
clipboard = SharedConstants.filterAllowedCharacters( clipboard );
if( !clipboard.isEmpty() )
{
// Clip to 512 characters and queue the event
if( clipboard.length() > 512 ) clipboard = clipboard.substring( 0, 512 );
queueEvent( "paste", clipboard );
}
return true;
}
}
}
if( key >= 0 && terminateTimer < 0 && rebootTimer < 0 && shutdownTimer < 0 )
{
// Queue the "key" event and add to the down set
boolean repeat = keysDown.get( key );
keysDown.set( key );
IComputer computer = this.computer.get();
if( computer != null ) computer.keyDown( key, repeat );
}
return true;
}
@Override
public boolean keyReleased( int key, int scancode, int modifiers )
{
// Queue the "key_up" event and remove from the down set
if( key >= 0 && keysDown.get( key ) )
{
keysDown.set( key, false );
IComputer computer = this.computer.get();
if( computer != null ) computer.keyUp( key );
}
switch( key )
{
case GLFW.GLFW_KEY_T:
terminateTimer = -1;
break;
case GLFW.GLFW_KEY_R:
rebootTimer = -1;
break;
case GLFW.GLFW_KEY_S:
shutdownTimer = -1;
break;
case GLFW.GLFW_KEY_LEFT_CONTROL:
case GLFW.GLFW_KEY_RIGHT_CONTROL:
terminateTimer = rebootTimer = shutdownTimer = -1;
break;
}
return true;
}
@Override
public boolean mouseClicked( double mouseX, double mouseY, int button )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
computer.mouseClick( button + 1, charX + 1, charY + 1 );
lastMouseButton = button;
lastMouseX = charX;
lastMouseY = charY;
}
return true;
}
@Override
public boolean mouseReleased( double mouseX, double mouseY, int button )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
if( lastMouseButton == button )
{
computer.mouseUp( lastMouseButton + 1, charX + 1, charY + 1 );
lastMouseButton = -1;
}
lastMouseX = charX;
lastMouseY = charY;
}
return false;
}
@Override
public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FixedWidthFontRenderer.FONT_WIDTH);
int charY = (int) (mouseY / FixedWidthFontRenderer.FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
computer.mouseDrag( button + 1, charX + 1, charY + 1 );
lastMouseX = charX;
lastMouseY = charY;
lastMouseButton = button;
}
return false;
}
@Override
public boolean mouseScrolled( double delta )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() ) return false;
if( lastMouseX >= 0 && lastMouseY >= 0 && delta != 0 )
{
queueEvent( "mouse_scroll", delta < 0 ? 1 : -1, lastMouseX + 1, lastMouseY + 1 );
}
return true;
}
public void update()
{
if( terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME )
{
queueEvent( "terminate" );
}
if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.shutdown();
}
if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.reboot();
}
}
@Override
public void focusChanged( boolean focused )
{
if( !focused )
{
public boolean changeFocus(boolean reversed) {
if (this.focused) {
// When blurring, we should make all keys go up
for( int key = 0; key < keysDown.size(); key++ )
{
if( keysDown.get( key ) ) queueEvent( "key_up", key );
for (int key = 0; key < this.keysDown.size(); key++) {
if (this.keysDown.get(key)) {
this.queueEvent("key_up", key);
}
}
keysDown.clear();
this.keysDown.clear();
// When blurring, we should make the last mouse button go up
if( lastMouseButton > 0 )
{
if (this.lastMouseButton > 0) {
IComputer computer = this.computer.get();
if( computer != null ) computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 );
lastMouseButton = -1;
if (computer != null) {
computer.mouseUp(this.lastMouseButton + 1, this.lastMouseX + 1, this.lastMouseY + 1);
}
this.lastMouseButton = -1;
}
shutdownTimer = terminateTimer = rebootTimer = -1;
this.shutdownTimer = this.terminateTimer = this.rebootTimer = -1;
}
this.focused = !this.focused;
return true;
}
@Override
public boolean isMouseOver(double x, double y) {
return true;
}
private void queueEvent(String event, Object... args) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.queueEvent(event, args);
}
}
public void draw( int originX, int originY )
{
synchronized( computer )
{
public void update() {
if (this.terminateTimer >= 0 && this.terminateTimer < TERMINATE_TIME && (this.terminateTimer += 0.05f) > TERMINATE_TIME) {
this.queueEvent("terminate");
}
if (this.shutdownTimer >= 0 && this.shutdownTimer < TERMINATE_TIME && (this.shutdownTimer += 0.05f) > TERMINATE_TIME) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.shutdown();
}
}
if (this.rebootTimer >= 0 && this.rebootTimer < TERMINATE_TIME && (this.rebootTimer += 0.05f) > TERMINATE_TIME) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.reboot();
}
}
}
private void queueEvent(String event) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.queueEvent(event);
}
}
public void draw(int originX, int originY) {
synchronized (this.computer) {
// Draw the screen contents
ClientComputer computer = this.computer.get();
Terminal terminal = computer != null ? computer.getTerminal() : null;
if( terminal != null )
{
// Draw the terminal
boolean greyscale = !computer.isColour();
Palette palette = terminal.getPalette();
// Get the data from the terminal first
// Unfortunately we have to keep the lock for the whole of drawing, so the text doesn't change under us.
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
boolean tblink = terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink();
int tw = terminal.getWidth();
int th = terminal.getHeight();
int tx = terminal.getCursorX();
int ty = terminal.getCursorY();
// Draw margins
TextBuffer emptyLine = new TextBuffer( ' ', tw );
if( topMargin > 0 )
{
fontRenderer.drawString( emptyLine, originX, originY - topMargin,
terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ),
leftMargin, rightMargin, greyscale, palette );
}
if( bottomMargin > 0 )
{
fontRenderer.drawString( emptyLine, originX, originY + bottomMargin + (th - 1) * FixedWidthFontRenderer.FONT_HEIGHT,
terminal.getTextColourLine( th - 1 ), terminal.getBackgroundColourLine( th - 1 ),
leftMargin, rightMargin, greyscale, palette );
}
// Draw lines
int y = originY;
for( int line = 0; line < th; line++ )
{
TextBuffer text = terminal.getLine( line );
TextBuffer colour = terminal.getTextColourLine( line );
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
fontRenderer.drawString( text, originX, y, colour, backgroundColour, leftMargin, rightMargin, greyscale, palette );
y += FixedWidthFontRenderer.FONT_HEIGHT;
}
if( tblink && tx >= 0 && ty >= 0 && tx < tw && ty < th )
{
TextBuffer cursor = new TextBuffer( '_', 1 );
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
cursor,
originX + FixedWidthFontRenderer.FONT_WIDTH * tx,
originY + FixedWidthFontRenderer.FONT_HEIGHT * ty,
cursorColour, null,
0, 0,
greyscale,
palette
);
}
}
else
{
// Draw a black background
Colour black = Colour.Black;
GlStateManager.color4f( black.getR(), black.getG(), black.getB(), 1.0f );
try
{
int x = originX - leftMargin;
int y = originY - rightMargin;
int width = termWidth * FixedWidthFontRenderer.FONT_WIDTH + leftMargin + rightMargin;
int height = termHeight * FixedWidthFontRenderer.FONT_HEIGHT + topMargin + bottomMargin;
client.getTextureManager().bindTexture( BACKGROUND );
Tessellator tesslector = Tessellator.getInstance();
BufferBuilder buffer = tesslector.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
buffer.pos( x, y + height, 0 ).tex( 0 / 256.0, height / 256.0 ).endVertex();
buffer.pos( x + width, y + height, 0 ).tex( width / 256.0, height / 256.0 ).endVertex();
buffer.pos( x + width, y, 0 ).tex( width / 256.0, 0 / 256.0 ).endVertex();
buffer.pos( x, y, 0 ).tex( 0 / 256.0, 0 / 256.0 ).endVertex();
tesslector.draw();
}
finally
{
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
}
if (terminal != null) {
FixedWidthFontRenderer.drawTerminal(originX, originY, terminal, !computer.isColour(), this.topMargin, this.bottomMargin, this.leftMargin,
this.rightMargin);
} else {
FixedWidthFontRenderer.drawEmptyTerminal(originX - this.leftMargin,
originY - this.rightMargin, this.termWidth * FONT_WIDTH + this.leftMargin + this.rightMargin,
this.termHeight * FONT_HEIGHT + this.topMargin + this.bottomMargin);
}
}
}
private void queueEvent( String event )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.queueEvent( event );
}
private void queueEvent( String event, Object... args )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.queueEvent( event, args );
}
}

View File

@@ -1,23 +1,21 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui.widgets;
import net.minecraft.client.gui.IGuiEventListener;
import net.minecraft.client.gui.Element;
public class WidgetWrapper implements IGuiEventListener
{
private final IGuiEventListener listener;
public class WidgetWrapper implements Element {
private final Element listener;
private final int x;
private final int y;
private final int width;
private final int height;
public WidgetWrapper( IGuiEventListener listener, int x, int y, int width, int height )
{
public WidgetWrapper(Element listener, int x, int y, int width, int height) {
this.listener = listener;
this.x = x;
this.y = y;
@@ -26,79 +24,68 @@ public class WidgetWrapper implements IGuiEventListener
}
@Override
public void focusChanged( boolean b )
{
listener.focusChanged( b );
}
@Override
public boolean canFocus()
{
return listener.canFocus();
}
@Override
public boolean mouseClicked( double x, double y, int button )
{
public boolean mouseClicked(double x, double y, int button) {
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseClicked( dx, dy, button );
return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseClicked(dx, dy, button);
}
@Override
public boolean mouseReleased( double x, double y, int button )
{
public boolean mouseReleased(double x, double y, int button) {
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseReleased( dx, dy, button );
return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseReleased(dx, dy, button);
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseDragged( dx, dy, button, deltaX, deltaY );
return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseDragged(dx, dy, button, deltaX, deltaY);
}
@Override
public boolean mouseScrolled( double delta )
{
return listener.mouseScrolled( delta );
public boolean mouseScrolled(double x, double y, double delta) {
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseScrolled(dx, dy, delta);
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
return listener.keyPressed( key, scancode, modifiers );
public boolean keyPressed(int key, int scancode, int modifiers) {
return this.listener.keyPressed(key, scancode, modifiers);
}
@Override
public boolean keyReleased( int key, int scancode, int modifiers )
{
return listener.keyReleased( key, scancode, modifiers );
public boolean keyReleased(int key, int scancode, int modifiers) {
return this.listener.keyReleased(key, scancode, modifiers);
}
@Override
public boolean charTyped( char character, int modifiers )
{
return listener.charTyped( character, modifiers );
public boolean charTyped(char character, int modifiers) {
return this.listener.charTyped(character, modifiers);
}
public int getX()
{
return x;
@Override
public boolean changeFocus(boolean b) {
return this.listener.changeFocus(b);
}
public int getY()
{
return y;
@Override
public boolean isMouseOver(double x, double y) {
double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height;
}
public int getWidth()
{
return width;
public int getX() {
return this.x;
}
public int getHeight()
{
return height;
public int getY() {
return this.y;
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
}
}

View File

@@ -1,96 +1,141 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.proxy;
import java.util.function.Supplier;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityCableRenderer;
import dan200.computercraft.client.ClientRegistry;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.gui.GuiComputer;
import dan200.computercraft.client.gui.GuiDiskDrive;
import dan200.computercraft.client.gui.GuiPrinter;
import dan200.computercraft.client.gui.GuiPrintout;
import dan200.computercraft.client.gui.GuiTurtle;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.ComputerCraftRegistry;
import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.*;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
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.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ExtensionPoint;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import java.util.function.BiFunction;
import net.minecraft.client.item.ModelPredicateProvider;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.item.Item;
import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD )
public final class ComputerCraftProxyClient
{
@SubscribeEvent
public static void setupClient( FMLClientSetupEvent event )
{
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.fabricmc.fabric.api.client.rendereregistry.v1.BlockEntityRendererRegistry;
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry;
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.mixin.object.builder.ModelPredicateProviderRegistrySpecificAccessor;
@Environment (EnvType.CLIENT)
public final class ComputerCraftProxyClient implements ClientModInitializer {
public static void initEvents() {
}
@Override
public void onInitializeClient() {
FrameInfo.init();
registerContainers();
// While turtles themselves are not transparent, their upgrades may be.
BlockRenderLayerMap.INSTANCE.putBlock(ComputerCraftRegistry.ModBlocks.TURTLE_NORMAL, RenderLayer.getTranslucent());
BlockRenderLayerMap.INSTANCE.putBlock(ComputerCraftRegistry.ModBlocks.TURTLE_ADVANCED, RenderLayer.getTranslucent());
// Monitors' textures have transparent fronts and so count as cutouts.
BlockRenderLayerMap.INSTANCE.putBlock(ComputerCraftRegistry.ModBlocks.MONITOR_NORMAL, RenderLayer.getCutout());
BlockRenderLayerMap.INSTANCE.putBlock(ComputerCraftRegistry.ModBlocks.MONITOR_ADVANCED, RenderLayer.getCutout());
// Setup TESRs
ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() );
ClientRegistry.bindTileEntitySpecialRenderer( TileCable.class, new TileEntityCableRenderer() );
ClientRegistry.bindTileEntitySpecialRenderer( TileTurtle.class, new TileEntityTurtleRenderer() );
BlockEntityRendererRegistry.INSTANCE.register(ComputerCraftRegistry.ModTiles.MONITOR_NORMAL, TileEntityMonitorRenderer::new);
BlockEntityRendererRegistry.INSTANCE.register(ComputerCraftRegistry.ModTiles.MONITOR_ADVANCED, TileEntityMonitorRenderer::new);
BlockEntityRendererRegistry.INSTANCE.register(ComputerCraftRegistry.ModTiles.TURTLE_NORMAL, TileEntityTurtleRenderer::new);
BlockEntityRendererRegistry.INSTANCE.register(ComputerCraftRegistry.ModTiles.TURTLE_ADVANCED, TileEntityTurtleRenderer::new);
// TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE)
.register(ClientRegistry::onTextureStitchEvent);
ModelLoadingRegistry.INSTANCE.registerAppender(ClientRegistry::onModelBakeEvent);
ModelLoadingRegistry.INSTANCE.registerResourceProvider(loader -> (name, context) -> TurtleModelLoader.INSTANCE.accepts(name) ?
TurtleModelLoader.INSTANCE.loadModel(
name) : null);
EntityRendererRegistry.INSTANCE.register(ComputerCraftRegistry.ModEntities.TURTLE_PLAYER, TurtlePlayerRenderer::new);
registerItemProperty("state",
(stack, world, player) -> ItemPocketComputer.getState(stack)
.ordinal(),
() -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL,
() -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED);
registerItemProperty("state",
(stack, world, player) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
() -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL,
() -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED);
ClientRegistry.onItemColours();
// TODO Verify this does things properly
ServerWorldEvents.UNLOAD.register(((minecraftServer, serverWorld) -> {
ClientMonitor.destroyAll();
}));
}
private static void registerContainers()
{
ContainerType.registerGui( TileEntityContainerType::computer, ( packet, player ) ->
new GuiComputer( (TileComputer) packet.getTileEntity( player ) ) );
ContainerType.registerGui( TileEntityContainerType::diskDrive, GuiDiskDrive::new );
ContainerType.registerGui( TileEntityContainerType::printer, GuiPrinter::new );
ContainerType.registerGui( TileEntityContainerType::turtle, ( packet, player ) -> {
TileTurtle turtle = (TileTurtle) packet.getTileEntity( player );
return new GuiTurtle( turtle, new ContainerTurtle( player.inventory, turtle.getAccess(), turtle.getClientComputer() ) );
} );
ContainerType.registerGui( PocketComputerContainerType::new, GuiPocketComputer::new );
ContainerType.registerGui( PrintoutContainerType::new, GuiPrintout::new );
ContainerType.registerGui( ViewComputerContainerType::new, ( packet, player ) -> {
ClientComputer computer = ComputerCraft.clientComputerRegistry.get( packet.instanceId );
if( computer == null )
{
ComputerCraft.clientComputerRegistry.add( packet.instanceId, computer = new ClientComputer( packet.instanceId ) );
}
// My IDE doesn't think so, but we do actually need these generics.
private static void registerContainers() {
ScreenRegistry.<ContainerComputer, GuiComputer<ContainerComputer>>register(ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create);
ScreenRegistry.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register(ComputerCraftRegistry.ModContainers.POCKET_COMPUTER,
GuiComputer::createPocket);
ScreenRegistry.<ContainerTurtle, GuiTurtle>register(ComputerCraftRegistry.ModContainers.TURTLE, GuiTurtle::new);
ContainerViewComputer container = new ContainerViewComputer( computer );
return new GuiComputer( container, packet.family, computer, packet.width, packet.height );
} );
ScreenRegistry.<ContainerPrinter, GuiPrinter>register(ComputerCraftRegistry.ModContainers.PRINTER, GuiPrinter::new);
ScreenRegistry.<ContainerDiskDrive, GuiDiskDrive>register(ComputerCraftRegistry.ModContainers.DISK_DRIVE, GuiDiskDrive::new);
ScreenRegistry.<ContainerHeldItem, GuiPrintout>register(ComputerCraftRegistry.ModContainers.PRINTOUT, GuiPrintout::new);
ModLoadingContext.get().registerExtensionPoint( ExtensionPoint.GUIFACTORY, () -> packet -> {
ContainerType<?> type = ContainerType.factories.get( packet.getId() ).get();
if( packet.getAdditionalData() != null ) type.fromBytes( packet.getAdditionalData() );
@SuppressWarnings( "unchecked" )
BiFunction<ContainerType<?>, EntityPlayer, GuiContainer> factory = (BiFunction<ContainerType<?>, EntityPlayer, GuiContainer>) ContainerType.guiFactories.get( packet.getId() );
return factory.apply( type, Minecraft.getInstance().player );
} );
ScreenRegistry.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ComputerCraftRegistry.ModContainers.VIEW_COMPUTER,
GuiComputer::createView);
}
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public static final class ForgeHandlers
{
@SubscribeEvent
public static void onWorldUnload( WorldEvent.Unload event )
{
if( event.getWorld().isRemote() )
{
ClientMonitor.destroyAll();
}
@SafeVarargs
private static void registerItemProperty(String name, ModelPredicateProvider getter, Supplier<? extends Item>... items) {
Identifier id = new Identifier(ComputerCraft.MOD_ID, name);
for (Supplier<? extends Item> item : items) {
ModelPredicateProviderRegistrySpecificAccessor.callRegister(item.get(), id, getter);
}
}
// @Mod.EventBusSubscriber (modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
// public static final class ForgeHandlers {
// @SubscribeEvent
// public static void onWorldUnload(WorldEvent.Unload event) {
// if (event.getWorld()
// .isClient()) {
// ClientMonitor.destroyAll();
// }
// }
// }
}

View File

@@ -1,88 +1,71 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.ComputerCraftRegistry;
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.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class CableHighlightRenderer
{
private CableHighlightRenderer()
{
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.World;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment (EnvType.CLIENT)
public final class CableHighlightRenderer {
private CableHighlightRenderer() {
}
/**
* Draw an outline for a specific part of a cable "Multipart".
*
* @param event The event to observe
* @see WorldRenderer#drawSelectionBox(EntityPlayer, RayTraceResult, int, float)
*/
@SubscribeEvent
public static void drawHighlight( DrawBlockHighlightEvent event )
{
if( event.getTarget().type != RayTraceResult.Type.BLOCK ) return;
BlockPos pos = event.getTarget().getBlockPos();
World world = event.getPlayer().getEntityWorld();
IBlockState state = world.getBlockState( pos );
public static boolean drawHighlight(MatrixStack stack, VertexConsumer consumer, Entity entity, double d, double e, double f, BlockPos pos,
BlockState state) {
World world = entity.getEntityWorld();
Camera info = MinecraftClient.getInstance().gameRenderer.getCamera();
// 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 ) )
{
return;
if (state.getBlock() != ComputerCraftRegistry.ModBlocks.CABLE || state.get(BlockCable.MODEM)
.getFacing() == null || !state.get(BlockCable.CABLE)) {
return false;
}
event.setCanceled( true );
VoxelShape shape = WorldUtil.isVecInside(CableShapes.getModemShape(state),
new Vec3d(d, e, f).subtract(pos.getX(),
pos.getY(),
pos.getZ())) ? CableShapes.getModemShape(state) : CableShapes.getCableShape(
state);
EntityPlayer player = event.getPlayer();
Minecraft mc = Minecraft.getInstance();
float partialTicks = event.getPartialTicks();
Vec3d cameraPos = info.getPos();
double xOffset = pos.getX() - cameraPos.getX();
double yOffset = pos.getY() - cameraPos.getY();
double zOffset = pos.getZ() - cameraPos.getZ();
Matrix4f matrix4f = stack.peek()
.getModel();
shape.forEachEdge((x1, y1, z1, x2, y2, z2) -> {
consumer.vertex(matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset))
.color(0, 0, 0, 0.4f)
.next();
consumer.vertex(matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset))
.color(0, 0, 0, 0.4f)
.next();
});
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.lineWidth( Math.max( 2.5F, mc.mainWindow.getFramebufferWidth() / 1920.0F * 2.5F ) );
GlStateManager.disableTexture2D();
GlStateManager.depthMask( false );
GlStateManager.matrixMode( GL11.GL_PROJECTION );
GlStateManager.pushMatrix();
GlStateManager.scalef( 1.0F, 1.0F, 0.999F );
double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * partialTicks;
double y = player.lastTickPosY + (player.posY - player.lastTickPosY) * partialTicks;
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialTicks;
VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), event.getTarget().hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? CableShapes.getModemShape( state )
: CableShapes.getCableShape( state );
WorldRenderer.drawShape( shape, pos.getX() - x, pos.getY() - y, pos.getZ() - z, 0.0F, 0.0F, 0.0F, 0.4F );
GlStateManager.popMatrix();
GlStateManager.matrixMode( GL11.GL_MODELVIEW );
GlStateManager.depthMask( true );
GlStateManager.enableTexture2D();
GlStateManager.disableBlend();
return true;
}
}

View File

@@ -0,0 +1,167 @@
/*
* 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 javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Matrix4f;
public class ComputerBorderRenderer {
public static final Identifier BACKGROUND_NORMAL = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_normal.png");
public static final Identifier BACKGROUND_ADVANCED = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png");
public static final Identifier BACKGROUND_COMMAND = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_command.png");
public static final Identifier BACKGROUND_COLOUR = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_colour.png");
/**
* 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 Matrix4f IDENTITY = new Matrix4f();
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;
static {
IDENTITY.loadIdentity();
}
private final Matrix4f transform;
private final VertexConsumer builder;
private final int z;
private final float r, g, b;
public ComputerBorderRenderer(Matrix4f transform, VertexConsumer 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 Identifier 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, VertexFormats.POSITION_COLOR_TEXTURE);
render(IDENTITY, buffer, x, y, z, width, height);
RenderSystem.enableAlphaTest();
tessellator.draw();
}
public static void render(Matrix4f transform, VertexConsumer 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, VertexConsumer 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, VertexConsumer 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
this.renderLine(x - BORDER, y, 0, CORNER_TOP_Y, BORDER, endY - y);
this.renderLine(endX, y, BORDER_RIGHT_X, CORNER_TOP_Y, BORDER, endY - y);
// Top bar
this.renderLine(x, y - BORDER, 0, 0, endX - x, BORDER);
this.renderCorner(x - BORDER, y - BORDER, CORNER_LEFT_X, CORNER_TOP_Y);
this.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) {
this.renderLine(x, endY, 0, BORDER, endX - x, BORDER);
this.renderCorner(x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y);
this.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.
this.renderTexture(x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2);
this.renderTexture(x, endY, 0, BORDER, width, BORDER / 2, BORDER, BORDER / 2);
this.renderTexture(endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2);
this.renderTexture(x - BORDER, endY + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP);
this.renderTexture(x, endY + BORDER / 2, 0, BORDER + GAP, width, bottomHeight, BORDER, GAP);
this.renderTexture(endX, endY + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP);
this.renderTexture(x - BORDER, endY + bottomHeight + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2);
this.renderTexture(x, endY + bottomHeight + BORDER / 2, 0, BORDER + BORDER / 2, width, BORDER / 2);
this.renderTexture(endX, endY + bottomHeight + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2);
}
}
private void renderLine(int x, int y, int u, int v, int width, int height) {
this.renderTexture(x, y, u, v, width, height, BORDER, BORDER);
}
private void renderCorner(int x, int y, int u, int v) {
this.renderTexture(x, y, u, v, BORDER, BORDER, BORDER, BORDER);
}
private void renderTexture(int x, int y, int u, int v, int width, int height) {
this.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) {
this.builder.vertex(this.transform, x, y + height, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture(u * TEX_SCALE, (v + textureHeight) * TEX_SCALE)
.next();
this.builder.vertex(this.transform, x + width, y + height, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture((u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE)
.next();
this.builder.vertex(this.transform, x + width, y, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture((u + textureWidth) * TEX_SCALE, v * TEX_SCALE)
.next();
this.builder.vertex(this.transform, x, y, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture(u * TEX_SCALE, v * TEX_SCALE)
.next();
}
}

View File

@@ -1,118 +1,142 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FirstPersonRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.EntityPlayer;
import dan200.computercraft.mixin.HeldItemRendererAccess;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.HeldItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.Arm;
import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper;
public abstract class ItemMapLikeRenderer
{
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment (EnvType.CLIENT)
public abstract class ItemMapLikeRenderer {
public void renderItemFirstPerson(MatrixStack transform, VertexConsumerProvider render, int lightTexture, Hand hand, float pitch, float equipProgress
, float swingProgress, ItemStack stack) {
PlayerEntity player = MinecraftClient.getInstance().player;
transform.push();
if (hand == Hand.MAIN_HAND && player.getOffHandStack()
.isEmpty()) {
this.renderItemFirstPersonCenter(transform, render, lightTexture, pitch, equipProgress, swingProgress, stack);
} else {
this.renderItemFirstPersonSide(transform,
render,
lightTexture,
hand == Hand.MAIN_HAND ? player.getMainArm() : player.getMainArm()
.getOpposite(),
equipProgress,
swingProgress,
stack);
}
transform.pop();
}
/**
* The main rendering method for the item
* Render an item in the middle of the screen.
*
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param combinedLight The current light level
* @param pitch The pitch of the player
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see FirstPersonRenderer#renderMapFirstPerson(ItemStack)
*/
protected abstract void renderItem( ItemStack stack );
private void renderItemFirstPersonCenter(MatrixStack transform, VertexConsumerProvider render, int combinedLight, float pitch, float equipProgress,
float swingProgress, ItemStack stack) {
MinecraftClient minecraft = MinecraftClient.getInstance();
HeldItemRenderer renderer = minecraft.getHeldItemRenderer();
protected void renderItemFirstPerson( EnumHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack )
{
EntityPlayer player = Minecraft.getInstance().player;
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
float swingRt = MathHelper.sqrt(swingProgress);
float tX = -0.2f * MathHelper.sin(swingProgress * (float) Math.PI);
float tZ = -0.4f * MathHelper.sin(swingRt * (float) Math.PI);
transform.translate(0, -tX / 2, tZ);
GlStateManager.pushMatrix();
if( hand == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() )
{
renderItemFirstPersonCenter( pitch, equipProgress, swingProgress, stack );
HeldItemRendererAccess access = (HeldItemRendererAccess) renderer;
float pitchAngle = access.callGetMapAngle(pitch);
transform.translate(0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f);
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(pitchAngle * -85.0f));
if (!minecraft.player.isInvisible()) {
transform.push();
transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(90.0F));
access.callRenderArm(transform, render, combinedLight, Arm.RIGHT);
access.callRenderArm(transform, render, combinedLight, Arm.LEFT);
transform.pop();
}
else
{
renderItemFirstPersonSide(
hand == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(),
equipProgress, swingProgress, stack
);
}
GlStateManager.popMatrix();
float rX = MathHelper.sin(swingRt * (float) Math.PI);
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(rX * 20.0F));
transform.scale(2.0F, 2.0F, 2.0F);
this.renderItem(transform, render, stack);
}
/**
* Renders the item to one side of the player.
*
* @param side The side to render on
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param combinedLight The current light level
* @param side The side to render on
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see FirstPersonRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack)
* @param stack The stack to render
*/
private void renderItemFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack )
{
Minecraft minecraft = Minecraft.getInstance();
float offset = side == EnumHandSide.RIGHT ? 1f : -1f;
GlStateManager.translatef( offset * 0.125f, -0.125f, 0f );
private void renderItemFirstPersonSide(MatrixStack transform, VertexConsumerProvider render, int combinedLight, Arm side, float equipProgress,
float swingProgress, ItemStack stack) {
MinecraftClient minecraft = MinecraftClient.getInstance();
float offset = side == Arm.RIGHT ? 1f : -1f;
transform.translate(offset * 0.125f, -0.125f, 0f);
// If the player is not invisible then render a single arm
if( !minecraft.player.isInvisible() )
{
GlStateManager.pushMatrix();
GlStateManager.rotatef( offset * 10f, 0f, 0f, 1f );
minecraft.getFirstPersonRenderer().renderArmFirstPerson( equipProgress, swingProgress, side );
GlStateManager.popMatrix();
if (!minecraft.player.isInvisible()) {
transform.push();
transform.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(offset * 10f));
((HeldItemRendererAccess)minecraft.getHeldItemRenderer())
.callRenderArmHoldingItem(transform, render, combinedLight, equipProgress, swingProgress, side);
transform.pop();
}
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
GlStateManager.pushMatrix();
GlStateManager.translatef( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
float f1 = MathHelper.sqrt( swingProgress );
float f2 = MathHelper.sin( f1 * (float) Math.PI );
transform.push();
transform.translate(offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f);
float f1 = MathHelper.sqrt(swingProgress);
float f2 = MathHelper.sin(f1 * (float) Math.PI);
float f3 = -0.5f * f2;
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
GlStateManager.translatef( offset * f3, f4 - 0.3f * f2, f5 );
GlStateManager.rotatef( f2 * -45f, 1f, 0f, 0f );
GlStateManager.rotatef( offset * f2 * -30f, 0f, 1f, 0f );
float f4 = 0.4f * MathHelper.sin(f1 * ((float) Math.PI * 2f));
float f5 = -0.3f * MathHelper.sin(swingProgress * (float) Math.PI);
transform.translate(offset * f3, f4 - 0.3f * f2, f5);
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(f2 * -45f));
transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(offset * f2 * -30f));
renderItem( stack );
this.renderItem(transform, render, stack);
GlStateManager.popMatrix();
transform.pop();
}
/**
* Render an item in the middle of the screen
* The main rendering method for the item.
*
* @param pitch The pitch of the player
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
* @see FirstPersonRenderer#renderMapFirstPerson(float, float, float)
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param stack The stack to render
*/
private void renderItemFirstPersonCenter( float pitch, float equipProgress, float swingProgress, ItemStack stack )
{
FirstPersonRenderer renderer = Minecraft.getInstance().getFirstPersonRenderer();
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
float swingRt = MathHelper.sqrt( swingProgress );
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI );
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI );
GlStateManager.translatef( 0f, -tX / 2f, tZ );
float pitchAngle = renderer.getMapAngleFromPitch( pitch );
GlStateManager.translatef( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f );
GlStateManager.rotatef( pitchAngle * -85f, 1f, 0f, 0f );
renderer.renderArms();
float rX = MathHelper.sin( swingRt * (float) Math.PI );
GlStateManager.rotatef( rX * 20f, 1f, 0f, 0f );
GlStateManager.scalef( 2f, 2f, 2f );
renderItem( stack );
}
protected abstract void renderItem(MatrixStack transform, VertexConsumerProvider render, ItemStack stack);
}

View File

@@ -1,76 +1,56 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
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.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.client.gui.GuiComputer.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Matrix4f;
/**
* Emulates map rendering for pocket computers
* Emulates map rendering for pocket computers.
*/
@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;
public final class ItemPocketRenderer extends ItemMapLikeRenderer {
public static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private static final int LIGHT_HEIGHT = 8;
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private ItemPocketRenderer()
{
}
@SubscribeEvent
public static void renderItem( RenderSpecificHandEvent event )
{
ItemStack stack = event.getItemStack();
if( !(stack.getItem() instanceof ItemPocketComputer) ) return;
event.setCanceled( true );
INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() );
private ItemPocketRenderer() {
}
@Override
protected void renderItem( ItemStack stack )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
protected void renderItem(MatrixStack transform, VertexConsumerProvider render, ItemStack stack) {
ClientComputer computer = ItemPocketComputer.createClientComputer(stack);
Terminal terminal = computer == null ? null : computer.getTerminal();
int termWidth, termHeight;
if( terminal == null )
{
if (terminal == null) {
termWidth = ComputerCraft.terminalWidth_pocketComputer;
termHeight = ComputerCraft.terminalHeight_pocketComputer;
}
else
{
} else {
termWidth = terminal.getWidth();
termHeight = terminal.getHeight();
}
@@ -80,61 +60,44 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
// Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer
GlStateManager.pushMatrix();
transform.push();
transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(180f));
transform.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(180f));
transform.scale(0.5f, 0.5f, 0.5f);
GlStateManager.disableLighting();
GlStateManager.disableDepthTest();
GlStateManager.rotatef( 180f, 0f, 1f, 0f );
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
GlStateManager.scalef( 0.5f, 0.5f, 0.5f );
double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
GlStateManager.scaled( scale, scale, 0 );
GlStateManager.translated( -0.5 * width, -0.5 * height, 0 );
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);
// Render the main frame
ItemPocketComputer item = (ItemPocketComputer) stack.getItem();
ComputerFamily family = item.getFamily();
int frameColour = item.getColour( stack );
renderFrame( family, frameColour, width, height );
int frameColour = item.getColour(stack);
Matrix4f matrix = transform.peek()
.getModel();
renderFrame(matrix, family, frameColour, width, height);
// Render the light
int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.Black.getHex();
renderLight( lightColour, width, height );
if( computer != null && terminal != null )
{
// If we've a computer and terminal then attempt to render it.
renderTerminal( terminal, !computer.isColour(), width, height );
int lightColour = ItemPocketComputer.getLightState(stack);
if (lightColour == -1) {
lightColour = Colour.BLACK.getHex();
}
else
{
// Otherwise render a plain background
Minecraft.getInstance().getTextureManager().bindTexture( BACKGROUND );
renderLight(matrix, lightColour, width, height);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Colour black = Colour.Black;
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION );
renderTexture( buffer, 0, 0, 0, 0, width, height, black.getR(), black.getG(), black.getB() );
tessellator.draw();
if (computer != null && terminal != null) {
FixedWidthFontRenderer.drawTerminal(matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN);
} else {
FixedWidthFontRenderer.drawEmptyTerminal(matrix, 0, 0, width, height);
}
GlStateManager.enableDepthTest();
GlStateManager.enableLighting();
GlStateManager.popMatrix();
transform.pop();
}
private static void renderFrame( ComputerFamily family, int colour, int width, int height )
{
Minecraft.getInstance().getTextureManager().bindTexture( colour != -1
? BACKGROUND_COLOUR
: family == ComputerFamily.Normal ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
);
private static void renderFrame(Matrix4f transform, ComputerFamily family, int colour, int width, int height) {
MinecraftClient.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;
@@ -142,38 +105,16 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR );
buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE);
// Top left, middle, right
renderTexture( buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
renderTexture( buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
renderTexture( buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
// Left and bright border
renderTexture( buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
renderTexture( 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( buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
renderTexture( buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
renderTexture( 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();
}
private static void renderLight( int colour, int width, int height )
{
GlStateManager.enableBlend();
GlStateManager.disableTexture2D();
private static void renderLight(Matrix4f transform, int colour, int width, int height) {
RenderSystem.enableBlend();
RenderSystem.disableTexture();
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
@@ -181,74 +122,21 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
buffer.pos( width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR);
buffer.vertex(transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
buffer.vertex(transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
buffer.vertex(transform, width, height + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
buffer.vertex(transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
tessellator.draw();
GlStateManager.enableTexture2D();
}
private static void renderTerminal( Terminal terminal, boolean greyscale, int width, int height )
{
synchronized( terminal )
{
int termWidth = terminal.getWidth();
int termHeight = terminal.getHeight();
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
Palette palette = terminal.getPalette();
// Render top/bottom borders
TextBuffer emptyLine = new TextBuffer( ' ', termWidth );
fontRenderer.drawString(
emptyLine, MARGIN, 0,
terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ), MARGIN, MARGIN, greyscale, palette
);
fontRenderer.drawString(
emptyLine, MARGIN, 2 * MARGIN + (termHeight - 1) * FixedWidthFontRenderer.FONT_HEIGHT,
terminal.getTextColourLine( termHeight - 1 ), terminal.getBackgroundColourLine( termHeight - 1 ), MARGIN, MARGIN, greyscale, palette
);
// Render the actual text
for( int line = 0; line < termWidth; line++ )
{
TextBuffer text = terminal.getLine( line );
TextBuffer colour = terminal.getTextColourLine( line );
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
fontRenderer.drawString(
text, MARGIN, MARGIN + line * FONT_HEIGHT,
colour, backgroundColour, MARGIN, MARGIN, greyscale, palette
);
}
// And render the cursor;
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
tx >= 0 && ty >= 0 && tx < termWidth && ty < termHeight )
{
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
new TextBuffer( '_', 1 ), MARGIN + FONT_WIDTH * tx, MARGIN + FONT_HEIGHT * ty,
cursorColour, null, 0, 0, greyscale, palette
);
}
}
}
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
{
renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
}
private static void renderTexture( BufferBuilder 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( x, y + height, 0 ).tex( textureX * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x + width, y + height, 0 ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x + width, y, 0 ).tex( (textureX + textureWidth) * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x, y, 0 ).tex( textureX * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
RenderSystem.enableTexture();
}
}

View File

@@ -1,116 +1,94 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderItemInFrameEvent;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
import static dan200.computercraft.client.render.PrintoutRenderer.COVER_SIZE;
import static dan200.computercraft.client.render.PrintoutRenderer.X_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.Y_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.drawBorder;
import static dan200.computercraft.client.render.PrintoutRenderer.drawText;
import static dan200.computercraft.client.render.PrintoutRenderer.offsetAt;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Matrix4f;
/**
* Emulates map and item-frame rendering for printouts
* Emulates map and item-frame rendering for printouts.
*/
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
{
private static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer();
public final class ItemPrintoutRenderer extends ItemMapLikeRenderer {
public static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer();
private ItemPrintoutRenderer()
{
}
@SubscribeEvent
public static void onRenderInHand( RenderSpecificHandEvent event )
{
ItemStack stack = event.getItemStack();
if( !(stack.getItem() instanceof ItemPrintout) ) return;
event.setCanceled( true );
INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() );
private ItemPrintoutRenderer() {
}
@Override
protected void renderItem( ItemStack stack )
{
// Setup various transformations. Note that these are partially adapated from the corresponding method
// in FirstPersonRenderer.renderFirstPersonMap
GlStateManager.disableLighting();
protected void renderItem(MatrixStack transform, VertexConsumerProvider render, ItemStack stack) {
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(180f));
transform.scale(0.42f, 0.42f, -0.42f);
transform.translate(-0.5f, -0.48f, 0.0f);
GlStateManager.rotatef( 180f, 0f, 1f, 0f );
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
GlStateManager.scalef( 0.42f, 0.42f, -0.42f );
GlStateManager.translatef( -0.5f, -0.48f, 0.0f );
drawPrintout( stack );
GlStateManager.enableLighting();
drawPrintout(transform, render, stack);
}
@SubscribeEvent
public static void onRenderInFrame( RenderItemInFrameEvent event )
{
ItemStack stack = event.getItem();
if( !(stack.getItem() instanceof ItemPrintout) ) return;
event.setCanceled( true );
GlStateManager.disableLighting();
// Move a little bit forward to ensure we're not clipping with the frame
GlStateManager.translatef( 0.0f, 0.0f, -0.001f );
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
GlStateManager.scalef( 0.95f, 0.95f, -0.95f );
GlStateManager.translatef( -0.5f, -0.5f, 0.0f );
drawPrintout( stack );
GlStateManager.enableLighting();
GlStateManager.disableBlend();
}
private static void drawPrintout( ItemStack stack )
{
int pages = ItemPrintout.getPageCount( stack );
private static void drawPrintout(MatrixStack transform, VertexConsumerProvider render, ItemStack stack) {
int pages = ItemPrintout.getPageCount(stack);
boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK;
double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
// Non-books will be left aligned
if( !book ) width += offsetAt( pages );
if (!book) {
width += offsetAt(pages);
}
double visualWidth = width, visualHeight = height;
// Meanwhile books will be centred
if( book )
{
visualWidth += 2 * COVER_SIZE + 2 * offsetAt( pages );
if (book) {
visualWidth += 2 * COVER_SIZE + 2 * offsetAt(pages);
visualHeight += 2 * COVER_SIZE;
}
double max = Math.max( visualHeight, visualWidth );
double max = Math.max(visualHeight, visualWidth);
// Scale the printout to fit correctly.
double scale = 1.0 / max;
GlStateManager.scaled( scale, scale, scale );
GlStateManager.translated( (max - width) / 2.0, (max - height) / 2.0, 0.0 );
float scale = (float) (1.0 / max);
transform.scale(scale, scale, scale);
transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
drawBorder( 0, 0, -0.01, 0, pages, book );
drawText( X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) );
Matrix4f matrix = transform.peek()
.getModel();
drawBorder(matrix, render, 0, 0, -0.01f, 0, pages, book);
drawText(matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText(stack), ItemPrintout.getColours(stack));
}
public boolean renderInFrame(MatrixStack matrixStack, VertexConsumerProvider consumerProvider, ItemStack stack) {
if (!(stack.getItem() instanceof ItemPrintout)) {
return false;
}
// Move a little bit forward to ensure we're not clipping with the frame
matrixStack.translate(0.0f, 0.0f, -0.001f);
matrixStack.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(180f));
matrixStack.scale(0.95f, 0.95f, -0.95f);
matrixStack.translate(-0.5f, -0.5f, 0.0f);
drawPrintout(matrixStack, consumerProvider, stack);
return true;
}
}

View File

@@ -6,265 +6,93 @@
package dan200.computercraft.client.render;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.pipeline.LightUtil;
import net.minecraftforge.client.model.pipeline.VertexTransformer;
import net.minecraftforge.common.model.TRSRTransformation;
import javax.annotation.Nonnull;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;
import java.util.List;
import dan200.computercraft.mixin.BakedQuadAccess;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormatElement;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Quaternion;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/**
* Transforms vertices of a model, remaining aware of winding order, and rearranging
* vertices if needed.
* Transforms vertices of a model, remaining aware of winding order, and rearranging vertices if needed.
*/
public final class ModelTransformer
{
@Environment (EnvType.CLIENT)
public final class ModelTransformer {
private static final Matrix4f identity;
static
{
static {
identity = new Matrix4f();
identity.setIdentity();
identity.loadIdentity();
}
private ModelTransformer()
{
private ModelTransformer() {
}
public static void transformQuadsTo( List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform )
{
if( transform == null || transform.equals( identity ) )
{
output.addAll( input );
}
else
{
Matrix4f normalMatrix = new Matrix4f( transform );
normalMatrix.invert();
normalMatrix.transpose();
public static void transformQuadsTo(List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform) {
transformQuadsTo(VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL, output, input, transform);
}
for( BakedQuad quad : input ) output.add( doTransformQuad( quad, transform, normalMatrix ) );
public static void transformQuadsTo(VertexFormat format, List<BakedQuad> output, List<BakedQuad> input, Matrix4f transform) {
if (transform == null || transform.equals(identity)) {
output.addAll(input);
} else {
for (BakedQuad quad : input) {
output.add(doTransformQuad(format, quad, transform));
}
}
}
public static BakedQuad transformQuad( BakedQuad input, Matrix4f transform )
{
if( transform == null || transform.equals( identity ) ) return input;
Matrix4f normalMatrix = new Matrix4f( transform );
normalMatrix.invert();
normalMatrix.transpose();
return doTransformQuad( input, transform, normalMatrix );
}
private static BakedQuad doTransformQuad( BakedQuad input, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
BakedQuadBuilder builder = new BakedQuadBuilder( input.getFormat() );
NormalAwareTransformer transformer = new NormalAwareTransformer( builder, positionMatrix, normalMatrix );
input.pipe( transformer );
if( transformer.areNormalsInverted() )
private static BakedQuad doTransformQuad(VertexFormat format, BakedQuad quad, Matrix4f transform) {
int[] vertexData = quad.getVertexData()
.clone();
int offset = 0;
BakedQuad copy = new BakedQuad(vertexData, -1, quad.getFace(), ((BakedQuadAccess)quad).getSprite(), true);
for (int i = 0; i < format.getElements()
.size(); ++i) // For each vertex element
{
builder.swap( 1, 3 );
transformer.areNormalsInverted();
}
return builder.build();
}
/**
* A vertex transformer that tracks whether the normals have been inverted and so the vertices
* should be reordered so backface culling works as expected.
*/
private static class NormalAwareTransformer extends VertexTransformer
{
private final Matrix4f positionMatrix;
private final Matrix4f normalMatrix;
private int vertexIndex = 0, elementIndex = 0;
private final Point3f[] before = new Point3f[4];
private final Point3f[] after = new Point3f[4];
public NormalAwareTransformer( IVertexConsumer parent, Matrix4f positionMatrix, Matrix4f normalMatrix )
{
super( parent );
this.positionMatrix = positionMatrix;
this.normalMatrix = normalMatrix;
}
@Override
public void setQuadOrientation( @Nonnull EnumFacing orientation )
{
super.setQuadOrientation( orientation == null ? orientation : TRSRTransformation.rotate( positionMatrix, orientation ) );
}
@Override
public void put( int element, @Nonnull float... data )
{
switch( getVertexFormat().getElement( element ).getUsage() )
VertexFormatElement element = format.getElements()
.get(i);
if (element.getType() == VertexFormatElement.Type.POSITION && element.getFormat() == VertexFormatElement.Format.FLOAT && element.getSize() == 3) // When we find a position
// element
{
case POSITION:
for (int j = 0; j < 4; ++j) // For each corner of the quad
{
Point3f vec = new Point3f( data );
Point3f newVec = new Point3f();
positionMatrix.transform( vec, newVec );
int start = offset + j * format.getVertexSize();
if ((start % 4) == 0) {
start = start / 4;
float[] newData = new float[4];
newVec.get( newData );
super.put( element, newData );
// Extract the position
Quaternion pos = new Quaternion(Float.intBitsToFloat(vertexData[start]),
Float.intBitsToFloat(vertexData[start + 1]),
Float.intBitsToFloat(vertexData[start + 2]),
1);
// Transform the position
transform.multiply(pos);
before[vertexIndex] = vec;
after[vertexIndex] = newVec;
break;
// Insert the position
vertexData[start] = Float.floatToRawIntBits(pos.getX());
vertexData[start + 1] = Float.floatToRawIntBits(pos.getY());
vertexData[start + 2] = Float.floatToRawIntBits(pos.getZ());
}
}
case NORMAL:
{
Vector3f vec = new Vector3f( data );
normalMatrix.transform( vec );
float[] newData = new float[4];
vec.get( newData );
super.put( element, newData );
break;
}
default:
super.put( element, data );
break;
}
elementIndex++;
if( elementIndex == getVertexFormat().getElementCount() )
{
vertexIndex++;
elementIndex = 0;
}
offset += element.getSize();
}
public boolean areNormalsInverted()
{
Vector3f temp1 = new Vector3f(), temp2 = new Vector3f();
Vector3f crossBefore = new Vector3f(), crossAfter = new Vector3f();
// Determine what cross product we expect to have
temp1.sub( before[1], before[0] );
temp2.sub( before[1], before[2] );
crossBefore.cross( temp1, temp2 );
normalMatrix.transform( crossBefore );
// And determine what cross product we actually have
temp1.sub( after[1], after[0] );
temp2.sub( after[1], after[2] );
crossAfter.cross( temp1, temp2 );
// If the angle between expected and actual cross product is greater than
// pi/2 radians then we will need to reorder our quads.
return Math.abs( crossBefore.angle( crossAfter ) ) >= Math.PI / 2;
}
return copy;
}
/**
* A vertex consumer which is capable of building {@link BakedQuad}s.
*
* Equivalent to {@link net.minecraftforge.client.model.pipeline.UnpackedBakedQuad.Builder} but more memory
* efficient.
*
* This also provides the ability to swap vertices through {@link #swap(int, int)} to allow reordering.
*/
private static final class BakedQuadBuilder implements IVertexConsumer
{
private final VertexFormat format;
private final int[] vertexData;
private int vertexIndex = 0, elementIndex = 0;
private EnumFacing orientation;
private int quadTint;
private boolean diffuse;
private TextureAtlasSprite texture;
private BakedQuadBuilder( VertexFormat format )
{
this.format = format;
vertexData = new int[format.getSize()];
}
@Nonnull
@Override
public VertexFormat getVertexFormat()
{
return format;
}
@Override
public void setQuadTint( int tint )
{
quadTint = tint;
}
@Override
public void setQuadOrientation( @Nonnull EnumFacing orientation )
{
this.orientation = orientation;
}
@Override
public void setApplyDiffuseLighting( boolean diffuse )
{
this.diffuse = diffuse;
}
@Override
public void setTexture( @Nonnull TextureAtlasSprite texture )
{
this.texture = texture;
}
@Override
public void put( int element, @Nonnull float... data )
{
LightUtil.pack( data, vertexData, format, vertexIndex, element );
elementIndex++;
if( elementIndex == getVertexFormat().getElementCount() )
{
vertexIndex++;
elementIndex = 0;
}
}
public void swap( int a, int b )
{
int length = vertexData.length / 4;
for( int i = 0; i < length; i++ )
{
int temp = vertexData[a * length + i];
vertexData[a * length + i] = vertexData[b * length + i];
vertexData[b * length + i] = temp;
}
}
public BakedQuad build()
{
if( elementIndex != 0 || vertexIndex != 4 )
{
throw new IllegalStateException( "Got an unexpected number of elements/vertices" );
}
if( texture == null )
{
throw new IllegalStateException( "Texture has not been set" );
}
return new BakedQuad( vertexData, quadTint, orientation, texture, diffuse, format );
public static BakedQuad transformQuad(VertexFormat format, BakedQuad input, Matrix4f transform) {
if (transform == null || transform.equals(identity)) {
return input;
}
return doTransformQuad(format, input, transform);
}
}

View File

@@ -1,117 +1,137 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
import static net.minecraft.util.math.Direction.DOWN;
import static net.minecraft.util.math.Direction.EAST;
import static net.minecraft.util.math.Direction.NORTH;
import static net.minecraft.util.math.Direction.SOUTH;
import static net.minecraft.util.math.Direction.UP;
import static net.minecraft.util.math.Direction.WEST;
import java.util.EnumSet;
import static net.minecraft.util.EnumFacing.*;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
public final class MonitorHighlightRenderer
{
private static final float EXPAND = 0.002f;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
private MonitorHighlightRenderer()
{
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/**
* Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current block. This means you do not get an
* intrusive outline on top of the screen.
*/
@Environment (EnvType.CLIENT)
public final class MonitorHighlightRenderer {
private MonitorHighlightRenderer() {
}
@SubscribeEvent
public static void drawHighlight( DrawBlockHighlightEvent event )
{
if( event.getTarget().type != RayTraceResult.Type.BLOCK || event.getPlayer().isSneaking() ) return;
public static boolean drawHighlight(MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos pos
, BlockState blockState) {
// Preserve normal behaviour when crouching.
if (entity.isInSneakingPose()) {
return false;
}
World world = event.getPlayer().getEntityWorld();
BlockPos pos = event.getTarget().getBlockPos();
World world = entity.getEntityWorld();
TileEntity tile = world.getTileEntity( pos );
if( !(tile instanceof TileMonitor) ) return;
BlockEntity tile = world.getBlockEntity(pos);
if (!(tile instanceof TileMonitor)) {
return false;
}
TileMonitor monitor = (TileMonitor) tile;
event.setCanceled( true );
// Determine which sides are part of the external faces of the monitor, and so which need to be rendered.
EnumSet<EnumFacing> faces = EnumSet.allOf( EnumFacing.class );
EnumFacing front = monitor.getFront();
faces.remove( front );
if( monitor.getXIndex() != 0 ) faces.remove( monitor.getRight().getOpposite() );
if( monitor.getXIndex() != monitor.getWidth() - 1 ) faces.remove( monitor.getRight() );
if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() );
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
EnumSet<Direction> faces = EnumSet.allOf(Direction.class);
Direction front = monitor.getFront();
faces.remove(front);
if (monitor.getXIndex() != 0) {
faces.remove(monitor.getRight()
.getOpposite());
}
if (monitor.getXIndex() != monitor.getWidth() - 1) {
faces.remove(monitor.getRight());
}
if (monitor.getYIndex() != 0) {
faces.remove(monitor.getDown()
.getOpposite());
}
if (monitor.getYIndex() != monitor.getHeight() - 1) {
faces.remove(monitor.getDown());
}
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
GlStateManager.lineWidth(Math.max(2.5F, (float) Minecraft.getInstance().mainWindow.getFramebufferWidth() / 1920.0F * 2.5F));
GlStateManager.disableTexture2D();
GlStateManager.depthMask( false );
GlStateManager.pushMatrix();
EntityPlayer player = event.getPlayer();
double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks();
double y = player.lastTickPosY + (player.posY - player.lastTickPosY) * event.getPartialTicks();
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * event.getPartialTicks();
GlStateManager.translated( -x + pos.getX(), -y + pos.getY(), -z + pos.getZ() );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR );
Vec3d cameraPos = MinecraftClient.getInstance().gameRenderer.getCamera()
.getPos();
matrixStack.push();
matrixStack.translate(pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ());
// I wish I could think of a better way to do this
if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 0, UP );
if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 1, UP );
if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 0, UP );
if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 1, UP );
if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, EAST );
if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 1, EAST );
if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, EAST );
if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 1, EAST );
if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, SOUTH );
if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, 1, 0, 0, SOUTH );
if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, SOUTH );
if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, 1, 1, 0, SOUTH );
Matrix4f transform = matrixStack.peek()
.getModel();
if (faces.contains(NORTH) || faces.contains(WEST)) {
line(vertexConsumer, transform, 0, 0, 0, UP);
}
if (faces.contains(SOUTH) || faces.contains(WEST)) {
line(vertexConsumer, transform, 0, 0, 1, UP);
}
if (faces.contains(NORTH) || faces.contains(EAST)) {
line(vertexConsumer, transform, 1, 0, 0, UP);
}
if (faces.contains(SOUTH) || faces.contains(EAST)) {
line(vertexConsumer, transform, 1, 0, 1, UP);
}
if (faces.contains(NORTH) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 0, 0, 0, EAST);
}
if (faces.contains(SOUTH) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 0, 0, 1, EAST);
}
if (faces.contains(NORTH) || faces.contains(UP)) {
line(vertexConsumer, transform, 0, 1, 0, EAST);
}
if (faces.contains(SOUTH) || faces.contains(UP)) {
line(vertexConsumer, transform, 0, 1, 1, EAST);
}
if (faces.contains(WEST) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 0, 0, 0, SOUTH);
}
if (faces.contains(EAST) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 1, 0, 0, SOUTH);
}
if (faces.contains(WEST) || faces.contains(UP)) {
line(vertexConsumer, transform, 0, 1, 0, SOUTH);
}
if (faces.contains(EAST) || faces.contains(UP)) {
line(vertexConsumer, transform, 1, 1, 0, SOUTH);
}
tessellator.draw();
matrixStack.pop();
GlStateManager.popMatrix();
GlStateManager.depthMask( true );
GlStateManager.enableTexture2D();
GlStateManager.disableBlend();
return true;
}
private static void line( BufferBuilder buffer, int x, int y, int z, EnumFacing direction )
{
double minX = x == 0 ? -EXPAND : 1 + EXPAND;
double minY = y == 0 ? -EXPAND : 1 + EXPAND;
double minZ = z == 0 ? -EXPAND : 1 + EXPAND;
buffer.pos( minX, minY, minZ ).color( 0, 0, 0, 0.4f ).endVertex();
buffer.pos(
minX + direction.getXOffset() * (1 + EXPAND * 2),
minY + direction.getYOffset() * (1 + EXPAND * 2),
minZ + direction.getZOffset() * (1 + EXPAND * 2)
).color( 0, 0, 0, 0.4f ).endVertex();
private static void line(VertexConsumer buffer, Matrix4f transform, float x, float y, float z, Direction direction) {
buffer.vertex(transform, x, y, z)
.color(0, 0, 0, 0.4f)
.next();
buffer.vertex(transform, x + direction.getOffsetX(), y + direction.getOffsetY(), z + direction.getOffsetZ())
.color(0, 0, 0, 0.4f)
.next();
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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 java.io.InputStream;
import java.nio.FloatBuffer;
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 org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import net.minecraft.client.texture.TextureUtil;
import net.minecraft.util.math.Matrix4f;
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.writeToBuffer(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.readAllToString(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;
}
}

View File

@@ -1,180 +1,199 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.GlStateManager.DestFactor;
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
public final class PrintoutRenderer
{
private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" );
private static final double BG_SIZE = 256.0;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderPhase;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Matrix4f;
public final class PrintoutRenderer {
/**
* Width of a page
* Width of a page.
*/
public static final int X_SIZE = 172;
/**
* Height of a page
* Height of a page.
*/
public static final int Y_SIZE = 209;
/**
* Padding between the left and right of a page and the text
* Padding between the left and right of a page and the text.
*/
public static final int X_TEXT_MARGIN = 13;
/**
* Padding between the top and bottom of a page and the text
* Padding between the top and bottom of a page and the text.
*/
public static final int Y_TEXT_MARGIN = 11;
/**
* Width of the extra page texture
*/
private static final int X_FOLD_SIZE = 12;
/**
* Size of the leather cover
* Size of the leather cover.
*/
public static final int COVER_SIZE = 12;
private static final Identifier BG = new Identifier("computercraft", "textures/gui/printout.png");
private static final float BG_SIZE = 256.0f;
/**
* Width of the extra page texture.
*/
private static final int X_FOLD_SIZE = 12;
private static final int COVER_Y = Y_SIZE;
private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE;
private PrintoutRenderer() {}
public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
{
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
fontRenderer.drawString( text[start + line], x, y + line * FONT_HEIGHT, colours[start + line], null, 0, 0, false, Palette.DEFAULT );
public static void drawText(Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours) {
VertexConsumer buffer = renderer.getBuffer(FixedWidthFontRenderer.TYPE);
for (int line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
FixedWidthFontRenderer.drawString(transform,
buffer,
x,
y + line * FONT_HEIGHT,
text[start + line],
colours[start + line],
null,
Palette.DEFAULT,
false,
0,
0);
}
}
public static void drawText( int x, int y, int start, String[] text, String[] colours )
{
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
GlStateManager.enableBlend();
GlStateManager.enableTexture2D();
GlStateManager.blendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO );
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
fontRenderer.drawString( new TextBuffer( text[start + line] ), x, y + line * FONT_HEIGHT, new TextBuffer( colours[start + line] ), null, 0, 0, false, Palette.DEFAULT );
public static void drawText(Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, String[] text, String[] colours) {
VertexConsumer buffer = renderer.getBuffer(FixedWidthFontRenderer.TYPE);
for (int line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
FixedWidthFontRenderer.drawString(transform,
buffer,
x,
y + line * FONT_HEIGHT,
new TextBuffer(text[start + line]),
new TextBuffer(colours[start + line]),
null,
Palette.DEFAULT,
false,
0,
0);
}
}
public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook )
{
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
GlStateManager.enableBlend();
GlStateManager.enableTexture2D();
GlStateManager.blendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO );
Minecraft.getInstance().getTextureManager().bindTexture( BG );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX );
public static void drawBorder(Matrix4f transform, VertexConsumerProvider renderer, float x, float y, float z, int page, int pages, boolean isBook) {
int leftPages = page;
int rightPages = pages - page - 1;
if( isBook )
{
VertexConsumer buffer = renderer.getBuffer(Type.TYPE);
if (isBook) {
// Border
double offset = offsetAt( pages );
final double left = x - 4 - offset;
final double right = x + X_SIZE + offset - 4;
float offset = offsetAt(pages);
float left = x - 4 - offset;
float right = x + X_SIZE + offset - 4;
// Left and right border
drawTexture( buffer, left - 4, y - 8, z - 0.02, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
drawTexture( buffer, right, y - 8, z - 0.02, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
drawTexture(transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2);
drawTexture(transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2);
// Draw centre panel (just stretched texture, sorry).
drawTexture( buffer,
x - offset, y, z - 0.02, X_SIZE + offset * 2, Y_SIZE,
COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE
);
drawTexture(transform,
buffer,
x - offset,
y,
z - 0.02f,
X_SIZE + offset * 2,
Y_SIZE,
COVER_X + COVER_SIZE / 2.0f,
COVER_SIZE,
COVER_SIZE,
Y_SIZE);
double borderX = left;
while( borderX < right )
{
double thisWidth = Math.min( right - borderX, X_SIZE );
drawTexture( buffer, borderX, y - 8, z - 0.02, 0, COVER_Y, thisWidth, COVER_SIZE );
drawTexture( buffer, borderX, y + Y_SIZE - 4, z - 0.02, 0, COVER_Y + COVER_SIZE, thisWidth, COVER_SIZE );
float borderX = left;
while (borderX < right) {
double thisWidth = Math.min(right - borderX, X_SIZE);
drawTexture(transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE);
drawTexture(transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE);
borderX += thisWidth;
}
}
// Left half
drawTexture( buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE );
for( int n = 0; n <= leftPages; n++ )
{
drawTexture( buffer,
x - offsetAt( n ), y, z - 1e-3 * n,
// Use the left "bold" fold for the outermost page
n == leftPages ? 0 : X_FOLD_SIZE, 0,
X_FOLD_SIZE, Y_SIZE
);
drawTexture(transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE);
for (int n = 0; n <= leftPages; n++) {
drawTexture(transform, buffer, x - offsetAt(n), y, z - 1e-3f * n,
// Use the left "bold" fold for the outermost page
n == leftPages ? 0 : X_FOLD_SIZE, 0, X_FOLD_SIZE, Y_SIZE);
}
// Right half
drawTexture( buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE );
for( int n = 0; n <= rightPages; n++ )
{
drawTexture( buffer,
x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3 * n,
// Two folds, then the main page. Use the right "bold" fold for the outermost page.
X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0,
X_FOLD_SIZE, Y_SIZE
);
drawTexture(transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE);
for (int n = 0; n <= rightPages; n++) {
drawTexture(transform, buffer, x + (X_SIZE - X_FOLD_SIZE) + offsetAt(n), y, z - 1e-3f * n,
// Two folds, then the main page. Use the right "bold" fold for the outermost page.
X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, X_FOLD_SIZE, Y_SIZE);
}
tessellator.draw();
}
private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double u, double v, double width, double height )
{
buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
buffer.pos( x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
buffer.pos( x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex();
buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
public static float offsetAt(int page) {
return (float) (32 * (1 - Math.pow(1.2, -page)));
}
private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double width, double height, double u, double v, double tWidth, double tHeight )
{
buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
buffer.pos( x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
buffer.pos( x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex();
buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex();
private static void drawTexture(Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float u, float v, float width, float height) {
buffer.vertex(matrix, x, y + height, z)
.texture(u / BG_SIZE, (v + height) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y + height, z)
.texture((u + width) / BG_SIZE, (v + height) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y, z)
.texture((u + width) / BG_SIZE, v / BG_SIZE)
.next();
buffer.vertex(matrix, x, y, z)
.texture(u / BG_SIZE, v / BG_SIZE)
.next();
}
public static double offsetAt( int page )
{
return 32 * (1 - Math.pow( 1.2, -page ));
private static void drawTexture(Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float width, float height, float u, float v,
float tWidth, float tHeight) {
buffer.vertex(matrix, x, y + height, z)
.texture(u / BG_SIZE, (v + tHeight) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y + height, z)
.texture((u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y, z)
.texture((u + tWidth) / BG_SIZE, v / BG_SIZE)
.next();
buffer.vertex(matrix, x, y, z)
.texture(u / BG_SIZE, v / BG_SIZE)
.next();
}
private static final class Type extends RenderPhase {
static final RenderLayer TYPE = RenderLayer.of("printout_background",
VertexFormats.POSITION_TEXTURE,
GL11.GL_QUADS,
1024,
false,
false,
// useDelegate, needsSorting
RenderLayer.MultiPhaseParameters.builder()
.texture(new RenderPhase.Texture(BG, false, false)) // blur, minimap
.alpha(ONE_TENTH_ALPHA)
.lightmap(DISABLE_LIGHTMAP)
.build(false));
private Type(String name, Runnable setup, Runnable destroy) {
super(name, setup, destroy);
}
}
}

View File

@@ -1,125 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
import java.util.Random;
/**
* Render breaking animation only over part of a {@link TileCable}.
*/
public class TileEntityCableRenderer extends TileEntityRenderer<TileCable>
{
private static final Random random = new Random();
@Override
public void render( @Nonnull TileCable te, double x, double y, double z, float partialTicks, int destroyStage )
{
if( destroyStage < 0 ) return;
BlockPos pos = te.getPos();
Minecraft mc = Minecraft.getInstance();
RayTraceResult hit = mc.objectMouseOver;
if( hit == null || !hit.getBlockPos().equals( pos ) ) return;
if( MinecraftForgeClient.getRenderPass() != 0 ) return;
World world = te.getWorld();
IBlockState state = world.getBlockState( pos );
Block block = state.getBlock();
if( block != ComputerCraft.Blocks.cable ) return;
state = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) )
? block.getDefaultState().with( BlockCable.MODEM, state.get( BlockCable.MODEM ) )
: state.with( BlockCable.MODEM, CableModemVariant.None );
IBakedModel model = mc.getBlockRendererDispatcher().getModelForState( state );
preRenderDamagedBlocks();
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.BLOCK );
buffer.setTranslation( x - pos.getX(), y - pos.getY(), z - pos.getZ() );
buffer.noColor();
ForgeHooksClient.setRenderLayer( block.getRenderLayer() );
// See BlockRendererDispatcher#renderBlockDamage
TextureAtlasSprite breakingTexture = mc.getTextureMap().getSprite( DESTROY_STAGES[destroyStage] );
mc.getBlockRendererDispatcher().getBlockModelRenderer().renderModel(
world,
ForgeHooksClient.getDamageModel( model, breakingTexture, state, world, pos ),
state, pos, buffer, true, random, state.getPositionRandom( pos ), EmptyModelData.INSTANCE
);
ForgeHooksClient.setRenderLayer( BlockRenderLayer.SOLID );
buffer.setTranslation( 0, 0, 0 );
Tessellator.getInstance().draw();
postRenderDamagedBlocks();
}
/**
* @see WorldRenderer#preRenderDamagedBlocks()
*/
private void preRenderDamagedBlocks()
{
GlStateManager.disableLighting();
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate( GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO );
GlStateManager.enableBlend();
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 0.5F );
GlStateManager.polygonOffset( -3.0F, -3.0F );
GlStateManager.enablePolygonOffset();
GlStateManager.alphaFunc( 516, 0.1F );
GlStateManager.enableAlphaTest();
GlStateManager.pushMatrix();
}
/**
* @see WorldRenderer#postRenderDamagedBlocks()
*/
private void postRenderDamagedBlocks()
{
GlStateManager.disableAlphaTest();
GlStateManager.polygonOffset( 0.0F, 0.0F );
GlStateManager.disablePolygonOffset();
GlStateManager.disablePolygonOffset();
GlStateManager.depthMask( true );
GlStateManager.popMatrix();
}
}

View File

@@ -1,48 +1,72 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.getColour;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.platform.GlStateManager;
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 dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
{
@Override
public void render( TileMonitor tileEntity, double posX, double posY, double posZ, float f, int i )
{
if( tileEntity != null )
{
renderMonitorAt( tileEntity, posX, posY, posZ, f, i );
}
import net.minecraft.client.gl.VertexBuffer;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.util.GlAllocationUtils;
import net.minecraft.client.util.math.AffineTransformation;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f;
public class TileEntityMonitorRenderer extends BlockEntityRenderer<TileMonitor> {
/**
* {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between the monitor frame and contents.
*/
private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
private static final Matrix4f IDENTITY = AffineTransformation.identity()
.getMatrix();
private static ByteBuffer tboContents;
public TileEntityMonitorRenderer(BlockEntityRenderDispatcher rendererDispatcher) {
super(rendererDispatcher);
}
private static void renderMonitorAt( TileMonitor monitor, double posX, double posY, double posZ, float f, int i )
{
@Override
public void render(@Nonnull TileMonitor monitor, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer,
int lightmapCoord, int overlayLight) {
// Render from the origin monitor
ClientMonitor originTerminal = monitor.getClientMonitor();
if( originTerminal == null ) return;
if (originTerminal == null) {
return;
}
TileMonitor origin = originTerminal.getOrigin();
BlockPos monitorPos = monitor.getPos();
@@ -50,8 +74,7 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
// multiple times in a single frame to ensure compatibility with shaders which may run a
// pass multiple times.
long renderFrame = FrameInfo.getRenderFrame();
if( originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals( originTerminal.lastRenderPos ) )
{
if (originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals(originTerminal.lastRenderPos)) {
return;
}
@@ -59,234 +82,170 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
originTerminal.lastRenderPos = monitorPos;
BlockPos originPos = origin.getPos();
posX += originPos.getX() - monitorPos.getX();
posY += originPos.getY() - monitorPos.getY();
posZ += originPos.getZ() - monitorPos.getZ();
// Determine orientation
EnumFacing dir = origin.getDirection();
EnumFacing front = origin.getFront();
float yaw = dir.getHorizontalAngle();
float pitch = DirectionUtil.toPitchAngle( front );
Direction dir = origin.getDirection();
Direction front = origin.getFront();
float yaw = dir.asRotation();
float pitch = DirectionUtil.toPitchAngle(front);
GlStateManager.pushMatrix();
try
{
// Setup initial transform
GlStateManager.translated( posX + 0.5, posY + 0.5, posZ + 0.5 );
GlStateManager.rotatef( -yaw, 0.0f, 1.0f, 0.0f );
GlStateManager.rotatef( pitch, 1.0f, 0.0f, 0.0f );
GlStateManager.translated(
-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0,
0.5
);
double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
// Setup initial transform
transform.push();
transform.translate(originPos.getX() - monitorPos.getX() + 0.5,
originPos.getY() - monitorPos.getY() + 0.5,
originPos.getZ() - monitorPos.getZ() + 0.5);
// Get renderers
Minecraft mc = Minecraft.getInstance();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder renderer = tessellator.getBuffer();
transform.multiply(Vector3f.NEGATIVE_Y.getDegreesQuaternion(yaw));
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(pitch));
transform.translate(-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0,
0.5);
double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
// Get terminal
boolean redraw = originTerminal.pollTerminalChanged();
// Draw the contents
Terminal terminal = originTerminal.getTerminal();
if (terminal != null) {
// Draw a terminal
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);
// Draw the contents
GlStateManager.depthMask( false );
OpenGlHelper.glMultiTexCoord2f( OpenGlHelper.GL_TEXTURE1, 0xFFFF, 0xFFFF );
GlStateManager.disableLighting();
mc.gameRenderer.disableLightmap();
try
{
Terminal terminal = originTerminal.getTerminal();
if( terminal != null )
{
Palette palette = terminal.getPalette();
Matrix4f matrix = transform.peek()
.getModel();
// Allocate display lists
if( originTerminal.renderDisplayLists == null )
{
originTerminal.createLists();
redraw = true;
}
// 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.
VertexConsumer buffer = renderer.getBuffer(FixedWidthFontRenderer.TYPE);
FixedWidthFontRenderer.TYPE.startDrawing();
// Draw a terminal
boolean greyscale = !originTerminal.isColour();
int width = terminal.getWidth();
int height = terminal.getHeight();
int cursorX = terminal.getCursorX();
int cursorY = terminal.getCursorY();
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
renderTerminal(matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale));
GlStateManager.pushMatrix();
try
{
double xScale = xSize / (width * FixedWidthFontRenderer.FONT_WIDTH);
double yScale = ySize / (height * FixedWidthFontRenderer.FONT_HEIGHT);
GlStateManager.scaled( xScale, -yScale, 1.0 );
// 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.
FixedWidthFontRenderer.drawCursor(matrix, buffer, 0, 0, terminal, !originTerminal.isColour());
// Draw background
mc.getTextureManager().bindTexture( FixedWidthFontRenderer.BACKGROUND );
if( redraw )
{
// Build background display list
GlStateManager.newList( originTerminal.renderDisplayLists[0], GL11.GL_COMPILE );
try
{
double marginXSize = TileMonitor.RENDER_MARGIN / xScale;
double marginYSize = TileMonitor.RENDER_MARGIN / yScale;
double marginSquash = marginYSize / FixedWidthFontRenderer.FONT_HEIGHT;
// Top and bottom margins
GlStateManager.pushMatrix();
try
{
GlStateManager.scaled( 1.0, marginSquash, 1.0 );
GlStateManager.translated( 0.0, -marginYSize / marginSquash, 0.0 );
fontRenderer.drawStringBackgroundPart( 0, 0, terminal.getBackgroundColourLine( 0 ), marginXSize, marginXSize, greyscale, palette );
GlStateManager.translated( 0.0, (marginYSize + height * FixedWidthFontRenderer.FONT_HEIGHT) / marginSquash, 0.0 );
fontRenderer.drawStringBackgroundPart( 0, 0, terminal.getBackgroundColourLine( height - 1 ), marginXSize, marginXSize, greyscale, palette );
}
finally
{
GlStateManager.popMatrix();
}
// Backgrounds
for( int y = 0; y < height; y++ )
{
fontRenderer.drawStringBackgroundPart(
0, FixedWidthFontRenderer.FONT_HEIGHT * y,
terminal.getBackgroundColourLine( y ),
marginXSize, marginXSize,
greyscale,
palette
);
}
}
finally
{
GlStateManager.endList();
}
}
GlStateManager.callList( originTerminal.renderDisplayLists[0] );
GlStateManager.resetColor();
// Draw text
fontRenderer.bindFont();
if( redraw )
{
// Build text display list
GlStateManager.newList( originTerminal.renderDisplayLists[1], GL11.GL_COMPILE );
try
{
// Lines
for( int y = 0; y < height; y++ )
{
fontRenderer.drawStringTextPart(
0, FixedWidthFontRenderer.FONT_HEIGHT * y,
terminal.getLine( y ),
terminal.getTextColourLine( y ),
greyscale,
palette
);
}
}
finally
{
GlStateManager.endList();
}
}
GlStateManager.callList( originTerminal.renderDisplayLists[1] );
GlStateManager.resetColor();
// Draw cursor
fontRenderer.bindFont();
if( redraw )
{
// Build cursor display list
GlStateManager.newList( originTerminal.renderDisplayLists[2], GL11.GL_COMPILE );
try
{
// Cursor
if( terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height )
{
TextBuffer cursor = new TextBuffer( "_" );
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
cursor,
FixedWidthFontRenderer.FONT_WIDTH * cursorX,
FixedWidthFontRenderer.FONT_HEIGHT * cursorY,
cursorColour, null,
0, 0,
greyscale,
palette
);
}
}
finally
{
GlStateManager.endList();
}
}
if( FrameInfo.getGlobalCursorBlink() )
{
GlStateManager.callList( originTerminal.renderDisplayLists[2] );
GlStateManager.resetColor();
}
}
finally
{
GlStateManager.popMatrix();
}
}
else
{
// Draw a big black quad
mc.getTextureManager().bindTexture( FixedWidthFontRenderer.BACKGROUND );
final Colour colour = Colour.Black;
final float r = colour.getR();
final float g = colour.getG();
final float b = colour.getB();
renderer.begin( GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR );
renderer.pos( -TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0D ).tex( 0.0, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( -TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).tex( 0.0, 1.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( xSize + TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0D ).tex( 1.0, 0.0 ).color( r, g, b, 1.0f ).endVertex();
renderer.pos( xSize + TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).tex( 1.0, 1.0 ).color( r, g, b, 1.0f ).endVertex();
tessellator.draw();
}
}
finally
{
GlStateManager.depthMask( true );
mc.gameRenderer.enableLightmap();
GlStateManager.enableLighting();
}
// Draw the depth blocker
GlStateManager.colorMask( false, false, false, false );
try
{
mc.getTextureManager().bindTexture( FixedWidthFontRenderer.BACKGROUND );
renderer.begin( GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION );
renderer.pos( -TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0 ).endVertex();
renderer.pos( -TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).endVertex();
renderer.pos( xSize + TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0 ).endVertex();
renderer.pos( xSize + TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).endVertex();
tessellator.draw();
}
finally
{
GlStateManager.colorMask( true, true, true, true );
}
transform.pop();
} else {
FixedWidthFontRenderer.drawEmptyTerminal(transform.peek()
.getModel(),
renderer,
-MARGIN,
MARGIN,
(float) (xSize + 2 * MARGIN),
(float) -(ySize + MARGIN * 2));
}
FixedWidthFontRenderer.drawBlocker(transform.peek()
.getModel(),
renderer,
(float) -TileMonitor.RENDER_MARGIN,
(float) TileMonitor.RENDER_MARGIN,
(float) (xSize + 2 * TileMonitor.RENDER_MARGIN),
(float) -(ySize + TileMonitor.RENDER_MARGIN * 2));
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 = GlAllocationUtils.allocateByteBuffer(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.bindBuffers(GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer);
GlStateManager.bufferData(GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW);
GlStateManager.bindBuffers(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, VertexFormats.POSITION);
buffer.vertex(-xMargin, -yMargin, 0)
.next();
buffer.vertex(-xMargin, pixelHeight + yMargin, 0)
.next();
buffer.vertex(pixelWidth + xMargin, -yMargin, 0)
.next();
buffer.vertex(pixelWidth + xMargin, pixelHeight + yMargin, 0)
.next();
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.end();
vbo.upload(builder);
}
vbo.bind();
FixedWidthFontRenderer.TYPE.getVertexFormat()
.startDrawing(0L);
vbo.draw(matrix, FixedWidthFontRenderer.TYPE.getDrawMode());
VertexBuffer.unbind();
FixedWidthFontRenderer.TYPE.getVertexFormat()
.endDrawing();
break;
}
finally
{
GlStateManager.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
GlStateManager.popMatrix();
}
}
}

View File

@@ -1,11 +1,17 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.computer.core.ComputerFamily;
@@ -13,222 +19,198 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelManager;
import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.render.TexturedRenderLayers;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.pipeline.LightUtil;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
import javax.vecmath.Matrix4f;
import java.util.List;
import java.util.Random;
public class TileEntityTurtleRenderer extends BlockEntityRenderer<TileTurtle> {
private static final ModelIdentifier NORMAL_TURTLE_MODEL = new ModelIdentifier("computercraft:turtle_normal", "inventory");
private static final ModelIdentifier ADVANCED_TURTLE_MODEL = new ModelIdentifier("computercraft:turtle_advanced", "inventory");
private static final ModelIdentifier COLOUR_TURTLE_MODEL = new ModelIdentifier("computercraft:turtle_colour", "inventory");
private static final ModelIdentifier ELF_OVERLAY_MODEL = new ModelIdentifier("computercraft:turtle_elf_overlay", "inventory");
public class TileEntityTurtleRenderer extends TileEntityRenderer<TileTurtle>
{
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation( "computercraft:turtle_normal", "inventory" );
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation( "computercraft:turtle_advanced", "inventory" );
private static final ModelResourceLocation COLOUR_TURTLE_MODEL = new ModelResourceLocation( "computercraft:turtle_colour", "inventory" );
private static final ModelResourceLocation ELF_OVERLAY_MODEL = new ModelResourceLocation( "computercraft:turtle_elf_overlay", "inventory" );
private final Random random = new Random(0);
@Override
public void render( TileTurtle tileEntity, double posX, double posY, double posZ, float partialTicks, int breaking )
{
if( tileEntity != null ) renderTurtleAt( tileEntity, posX, posY, posZ, partialTicks );
public TileEntityTurtleRenderer(BlockEntityRenderDispatcher renderDispatcher) {
super(renderDispatcher);
}
public static ModelResourceLocation getTurtleModel( ComputerFamily family, boolean coloured )
{
switch( family )
{
case Normal:
default:
return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
case Advanced:
return coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
public static ModelIdentifier getTurtleModel(ComputerFamily family, boolean coloured) {
switch (family) {
case NORMAL:
default:
return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
case ADVANCED:
return coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
}
}
public static ModelResourceLocation getTurtleOverlayModel( ResourceLocation overlay, boolean christmas )
{
if( overlay != null )
{
return new ModelResourceLocation( overlay, "inventory" );
public static ModelIdentifier getTurtleOverlayModel(Identifier overlay, boolean christmas) {
if (overlay != null) {
return new ModelIdentifier(overlay, "inventory");
}
else if( christmas )
{
if (christmas) {
return ELF_OVERLAY_MODEL;
}
else
{
return null;
return null;
}
private static void renderQuads(@Nonnull MatrixStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight,
List<BakedQuad> quads, int[] tints) {
MatrixStack.Entry matrix = transform.peek();
for (BakedQuad bakedquad : quads) {
int tint = -1;
if (tints != null && bakedquad.hasColor()) {
int idx = bakedquad.getColorIndex();
if (idx >= 0 && idx < tints.length) {
tint = tints[bakedquad.getColorIndex()];
}
}
float f = (float) (tint >> 16 & 255) / 255.0F;
float f1 = (float) (tint >> 8 & 255) / 255.0F;
float f2 = (float) (tint & 255) / 255.0F;
buffer.quad(matrix,
bakedquad,
new float[] {
1.0F,
1.0F,
1.0F,
1.0F
},
f,
f1,
f2,
new int[] {
lightmapCoord,
lightmapCoord,
lightmapCoord,
lightmapCoord
},
overlayLight,
true);
}
}
private void renderTurtleAt( TileTurtle turtle, double posX, double posY, double posZ, float partialTicks )
{
@Override
public void render(@Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer,
int lightmapCoord, int overlayLight) {
// Render the label
String label = turtle.createProxy().getLabel();
if( label != null && rendererDispatcher.cameraHitResult != null && turtle.getPos().equals( rendererDispatcher.cameraHitResult.getBlockPos() ) )
{
setLightmapDisabled( true );
GameRenderer.drawNameplate(
getFontRenderer(), label,
(float) posX + 0.5F, (float) posY + 1.2F, (float) posZ + 0.5F, 0,
rendererDispatcher.entityYaw, rendererDispatcher.entityPitch, false, false
);
setLightmapDisabled( false );
String label = turtle.createProxy()
.getLabel();
HitResult hit = this.dispatcher.crosshairTarget;
if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos()
.equals(((BlockHitResult) hit).getBlockPos())) {
MinecraftClient mc = MinecraftClient.getInstance();
TextRenderer font = mc.textRenderer;
transform.push();
transform.translate(0.5, 1.2, 0.5);
transform.multiply(mc.getEntityRenderDispatcher()
.getRotation());
transform.scale(-0.025f, -0.025f, 0.025f);
Matrix4f matrix = transform.peek()
.getModel();
int opacity = (int) (mc.options.getTextBackgroundOpacity(0.25f) * 255) << 24;
float width = -font.getWidth(label) / 2.0f;
font.draw(label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord);
font.draw(label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord);
transform.pop();
}
GlStateManager.pushMatrix();
try
{
IBlockState state = turtle.getBlockState();
// Setup the transform
Vec3d offset = turtle.getRenderOffset( partialTicks );
float yaw = turtle.getRenderYaw( partialTicks );
GlStateManager.translated( posX + offset.x, posY + offset.y, posZ + offset.z );
// Render the turtle
GlStateManager.translatef( 0.5f, 0.5f, 0.5f );
GlStateManager.rotatef( 180.0f - yaw, 0.0f, 1.0f, 0.0f );
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
{
// Flip the model and swap the cull face as winding order will have changed.
GlStateManager.scalef( 1.0f, -1.0f, 1.0f );
GlStateManager.cullFace( GlStateManager.CullFace.FRONT );
}
GlStateManager.translatef( -0.5f, -0.5f, -0.5f );
// Render the turtle
int colour = turtle.getColour();
ComputerFamily family = turtle.getFamily();
ResourceLocation overlay = turtle.getOverlay();
transform.push();
renderModel( state, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
// Setup the transform.
Vec3d offset = turtle.getRenderOffset(partialTicks);
float yaw = turtle.getRenderYaw(partialTicks);
transform.translate(offset.x, offset.y, offset.z);
// Render the overlay
ModelResourceLocation overlayModel = getTurtleOverlayModel(
overlay,
HolidayUtil.getCurrentHoliday() == Holiday.Christmas
);
if( overlayModel != null )
{
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.blendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
try
{
renderModel( state, overlayModel, null );
}
finally
{
GlStateManager.disableBlend();
GlStateManager.enableCull();
}
}
// Render the upgrades
renderUpgrade( state, turtle, TurtleSide.Left, partialTicks );
renderUpgrade( state, turtle, TurtleSide.Right, partialTicks );
transform.translate(0.5f, 0.5f, 0.5f);
transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(180.0f - yaw));
if (label != null && (label.equals("Dinnerbone") || label.equals("Grumm"))) {
// Flip the model
transform.scale(1.0f, -1.0f, 1.0f);
}
finally
{
GlStateManager.popMatrix();
GlStateManager.cullFace( GlStateManager.CullFace.BACK );
transform.translate(-0.5f, -0.5f, -0.5f);
// Render the turtle
int colour = turtle.getColour();
ComputerFamily family = turtle.getFamily();
Identifier overlay = turtle.getOverlay();
VertexConsumer buffer = renderer.getBuffer(TexturedRenderLayers.getEntityTranslucentCull());
renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[] {colour});
// Render the overlay
ModelIdentifier overlayModel = getTurtleOverlayModel(overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS);
if (overlayModel != null) {
renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null);
}
// Render the upgrades
renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
transform.pop();
}
private void renderUpgrade( IBlockState state, TileTurtle turtle, TurtleSide side, float f )
{
ITurtleUpgrade upgrade = turtle.getUpgrade( side );
if( upgrade != null )
{
GlStateManager.pushMatrix();
try
{
float toolAngle = turtle.getToolRenderAngle( side, f );
GlStateManager.translatef( 0.0f, 0.5f, 0.5f );
GlStateManager.rotatef( -toolAngle, 1.0f, 0.0f, 0.0f );
GlStateManager.translatef( 0.0f, -0.5f, -0.5f );
Pair<IBakedModel, Matrix4f> pair = upgrade.getModel( turtle.getAccess(), side );
if( pair != null )
{
if( pair.getRight() != null )
{
ForgeHooksClient.multiplyCurrentGlMatrix( pair.getRight() );
}
if( pair.getLeft() != null )
{
renderModel( state, pair.getLeft(), null );
}
}
}
finally
{
GlStateManager.popMatrix();
}
public static void renderUpgrade(@Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle,
TurtleSide side, float f) {
ITurtleUpgrade upgrade = turtle.getUpgrade(side);
if (upgrade == null) {
return;
}
transform.push();
float toolAngle = turtle.getToolRenderAngle(side, f);
transform.translate(0.0f, 0.5f, 0.5f);
transform.multiply(Vector3f.NEGATIVE_X.getDegreesQuaternion(toolAngle));
transform.translate(0.0f, -0.5f, -0.5f);
TransformedModel model = upgrade.getModel(turtle.getAccess(), side);
model.push(transform);
TileEntityTurtleRenderer.renderModel(transform, renderer, lightmapCoord, overlayLight, model.getModel(), null);
transform.pop();
transform.pop();
}
private void renderModel( IBlockState state, ModelResourceLocation modelLocation, int[] tints )
{
Minecraft mc = Minecraft.getInstance();
ModelManager modelManager = mc.getItemRenderer().getItemModelMesher().getModelManager();
renderModel( state, modelManager.getModel( modelLocation ), tints );
public static void renderModel(@Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight,
ModelIdentifier modelLocation, int[] tints) {
BakedModelManager modelManager = MinecraftClient.getInstance()
.getItemRenderer()
.getModels()
.getModelManager();
renderModel(transform, renderer, lightmapCoord, overlayLight, modelManager.getModel(modelLocation), tints);
}
private void renderModel( IBlockState state, IBakedModel model, int[] tints )
{
Random random = new Random( 0 );
Tessellator tessellator = Tessellator.getInstance();
rendererDispatcher.textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
renderQuads( tessellator, model.getQuads( state, null, random, EmptyModelData.INSTANCE ), tints );
for( EnumFacing facing : DirectionUtil.FACINGS )
{
renderQuads( tessellator, model.getQuads( state, facing, random, EmptyModelData.INSTANCE ), tints );
public static void renderModel(@Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model,
int[] tints) {
Random random = new Random();
random.setSeed(0);
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, random), tints);
for (Direction facing : DirectionUtil.FACINGS) {
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, random), tints);
}
}
private static void renderQuads( Tessellator tessellator, List<BakedQuad> quads, int[] tints )
{
BufferBuilder buffer = tessellator.getBuffer();
VertexFormat format = DefaultVertexFormats.ITEM;
buffer.begin( GL11.GL_QUADS, format );
for( BakedQuad quad : quads )
{
VertexFormat quadFormat = quad.getFormat();
if( quadFormat != format )
{
tessellator.draw();
format = quadFormat;
buffer.begin( GL11.GL_QUADS, format );
}
int colour = 0xFFFFFFFF;
if( quad.hasTintIndex() && tints != null )
{
int index = quad.getTintIndex();
if( index >= 0 && index < tints.length ) colour = tints[index] | 0xFF000000;
}
LightUtil.renderQuadColor( buffer, quad, colour );
}
tessellator.draw();
}
}

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