1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-01 22:22:59 +00:00

Compare commits

..

65 Commits

Author SHA1 Message Date
Jonathan Coates
159f90896e Merge branch 'mc-1.16.x' into mc-1.18.x 2022-04-27 13:52:11 +01:00
Jonathan Coates
f108ba93af Bump version to 1.100.5
> Modulo any game-breaking bugs [...] this will be the last CC: Tweaked
> release.

Terrible performance is game-breaking right? Or am I just petty?
2022-04-27 13:46:55 +01:00
Jonathan Coates
2a4f75ba15 Use Forge's new sound stream API
- Bump Forge version to latest RB.
 - Generate an 8-bit audio stream again, as we no longer need to be
   compatible with MC's existing streams.

No functionality changes, just mildly less hacky.
2022-04-27 10:59:28 +01:00
Jonathan Coates
ad228e94a3 Merge remote-tracking branch 'origin/mc-1.16.x' into mc-1.16.x
I hate doing this, but I have too many merges in progress to rebase.
2022-04-26 22:43:22 +01:00
Jonathan Coates
42b98bce28 Reset the BufferUploader state on Linux
GlStateManager.glDeleteBuffers clears a buffer before deleting it on
Linux - I assume otherwise there's memory leaks on some drivers? - which
clobbers BufferUploader's cache. Roll our own version which resets the
cache when needed.

Also always reset the cache when deleting/creating a DirectVertexBuffer.
2022-04-26 22:39:34 +01:00
Jonathan Coates
59e3608d2a Merge branch 'mc-1.16.x' into mc-1.18.x
I was right: I did not enjoy this.
2022-04-26 22:17:42 +01:00
Jonathan Coates
fccca22d3f Merge branch 'feature/font-rendering-optimise' into mc-1.16.x
This /significantly/ improves performance of the VBO renderer (3fps to
80fps with 120 constantly changing monitors) and offers some minor FPS
improvements to the TBO renderer.

This also makes the 1.16 rendering code a little more consistent with
the 1.18 code, cleaning it up a little in the process.

Closes #1065 - this is a backport of those changes for 1.16. I will
merge these changes into 1.18, as with everything else (oh boy, that'll
be fun).

Please note this is only tested on my machine right now - any help
testing on other CPU/GPU configurations is much appreciated.
2022-04-26 21:43:53 +01:00
Jonathan Coates
4bfdb65989 Use VBO renderer when Optifine is installed
Historically I've been reluctant to do this as people might be running
Optifine for performance rather than shaders, and the VBO renderer was
significantly slower when monitors were changing.

With the recent performance optimisations, the difference isn't as bad.
Given how many people ask/complain about the TBO renderer and shaders, I
think it's worth doing this, even if it's not as granular as I'd like.

Also changes how we do the monitor backend check. We now only check for
compatibility if BEST is selected - if there's an override, we assume
the user knows what they're doing (a bold assumption, if I may say so
myself).
2022-04-26 21:43:21 +01:00
Jonathan Coates
22e8b9b587 Don't render cursors separately
- For TBOs, we now pass cursor position, colour and blink state as
   variables to the shader, and use them to overlay the cursor texture
   in the right place.

   As we no longer need to render the cursor, we can skip the depth
   buffer, meaning we have to do one fewer upload+draw cycle.

 - For VBOs, we bake the cursor into the main VBO, and switch between
   rendering n and n+1 quads. We still need the depth blocker, but can
   save one upload+draw cycle when the cursor is visible.

This saves significant time on the TBO renderer - somewhere between 4
and 7ms/frame, which bumps us up from 35 to 47fps on my test world (480
full-sized monitors, changing every tick). [Taken on 1.18, but should be
similar on 1.16]
2022-04-26 21:43:21 +01:00
Jonathan Coates
77a00b14ae Use UBOs for the TBO renderer
Like #455, this sets our uniforms via a UBO rather than having separate
ones for each value. There are a couple of small differences:

 - Have a UBO for each monitor, rather than sharing one and rewriting it
   every monitor. This means we only need to update the buffer when the
   monitor changes.

 - Use std140 rather than the default layout. This means we don't have
   to care about location/stride in the buffer.

Also like #455, this doesn't actually seem to result in any performance
improvements for me. However, it does make it a bit easier to handle a
large number of uniforms.

Also cleans up the generation of the main monitor texture buffer:

 - Move buffer generation into a separate method - just ensures that it
   shows up separately in profilers.
 - Explicitly pass the position when setting bytes, rather than
   incrementing the internal one. This saves some memory reads/writes (I
   thought Java optimised them out, evidently not!). Saves a few fps
   when updating.
 - Use DSA when possible. Unclear if it helps at all, but nice to do :).
2022-04-26 21:43:21 +01:00
Jonathan Coates
78aa757549 Memorize getRenderBoundingBox
This takes a non-trivial amount of time on the render thread[^1], so
worth doing.

I don't actually think the allocation is the heavy thing here -
VisualVM says it's toWorldPos being slow. I'm not sure why - possibly
just all the block property lookups? [^2]

[^1]: To be clear, this is with 120 monitors and no other block entities
with custom renderers. so not really representative.

[^2]: I wish I could provide a narrower range, but it varies so much
between me restarting the game. Makes it impossible to benchmark
anything!
2022-04-26 21:43:21 +01:00
Jonathan Coates
1196568a7c Use Ű̶̹̚n̵̦̂́s̷̭̲͐a̶̞͔̔f̸̠́̀e̵͔̋̀ to upload the monitor's contents
The VBO renderer needs to generate a buffer with two quads for each
cell, and then transfer it to the GPU. For large monitors, generating
this buffer can get quite slow. Most of the issues come from
IVertexBuilder (VertexConsumer under MojMap) having a lot of overhead.

By emitting a ByteBuffer directly (and doing so with Unsafe to avoid
bounds checks), we can improve performance 10 fold, going from
3fps/300ms for 120 monitors to 111fps/9ms.

See 41fa95bce4 and #1065 for some more
context and other exploratory work. The key thing to note is we _need_ a
separate version of FWFR for emitting to a ByteBuffer, as introducing
polymorphism to it comes with a significant performance hit.
2022-04-26 21:43:21 +01:00
Jonathan Coates
48c4f397f9 Backport some text rendering code from 1.18
- Move all RenderType instances into a common class.

Cherry-picked from 41fa95bce4:
 - Render GL_QUADS instead of GL_TRIANGLES.

 - Remove any "immediate mode" methods from FWFR. Most use-cases can be
   replaced with the global MultiBufferSource and a proper RenderType
   (which we weren't using correctly before!).

   Only the GUI code (WidgetTerminal) needs to use the immediate mode.

 - Pre-convert palette colours to bytes, storing both the coloured and
   greyscale versions as a byte array.

Cherry-picked from 3eb601e554:
 - Pass lightmap variables around the various renderers. Fixes #919 for
   1.16!
2022-04-26 17:56:43 +01:00
Jonathan Coates
8871f40ced Move FWFR into a separate package
Just a precursor to doing any work on it.
2022-04-26 16:27:49 +01:00
Jonathan Coates
aa62c1f206 Remove redundant CSS in src/web/styles.css
Moved to illuaminate itself
2022-04-26 16:20:04 +01:00
Jonathan Coates
fd32b06d6f Merge pull request #1078 from Hasaabitt/patch-1
Fixed typo
2022-04-24 22:51:21 +01:00
Hasaabitt
739d6813c0 Fixed typo
"Instead, it is a standard program, which its API into the programs that it launches."
becomes
"Instead, it is a standard program, which injects its API into the programs that it launches."
2022-04-24 16:56:31 -04:00
Jonathan Coates
daf81b897a Use vector helpers to convert BlockPos to Vector3d
A little shorter and more explicit than constructing the Vector3d
manually. Fixes an issue where sounds were centered on the bottom left
of speakers, not the middle (see cc-tweaked/cc-restitched#85).
2022-04-22 09:30:04 +01:00
Jonathan Coates
e865d96f7b Fix some typos in the colors API
Closes #1072
2022-04-16 21:14:43 +01:00
Jonathan Coates
79b1872cab Fall back to the given side if the internal one isn't provided
See #1061, closes #1064.

Nobody ever seems to implement this correctly (though it's better than
1.12, at least we've not seen any crashes), and this isn't a fight I
care enough about fighting any more.
2022-04-08 10:12:41 +01:00
Jonathan Coates
41fa95bce4 Cleanup and optimise terminal rendering (#1057)
- Remove the POSITION_COLOR render type. Instead we just render a
   background terminal quad as the pocket computer light - it's a little
   (lot?) more cheaty, but saves having to create a render type.

 - Use the existing position_color_tex shader instead of our copy. I
   looked at using RenderType.text, but had a bunch of problems with GUI
   terminals. Its possible we can fix it, but didn't want to spend too
   much time on it.

 - Remove some methods from FixedWidthFontRenderer, inlining them into
   the call site.

 - Switch back to using GL_QUADS rather than GL_TRIANGLES. I know Lig
   will shout at me for this, but the rest of MC uses QUADS, so I don't
   think best practice really matters here.

 - Fix the TBO backend monitor not rendering monitors with fog.
 
   Unfortunately we can't easily do this to the VBO one without writing
   a custom shader (which defeats the whole point of the VBO backend!),
   as the distance calculation of most render types expect an
   already-transformed position (camera-relative I think!) while we pass
   a world-relative one.

 - When rendering to a VBO we push vertices to a ByteBuffer directly,
   rather than going through MC's VertexConsumer system. This removes
   the overhead which comes with VertexConsumer, significantly improving
   performance.

 - Pre-convert palette colours to bytes, storing both the coloured and
   greyscale versions as a byte array. This allows us to remove the
   multiple casts and conversions (double -> float -> (greyscale) ->
   byte), offering noticeable performance improvements (multiple ms per
   frame).

   We're using a byte[] here rather than a record of three bytes as
   notionally it provides better performance when writing to a
   ByteBuffer directly compared to calling .put() four times. [^1]

 - Memorize getRenderBoundingBox. This was taking about 5% of the total
   time on the render thread[^2], so worth doing.

   I don't actually think the allocation is the heavy thing here -
   VisualVM says it's toWorldPos being slow. I'm not sure why - possibly
   just all the block property lookups? [^2]

Note that none of these changes improve compatibility with Optifine.
Right now there's some serious issues where monitors are writing _over_
blocks in front of them. To fix this, we probably need to remove the
depth blocker and just render characters with a z offset. Will do that
in a separate commit, as I need to evaluate how well that change will
work first.

The main advantage of this commit is the improved performance. In my 
stress test with 120 monitors updating every tick, I'm getting 10-20fps
[^3] (still much worse than TBOs, which manages a solid 60-100).

In practice, we'll actually be much better than this. Our network
bandwidth limits means only 40 change in a single tick - and so FPS is
much more reasonable (+60fps).

[^1]: In general, put(byte[]) is faster than put(byte) multiple times.
Just not clear if this is true when dealing with a small (and loop
unrolled) number of bytes.

[^2]: To be clear, this is with 120 monitors and no other block entities
with custom renderers. so not really representative.

[^3]: I wish I could provide a narrower range, but it varies so much
between me restarting the game. Makes it impossible to benchmark
anything!
2022-04-02 10:54:03 +01:00
Jonathan Coates
2a92794da3 Merge pull request #1055 from bclindner/mc-1.16.x
Documentation fix for rednet.broadcast
2022-03-27 17:40:33 +01:00
Brian C. Lindner
b3e009cca5 doc fix 2022-03-27 16:12:42 +00:00
Jonathan Coates
ba7598c689 Merge branch 'mc-1.17.x' into mc-1.18.x 2022-03-23 08:40:25 +00:00
Jonathan Coates
70c5cbafec Merge branch 'mc-1.16.x' into mc-1.17.x 2022-03-23 08:39:39 +00:00
Jonathan Coates
2c64186965 Bump version to 1.100.4 2022-03-23 08:36:09 +00:00
Jonathan Coates
7731759c77 Bump Forge version one more time 2022-03-23 08:34:57 +00:00
Jonathan Coates
e6339b2847 Update to Forge's latest registry API
Forge 4.0.18 deprecated a lot of methods and moved where
RegistryEvent.NewRegistry lives, so we needed to update. This does break
the CC API a little bit (sorry!) though given Forge 1.18.2 is still in
flux, that's probably inevitable.
2022-03-19 08:45:14 +00:00
Jonathan Coates
31ba17d085 Don't wait for the chunk to be loaded when checking for monitors
There's a couple of alternative ways to solve this. Ideally we'd send
our network messages at the same time as MC does
(ChunkManager.playerLoadedChunk), but this'd require a mixin.

Instead we just rely on the fact that if the chunk isn't loaded,
monitors won't have done anything and so we don't need to send their
contents!

Fixes #1047, probably doesn't cause any regressions. I've not seen any
issues on 1.16, but I also hadn't before so ¯\_(ツ)_/¯.
2022-03-18 19:59:02 +00:00
Jonathan Coates
6353e8d930 Merge branch 'mc-1.16.x' into mc-1.17.x 2022-03-03 10:45:25 +00:00
Jonathan Coates
78cce4981a Merge branch 'mc-1.17.x' into mc-1.18.x 2022-03-03 10:45:25 +00:00
Jonathan Coates
97c953a9be Fix not running the test server during a build 2022-03-03 10:11:17 +00:00
Jonathan Coates
52df7cb8a4 Switch to Forge's game test system
It's now impossible to run the client tests (tests are still there, but
none of the other infrastructure is). We've not run these for months now
due to their severe flakiness :(.
2022-03-03 09:57:36 +00:00
Jonathan Coates
6735cfd12e Update to 1.18.2
Did not enjoy, would not recommend.
2022-03-03 09:17:40 +00:00
Jonathan Coates
bcc7dd6991 Fix typo in Javadoc 2022-03-02 12:54:59 +00:00
Jonathan Coates
f994696161 Merge branch 'mc-1.17.x' into mc-1.18.x 2022-02-28 16:26:16 +00:00
Jonathan Coates
4a4e8bb4b6 Merge branch 'mc-1.16.x' into mc-1.17.x 2022-02-28 16:25:33 +00:00
Jonathan Coates
bd36185662 Bump version
Holding off until Forge releases for 1.18.2
2022-02-28 15:35:16 +00:00
Jonathan Coates
045c4fc88c Merge pull request #1027 from Toad-Dev/issue-1026
Fix large file uploads producing oversized packets.
2022-02-28 11:06:59 +00:00
Jonathan Coates
e0fcc425c6 Prevent id map being null when file is empty
Fixes #1030
2022-02-28 11:01:04 +00:00
Jonathan Coates
e01895d719 Remove turtle_player EntityType
This was added in the 1.13 update and I'm still not sure why. Other mods
seem to get away without it, so I think it's fine to remove.

Also remove the fake net manager, as that's part of Forge nowadays.

Fixes #1044.
2022-02-28 10:34:41 +00:00
Jonathan Coates
87b38f4249 Fix incorrect recipe name in turtle advancement 2022-02-28 10:32:33 +00:00
Toad-Dev
60d1d1bb18 Fix large file uploads producing oversized packets.
- Fixes #1026
- The remaining bytes counter wasn't being decremented, so the code that
  splits off smaller packets was unreachable. Thus all file slices were
  being put into a single UploadFileMessage packet.
2022-01-23 22:31:27 -08:00
Jonathan Coates
cdf8b77ffd Merge pull request #1017 from Possseidon/patch-1
Fix table with mouse button codes in documentation.
2022-01-20 12:40:09 +00:00
Possseidon
e2ce52fe81 Fix table with mouse button codes.
Codes for right and middle mouse buttons were swapped.
2022-01-19 17:39:19 +01:00
Jonathan Coates
9edce36efd Merge branch 'mc-1.17.x' into mc-1.18.x 2022-01-14 23:01:12 +00:00
Jonathan Coates
e05588c662 Merge branch 'mc-1.16.x' into mc-1.17.x 2022-01-14 23:00:17 +00:00
Jonathan Coates
9cf70b10ef Bump version 2022-01-14 22:58:19 +00:00
Jonathan Coates
9ac8f3aeea Fix wired modems having incorrect blockstate
Fixes #1010
2022-01-14 15:37:49 +00:00
Jonathan Coates
e191b08eb5 Use Guava instead of commons-codec for hex encoding
The latter was removed in 1.18 on the server side.

Fixes #1011.
2022-01-14 15:35:58 +00:00
Jonathan Coates
a1221b99e1 Remove debugging log line
Fixes #1014
2022-01-14 14:45:55 +00:00
Jonathan Coates
85bced6b1d Merge pull request #1015 from Paspartout/patch-1
speaker_audio.md: Fix missing add/sum typo
2022-01-14 10:55:08 +00:00
Paspartout
fc4569e0cc speaker_audio.md: Fix missing add/sum typo 2022-01-13 17:36:26 +01:00
Weblate
e7f08313d9 Translations for Danish
Co-authored-by: Christian L.W <christianlw@hotmail.dk>
Co-authored-by: Christian L.W. <christianlw@hotmail.dk>
2022-01-02 23:29:24 +00:00
Jonathan Coates
79366bf2f5 Merge branch 'mc-1.17.x' into mc-1.18.x 2022-01-01 15:41:24 +00:00
Jonathan Coates
2b901f2d5e Merge branch 'mc-1.17.x' into mc-1.18.x 2021-12-25 08:05:25 +00:00
Jonathan Coates
901d8d4c3b Merge branch 'mc-1.17.x' into mc-1.18.x 2021-12-21 15:15:53 +00:00
Jonathan Coates
2562642664 Re-add JEI integration 2021-12-18 11:42:22 +00:00
Jonathan Coates
632db1cfa5 Merge branch 'mc-1.17.x' into mc-1.18.x 2021-12-18 11:38:48 +00:00
Jonathan Coates
f6fcba7a39 Merge branch 'mc-1.17.x' into mc-1.18.x 2021-12-14 20:13:53 +00:00
Jonathan Coates
7c373c6e06 Merge branch 'mc-1.17.x' into mc-1.18.x 2021-12-11 07:50:18 +00:00
Jonathan Coates
57c5d19f95 Update to Forge 1.18.1 2021-12-11 07:31:41 +00:00
Jonathan Coates
23c17075be save -> saveAdditional
Also add in a janky workabround for handleUpdateTag not being called.
Being an early porter is always fun :D:.
2021-12-02 09:20:06 +00:00
Jonathan Coates
87988a705b Exclude Jetbrains annotations from testModExtra
testModExtra must /strictly/ be the set of dependencies which are not
present in implementation - there can't be any duplicates.

Yes, it's stupid, but the whole lazyToken("minecraft_classpath") thing
wasn't really built with this in mind, so not much we can do :)
2021-12-01 20:40:46 +00:00
Jonathan Coates
179da1d8cf Update to MC 1.18
- Build fails right now due to module issues, so this won't be pushed
   to GitHub.
 - Monitors render transparently when loaded into the world. I don't
   think this is a 1.17 bug, so not sure what's going on here!
2021-11-30 22:48:38 +00:00
158 changed files with 1531 additions and 1295 deletions

View File

@@ -5,7 +5,7 @@ buildscript {
maven { url = 'https://maven.parchmentmc.org' }
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.24'
classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+'
classpath "org.spongepowered:mixingradle:0.7.+"
classpath 'org.parchmentmc:librarian:1.+'
}
@@ -31,7 +31,7 @@ version = mod_version
group = "org.squiddev"
archivesBaseName = "cc-tweaked-${mc_version}"
def javaVersion = JavaLanguageVersion.of(16)
def javaVersion = JavaLanguageVersion.of(17)
java {
toolchain {
languageVersion = javaVersion
@@ -88,26 +88,8 @@ minecraft {
args '--mod', 'computercraft', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
}
testClient {
workingDirectory project.file('test-files/client')
parent runs.client
mods {
cctest {
source sourceSets.testMod
}
}
lazyToken('minecraft_classpath') {
(configurations.shade.copyRecursive().resolve() + configurations.testModExtra.copyRecursive().resolve())
.collect { it.absolutePath }
.join(File.pathSeparator)
}
}
testServer {
gameTestServer {
workingDirectory project.file('test-files/server')
parent runs.server
mods {
cctest {
@@ -158,9 +140,8 @@ dependencies {
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
annotationProcessor 'org.spongepowered:mixin:0.8.4:processor'
compileOnly fg.deobf("mezz.jei:jei-1.17.1:8.0.0.14:api")
runtimeOnly fg.deobf("mezz.jei:jei-1.17.1:8.0.0.14")
compileOnly fg.deobf("mezz.jei:jei-1.18.2:9.4.1.116:api")
runtimeOnly fg.deobf("mezz.jei:jei-1.18.2:9.4.1.116")
shade 'org.squiddev:Cobalt:0.5.2-SNAPSHOT'
@@ -172,7 +153,9 @@ dependencies {
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
testModImplementation sourceSets.main.output
testModExtra 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
testModExtra('org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0') {
exclude group: "org.jetbrains", module: "annotations"
}
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.5'
}
@@ -372,69 +355,49 @@ task licenseFormatAPI(type: LicenseFormat)
}
}
task setupServer(type: Copy) {
group "test server"
description "Sets up the environment for the test server."
tasks.register("testServer", JavaExec.class).configure {
it.group('In-game tests')
it.description("Runs tests on a temporary Minecraft instance.")
it.dependsOn("prepareRunGameTestServer", "cleanTestServer", 'compileTestModJava')
from("src/testMod/server-files") {
include "eula.txt"
include "server.properties"
}
into "test-files/server"
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
// isn't created until the task is configured (which is no good for us).
JavaExec exec = tasks.getByName("runGameTestServer")
exec.copyTo(it)
it.setClasspath(exec.getClasspath())
it.mainClass = exec.mainClass
it.setArgs(exec.getArgs())
// Jacoco and modlauncher don't play well together as the classes loaded in-game don't
// match up with those written to disk. We get Jacoco to dump all classes to disk, and
// use that when generating the report.
def coverageOut = new File(buildDir, "jacocoClassDump/testServer")
jacoco.applyTo(it)
it.jacoco.setIncludes(["dan200.computercraft.*"])
it.jacoco.setClassDumpDir(coverageOut)
it.outputs.dir(coverageOut)
// Older versions of modlauncher don't include a protection domain (and thus no code
// source). Jacoco skips such classes by default, so we need to explicitly include them.
it.jacoco.setIncludeNoLocationClasses(true)
}
["Client", "Server"].forEach { name ->
tasks.register("test$name", JavaExec.class).configure {
it.group('In-game tests')
it.description("Runs tests on a temporary Minecraft instance.")
it.dependsOn(setupServer, "prepareRunTest$name", "cleanTest$name", 'compileTestModJava')
tasks.register("jacocoTestServerReport", JacocoReport.class).configure {
it.group('In-game')
it.description("Generate coverage reports for testServer")
it.dependsOn("testServer")
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
// isn't created until the task is configured (which is no good for us).
JavaExec exec = tasks.getByName("runTest$name")
exec.copyTo(it)
it.setClasspath(exec.getClasspath())
it.mainClass = exec.mainClass
it.setArgs(exec.getArgs())
it.executionData(new File(buildDir, "jacoco/testServer.exec"))
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
it.classDirectories.from(new File(buildDir, "jacocoClassDump/testServer"))
it.systemProperty('forge.logging.console.level', 'info')
it.systemProperty('cctest.run', 'true')
// Jacoco and modlauncher don't play well together as the classes loaded in-game don't
// match up with those written to disk. We get Jacoco to dump all classes to disk, and
// use that when generating the report.
def coverageOut = new File(buildDir, "jacocoClassDump/test$name")
jacoco.applyTo(it)
it.jacoco.setIncludes(["dan200.computercraft.*"])
it.jacoco.setClassDumpDir(coverageOut)
it.outputs.dir(coverageOut)
// Older versions of modlauncher don't include a protection domain (and thus no code
// source). Jacoco skips such classes by default, so we need to explicitly include them.
it.jacoco.setIncludeNoLocationClasses(true)
}
tasks.register("jacocoTest${name}Report", JacocoReport.class).configure {
it.group('In-game')
it.description("Generate coverage reports for test$name")
it.dependsOn("test$name")
it.executionData(new File(buildDir, "jacoco/test${name}.exec"))
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
it.classDirectories.from(new File(buildDir, "jacocoClassDump/test$name"))
it.reports {
xml.enabled true
html.enabled true
}
}
if (name != "Client" || project.findProperty('cc.tweaked.clientTests') == 'true') {
// Don't run client tests unless explicitly opted into them. They're a bit of a faff
// to run and pretty flakey.
check.dependsOn("jacocoTest${name}Report")
it.reports {
xml.enabled true
html.enabled true
}
}
check.dependsOn("jacocoTestServerReport")
// Upload tasks

View File

@@ -19,8 +19,8 @@ numerical value depending on which button on your mouse was last pressed when th
<!-- Our markdown parser doesn't work on tables!? Guess I'll have to roll my own soonish :/. -->
<tr><th>Button code</th><th>Mouse button</th></tr>
<tr><td align="right">1</td><td>Left button</td></tr>
<tr><td align="right">2</td><td>Middle button</td></tr>
<tr><td align="right">3</td><td>Right button</td></tr>
<tr><td align="right">2</td><td>Right button</td></tr>
<tr><td align="right">3</td><td>Middle button</td></tr>
</table>
## Example

View File

@@ -18,7 +18,7 @@ representable value.
This representation of sound - a long, uniformally sampled list of amplitudes is referred to as [Pulse-code
Modulation][PCM] (PCM). PCM can be thought of as the "standard" audio format, as it's incredibly easy to work with. For
instance, to mix two pieces of audio together, you can just samples from the two tracks together and take the average.
instance, to mix two pieces of audio together, you can just add samples from the two tracks together and take the average.
CC: Tweaked's speakers also work with PCM audio. It plays back 48,000 samples a second, where each sample is an integer
between -128 and 127. This is more commonly referred to as 48kHz and an 8-bit resolution.

View File

@@ -1,10 +1,10 @@
org.gradle.jvmargs=-Xmx3G
# Mod properties
mod_version=1.100.1
mod_version=1.100.5
# Minecraft properties (update mods.toml when changing)
mc_version=1.17.1
mapping_version=2021.09.05
forge_version=37.0.85
mc_version=1.18.2
mapping_version=2022.03.13
forge_version=40.1.0
# NO SERIOUSLY, UPDATE mods.toml WHEN CHANGING

View File

@@ -22,8 +22,7 @@
"cable": "true"
}
}
],
"functions": []
]
},
{
"name": "wired_modem",
@@ -49,8 +48,7 @@
}
}
}
],
"functions": []
]
}
]
}

View File

@@ -29,8 +29,7 @@
}
]
}
],
"functions": []
]
}
]
}

View File

@@ -29,8 +29,7 @@
}
]
}
],
"functions": []
]
}
]
}

View File

@@ -29,8 +29,7 @@
}
]
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -29,8 +29,7 @@
}
]
}
],
"functions": []
]
}
]
}

View File

@@ -29,8 +29,7 @@
}
]
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -15,8 +15,7 @@
{
"condition": "minecraft:survives_explosion"
}
],
"functions": []
]
}
]
}

View File

@@ -30,7 +30,6 @@ import dan200.computercraft.shared.wired.WiredNode;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
@@ -38,7 +37,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fmllegacy.server.ServerLifecycleHooks;
import net.minecraftforge.server.ServerLifecycleHooks;
import javax.annotation.Nonnull;
import java.io.File;
@@ -59,7 +58,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
public static InputStream getResourceFile( String domain, String subPath )
{
ReloadableResourceManager manager = (ReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getResourceManager();
var manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();
try
{
return manager.getResource( new ResourceLocation( domain, subPath ) ).getInputStream();

View File

@@ -111,7 +111,7 @@ public final class ComputerCraftAPI
}
/**
* rers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
* Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
*
* @param provider The peripheral provider to register.
* @see IPeripheral

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.ComputerCraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.Tag;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
@@ -20,47 +20,47 @@ public class ComputerCraftTags
{
public static class Items
{
public static final Tag.Named<Item> COMPUTER = make( "computer" );
public static final Tag.Named<Item> TURTLE = make( "turtle" );
public static final Tag.Named<Item> WIRED_MODEM = make( "wired_modem" );
public static final Tag.Named<Item> MONITOR = make( "monitor" );
public static final TagKey<Item> COMPUTER = make( "computer" );
public static final TagKey<Item> TURTLE = make( "turtle" );
public static final TagKey<Item> WIRED_MODEM = make( "wired_modem" );
public static final TagKey<Item> MONITOR = make( "monitor" );
private static Tag.Named<Item> make( String name )
private static TagKey<Item> make( String name )
{
return ItemTags.bind( new ResourceLocation( ComputerCraft.MOD_ID, name ).toString() );
return ItemTags.create( new ResourceLocation( ComputerCraft.MOD_ID, name ) );
}
}
public static class Blocks
{
public static final Tag.Named<Block> COMPUTER = make( "computer" );
public static final Tag.Named<Block> TURTLE = make( "turtle" );
public static final Tag.Named<Block> WIRED_MODEM = make( "wired_modem" );
public static final Tag.Named<Block> MONITOR = make( "monitor" );
public static final TagKey<Block> COMPUTER = make( "computer" );
public static final TagKey<Block> TURTLE = make( "turtle" );
public static final TagKey<Block> WIRED_MODEM = make( "wired_modem" );
public static final TagKey<Block> MONITOR = make( "monitor" );
/**
* Blocks which can be broken by any turtle tool.
*/
public static final Tag.Named<Block> TURTLE_ALWAYS_BREAKABLE = make( "turtle_always_breakable" );
public static final TagKey<Block> TURTLE_ALWAYS_BREAKABLE = make( "turtle_always_breakable" );
/**
* Blocks which can be broken by the default shovel tool.
*/
public static final Tag.Named<Block> TURTLE_SHOVEL_BREAKABLE = make( "turtle_shovel_harvestable" );
public static final TagKey<Block> TURTLE_SHOVEL_BREAKABLE = make( "turtle_shovel_harvestable" );
/**
* Blocks which can be broken with the default sword tool.
*/
public static final Tag.Named<Block> TURTLE_SWORD_BREAKABLE = make( "turtle_sword_harvestable" );
public static final TagKey<Block> TURTLE_SWORD_BREAKABLE = make( "turtle_sword_harvestable" );
/**
* Blocks which can be broken with the default hoe tool.
*/
public static final Tag.Named<Block> TURTLE_HOE_BREAKABLE = make( "turtle_hoe_harvestable" );
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make( "turtle_hoe_harvestable" );
private static Tag.Named<Block> make( String name )
private static TagKey<Block> make( String name )
{
return BlockTags.bind( new ResourceLocation( ComputerCraft.MOD_ID, name ).toString() );
return BlockTags.create( new ResourceLocation( ComputerCraft.MOD_ID, name ) );
}
}
}

View File

@@ -25,6 +25,6 @@ public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPoc
{
public PocketUpgradeDataProvider( @Nonnull DataGenerator generator )
{
super( generator, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.TYPE );
super( generator, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID );
}
}

View File

@@ -5,10 +5,13 @@
*/
package dan200.computercraft.api.pocket;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
@@ -33,11 +36,12 @@ import java.util.function.Function;
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T, PocketUpgradeSerialiser<?>>
{
/**
* A {@link Class} representing an abstract {@link PocketUpgradeSerialiser}. This is largely intended for use with
* Forge Registry methods/classes, such as {@link DeferredRegister} and {@link RegistryManager#getRegistry(Class)}.
* The ID for the associated registry.
*
* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and
* {@link RegistryManager#getRegistry(ResourceKey)}.
*/
@SuppressWarnings( "unchecked" )
Class<PocketUpgradeSerialiser<?>> TYPE = (Class<PocketUpgradeSerialiser<?>>) (Class<?>) PocketUpgradeSerialiser.class;
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_upgrade_serialiser" ) );
/**
* A convenient base class to inherit to implement {@link PocketUpgradeSerialiser}.

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.Tag;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
@@ -34,7 +34,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
public TurtleUpgradeDataProvider( DataGenerator generator )
{
super( generator, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.TYPE );
super( generator, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID );
}
/**
@@ -42,7 +42,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
*
* @param id The ID of this tool.
* @param item The item used for tool actions. Note, this doesn't inherit all properties of the tool, you may need
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(Tag.Named)}.
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(TagKey)}.
* @return A tool builder,
*/
@Nonnull
@@ -64,7 +64,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
private String adjective;
private Item craftingItem;
private Float damageMultiplier = null;
private Tag.Named<Block> breakable;
private TagKey<Block> breakable;
ToolBuilder( ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem )
{
@@ -123,7 +123,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
* @return The tool builder, for further use.
* @see ComputerCraftTags.Blocks
*/
public ToolBuilder breakable( @Nonnull Tag.Named<Block> breakable )
public ToolBuilder breakable( @Nonnull TagKey<Block> breakable )
{
this.breakable = breakable;
return this;
@@ -141,7 +141,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
if( adjective != null ) s.addProperty( "adjective", adjective );
if( craftingItem != null ) s.addProperty( "craftItem", craftingItem.getRegistryName().toString() );
if( damageMultiplier != null ) s.addProperty( "damageMultiplier", damageMultiplier );
if( breakable != null ) s.addProperty( "breakable", breakable.getName().toString() );
if( breakable != null ) s.addProperty( "breakable", breakable.location().toString() );
} ) );
}
}

View File

@@ -5,10 +5,13 @@
*/
package dan200.computercraft.api.turtle;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
@@ -61,11 +64,12 @@ import java.util.function.Function;
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T, TurtleUpgradeSerialiser<?>>
{
/**
* A {@link Class} representing an abstract {@link TurtleUpgradeSerialiser}. This is largely intended for use with
* Forge Registry methods/classes, such as {@link DeferredRegister} and {@link RegistryManager#getRegistry(Class)}.
* The ID for the associated registry.
*
* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and
* {@link RegistryManager#getRegistry(ResourceKey)}.
*/
@SuppressWarnings( "unchecked" )
Class<TurtleUpgradeSerialiser<?>> TYPE = (Class<TurtleUpgradeSerialiser<?>>) (Class<?>) TurtleUpgradeSerialiser.class;
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_upgrade_serialiser" ) );
/**
* A convenient base class to inherit to implement {@link TurtleUpgradeSerialiser}.

View File

@@ -12,9 +12,11 @@ import com.google.gson.JsonParseException;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.HashCache;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.registries.RegistryManager;
@@ -43,16 +45,16 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
private final DataGenerator generator;
private final String name;
private final String folder;
private final Class<R> klass;
private final ResourceKey<Registry<R>> registry;
private List<T> upgrades;
protected UpgradeDataProvider( @Nonnull DataGenerator generator, @Nonnull String name, @Nonnull String folder, @Nonnull Class<R> klass )
protected UpgradeDataProvider( @Nonnull DataGenerator generator, @Nonnull String name, @Nonnull String folder, @Nonnull ResourceKey<Registry<R>> registry )
{
this.generator = generator;
this.name = name;
this.folder = folder;
this.klass = klass;
this.registry = registry;
}
/**
@@ -155,8 +157,8 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
@Nonnull
public final R existingSerialiser( @Nonnull ResourceLocation id )
{
var result = RegistryManager.ACTIVE.getRegistry( klass ).getValue( id );
if( result == null ) throw new IllegalArgumentException( "No such serialiser " + klass );
var result = RegistryManager.ACTIVE.getRegistry( registry ).getValue( id );
if( result == null ) throw new IllegalArgumentException( "No such serialiser " + registry );
return result;
}

View File

@@ -10,7 +10,6 @@ import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
@@ -30,9 +29,8 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.client.event.EntityRenderersEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ForgeModelBakery;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@@ -76,7 +74,7 @@ public final class ClientRegistry
ModelLoaderRegistry.registerLoader( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ), TurtleModelLoader.INSTANCE );
for( String model : EXTRA_MODELS )
{
ModelLoader.addSpecialModel( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ) );
ForgeModelBakery.addSpecialModel( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ) );
}
}
@@ -122,12 +120,6 @@ public final class ClientRegistry
);
}
@SubscribeEvent
public static void registerEntityRenderers( EntityRenderersEvent.RegisterRenderers event )
{
event.registerEntityRenderer( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
}
@SubscribeEvent
public static void setupClient( FMLClientSetupEvent event )
{

View File

@@ -1,288 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.RenderTypes;
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.renderer.MultiBufferSource;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
/**
* Handles rendering fixed width text and computer terminals.
*
* This class has several modes of usage:
* <ul>
* <li>{@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods,
* this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.</li>
* <li>{@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor
* separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically.
* </li>
* <li>{@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the
* whole term.</li>
* <li>{@link #drawBlocker}: When rendering a terminal using {@link RenderTypes#TERMINAL_WITHOUT_DEPTH} you need to
* render an additional "depth blocker" on top of the monitor.</li>
* </ul>
*/
public final class FixedWidthFontRenderer
{
public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
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 FixedWidthFontRenderer()
{
}
public static float toGreyscale( double[] rgb )
{
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
}
public static int getColour( char c, Colour def )
{
return 15 - Terminal.getColour( c, def );
}
private static void drawChar( Matrix4f transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b, int light )
{
// 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;
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
buffer.vertex( transform, x, y, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, yStart / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex();
}
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 ).uv( BACKGROUND_START, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.vertex( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_END ).endVertex();
}
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];
}
drawQuad( transform, buffer, x, y, width, height, 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 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, int light
)
{
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, light );
}
}
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, FULL_BRIGHT_LIGHTMAP
);
}
}
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];
}
drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b, FULL_BRIGHT_LIGHTMAP );
}
}
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 static void drawTerminal(
@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
VertexConsumer buffer = renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
renderer.endBatch();
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull MultiBufferSource renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height )
{
MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
drawEmptyTerminal( transform, renderer, x, y, width, height );
renderer.endBatch();
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull MultiBufferSource renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
}

View File

@@ -10,7 +10,6 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import net.minecraft.network.chat.Component;
@@ -19,6 +18,7 @@ import net.minecraft.world.entity.player.Inventory;
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
public final class GuiComputer<T extends ContainerComputerBase> extends ComputerScreenBase<T>
{
@@ -78,7 +78,7 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Computer
// Draw a border around the terminal
ComputerBorderRenderer.render(
ComputerBorderRenderer.getTexture( family ), terminal.x, terminal.y, getBlitOffset(),
RenderTypes.FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
);
ComputerSidebar.renderBackground( stack, leftPos, topPos + sidebarYOffset );
}

View File

@@ -71,12 +71,12 @@ public class DynamicImageButton extends Button
RenderSystem.disableDepthTest();
int yTex = yTexStart;
if( isHovered() ) yTex += yDiffTex;
if( isHoveredOrFocused() ) yTex += yDiffTex;
blit( stack, x, y, xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight );
RenderSystem.enableDepthTest();
if( isHovered() ) renderToolTip( stack, mouseX, mouseY );
if( isHovered ) renderToolTip( stack, mouseX, mouseY );
}
@Nonnull

View File

@@ -6,23 +6,26 @@
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.TextComponent;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.util.BitSet;
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.MARGIN;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
public class WidgetTerminal extends AbstractWidget
{
@@ -315,14 +318,24 @@ public class WidgetTerminal extends AbstractWidget
if( !visible ) return;
Matrix4f matrix = transform.last().pose();
Terminal terminal = computer.getTerminal();
var bufferSource = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
var emitter = FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) );
if( terminal != null )
{
FixedWidthFontRenderer.drawTerminal( matrix, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
boolean greyscale = !computer.isColour();
FixedWidthFontRenderer.drawTerminal(
emitter,
(float) innerX, (float) innerY, terminal, greyscale, (float) MARGIN, (float) MARGIN, (float) MARGIN, (float) MARGIN
);
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, x, y, width, height );
FixedWidthFontRenderer.drawEmptyTerminal( emitter, (float) x, (float) y, (float) width, (float) height );
}
bufferSource.endBatch();
}
@Override

View File

@@ -45,8 +45,8 @@ public final class CableHighlightRenderer
{
BlockHitResult hit = event.getTarget();
BlockPos pos = hit.getBlockPos();
Level world = event.getInfo().getEntity().getCommandSenderWorld();
Camera info = event.getInfo();
Level world = event.getCamera().getEntity().getCommandSenderWorld();
Camera info = event.getCamera();
BlockState state = world.getBlockState( pos );
@@ -67,9 +67,9 @@ public final class CableHighlightRenderer
double yOffset = pos.getY() - cameraPos.y();
double zOffset = pos.getZ() - cameraPos.z();
VertexConsumer buffer = event.getBuffers().getBuffer( RenderType.lines() );
Matrix4f matrix4f = event.getMatrix().last().pose();
Matrix3f normal = event.getMatrix().last().normal();
VertexConsumer buffer = event.getMultiBufferSource().getBuffer( RenderType.lines() );
Matrix4f matrix4f = event.getPoseStack().last().pose();
Matrix3f normal = event.getPoseStack().last().normal();
// TODO: Can we just accesstransformer out LevelRenderer.renderShape?
shape.forAllEdges( ( x1, y1, z1, x2, y2, z2 ) -> {
float xDelta = (float) (x2 - x1);

View File

@@ -70,7 +70,6 @@ public class ComputerBorderRenderer
this.b = b;
}
@Nonnull
public static ResourceLocation getTexture( @Nonnull ComputerFamily family )
{

View File

@@ -10,7 +10,7 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
@@ -24,9 +24,9 @@ import net.minecraftforge.client.event.RenderHandEvent;
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.ComputerBorderRenderer.*;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
/**
* Emulates map rendering for pocket computers.
@@ -48,13 +48,13 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
event.setCanceled( true );
INSTANCE.renderItemFirstPerson(
event.getMatrixStack(), event.getBuffers(), event.getLight(),
event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
);
}
@Override
protected void renderItem( PoseStack transform, MultiBufferSource renderer, ItemStack stack, int light )
protected void renderItem( PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal();
@@ -91,24 +91,30 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
int frameColour = item.getColour( stack );
Matrix4f matrix = transform.last().pose();
renderFrame( matrix, renderer, family, frameColour, light, width, height );
renderFrame( matrix, bufferSource, family, frameColour, light, width, height );
// Render the light
int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.BLACK.getHex();
renderLight( matrix, renderer, lightColour, width, height );
renderLight( matrix, bufferSource, lightColour, width, height );
if( computer != null && terminal != null )
{
FixedWidthFontRenderer.drawTerminal(
matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ),
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ) ),
MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN
);
FixedWidthFontRenderer.drawBlocker( transform.last().pose(), renderer, 0, 0, width, height );
FixedWidthFontRenderer.drawBlocker(
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ),
0, 0, width, height
);
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, renderer, 0, 0, width, height );
FixedWidthFontRenderer.drawEmptyTerminal(
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) ),
0, 0, width, height
);
}
transform.popPose();
@@ -127,15 +133,16 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
private static void renderLight( Matrix4f transform, MultiBufferSource render, int colour, int width, int height )
{
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
float z = 0.001f;
byte r = (byte) ((colour >>> 16) & 0xFF);
byte g = (byte) ((colour >>> 8) & 0xFF);
byte b = (byte) (colour & 0xFF);
byte[] c = new byte[] { r, g, b, (byte) 255 };
VertexConsumer buffer = render.getBuffer( RenderTypes.POSITION_COLOR );
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
VertexConsumer buffer = render.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
FixedWidthFontRenderer.drawQuad(
FixedWidthFontRenderer.toVertexConsumer( transform, buffer ),
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
);
}
}

View File

@@ -19,8 +19,8 @@ import net.minecraftforge.client.event.RenderItemInFrameEvent;
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.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
@@ -45,7 +45,7 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
event.setCanceled( true );
INSTANCE.renderItemFirstPerson(
event.getMatrixStack(), event.getBuffers(), event.getLight(),
event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
);
}
@@ -63,11 +63,11 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
@SubscribeEvent
public static void onRenderInFrame( RenderItemInFrameEvent event )
{
ItemStack stack = event.getItem();
ItemStack stack = event.getItemStack();
if( !(stack.getItem() instanceof ItemPrintout) ) return;
event.setCanceled( true );
PoseStack transform = event.getMatrix();
PoseStack transform = event.getPoseStack();
// Move a little bit forward to ensure we're not clipping with the frame
transform.translate( 0.0f, 0.0f, -0.001f );
@@ -75,8 +75,8 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
transform.scale( 0.95f, 0.95f, -0.95f );
transform.translate( -0.5f, -0.5f, 0.0f );
int light = event.getEntityItemFrame().getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : event.getLight(); // See getLightVal.
drawPrintout( transform, event.getBuffers(), stack, light );
int light = event.getItemFrameEntity().getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : event.getPackedLight(); // See getLightVal.
drawPrintout( transform, event.getMultiBufferSource(), stack, light );
}
private static void drawPrintout( PoseStack transform, MultiBufferSource render, ItemStack stack, int light )

View File

@@ -41,9 +41,9 @@ public final class MonitorHighlightRenderer
public static void drawHighlight( DrawSelectionEvent.HighlightBlock event )
{
// Preserve normal behaviour when crouching.
if( event.getInfo().getEntity().isCrouching() ) return;
if( event.getCamera().getEntity().isCrouching() ) return;
Level world = event.getInfo().getEntity().getCommandSenderWorld();
Level world = event.getCamera().getEntity().getCommandSenderWorld();
BlockPos pos = event.getTarget().getBlockPos();
BlockEntity tile = world.getBlockEntity( pos );
@@ -60,13 +60,13 @@ public final class MonitorHighlightRenderer
if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() );
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
PoseStack transformStack = event.getMatrix();
Vec3 cameraPos = event.getInfo().getPosition();
PoseStack transformStack = event.getPoseStack();
Vec3 cameraPos = event.getCamera().getPosition();
transformStack.pushPose();
transformStack.translate( pos.getX() - cameraPos.x(), pos.getY() - cameraPos.y(), pos.getZ() - cameraPos.z() );
// I wish I could think of a better way to do this
VertexConsumer buffer = event.getBuffers().getBuffer( RenderType.lines() );
VertexConsumer buffer = event.getMultiBufferSource().getBuffer( RenderType.lines() );
Matrix4f transform = transformStack.last().pose();
Matrix3f normal = transformStack.last().normal();
if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, transform, normal, 0, 0, 0, UP );

View File

@@ -7,95 +7,63 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.shaders.Uniform;
import com.mojang.blaze3d.vertex.VertexFormat;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.shared.util.Palette;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL31;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.ByteBuffer;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour;
public class MonitorTextureBufferShader extends ShaderInstance
{
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
private static final Logger LOGGER = LogManager.getLogger();
private final Uniform palette;
private final Uniform width;
private final Uniform height;
private final int monitorData;
private int uniformBuffer = 0;
private final Uniform cursorBlink;
public MonitorTextureBufferShader( ResourceProvider provider, ResourceLocation location, VertexFormat format ) throws IOException
{
super( provider, location, format );
monitorData = GL31.glGetUniformBlockIndex( getId(), "MonitorData" );
if( monitorData == -1 ) throw new IllegalStateException( "Could not find MonitorData uniform." );
width = getUniformChecked( "Width" );
height = getUniformChecked( "Height" );
palette = new Uniform( "Palette", Uniform.UT_FLOAT3, 16 * 3, this );
updateUniformLocation( palette );
cursorBlink = getUniformChecked( "CursorBlink" );
Uniform tbo = getUniformChecked( "Tbo" );
if( tbo != null ) tbo.set( TEXTURE_INDEX - GL13.GL_TEXTURE0 );
}
void setupUniform( int width, int height, Palette palette, boolean greyscale )
public void setupUniform( int buffer )
{
if( this.width != null ) this.width.set( width );
if( this.height != null ) this.height.set( height );
setupPalette( palette, greyscale );
}
uniformBuffer = buffer;
private void setupPalette( Palette palette, boolean greyscale )
{
if( this.palette == null ) return;
FloatBuffer paletteBuffer = this.palette.getFloatBuffer();
paletteBuffer.rewind();
for( int i = 0; i < 16; i++ )
{
double[] colour = palette.getColour( i );
if( greyscale )
{
float f = FixedWidthFontRenderer.toGreyscale( colour );
paletteBuffer.put( f ).put( f ).put( f );
}
else
{
paletteBuffer.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] );
}
}
int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0;
if( cursorBlink != null && cursorBlink.getIntBuffer().get( 0 ) != cursorAlpha ) cursorBlink.set( cursorAlpha );
}
@Override
public void apply()
{
super.apply();
palette.upload();
}
@Override
public void close()
{
palette.close();
super.close();
}
private void updateUniformLocation( Uniform uniform )
{
int id = Uniform.glGetUniformLocation( getId(), uniform.getName() );
if( id == -1 )
{
LOGGER.warn( "Shader {} could not find uniform named {} in the specified shader program.", getName(), uniform.getName() );
}
else
{
uniform.setLocation( id );
}
GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, monitorData, uniformBuffer );
}
@Nullable
@@ -109,4 +77,56 @@ public class MonitorTextureBufferShader extends ShaderInstance
return uniform;
}
public static void setTerminalData( ByteBuffer buffer, Terminal terminal )
{
int width = terminal.getWidth(), height = terminal.getHeight();
int pos = 0;
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++ )
{
buffer.put( pos, (byte) (text.charAt( x ) & 0xFF) );
buffer.put( pos + 1, (byte) getColour( textColour.charAt( x ), Colour.WHITE ) );
buffer.put( pos + 2, (byte) getColour( background.charAt( x ), Colour.BLACK ) );
pos += 3;
}
}
buffer.limit( pos );
}
public static void setUniformData( ByteBuffer buffer, Terminal terminal, boolean greyscale )
{
int pos = 0;
var palette = terminal.getPalette();
for( int i = 0; i < 16; i++ )
{
{
double[] colour = palette.getColour( i );
if( greyscale )
{
float f = FixedWidthFontRenderer.toGreyscale( colour );
buffer.putFloat( pos, f ).putFloat( pos + 4, f ).putFloat( pos + 8, f );
}
else
{
buffer.putFloat( pos, (float) colour[0] ).putFloat( pos + 4, (float) colour[1] ).putFloat( pos + 8, (float) colour[2] );
}
}
pos += 4 * 4; // std140 requires these are 4-wide
}
boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal );
buffer
.putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() )
.putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 )
.putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 )
.putInt( pos + 16, 15 - terminal.getTextColour() );
buffer.limit( UNIFORM_SIZE );
}
}

View File

@@ -7,12 +7,12 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.MultiBufferSource;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
public final class PrintoutRenderer
@@ -54,39 +54,39 @@ public final class PrintoutRenderer
private PrintoutRenderer() {}
public static void drawText( Matrix4f transform, MultiBufferSource renderer, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours )
public static void drawText( Matrix4f transform, MultiBufferSource bufferSource, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours )
{
VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT );
var buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_TEXT );
var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer );
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,
light
FixedWidthFontRenderer.drawString( emitter,
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line],
Palette.DEFAULT, false, light
);
}
}
public static void drawText( Matrix4f transform, MultiBufferSource renderer, int x, int y, int start, int light, String[] text, String[] colours )
public static void drawText( Matrix4f transform, MultiBufferSource bufferSource, int x, int y, int start, int light, String[] text, String[] colours )
{
VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT );
var buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_TEXT );
var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
FixedWidthFontRenderer.drawString( transform, buffer,
FixedWidthFontRenderer.drawString( emitter,
x, y + line * FONT_HEIGHT,
new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ),
null, Palette.DEFAULT, false, 0, 0,
light
Palette.DEFAULT, false, light
);
}
}
public static void drawBorder( Matrix4f transform, MultiBufferSource renderer, float x, float y, float z, int page, int pages, boolean isBook, int light )
public static void drawBorder( Matrix4f transform, MultiBufferSource bufferSource, float x, float y, float z, int page, int pages, boolean isBook, int light )
{
int leftPages = page;
int rightPages = pages - page - 1;
VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_BACKGROUND );
VertexConsumer buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_BACKGROUND );
if( isBook )
{

View File

@@ -8,7 +8,8 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
@@ -27,24 +28,45 @@ public class RenderTypes
public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20);
private static MonitorTextureBufferShader monitorTboShader;
private static ShaderInstance terminalShader;
public static final RenderType TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH;
public static final RenderType TERMINAL_BLOCKER = Types.TERMINAL_BLOCKER;
public static final RenderType TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH;
public static final RenderType MONITOR_TBO = Types.MONITOR_TBO;
public static final RenderType PRINTOUT_TEXT = Types.PRINTOUT_TEXT;
/**
* This looks wrong (it should be POSITION_COLOR_TEX_LIGHTMAP surely!) but the fragment/vertex shader for that
* appear to entirely ignore the lightmap.
* Renders a fullbright terminal without writing to the depth layer. This is used in combination with
* {@link #TERMINAL_BLOCKER} to ensure we can render a terminal without z-fighting.
*/
public static final RenderType TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH;
/**
* A transparent texture which only writes to the depth layer.
*/
public static final RenderType TERMINAL_BLOCKER = Types.TERMINAL_BLOCKER;
/**
* Renders a fullbright terminal which also writes to the depth layer. This is used when z-fighting isn't an issue -
* for instance rendering an empty terminal or inside a GUI.
*
* Note that vanilla maps do the same, so this isn't unreasonable.
* This is identical to <em>vanilla's</em> {@link RenderType#text}. Forge overrides one with a definition which sets
* sortOnUpload to true, which is entirely broken!
*/
public static final RenderType TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH;
/**
* Renders a monitor with the TBO shader.
*
* @see MonitorTextureBufferShader
*/
public static final RenderType MONITOR_TBO = Types.MONITOR_TBO;
/**
* A variant of {@link #TERMINAL_WITH_DEPTH} which uses the lightmap rather than rendering fullbright.
*/
public static final RenderType PRINTOUT_TEXT = RenderType.text( FixedWidthFontRenderer.FONT );
/**
* Printout's background texture. {@link RenderType#text(ResourceLocation)} is a <em>little</em> questionable, but
* it is what maps use, so should behave the same as vanilla in both item frames and in-hand.
*/
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text( new ResourceLocation( "computercraft", "textures/gui/printout.png" ) );
public static final RenderType POSITION_COLOR = Types.POSITION_COLOR;
@Nonnull
static MonitorTextureBufferShader getMonitorTextureBufferShader()
{
@@ -55,8 +77,7 @@ public class RenderTypes
@Nonnull
static ShaderInstance getTerminalShader()
{
if( terminalShader == null ) throw new NullPointerException( "MonitorTboShader has not been registered" );
return terminalShader;
return GameRenderer.getPositionColorTexShader();
}
@SubscribeEvent
@@ -70,15 +91,6 @@ public class RenderTypes
),
x -> monitorTboShader = (MonitorTextureBufferShader) x
);
event.registerShader(
new ShaderInstance(
event.getResourceManager(),
new ResourceLocation( ComputerCraft.MOD_ID, "terminal" ),
TERMINAL_WITHOUT_DEPTH.format()
),
x -> terminalShader = x
);
}
private static final class Types extends RenderStateShard
@@ -88,7 +100,6 @@ public class RenderTypes
false, false // blur, minimap
);
private static final VertexFormat TERM_FORMAT = DefaultVertexFormat.POSITION_COLOR_TEX;
private static final VertexFormat.Mode TERM_MODE = VertexFormat.Mode.TRIANGLES;
private static final ShaderStateShard TERM_SHADER = new ShaderStateShard( RenderTypes::getTerminalShader );
static final RenderType MONITOR_TBO = RenderType.create(
@@ -97,57 +108,36 @@ public class RenderTypes
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( new ShaderStateShard( RenderTypes::getMonitorTextureBufferShader ) )
.setWriteMaskState( COLOR_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_WITHOUT_DEPTH = RenderType.create(
"terminal_without_depth", TERM_FORMAT, TERM_MODE, 1024,
"terminal_without_depth", TERM_FORMAT, VertexFormat.Mode.QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( TERM_SHADER )
.setLightmapState( LIGHTMAP )
.setWriteMaskState( COLOR_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_BLOCKER = RenderType.create(
"terminal_blocker", TERM_FORMAT, TERM_MODE, 256,
"terminal_blocker", DefaultVertexFormat.POSITION, VertexFormat.Mode.QUADS, 256,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( TERM_SHADER )
.setShaderState( POSITION_SHADER )
.setWriteMaskState( DEPTH_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_WITH_DEPTH = RenderType.create(
"terminal_with_depth", TERM_FORMAT, TERM_MODE, 1024,
"terminal_with_depth", TERM_FORMAT, VertexFormat.Mode.QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( TERM_SHADER )
.createCompositeState( false )
);
/**
* A variant of {@link #TERMINAL_WITH_DEPTH} which uses the lightmap rather than rendering fullbright.
*/
static final RenderType PRINTOUT_TEXT = RenderType.create(
"printout_text", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, TERM_MODE, 1024,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( RenderStateShard.RENDERTYPE_TEXT_SHADER )
.setLightmapState( RenderStateShard.LIGHTMAP )
.createCompositeState( false )
);
static final RenderType POSITION_COLOR = RenderType.create(
"position_color", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 128,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setShaderState( POSITION_COLOR_SHADER )
.setLightmapState( LIGHTMAP )
.createCompositeState( false )
);

View File

@@ -8,19 +8,19 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
@@ -35,7 +35,8 @@ import org.lwjgl.opengl.GL31;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonitor>
{
@@ -44,16 +45,14 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
* the monitor frame and contents.
*/
private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
private static ByteBuffer tboContents;
private static final Matrix4f IDENTITY = Transformation.identity().getMatrix();
private static ByteBuffer backingBuffer;
public TileEntityMonitorRenderer( BlockEntityRendererProvider.Context context )
{
}
@Override
public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource renderer, int lightmapCoord, int overlayLight )
public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource bufferSource, int lightmapCoord, int overlayLight )
{
// Render from the origin monitor
ClientMonitor originTerminal = monitor.getClientMonitor();
@@ -114,31 +113,14 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
Matrix4f matrix = transform.last().pose();
renderTerminal( renderer, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
// We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is
// reasonable.
FixedWidthFontRenderer.drawCursor(
matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ),
0, 0, terminal, !originTerminal.isColour()
);
renderTerminal( bufferSource, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
transform.popPose();
FixedWidthFontRenderer.drawBlocker(
transform.last().pose(), renderer,
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
// Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in
// buffers before calling renderer.finish, which means the blocker isn't actually rendered at that point!
renderer.getBuffer( RenderType.solid() );
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal(
transform.last().pose(), renderer,
FixedWidthFontRenderer.toVertexConsumer( transform.last().pose(), bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) ),
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
@@ -147,9 +129,11 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
transform.popPose();
}
private static void renderTerminal( @Nonnull MultiBufferSource renderer, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
private static void renderTerminal( @Nonnull MultiBufferSource bufferSource, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
{
Terminal terminal = monitor.getTerminal();
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
MonitorRenderer renderType = MonitorRenderer.current();
boolean redraw = monitor.pollTerminalChanged();
@@ -159,34 +143,15 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
{
case TBO:
{
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 = MemoryTracker.create( size );
}
var terminalBuffer = getBuffer( width * height * 3 );
MonitorTextureBufferShader.setTerminalData( terminalBuffer, terminal );
DirectBuffers.setBufferData( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer, terminalBuffer, GL20.GL_STATIC_DRAW );
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._glBindBuffer( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer );
GlStateManager._glBufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW );
GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 );
var uniformBuffer = getBuffer( MonitorTextureBufferShader.UNIFORM_SIZE );
MonitorTextureBufferShader.setUniformData( uniformBuffer, terminal, !monitor.isColour() );
DirectBuffers.setBufferData( GL31.GL_UNIFORM_BUFFER, monitor.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW );
}
// Nobody knows what they're doing!
@@ -196,43 +161,61 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
RenderSystem.activeTexture( active );
MonitorTextureBufferShader shader = RenderTypes.getMonitorTextureBufferShader();
shader.setupUniform( width, height, terminal.getPalette(), !monitor.isColour() );
shader.setupUniform( monitor.tboUniform );
VertexConsumer buffer = renderer.getBuffer( RenderTypes.MONITOR_TBO );
VertexConsumer buffer = bufferSource.getBuffer( RenderTypes.MONITOR_TBO );
tboVertex( buffer, matrix, -xMargin, -yMargin );
tboVertex( buffer, matrix, -xMargin, pixelHeight + yMargin );
tboVertex( buffer, matrix, pixelWidth + xMargin, -yMargin );
tboVertex( buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin );
// And force things to flush. We strictly speaking do this later on anyway for the cursor, but nice to
// be consistent.
renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
break;
}
case VBO:
{
VertexBuffer vbo = monitor.buffer;
var vbo = monitor.buffer;
if( redraw )
{
Tesselator tessellator = Tesselator.getInstance();
BufferBuilder builder = tessellator.getBuilder();
builder.begin( RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format() );
FixedWidthFontRenderer.drawTerminalWithoutCursor(
IDENTITY, builder, 0, 0,
terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
);
int vertexSize = RenderTypes.TERMINAL_WITHOUT_DEPTH.format().getVertexSize();
ByteBuffer buffer = getBuffer( DirectFixedWidthFontRenderer.getVertexCount( terminal ) * vertexSize );
builder.end();
vbo.upload( builder );
// Draw the main terminal and store how many vertices it has.
DirectFixedWidthFontRenderer.drawTerminalWithoutCursor(
buffer, 0, 0, terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
);
int termIndexes = buffer.position() / vertexSize;
// If the cursor is visible, we append it to the end of our buffer. When rendering, we can either
// render n or n+1 quads and so toggle the cursor on and off.
DirectFixedWidthFontRenderer.drawCursor( buffer, 0, 0, terminal, !monitor.isColour() );
buffer.flip();
vbo.upload( termIndexes, RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer );
}
renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState();
vbo.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() );
vbo.drawWithShader(
matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(),
// As mentioned in the above comment, render the extra cursor quad if it is visible this frame. Each
// // quad has an index count of 6.
FixedWidthFontRenderer.isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() ? vbo.getIndexCount() + 6 : vbo.getIndexCount()
);
FixedWidthFontRenderer.drawBlocker(
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ),
-xMargin, -yMargin, pixelWidth + xMargin, pixelHeight + yMargin
);
break;
}
}
// Force a flush of the buffer. WorldRenderer.updateCameraAndRender will "finish" all the built-in buffers
// before calling renderer.finish, which means our TBO quad or depth blocker won't be rendered yet!
bufferSource.getBuffer( RenderType.solid() );
}
private static void tboVertex( VertexConsumer builder, Matrix4f matrix, float x, float y )
@@ -241,6 +224,20 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
builder.vertex( matrix, x, y, 0 ).uv( x, y ).endVertex();
}
@Nonnull
private static ByteBuffer getBuffer( int capacity )
{
ByteBuffer buffer = backingBuffer;
if( buffer == null || buffer.capacity() < capacity )
{
buffer = backingBuffer = buffer == null ? MemoryTracker.create( capacity ) : MemoryTracker.resize( buffer, capacity );
}
buffer.clear();
return buffer;
}
@Override
public int getViewDistance()
{

View File

@@ -0,0 +1,231 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render.text;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexConsumer;
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 org.lwjgl.system.MemoryUtil;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
import static org.lwjgl.system.MemoryUtil.memPutByte;
import static org.lwjgl.system.MemoryUtil.memPutFloat;
/**
* An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link ByteBuffer} rather than
* emitting to {@link VertexConsumer}. This allows us to emit vertices very quickly, when using the VBO renderer.
*
* There are some limitations here:
* <ul>
* <li>No transformation matrix (not needed for VBOs).</li>
* <li>Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX}.</li>
* <li>The buffer <strong>MUST</strong> be allocated with {@link MemoryTracker}, and not through any other means.</li>
* </ul>
*
* Note this is almost an exact copy of {@link FixedWidthFontRenderer}. While the code duplication is unfortunate,
* it is measurably faster than introducing polymorphism into {@link FixedWidthFontRenderer}.
*
* <strong>IMPORTANT: </strong> When making changes to this class, please check if you need to make the same changes to
* {@link FixedWidthFontRenderer}.
*/
public final class DirectFixedWidthFontRenderer
{
private DirectFixedWidthFontRenderer()
{
}
private static void drawChar( ByteBuffer buffer, float x, float y, int index, byte[] colour )
{
// 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;
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
quad(
buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, colour,
xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH
);
}
private static void drawQuad( ByteBuffer emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
{
byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
quad( emitter, x, y, x + width, y + height, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END );
}
private static void drawBackground(
@Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height
)
{
if( leftMarginSize > 0 )
{
drawQuad( buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
}
if( rightMarginSize > 0 )
{
drawQuad( buffer, 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( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
}
}
private static void drawString( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale )
{
for( int i = 0; i < text.length(); i++ )
{
byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( buffer, x + i * FONT_WIDTH, y, index, colour );
}
}
public static void drawTerminalWithoutCursor(
@Nonnull ByteBuffer 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(
buffer, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize
);
drawBackground(
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++ )
{
float rowY = y + FONT_HEIGHT * i;
drawBackground(
buffer, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale,
leftMarginSize, rightMarginSize, FONT_HEIGHT
);
drawString(
buffer, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ),
palette, greyscale
);
}
}
public static void drawCursor( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale )
{
if( isCursorVisible( terminal ) )
{
byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
drawChar( buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour );
}
}
public static int getVertexCount( Terminal terminal )
{
return (1 + (terminal.getHeight() + 2) * terminal.getWidth() * 2) * 4;
}
private static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, byte[] rgba, float u1, float v1, float u2, float v2 )
{
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
// This provides significant performance gains, at the cost of well, using Unsafe.
// Each vertex is 24 bytes, giving 96 bytes in total. Vertices are of the form (xyz:FFF)(rgba:BBBB)(uv:FF),
// which matches the POSITION_COLOR_TEX vertex format.
int position = buffer.position();
long addr = MemoryUtil.memAddress( buffer );
// We're doing terrible unsafe hacks below, so let's be really sure that what we're doing is reasonable.
if( position < 0 || 96 > buffer.limit() - position ) throw new IndexOutOfBoundsException();
// Require the pointer to be aligned to a 32-bit boundary.
if( (addr & 3) != 0 ) throw new IllegalStateException( "Memory is not aligned" );
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
if( rgba.length != 4 ) throw new IllegalStateException();
memPutFloat( addr + 0, x1 );
memPutFloat( addr + 4, y1 );
memPutFloat( addr + 8, 0 );
memPutByte( addr + 12, rgba[0] );
memPutByte( addr + 13, rgba[1] );
memPutByte( addr + 14, rgba[2] );
memPutByte( addr + 15, (byte) 255 );
memPutFloat( addr + 16, u1 );
memPutFloat( addr + 20, v1 );
memPutFloat( addr + 24, x1 );
memPutFloat( addr + 28, y2 );
memPutFloat( addr + 32, 0 );
memPutByte( addr + 36, rgba[0] );
memPutByte( addr + 37, rgba[1] );
memPutByte( addr + 38, rgba[2] );
memPutByte( addr + 39, (byte) 255 );
memPutFloat( addr + 40, u1 );
memPutFloat( addr + 44, v2 );
memPutFloat( addr + 48, x2 );
memPutFloat( addr + 52, y2 );
memPutFloat( addr + 56, 0 );
memPutByte( addr + 60, rgba[0] );
memPutByte( addr + 61, rgba[1] );
memPutByte( addr + 62, rgba[2] );
memPutByte( addr + 63, (byte) 255 );
memPutFloat( addr + 64, u2 );
memPutFloat( addr + 68, v2 );
memPutFloat( addr + 72, x2 );
memPutFloat( addr + 76, y1 );
memPutFloat( addr + 80, 0 );
memPutByte( addr + 84, rgba[0] );
memPutByte( addr + 85, rgba[1] );
memPutByte( addr + 86, rgba[2] );
memPutByte( addr + 87, (byte) 255 );
memPutFloat( addr + 88, u2 );
memPutFloat( addr + 92, v1 );
// Finally increment the position.
buffer.position( position + 96 );
// Well done for getting to the end of this method. I recommend you take a break and go look at cute puppies.
}
}

View File

@@ -0,0 +1,243 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render.text;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.RenderTypes;
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.resources.ResourceLocation;
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
/**
* Handles rendering fixed width text and computer terminals.
*
* This class has several modes of usage:
* <ul>
* <li>{@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods,
* this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.</li>
* <li>{@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor
* separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically.
* </li>
* <li>{@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the
* whole term.</li>
* <li>{@link #drawBlocker}: When rendering a terminal using {@link RenderTypes#TERMINAL_WITHOUT_DEPTH} you need to
* render an additional "depth blocker" on top of the monitor.</li>
* </ul>
*
* <strong>IMPORTANT: </strong> When making changes to this class, please check if you need to make the same changes to
* {@link DirectFixedWidthFontRenderer}.
*/
public final class FixedWidthFontRenderer
{
public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6;
static final float WIDTH = 256.0f;
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final byte[] BLACK = new byte[] { byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), (byte) 255 };
private FixedWidthFontRenderer()
{
}
private static byte byteColour( float c )
{
return (byte) (int) (c * 255);
}
public static float toGreyscale( double[] rgb )
{
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
}
public static int getColour( char c, Colour def )
{
return 15 - Terminal.getColour( c, def );
}
private static void drawChar( QuadEmitter emitter, float x, float y, int index, byte[] colour, int light )
{
// 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;
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
quad(
emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour,
xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light
);
}
public static void drawQuad( QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light )
{
quad( emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light );
}
private static void drawQuad( QuadEmitter emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light )
{
byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
drawQuad( emitter, x, y, 0, width, height, colour, light );
}
private static void drawBackground(
@Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height, int light
)
{
if( leftMarginSize > 0 )
{
drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light );
}
if( rightMarginSize > 0 )
{
drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light );
}
// 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( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light );
}
}
public static void drawString( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale, int light )
{
for( int i = 0; i < text.length(); i++ )
{
byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, light );
}
}
public static void drawTerminalWithoutCursor(
@Nonnull QuadEmitter emitter, 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(
emitter, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP
);
drawBackground(
emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
leftMarginSize, rightMarginSize, bottomMarginSize, FULL_BRIGHT_LIGHTMAP
);
// The main text
for( int i = 0; i < height; i++ )
{
float rowY = y + FixedWidthFontRenderer.FONT_HEIGHT * i;
drawBackground(
emitter, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale,
leftMarginSize, rightMarginSize, FONT_HEIGHT, FULL_BRIGHT_LIGHTMAP
);
drawString(
emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ),
palette, greyscale, FULL_BRIGHT_LIGHTMAP
);
}
}
public static boolean isCursorVisible( Terminal terminal )
{
if( !terminal.getCursorBlink() ) return false;
int cursorX = terminal.getCursorX();
int cursorY = terminal.getCursorY();
return cursorX >= 0 && cursorX < terminal.getWidth() && cursorY >= 0 && cursorY < terminal.getHeight();
}
public static void drawCursor( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale )
{
if( isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() )
{
byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
drawChar( emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP );
}
}
public static void drawTerminal(
@Nonnull QuadEmitter emitter, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminalWithoutCursor( emitter, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( emitter, x, y, terminal, greyscale );
}
public static void drawEmptyTerminal( @Nonnull QuadEmitter emitter, float x, float y, float width, float height )
{
drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
public static void drawBlocker( @Nonnull QuadEmitter emitter, float x, float y, float width, float height )
{
drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
public record QuadEmitter(Matrix4f matrix4f, VertexConsumer consumer) {}
public static QuadEmitter toVertexConsumer( Matrix4f matrix, VertexConsumer consumer )
{
return new QuadEmitter( matrix, consumer );
}
private static void quad( QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light )
{
var matrix = c.matrix4f();
var consumer = c.consumer();
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
consumer.vertex( matrix, x1, y1, z ).color( r, g, b, a ).uv( u1, v1 ).uv2( light ).endVertex();
consumer.vertex( matrix, x1, y2, z ).color( r, g, b, a ).uv( u1, v2 ).uv2( light ).endVertex();
consumer.vertex( matrix, x2, y2, z ).color( r, g, b, a ).uv( u2, v2 ).uv2( light ).endVertex();
consumer.vertex( matrix, x2, y1, z ).color( r, g, b, a ).uv( u2, v1 ).uv2( light ).endVertex();
}
}

View File

@@ -20,12 +20,10 @@ import java.util.Queue;
class DfpwmStream implements AudioStream
{
public static final int SAMPLE_RATE = SpeakerPeripheral.SAMPLE_RATE;
private static final int PREC = 10;
private static final int LPF_STRENGTH = 140;
private static final AudioFormat MONO_16 = new AudioFormat( SAMPLE_RATE, 16, 1, true, false );
private static final AudioFormat MONO_8 = new AudioFormat( SpeakerPeripheral.SAMPLE_RATE, 8, 1, true, false );
private final Queue<ByteBuffer> buffers = new ArrayDeque<>( 2 );
@@ -41,7 +39,7 @@ class DfpwmStream implements AudioStream
void push( @Nonnull ByteBuf input )
{
int readable = input.readableBytes();
ByteBuffer output = ByteBuffer.allocate( readable * 16 ).order( ByteOrder.nativeOrder() );
ByteBuffer output = ByteBuffer.allocate( readable * 8 ).order( ByteOrder.nativeOrder() );
for( int i = 0; i < readable; i++ )
{
@@ -73,9 +71,9 @@ class DfpwmStream implements AudioStream
strength = nextStrength;
previousBit = currentBit;
// Ideally we'd generate an 8-bit audio buffer. However, as we're piggybacking on top of another
// audio stream (which uses 16 bit audio), we need to keep in the same format.
output.putShort( (short) ((byte) (lowPassCharge & 0xFF) << 8) );
// OpenAL expects signed data ([0, 255]) while we produce unsigned ([-128, 127]). Do some bit twiddling
// magic to convert.
output.put( (byte) ((lowPassCharge & 0xFF) ^ 0x80) );
inputByte >>= 1;
}
@@ -92,7 +90,7 @@ class DfpwmStream implements AudioStream
@Override
public AudioFormat getFormat()
{
return MONO_16;
return MONO_8;
}
@Nonnull

View File

@@ -26,14 +26,11 @@ public class SpeakerManager
@SubscribeEvent
public static void playStreaming( PlayStreamingSourceEvent event )
{
if( !(event.getSound() instanceof SpeakerSound sound) ) return;
if( sound.stream == null ) return;
if( !(event.getSound() instanceof SpeakerSound sound) || sound.stream == null ) return;
event.getSource().attachBufferStream( sound.stream );
event.getSource().play();
sound.channel = event.getSource();
sound.executor = event.getManager().executor;
// Associate the sound with the current channel, so SpeakerInstance.pushAudio can queue audio immediately.
sound.channel = event.getChannel();
sound.executor = event.getEngine().executor;
}
public static SpeakerInstance getSound( UUID source )

View File

@@ -7,13 +7,16 @@ package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel;
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
import net.minecraft.client.resources.sounds.Sound;
import net.minecraft.client.resources.sounds.TickableSoundInstance;
import net.minecraft.client.sounds.AudioStream;
import net.minecraft.client.sounds.SoundBufferLibrary;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance
@@ -50,9 +53,10 @@ public class SpeakerSound extends AbstractSoundInstance implements TickableSound
{
}
@Nullable
public AudioStream getStream()
@Nonnull
@Override
public CompletableFuture<AudioStream> getStream( @Nonnull SoundBufferLibrary soundBuffers, @Nonnull Sound sound, boolean looping )
{
return stream;
return stream != null ? CompletableFuture.completedFuture( stream ) : super.getStream( soundBuffers, sound, looping );
}
}

View File

@@ -0,0 +1,84 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.util;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferUploader;
import net.minecraft.Util;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.GL45C;
import java.nio.ByteBuffer;
/**
* Provides utilities to interact with OpenGL's buffer objects, either using direct state access or binding/unbinding
* it.
*/
public class DirectBuffers
{
public static final boolean HAS_DSA;
static final boolean ON_LINUX = Util.getPlatform() == Util.OS.LINUX;
static
{
var capabilities = GL.getCapabilities();
HAS_DSA = capabilities.OpenGL45 || capabilities.GL_ARB_direct_state_access;
}
public static int createBuffer()
{
return HAS_DSA ? GL45C.glCreateBuffers() : GL15C.glGenBuffers();
}
/**
* Delete a previously created buffer.
*
* On Linux, {@link GlStateManager#_glDeleteBuffers(int)} clears a buffer before deleting it. However, this involves
* binding and unbinding the buffer, conflicting with {@link BufferUploader}'s cache. This deletion method uses
* our existing {@link #setEmptyBufferData(int, int, int)}, which correctly handles clearing the buffer.
*
* @param type The buffer's type.
* @param id The buffer's ID.
*/
public static void deleteBuffer( int type, int id )
{
RenderSystem.assertOnRenderThread();
if( ON_LINUX ) DirectBuffers.setEmptyBufferData( type, id, GL15C.GL_DYNAMIC_DRAW );
GL15C.glDeleteBuffers( id );
}
public static void setBufferData( int type, int id, ByteBuffer buffer, int flags )
{
if( HAS_DSA )
{
GL45C.glNamedBufferData( id, buffer, flags );
}
else
{
if( type == GL15C.GL_ARRAY_BUFFER ) BufferUploader.reset();
GlStateManager._glBindBuffer( type, id );
GlStateManager._glBufferData( type, buffer, flags );
GlStateManager._glBindBuffer( type, 0 );
}
}
public static void setEmptyBufferData( int type, int id, int flags )
{
if( HAS_DSA )
{
GL45C.glNamedBufferData( id, 0, flags );
}
else
{
if( type == GL15C.GL_ARRAY_BUFFER ) BufferUploader.reset();
GlStateManager._glBindBuffer( type, id );
GlStateManager._glBufferData( type, 0, flags );
GlStateManager._glBindBuffer( type, 0 );
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.util;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.ShaderInstance;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL45C;
import java.nio.ByteBuffer;
/**
* A version of {@link VertexBuffer} which allows uploading {@link ByteBuffer}s directly.
*
* This should probably be its own class (rather than subclassing), but I need access to {@link VertexBuffer#drawWithShader}.
*/
public class DirectVertexBuffer extends VertexBuffer
{
private int actualIndexCount;
public DirectVertexBuffer()
{
if( DirectBuffers.HAS_DSA )
{
RenderSystem.glDeleteBuffers( vertextBufferId );
if( DirectBuffers.ON_LINUX ) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
vertextBufferId = GL45C.glCreateBuffers();
}
}
public void upload( int vertexCount, VertexFormat.Mode mode, VertexFormat format, ByteBuffer buffer )
{
RenderSystem.assertOnRenderThread();
DirectBuffers.setBufferData( GL15.GL_ARRAY_BUFFER, vertextBufferId, buffer, GL15.GL_STATIC_DRAW );
this.format = format;
this.mode = mode;
actualIndexCount = indexCount = mode.indexCount( vertexCount );
indexType = VertexFormat.IndexType.SHORT;
sequentialIndices = true;
}
public void drawWithShader( Matrix4f modelView, Matrix4f projection, ShaderInstance shader, int indexCount )
{
this.indexCount = indexCount;
drawWithShader( modelView, projection, shader );
this.indexCount = actualIndexCount;
}
public int getIndexCount()
{
return actualIndexCount;
}
@Override
public void close()
{
super.close();
if( DirectBuffers.ON_LINUX ) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
}
}

View File

@@ -14,8 +14,8 @@ import dan200.computercraft.core.computer.TimeoutState;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.ThreadUtils;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.LuaTable;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LoadState;
import org.squiddev.cobalt.debug.DebugFrame;

View File

@@ -57,7 +57,8 @@ public final class ComputerMBean implements DynamicMBean, Tracker
{
ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) );
}
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | MalformedObjectNameException e )
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException |
MalformedObjectNameException e )
{
ComputerCraft.log.warn( "Failed to register JMX bean", e );
}

View File

@@ -26,7 +26,7 @@ import net.minecraft.world.level.storage.loot.predicates.AlternativeLootItemCond
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraftforge.fmllegacy.RegistryObject;
import net.minecraftforge.registries.RegistryObject;
import java.util.function.BiConsumer;

View File

@@ -27,7 +27,7 @@ import net.minecraft.data.DataGenerator;
import net.minecraft.data.recipes.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.Tag;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.*;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
@@ -370,7 +370,7 @@ class RecipeGenerator extends RecipeProvider
return DyeColor.byId( 15 - colour.ordinal() );
}
private static InventoryChangeTrigger.TriggerInstance inventoryChange( Tag<Item> stack )
private static InventoryChangeTrigger.TriggerInstance inventoryChange( TagKey<Item> stack )
{
return InventoryChangeTrigger.TriggerInstance.hasItems( ItemPredicate.Builder.item().of( stack ).build() );
}

View File

@@ -21,6 +21,7 @@ import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.storage.loot.LootPool;
@@ -28,11 +29,11 @@ import net.minecraft.world.level.storage.loot.entries.LootTableReference;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraftforge.event.*;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
import net.minecraftforge.fmlserverevents.FMLServerStartingEvent;
import net.minecraftforge.fmlserverevents.FMLServerStoppedEvent;
import java.util.Arrays;
import java.util.HashSet;
@@ -84,7 +85,7 @@ public final class CommonHooks
}
@SubscribeEvent
public static void onServerStarting( FMLServerStartingEvent event )
public static void onServerStarting( ServerStartingEvent event )
{
MinecraftServer server = event.getServer();
if( server instanceof DedicatedServer dediServer && dediServer.getProperties().enableJmxMonitoring )
@@ -94,7 +95,7 @@ public final class CommonHooks
}
@SubscribeEvent
public static void onServerStarted( FMLServerStartedEvent event )
public static void onServerStarted( ServerStartedEvent event )
{
ComputerCraft.serverComputerRegistry.reset();
WirelessNetwork.resetNetworks();
@@ -104,7 +105,7 @@ public final class CommonHooks
}
@SubscribeEvent
public static void onServerStopped( FMLServerStoppedEvent event )
public static void onServerStopped( ServerStoppedEvent event )
{
ComputerCraft.serverComputerRegistry.reset();
WirelessNetwork.resetNetworks();
@@ -161,4 +162,14 @@ public final class CommonHooks
NetworkHandler.sendToPlayer( event.getPlayer(), packet );
}
}
@SubscribeEvent
public static void onMissingEntityMappingsEvent( RegistryEvent.MissingMappings<EntityType<?>> event )
{
ResourceLocation id = new ResourceLocation( ComputerCraft.MOD_ID, "turtle_player" );
for( RegistryEvent.MissingMappings.Mapping<EntityType<?>> mapping : event.getMappings( ComputerCraft.MOD_ID ) )
{
if( mapping.key.equals( id ) ) mapping.ignore();
}
}
}

View File

@@ -14,7 +14,7 @@ import java.util.stream.Stream;
public final class PocketUpgrades
{
private static final UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> registry = new UpgradeManager<>(
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.TYPE
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID
);
private PocketUpgrades() {}

View File

@@ -61,7 +61,6 @@ import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
@@ -73,8 +72,6 @@ import dan200.computercraft.shared.util.ImpostorRecipe;
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
@@ -96,11 +93,8 @@ import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fmllegacy.RegistryObject;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryBuilder;
import net.minecraftforge.registries.*;
import java.util.function.BiFunction;
@@ -259,7 +253,7 @@ public final class Registry
public static class ModTurtleSerialisers
{
static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, ComputerCraft.MOD_ID );
static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.REGISTRY_ID.location(), ComputerCraft.MOD_ID );
public static final RegistryObject<TurtleUpgradeSerialiser<TurtleSpeaker>> SPEAKER =
SERIALISERS.register( "speaker", () -> TurtleUpgradeSerialiser.simpleWithCustomItem( TurtleSpeaker::new ) );
@@ -275,7 +269,7 @@ public final class Registry
public static class ModPocketUpgradeSerialisers
{
static final DeferredRegister<PocketUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( PocketUpgradeSerialiser.TYPE, ComputerCraft.MOD_ID );
static final DeferredRegister<PocketUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( PocketUpgradeSerialiser.REGISTRY_ID, ComputerCraft.MOD_ID );
public static final RegistryObject<PocketUpgradeSerialiser<PocketSpeaker>> SPEAKER =
SERIALISERS.register( "speaker", () -> PocketUpgradeSerialiser.simpleWithCustomItem( PocketSpeaker::new ) );
@@ -285,18 +279,6 @@ public final class Registry
SERIALISERS.register( "wireless_modem_advanced", () -> PocketUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new PocketModem( id, item, true ) ) );
}
public static class ModEntities
{
static final DeferredRegister<EntityType<?>> ENTITIES = DeferredRegister.create( ForgeRegistries.ENTITIES, ComputerCraft.MOD_ID );
public static final RegistryObject<EntityType<TurtlePlayer>> TURTLE_PLAYER = ENTITIES.register( "turtle_player", () ->
EntityType.Builder.<TurtlePlayer>createNothing( MobCategory.MISC )
.noSave()
.noSummon()
.sized( 0, 0 )
.build( ComputerCraft.MOD_ID + ":turtle_player" ) );
}
public static class ModContainers
{
static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create( ForgeRegistries.CONTAINERS, ComputerCraft.MOD_ID );
@@ -327,17 +309,21 @@ public final class Registry
}
@SubscribeEvent
public static void registerRegistries( RegistryEvent.NewRegistry event )
public static void registerRegistries( NewRegistryEvent event )
{
new RegistryBuilder<TurtleUpgradeSerialiser<?>>()
.setName( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_upgrade_serialiser" ) )
.setType( TurtleUpgradeSerialiser.TYPE )
.disableSaving().disableSync().create();
@SuppressWarnings( "unchecked" )
Class<TurtleUpgradeSerialiser<?>> turtleType = (Class<TurtleUpgradeSerialiser<?>>) (Class<?>) TurtleUpgradeSerialiser.class;
event.create( new RegistryBuilder<TurtleUpgradeSerialiser<?>>()
.setName( TurtleUpgradeSerialiser.REGISTRY_ID.location() )
.setType( turtleType )
.disableSaving().disableSync() );
new RegistryBuilder<PocketUpgradeSerialiser<?>>()
.setName( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_upgrade_serialiser" ) )
.setType( PocketUpgradeSerialiser.TYPE )
.disableSaving().disableSync().create();
@SuppressWarnings( "unchecked" )
Class<PocketUpgradeSerialiser<?>> pocketType = (Class<PocketUpgradeSerialiser<?>>) (Class<?>) PocketUpgradeSerialiser.class;
event.create( new RegistryBuilder<PocketUpgradeSerialiser<?>>()
.setName( PocketUpgradeSerialiser.REGISTRY_ID.location() )
.setType( pocketType )
.disableSaving().disableSync() );
}
@SubscribeEvent
@@ -425,7 +411,6 @@ public final class Registry
ModItems.ITEMS.register( bus );
ModTurtleSerialisers.SERIALISERS.register( bus );
ModPocketUpgradeSerialisers.SERIALISERS.register( bus );
ModEntities.ENTITIES.register( bus );
ModContainers.CONTAINERS.register( bus );
}
}

View File

@@ -14,7 +14,7 @@ import java.util.stream.Stream;
public final class TurtleUpgrades
{
private static final UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> registry = new UpgradeManager<>(
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.TYPE
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID
);
private TurtleUpgrades() {}

View File

@@ -9,6 +9,8 @@ import com.google.gson.*;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
@@ -45,16 +47,16 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T, R>, T exten
) {}
private final String kind;
private final Class<R> klass;
private final ResourceKey<Registry<R>> registry;
private Map<String, UpgradeWrapper<R, T>> current = Collections.emptyMap();
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Collections.emptyMap();
public UpgradeManager( @Nonnull String kind, @Nonnull String path, @Nonnull Class<R> klass )
public UpgradeManager( @Nonnull String kind, @Nonnull String path, @Nonnull ResourceKey<Registry<R>> registry )
{
super( GSON, path );
this.kind = kind;
this.klass = klass;
this.registry = registry;
}
@Nullable
@@ -126,7 +128,7 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T, R>, T exten
var root = GsonHelper.convertToJsonObject( json, "top element" );
var serialiserId = new ResourceLocation( GsonHelper.getAsString( root, "type" ) );
var serialiser = RegistryManager.ACTIVE.getRegistry( klass ).getValue( serialiserId );
var serialiser = RegistryManager.ACTIVE.getRegistry( registry ).getValue( serialiserId );
if( serialiser == null ) throw new JsonSyntaxException( "Unknown upgrade type '" + serialiserId + "'" );
// TODO: Can we track which mod this resource came from and use that instead? It's theoretically possible,

View File

@@ -13,7 +13,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fmllegacy.server.ServerLifecycleHooks;
import net.minecraftforge.server.ServerLifecycleHooks;
import java.io.File;

View File

@@ -32,7 +32,7 @@ public final class CommandUtils
}
@SuppressWarnings( "unchecked" )
public static CompletableFuture<Suggestions> suggestOnServer( CommandContext<?> context, SuggestionsBuilder builder, Function<CommandContext<CommandSourceStack>, CompletableFuture<Suggestions>> supplier )
public static CompletableFuture<Suggestions> suggestOnServer( CommandContext<?> context, Function<CommandContext<CommandSourceStack>, CompletableFuture<Suggestions>> supplier )
{
Object source = context.getSource();
if( !(source instanceof SharedSuggestionProvider) )
@@ -45,7 +45,7 @@ public final class CommandUtils
}
else
{
return ((SharedSuggestionProvider) source).customSuggestion( (CommandContext<SharedSuggestionProvider>) context, builder );
return ((SharedSuggestionProvider) source).customSuggestion( context );
}
}

View File

@@ -121,7 +121,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
}
// Verify we've a command source and we're running on the server
return suggestOnServer( context, builder, s -> {
return suggestOnServer( context, s -> {
if( remaining.startsWith( "@" ) )
{
suggestComputers( builder, remaining, x -> {

View File

@@ -19,7 +19,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.fmllegacy.RegistryObject;
import net.minecraftforge.registries.RegistryObject;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

View File

@@ -12,11 +12,11 @@ import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.util.Constants;
import javax.annotation.Nonnull;
@@ -36,7 +36,7 @@ public abstract class TileGeneric extends BlockEntity
setChanged();
BlockPos pos = getBlockPos();
BlockState state = getBlockState();
getLevel().sendBlockUpdated( pos, state, state, Constants.BlockFlags.DEFAULT );
getLevel().sendBlockUpdated( pos, state, state, Block.UPDATE_ALL );
}
@Nonnull
@@ -76,7 +76,8 @@ public abstract class TileGeneric extends BlockEntity
@Override
public final void onDataPacket( Connection net, ClientboundBlockEntityDataPacket packet )
{
if( packet.getType() == 0 ) handleUpdateTag( packet.getTag() );
var tag = packet.getTag();
if( tag != null ) handleUpdateTag( tag );
}
@Override

View File

@@ -16,7 +16,6 @@ import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
@@ -81,7 +80,7 @@ public class CommandAPI implements ILuaAPI
Map<String, Object> table = BlockData.fill( new HashMap<>(), state );
BlockEntity tile = world.getBlockEntity( pos );
if( tile != null ) table.put( "nbt", NBTUtil.toLua( tile.save( new CompoundTag() ) ) );
if( tile != null ) table.put( "nbt", NBTUtil.toLua( tile.saveWithFullMetadata() ) );
return table;
}

View File

@@ -18,7 +18,7 @@ import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraftforge.fmllegacy.RegistryObject;
import net.minecraftforge.registries.RegistryObject;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

View File

@@ -32,7 +32,7 @@ import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fmllegacy.RegistryObject;
import net.minecraftforge.registries.RegistryObject;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -119,7 +119,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
@Nonnull
@Override
public ItemStack getPickBlock( BlockState state, HitResult target, BlockGetter world, BlockPos pos, Player player )
public ItemStack getCloneItemStack( BlockState state, HitResult target, BlockGetter world, BlockPos pos, Player player )
{
BlockEntity tile = world.getBlockEntity( pos );
if( tile instanceof TileComputerBase )
@@ -128,7 +128,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
if( !result.isEmpty() ) return result;
}
return super.getPickBlock( state, target, world, pos, player );
return super.getCloneItemStack( state, target, world, pos, player );
}
@Override

View File

@@ -99,7 +99,7 @@ public class TileCommandComputer extends TileComputer
}
return new CommandSourceStack( receiver,
new Vec3( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vec2.ZERO,
Vec3.atCenterOf( worldPosition ), Vec2.ZERO,
(ServerLevel) getLevel(), 2,
name, new TextComponent( name ),
getLevel().getServer(), null

View File

@@ -188,16 +188,15 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
protected abstract void updateBlockState( ComputerState newState );
@Nonnull
@Override
public CompoundTag save( @Nonnull CompoundTag nbt )
public void saveAdditional( @Nonnull CompoundTag nbt )
{
// Save ID, label and power state
if( computerID >= 0 ) nbt.putInt( NBT_ID, computerID );
if( label != null ) nbt.putString( NBT_LABEL, label );
nbt.putBoolean( NBT_ON, on );
return super.save( nbt );
super.saveAdditional( nbt );
}
@Override
@@ -388,7 +387,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public final ClientboundBlockEntityDataPacket getUpdatePacket()
{
return new ClientboundBlockEntityDataPacket( worldPosition, 0, getUpdateTag() );
return ClientboundBlockEntityDataPacket.create( this );
}
@Nonnull

View File

@@ -28,7 +28,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraftforge.fmllegacy.server.ServerLifecycleHooks;
import net.minecraftforge.server.ServerLifecycleHooks;
import net.minecraftforge.versions.mcp.MCPVersion;
import javax.annotation.Nonnull;

View File

@@ -96,7 +96,7 @@ public class JEIComputerCraft implements IModPlugin
IRecipeCategory<?> category = registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING, false );
if( category != null )
{
for( Object wrapper : registry.getRecipes( category, null, false ) )
for( Object wrapper : registry.getRecipes( category, List.of(), false ) )
{
if( !(wrapper instanceof Recipe) ) continue;
ResourceLocation id = ((Recipe<?>) wrapper).getId();

View File

@@ -97,10 +97,10 @@ class RecipeResolver implements IRecipeManagerPlugin
@Override
public <V> List<ResourceLocation> getRecipeCategoryUids( @Nonnull IFocus<V> focus )
{
V value = focus.getValue();
V value = focus.getTypedValue().getIngredient();
if( !(value instanceof ItemStack stack) ) return Collections.emptyList();
switch( focus.getMode() )
switch( focus.getRole() )
{
case INPUT:
return stack.getItem() instanceof ITurtleItem || stack.getItem() instanceof ItemPocketComputer ||
@@ -120,12 +120,12 @@ class RecipeResolver implements IRecipeManagerPlugin
@Override
public <T, V> List<T> getRecipes( @Nonnull IRecipeCategory<T> recipeCategory, @Nonnull IFocus<V> focus )
{
if( !(focus.getValue() instanceof ItemStack stack) || !recipeCategory.getUid().equals( VanillaRecipeCategoryUid.CRAFTING ) )
if( !(focus.getTypedValue().getIngredient() instanceof ItemStack stack) || !recipeCategory.getUid().equals( VanillaRecipeCategoryUid.CRAFTING ) )
{
return Collections.emptyList();
}
switch( focus.getMode() )
switch( focus.getRole() )
{
case INPUT:
return cast( findRecipesWithInput( stack ) );

View File

@@ -46,7 +46,7 @@ public class DiskRecipe extends CustomRecipe
if( paperFound ) return false;
paperFound = true;
}
else if( Tags.Items.DUSTS_REDSTONE.contains( stack.getItem() ) )
else if( stack.is( Tags.Items.DUSTS_REDSTONE ) )
{
if( redstoneFound ) return false;
redstoneFound = true;
@@ -73,7 +73,7 @@ public class DiskRecipe extends CustomRecipe
if( stack.isEmpty() ) continue;
if( stack.getItem() != Items.PAPER && !Tags.Items.DUSTS_REDSTONE.contains( stack.getItem() ) )
if( stack.getItem() != Items.PAPER && !stack.is( Tags.Items.DUSTS_REDSTONE ) )
{
DyeColor dye = ColourUtils.getStackColour( stack );
if( dye != null ) tracker.addColour( dye );

View File

@@ -80,11 +80,11 @@ public final class PrintoutRecipe extends CustomRecipe
numPages++;
numPrintouts++;
}
else if( Tags.Items.STRING.contains( stack.getItem() ) && !stringFound )
else if( stack.is( Tags.Items.STRING ) && !stringFound )
{
stringFound = true;
}
else if( Tags.Items.LEATHER.contains( stack.getItem() ) && !leatherFound )
else if( stack.is( Tags.Items.LEATHER ) && !leatherFound )
{
leatherFound = true;
}

View File

@@ -16,11 +16,11 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fmllegacy.network.NetworkDirection;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.fmllegacy.network.NetworkRegistry;
import net.minecraftforge.fmllegacy.network.PacketDistributor;
import net.minecraftforge.fmllegacy.network.simple.SimpleChannel;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel;
import java.util.function.Function;
import java.util.function.Supplier;

View File

@@ -6,7 +6,7 @@
package dan200.computercraft.shared.network;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.shared.command.text.TableBuilder;
import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -7,7 +7,7 @@ package dan200.computercraft.shared.network.client;
import dan200.computercraft.ComputerCraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
public class ComputerDeletedClientMessage extends ComputerClientMessage
{

View File

@@ -6,7 +6,7 @@
package dan200.computercraft.shared.network.client;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -12,7 +12,7 @@ import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -13,7 +13,7 @@ import net.minecraft.network.chat.TextComponent;
import net.minecraft.sounds.SoundEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -11,7 +11,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;

View File

@@ -11,7 +11,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;
import java.util.UUID;

View File

@@ -12,7 +12,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;
import java.util.UUID;

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;
import java.util.UUID;

View File

@@ -17,7 +17,7 @@ import dan200.computercraft.shared.UpgradeManager;
import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryManager;
@@ -42,8 +42,8 @@ public class UpgradesLoadedMessage implements NetworkMessage
public UpgradesLoadedMessage( @Nonnull FriendlyByteBuf buf )
{
turtleUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( TurtleUpgradeSerialiser.TYPE ) );
pocketUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( PocketUpgradeSerialiser.TYPE ) );
turtleUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( TurtleUpgradeSerialiser.REGISTRY_ID ) );
pocketUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( PocketUpgradeSerialiser.REGISTRY_ID ) );
}
private <R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> Map<String, UpgradeManager.UpgradeWrapper<R, T>> fromBytes(

View File

@@ -13,7 +13,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -12,16 +12,16 @@ import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.fmllegacy.network.IContainerFactory;
import net.minecraftforge.fmllegacy.network.NetworkHooks;
import net.minecraftforge.common.extensions.IForgeMenuType;
import net.minecraftforge.network.IContainerFactory;
import net.minecraftforge.network.NetworkHooks;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An extension over the basic {@link IForgeContainerType}/{@link NetworkHooks#openGui(ServerPlayer, MenuProvider, Consumer)}
* An extension over the basic {@link IForgeMenuType}/{@link NetworkHooks#openGui(ServerPlayer, MenuProvider, Consumer)}
* hooks, with a more convenient way of reading and writing data.
*/
public interface ContainerData
@@ -35,7 +35,7 @@ public interface ContainerData
static <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> toType( Function<FriendlyByteBuf, T> reader, Factory<C, T> factory )
{
return IForgeContainerType.create( ( id, player, data ) -> factory.create( id, player, reader.apply( data ) ) );
return IForgeMenuType.create( ( id, player, data ) -> factory.create( id, player, reader.apply( data ) ) );
}
static <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> toType( Function<FriendlyByteBuf, T> reader, FixedFactory<C, T> factory )
@@ -60,7 +60,7 @@ public interface ContainerData
private FixedPointContainerFactory( Function<FriendlyByteBuf, T> reader, FixedFactory<C, T> factory )
{
MenuType<C> type = this.type = IForgeContainerType.create( this );
MenuType<C> type = this.type = IForgeMenuType.create( this );
impl = ( id, player, data ) -> factory.create( type, id, player, reader.apply( data ) );
}

View File

@@ -8,7 +8,7 @@ package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.InputState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.InputState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -10,7 +10,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

View File

@@ -9,7 +9,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;

View File

@@ -13,7 +13,7 @@ import dan200.computercraft.shared.network.NetworkHandler;
import io.netty.handler.codec.DecoderException;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.network.NetworkEvent;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
@@ -160,6 +160,7 @@ public class UploadFileMessage extends ComputerServerMessage
contents.position( currentOffset ).limit( currentOffset + canWrite );
slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) );
currentOffset += canWrite;
remaining -= canWrite;
}
contents.position( 0 ).limit( capacity );

View File

@@ -37,9 +37,9 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fmllegacy.network.NetworkHooks;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.minecraftforge.network.NetworkHooks;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -137,9 +137,8 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory
}
}
@Nonnull
@Override
public CompoundTag save( @Nonnull CompoundTag nbt )
public void saveAdditional( @Nonnull CompoundTag nbt )
{
if( customName != null ) nbt.putString( NBT_NAME, Component.Serializer.toJson( customName ) );
@@ -149,7 +148,7 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory
diskStack.save( item );
nbt.put( NBT_ITEM, item );
}
return super.save( nbt );
super.saveAdditional( nbt );
}
void serverTick()

View File

@@ -45,7 +45,7 @@ public class GenericPeripheralProvider
for( Capability<?> capability : capabilities )
{
LazyOptional<?> wrapper = tile.getCapability( capability );
LazyOptional<?> wrapper = CapabilityUtil.getCapability( tile, capability, side );
wrapper.ifPresent( contents -> {
List<NamedMethod<PeripheralMethod>> capabilityMethods = PeripheralMethod.GENERATOR.getMethods( contents.getClass() );
if( capabilityMethods.isEmpty() ) return;

View File

@@ -26,7 +26,7 @@ public class BlockData
stateTable.put( property.getName(), getPropertyValue( property, entry.getValue() ) );
}
data.put( "state", stateTable );
data.put( "tags", DataHelpers.getTags( state.getBlock().getTags() ) );
data.put( "tags", DataHelpers.getTags( state.getTags() ) );
return data;
}

View File

@@ -5,26 +5,31 @@
*/
package dan200.computercraft.shared.peripheral.generic.data;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class DataHelpers
{
private DataHelpers()
{}
@Nonnull
public static Map<String, Boolean> getTags( @Nonnull Collection<ResourceLocation> tags )
public static <T> Map<String, Boolean> getTags( Holder.Reference<T> object )
{
Map<String, Boolean> result = new HashMap<>( tags.size() );
for( ResourceLocation location : tags ) result.put( location.toString(), true );
return result;
return getTags( object.tags() );
}
@Nonnull
public static <T> Map<String, Boolean> getTags( @Nonnull Stream<TagKey<T>> tags )
{
return tags.collect( Collectors.toMap( x -> x.location().toString(), x -> true ) );
}
@Nullable

View File

@@ -24,7 +24,10 @@ public class FluidData
public static <T extends Map<? super String, Object>> T fill( @Nonnull T data, @Nonnull FluidStack stack )
{
fillBasic( data, stack );
data.put( "tags", DataHelpers.getTags( stack.getFluid().getTags() ) );
// FluidStack doesn't have a getTags method, so we need to use the deprecated builtInRegistryHolder.
@SuppressWarnings( "deprecation" )
var holder = stack.getFluid().builtInRegistryHolder();
data.put( "tags", DataHelpers.getTags( holder ) );
return data;
}
}

View File

@@ -60,12 +60,12 @@ public class ItemData
data.put( "maxDamage", stack.getMaxDamage() );
}
if( stack.getItem().showDurabilityBar( stack ) )
if( stack.getItem().isBarVisible( stack ) )
{
data.put( "durability", stack.getItem().getDurabilityForDisplay( stack ) );
data.put( "durability", stack.getItem().getBarWidth( stack ) / 13.0 );
}
data.put( "tags", DataHelpers.getTags( stack.getItem().getTags() ) );
data.put( "tags", DataHelpers.getTags( stack.getTags() ) );
CompoundTag tag = stack.getTag();
if( tag != null && tag.contains( "display", Tag.TAG_COMPOUND ) )

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