mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-11-03 23:22:59 +00:00 
			
		
		
		
	Compare commits
	
		
			30 Commits
		
	
	
		
			v1.20.1-1.
			...
			mc-1.20.y
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					bb933d0100 | ||
| 
						 | 
					de078e3037 | ||
| 
						 | 
					eb584aa94d | ||
| 
						 | 
					ad70e2ad90 | ||
| 
						 | 
					2c0d8263d3 | ||
| 
						 | 
					94c864759d | ||
| 
						 | 
					2226df7224 | ||
| 
						 | 
					959bdaeb61 | ||
| 
						 | 
					06ac373e83 | ||
| 
						 | 
					0aca6a4dc9 | ||
| 
						 | 
					bf203bb1f3 | ||
| 
						 | 
					cd9840d1c1 | ||
| 
						 | 
					b9a002586c | ||
| 
						 | 
					a3b07909b0 | ||
| 
						 | 
					d7786ee4b9 | ||
| 
						 | 
					188806e8b0 | ||
| 
						 | 
					01407544c9 | ||
| 
						 | 
					bd2fd9d4c8 | ||
| 
						 | 
					5c457950d8 | ||
| 
						 | 
					75f3ecce18 | ||
| 
						 | 
					688fdc40a6 | ||
| 
						 | 
					22bd5309ba | ||
| 
						 | 
					c50d56d9fa | ||
| 
						 | 
					7b9a156abc | ||
| 
						 | 
					0a9e5c78f3 | ||
| 
						 | 
					da5885ef35 | ||
| 
						 | 
					240528cce5 | ||
| 
						 | 
					83f1f86888 | ||
| 
						 | 
					9c202bd1c2 | ||
| 
						 | 
					fc834cd97f | 
							
								
								
									
										6
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							@@ -8,8 +8,10 @@ body:
 | 
				
			|||||||
    label: Minecraft Version
 | 
					    label: Minecraft Version
 | 
				
			||||||
    description: What version of Minecraft are you using?
 | 
					    description: What version of Minecraft are you using?
 | 
				
			||||||
    options:
 | 
					    options:
 | 
				
			||||||
      - 1.20.1
 | 
					      - 1.16.x
 | 
				
			||||||
      - 1.21.x
 | 
					      - 1.18.x
 | 
				
			||||||
 | 
					      - 1.19.x
 | 
				
			||||||
 | 
					      - 1.20.x
 | 
				
			||||||
  validations:
 | 
					  validations:
 | 
				
			||||||
    required: true
 | 
					    required: true
 | 
				
			||||||
- type: input
 | 
					- type: input
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.github/workflows/main-ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/main-ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,7 +14,7 @@ jobs:
 | 
				
			|||||||
    - name: 📥 Set up Java
 | 
					    - name: 📥 Set up Java
 | 
				
			||||||
      uses: actions/setup-java@v4
 | 
					      uses: actions/setup-java@v4
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        java-version: 17
 | 
					        java-version: 21
 | 
				
			||||||
        distribution: 'temurin'
 | 
					        distribution: 'temurin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: 📥 Setup Gradle
 | 
					    - name: 📥 Setup Gradle
 | 
				
			||||||
@@ -87,7 +87,7 @@ jobs:
 | 
				
			|||||||
    - name: 📥 Set up Java
 | 
					    - name: 📥 Set up Java
 | 
				
			||||||
      uses: actions/setup-java@v4
 | 
					      uses: actions/setup-java@v4
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        java-version: 17
 | 
					        java-version: 21
 | 
				
			||||||
        distribution: 'temurin'
 | 
					        distribution: 'temurin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: 📥 Setup Gradle
 | 
					    - name: 📥 Setup Gradle
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/make-doc.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/make-doc.yml
									
									
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ jobs:
 | 
				
			|||||||
    - name: 📥 Set up Java
 | 
					    - name: 📥 Set up Java
 | 
				
			||||||
      uses: actions/setup-java@v4
 | 
					      uses: actions/setup-java@v4
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        java-version: 17
 | 
					        java-version: 21
 | 
				
			||||||
        distribution: 'temurin'
 | 
					        distribution: 'temurin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: 📥 Setup Gradle
 | 
					    - name: 📥 Setup Gradle
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ repos:
 | 
				
			|||||||
    exclude: "^(.*\\.(bat)|LICENSE)$"
 | 
					    exclude: "^(.*\\.(bat)|LICENSE)$"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- repo: https://github.com/fsfe/reuse-tool
 | 
					- repo: https://github.com/fsfe/reuse-tool
 | 
				
			||||||
  rev: v4.0.3
 | 
					  rev: v2.1.0
 | 
				
			||||||
  hooks:
 | 
					  hooks:
 | 
				
			||||||
  - id: reuse
 | 
					  - id: reuse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										100
									
								
								.reuse/dep5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								.reuse/dep5
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
				
			||||||
 | 
					Source: https://github.com/cc-tweaked/cc-tweaked
 | 
				
			||||||
 | 
					Upstream-Name: CC: Tweaked
 | 
				
			||||||
 | 
					Upstream-Contact: Jonathan Coates <git@squiddev.cc>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/sounds.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
 | 
				
			||||||
 | 
					  projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/*
 | 
				
			||||||
 | 
					  projects/common/src/testMod/resources/data/cctest/structures/*
 | 
				
			||||||
 | 
					  projects/*/src/generated/*
 | 
				
			||||||
 | 
					  projects/web/src/htmlTransform/export/index.json
 | 
				
			||||||
 | 
					  projects/web/src/htmlTransform/export/items/minecraft/*
 | 
				
			||||||
 | 
					Comment: Generated/data files are CC0.
 | 
				
			||||||
 | 
					Copyright: The CC: Tweaked Developers
 | 
				
			||||||
 | 
					License: CC0-1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  doc/images/*
 | 
				
			||||||
 | 
					  package.json
 | 
				
			||||||
 | 
					  package-lock.json
 | 
				
			||||||
 | 
					  projects/common/src/client/resources/computercraft-client.mixins.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/computercraft.mixins.json
 | 
				
			||||||
 | 
					  projects/common/src/testMod/resources/computercraft-gametest.mixins.json
 | 
				
			||||||
 | 
					  projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json
 | 
				
			||||||
 | 
					  projects/common/src/testMod/resources/pack.mcmeta
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt
 | 
				
			||||||
 | 
					  projects/fabric-api/src/main/modJson/fabric.mod.json
 | 
				
			||||||
 | 
					  projects/fabric/src/client/resources/computercraft-client.fabric.mixins.json
 | 
				
			||||||
 | 
					  projects/fabric/src/main/resources/computercraft.fabric.mixins.json
 | 
				
			||||||
 | 
					  projects/fabric/src/main/resources/fabric.mod.json
 | 
				
			||||||
 | 
					  projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
 | 
				
			||||||
 | 
					  projects/fabric/src/testMod/resources/fabric.mod.json
 | 
				
			||||||
 | 
					  projects/forge/src/client/resources/computercraft-client.forge.mixins.json
 | 
				
			||||||
 | 
					  projects/forge/src/main/resources/computercraft.forge.mixins.json
 | 
				
			||||||
 | 
					  projects/web/src/frontend/mount/.settings
 | 
				
			||||||
 | 
					  projects/web/src/frontend/mount/example.nfp
 | 
				
			||||||
 | 
					  projects/web/src/frontend/mount/example.nft
 | 
				
			||||||
 | 
					  projects/web/src/frontend/mount/expr_template.lua
 | 
				
			||||||
 | 
					  projects/web/tsconfig.json
 | 
				
			||||||
 | 
					Comment: Several assets where it's inconvenient to create a .license file.
 | 
				
			||||||
 | 
					Copyright: The CC: Tweaked Developers
 | 
				
			||||||
 | 
					License: MPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  doc/logo.png
 | 
				
			||||||
 | 
					  doc/logo-darkmode.png
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/models/*
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/textures/*
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/pack.mcmeta
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/pack.png
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/help/*
 | 
				
			||||||
 | 
					  projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
 | 
				
			||||||
 | 
					  projects/web/src/htmlTransform/export/items/computercraft/*
 | 
				
			||||||
 | 
					Comment: Bulk-license original assets as CCPL.
 | 
				
			||||||
 | 
					Copyright: 2011 Daniel Ratcliffe
 | 
				
			||||||
 | 
					License: LicenseRef-CCPL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
 | 
				
			||||||
 | 
					Comment: Community-contributed license files
 | 
				
			||||||
 | 
					Copyright: 2017 The CC: Tweaked Developers
 | 
				
			||||||
 | 
					License: LicenseRef-CCPL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  projects/common/src/main/resources/assets/computercraft/lang/*
 | 
				
			||||||
 | 
					Comment: Community-contributed license files
 | 
				
			||||||
 | 
					Copyright: 2017 The CC: Tweaked Developers
 | 
				
			||||||
 | 
					License: MPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  .github/*
 | 
				
			||||||
 | 
					Comment:
 | 
				
			||||||
 | 
					  GitHub build scripts are CC0. While we could add a header to each file,
 | 
				
			||||||
 | 
					  it's unclear if it will break actions or issue templates in some way.
 | 
				
			||||||
 | 
					Copyright: Jonathan Coates <git@squiddev.cc>
 | 
				
			||||||
 | 
					License: CC0-1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:
 | 
				
			||||||
 | 
					  gradle/wrapper/*
 | 
				
			||||||
 | 
					  gradlew
 | 
				
			||||||
 | 
					  gradlew.bat
 | 
				
			||||||
 | 
					Copyright: Gradle Inc
 | 
				
			||||||
 | 
					License: Apache-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files: projects/core/src/test/resources/test-rom/data/json-parsing/*
 | 
				
			||||||
 | 
					Copyright: 2016 Nicolas Seriot
 | 
				
			||||||
 | 
					License: MIT
 | 
				
			||||||
@@ -12,6 +12,7 @@ If you've any other questions, [just ask the community][community] or [open an i
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Table of Contents
 | 
					## Table of Contents
 | 
				
			||||||
 - [Reporting issues](#reporting-issues)
 | 
					 - [Reporting issues](#reporting-issues)
 | 
				
			||||||
 | 
					 - [Translations](#translations)
 | 
				
			||||||
 - [Setting up a development environment](#setting-up-a-development-environment)
 | 
					 - [Setting up a development environment](#setting-up-a-development-environment)
 | 
				
			||||||
 - [Developing CC: Tweaked](#developing-cc-tweaked)
 | 
					 - [Developing CC: Tweaked](#developing-cc-tweaked)
 | 
				
			||||||
 - [Writing documentation](#writing-documentation)
 | 
					 - [Writing documentation](#writing-documentation)
 | 
				
			||||||
@@ -20,13 +21,17 @@ If you've any other questions, [just ask the community][community] or [open an i
 | 
				
			|||||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
 | 
					If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
 | 
				
			||||||
use the issue templates - they provide a useful hint on what information to provide.
 | 
					use the issue templates - they provide a useful hint on what information to provide.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Translations
 | 
				
			||||||
 | 
					Translations are managed through [Weblate], an online interface for managing language strings. This is synced
 | 
				
			||||||
 | 
					automatically with GitHub, so please don't submit PRs adding/changing translations!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Setting up a development environment
 | 
					## Setting up a development environment
 | 
				
			||||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
 | 
					In order to develop CC: Tweaked, you'll need to download the source code and then run it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - Make sure you've got the following software installed:
 | 
					 - Make sure you've got the following software installed:
 | 
				
			||||||
   - Java Development Kit 17 (JDK). This can be downloaded from [Adoptium].
 | 
					   - Java Development Kit (JDK). This can be downloaded from [Adoptium].
 | 
				
			||||||
   - [Git](https://git-scm.com/).
 | 
					   - [Git](https://git-scm.com/).
 | 
				
			||||||
   - [NodeJS 20 or later][node].
 | 
					   - [NodeJS][node].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - Download CC: Tweaked's source code:
 | 
					 - Download CC: Tweaked's source code:
 | 
				
			||||||
   ```
 | 
					   ```
 | 
				
			||||||
@@ -96,6 +101,7 @@ about how you can build on that until you've covered everything!
 | 
				
			|||||||
[community]: README.md#community "Get in touch with the community."
 | 
					[community]: README.md#community "Get in touch with the community."
 | 
				
			||||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
 | 
					[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
 | 
				
			||||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
 | 
					[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
 | 
				
			||||||
 | 
					[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
 | 
				
			||||||
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
 | 
					[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
 | 
				
			||||||
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
 | 
					[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
 | 
				
			||||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
 | 
					[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
								
							@@ -26,9 +26,8 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Community
 | 
					## Community
 | 
				
			||||||
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
 | 
					If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
 | 
				
			||||||
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
 | 
					ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
 | 
				
			||||||
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
 | 
					populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
 | 
				
			||||||
desktop client, or online using [KiwiIRC].
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
 | 
					We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,7 +39,7 @@ on is present.
 | 
				
			|||||||
```groovy
 | 
					```groovy
 | 
				
			||||||
repositories {
 | 
					repositories {
 | 
				
			||||||
  maven {
 | 
					  maven {
 | 
				
			||||||
    url "https://maven.squiddev.cc"
 | 
					    url "https://squiddev.cc/maven/"
 | 
				
			||||||
    content {
 | 
					    content {
 | 
				
			||||||
      includeGroup("cc.tweaked")
 | 
					      includeGroup("cc.tweaked")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -52,9 +51,8 @@ dependencies {
 | 
				
			|||||||
  compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
 | 
					  compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Forge Gradle
 | 
					  // Forge Gradle
 | 
				
			||||||
  compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
 | 
					  compileOnly("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")
 | 
				
			||||||
  compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion"))
 | 
					  runtimeOnly("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion")
 | 
				
			||||||
  runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Fabric Loom
 | 
					  // Fabric Loom
 | 
				
			||||||
  modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
 | 
					  modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
 | 
				
			||||||
@@ -87,6 +85,6 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
 | 
				
			|||||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
 | 
					[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
 | 
				
			||||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
 | 
					[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
 | 
				
			||||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
 | 
					[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
 | 
				
			||||||
 | 
					[forum]: https://forums.computercraft.cc/
 | 
				
			||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
					[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
				
			||||||
[EsperNet]: https://www.esper.net/
 | 
					[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
				
			||||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										110
									
								
								REUSE.toml
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								REUSE.toml
									
									
									
									
									
								
							@@ -1,110 +0,0 @@
 | 
				
			|||||||
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
version = 1
 | 
					 | 
				
			||||||
SPDX-PackageName = "CC: Tweaked"
 | 
					 | 
				
			||||||
SPDX-PackageSupplier = "Jonathan Coates <git@squiddev.cc>"
 | 
					 | 
				
			||||||
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
# Generated/data files are CC0.
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "CC0-1.0"
 | 
					 | 
				
			||||||
path = [
 | 
					 | 
				
			||||||
    "gradle/gradle-daemon-jvm.properties",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/sounds.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg",
 | 
					 | 
				
			||||||
    "projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/**",
 | 
					 | 
				
			||||||
    "projects/common/src/testMod/resources/data/cctest/structures/**",
 | 
					 | 
				
			||||||
    "projects/**/src/generated/**",
 | 
					 | 
				
			||||||
    "projects/web/src/htmlTransform/export/index.json",
 | 
					 | 
				
			||||||
    "projects/web/src/htmlTransform/export/items/minecraft/**",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
# Several assets where it's inconvenient to create a .license file.
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "MPL-2.0"
 | 
					 | 
				
			||||||
path = [
 | 
					 | 
				
			||||||
    "doc/images/**",
 | 
					 | 
				
			||||||
    "package.json",
 | 
					 | 
				
			||||||
    "package-lock.json",
 | 
					 | 
				
			||||||
    "projects/common/src/client/resources/computercraft-client.mixins.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/computercraft.mixins.json",
 | 
					 | 
				
			||||||
    "projects/common/src/testMod/resources/computercraft-gametest.mixins.json",
 | 
					 | 
				
			||||||
    "projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json",
 | 
					 | 
				
			||||||
    "projects/common/src/testMod/resources/pack.mcmeta",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt",
 | 
					 | 
				
			||||||
    "projects/fabric-api/src/main/modJson/fabric.mod.json",
 | 
					 | 
				
			||||||
    "projects/fabric/src/client/resources/computercraft-client.fabric.mixins.json",
 | 
					 | 
				
			||||||
    "projects/fabric/src/main/resources/computercraft.fabric.mixins.json",
 | 
					 | 
				
			||||||
    "projects/fabric/src/main/resources/fabric.mod.json",
 | 
					 | 
				
			||||||
    "projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json",
 | 
					 | 
				
			||||||
    "projects/fabric/src/testMod/resources/fabric.mod.json",
 | 
					 | 
				
			||||||
    "projects/forge/src/client/resources/computercraft-client.forge.mixins.json",
 | 
					 | 
				
			||||||
    "projects/web/src/frontend/mount/.settings",
 | 
					 | 
				
			||||||
    "projects/web/src/frontend/mount/example.nfp",
 | 
					 | 
				
			||||||
    "projects/web/src/frontend/mount/example.nft",
 | 
					 | 
				
			||||||
    "projects/web/src/frontend/mount/expr_template.lua",
 | 
					 | 
				
			||||||
    "projects/web/tsconfig.json",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
# Bulk-license original assets as CCPL.
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "2011 Daniel Ratcliffe"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "LicenseRef-CCPL"
 | 
					 | 
				
			||||||
path = [
 | 
					 | 
				
			||||||
    "doc/logo.png",
 | 
					 | 
				
			||||||
    "doc/logo-darkmode.png",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/models/**",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/textures/**",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/pack.mcmeta",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/pack.png",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/help/**",
 | 
					 | 
				
			||||||
    "projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/**",
 | 
					 | 
				
			||||||
    "projects/web/src/htmlTransform/export/items/computercraft/**",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
# Community-contributed license files
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "LicenseRef-CCPL"
 | 
					 | 
				
			||||||
path = [
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/pt_br.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json",
 | 
					 | 
				
			||||||
    "projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
# Community-contributed license files
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "MPL-2.0"
 | 
					 | 
				
			||||||
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
# GitHub build scripts are CC0. While we could add a header to each file,
 | 
					 | 
				
			||||||
# it's unclear if it will break actions or issue templates in some way.
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "Jonathan Coates <git@squiddev.cc>"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "CC0-1.0"
 | 
					 | 
				
			||||||
path = ".github/**"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
path = ["gradle/wrapper/**", "gradlew", "gradlew.bat"]
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "Gradle Inc"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "Apache-2.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[annotations]]
 | 
					 | 
				
			||||||
path = "projects/core/src/test/resources/test-rom/data/json-parsing/**"
 | 
					 | 
				
			||||||
SPDX-FileCopyrightText = "2016 Nicolas Seriot"
 | 
					 | 
				
			||||||
SPDX-License-Identifier = "MIT"
 | 
					 | 
				
			||||||
@@ -14,18 +14,14 @@ repositories {
 | 
				
			|||||||
    mavenCentral()
 | 
					    mavenCentral()
 | 
				
			||||||
    gradlePluginPortal()
 | 
					    gradlePluginPortal()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    maven("https://maven.minecraftforge.net") {
 | 
					    maven("https://maven.neoforged.net/releases") {
 | 
				
			||||||
        name = "Forge"
 | 
					        name = "NeoForge"
 | 
				
			||||||
        content {
 | 
					        content {
 | 
				
			||||||
            includeGroup("net.minecraftforge")
 | 
					            includeGroup("net.minecraftforge")
 | 
				
			||||||
            includeGroup("net.minecraftforge.gradle")
 | 
					            includeGroup("net.neoforged")
 | 
				
			||||||
        }
 | 
					            includeGroup("net.neoforged.gradle")
 | 
				
			||||||
    }
 | 
					            includeModule("codechicken", "DiffPatch")
 | 
				
			||||||
 | 
					            includeModule("net.covers1624", "Quack")
 | 
				
			||||||
    maven("https://maven.parchmentmc.org") {
 | 
					 | 
				
			||||||
        name = "Librarian"
 | 
					 | 
				
			||||||
        content {
 | 
					 | 
				
			||||||
            includeGroupByRegex("^org\\.parchmentmc.*")
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,7 +32,7 @@ repositories {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    maven("https://maven.squiddev.cc") {
 | 
					    maven("https://squiddev.cc/maven") {
 | 
				
			||||||
        name = "SquidDev"
 | 
					        name = "SquidDev"
 | 
				
			||||||
        content {
 | 
					        content {
 | 
				
			||||||
            includeGroup("cc.tweaked.vanilla-extract")
 | 
					            includeGroup("cc.tweaked.vanilla-extract")
 | 
				
			||||||
@@ -51,10 +47,9 @@ dependencies {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    implementation(libs.curseForgeGradle)
 | 
					    implementation(libs.curseForgeGradle)
 | 
				
			||||||
    implementation(libs.fabric.loom)
 | 
					    implementation(libs.fabric.loom)
 | 
				
			||||||
    implementation(libs.forgeGradle)
 | 
					 | 
				
			||||||
    implementation(libs.ideaExt)
 | 
					    implementation(libs.ideaExt)
 | 
				
			||||||
    implementation(libs.librarian)
 | 
					 | 
				
			||||||
    implementation(libs.minotaur)
 | 
					    implementation(libs.minotaur)
 | 
				
			||||||
 | 
					    implementation(libs.neoGradle.userdev)
 | 
				
			||||||
    implementation(libs.vanillaExtract)
 | 
					    implementation(libs.vanillaExtract)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,8 @@ import cc.tweaked.gradle.IdeaRunConfigurations
 | 
				
			|||||||
import cc.tweaked.gradle.MinecraftConfigurations
 | 
					import cc.tweaked.gradle.MinecraftConfigurations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
plugins {
 | 
					plugins {
 | 
				
			||||||
    id("net.minecraftforge.gradle")
 | 
					 | 
				
			||||||
    // We must apply java-convention after Forge, as we need the fg extension to be present.
 | 
					 | 
				
			||||||
    id("cc-tweaked.java-convention")
 | 
					    id("cc-tweaked.java-convention")
 | 
				
			||||||
    id("org.parchmentmc.librarian.forgegradle")
 | 
					    id("net.neoforged.gradle.userdev")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
plugins.apply(CCTweakedPlugin::class.java)
 | 
					plugins.apply(CCTweakedPlugin::class.java)
 | 
				
			||||||
@@ -21,10 +19,15 @@ plugins.apply(CCTweakedPlugin::class.java)
 | 
				
			|||||||
val mcVersion: String by extra
 | 
					val mcVersion: String by extra
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minecraft {
 | 
					minecraft {
 | 
				
			||||||
    val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
 | 
					    modIdentifier("computercraft")
 | 
				
			||||||
    mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg"))
 | 
					subsystems {
 | 
				
			||||||
 | 
					    parchment {
 | 
				
			||||||
 | 
					        val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
 | 
				
			||||||
 | 
					        minecraftVersion = libs.findVersion("parchmentMc").get().toString()
 | 
				
			||||||
 | 
					        mappingsVersion = libs.findVersion("parchment").get().toString()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MinecraftConfigurations.setup(project)
 | 
					MinecraftConfigurations.setup(project)
 | 
				
			||||||
@@ -32,13 +35,3 @@ MinecraftConfigurations.setup(project)
 | 
				
			|||||||
extensions.configure(CCTweakedExtension::class.java) {
 | 
					extensions.configure(CCTweakedExtension::class.java) {
 | 
				
			||||||
    linters(minecraft = true, loader = "forge")
 | 
					    linters(minecraft = true, loader = "forge")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
dependencies {
 | 
					 | 
				
			||||||
    val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
 | 
					 | 
				
			||||||
    "minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
tasks.configureEach {
 | 
					 | 
				
			||||||
    // genIntellijRuns isn't registered until much later, so we need this silly hijinks.
 | 
					 | 
				
			||||||
    if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,23 +38,15 @@ java {
 | 
				
			|||||||
repositories {
 | 
					repositories {
 | 
				
			||||||
    mavenCentral()
 | 
					    mavenCentral()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val mainMaven = maven("https://maven.squiddev.cc/mirror") {
 | 
					    val mainMaven = maven("https://squiddev.cc/maven") {
 | 
				
			||||||
        name = "SquidDev"
 | 
					        name = "SquidDev"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    exclusiveContent {
 | 
					    exclusiveContent {
 | 
				
			||||||
        forRepositories(mainMaven)
 | 
					        forRepositories(mainMaven)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Include the ForgeGradle repository if present. This requires that ForgeGradle is already present, which we
 | 
					 | 
				
			||||||
        // enforce in our Forge overlay.
 | 
					 | 
				
			||||||
        val fg =
 | 
					 | 
				
			||||||
            project.extensions.findByType(net.minecraftforge.gradle.userdev.DependencyManagementExtension::class.java)
 | 
					 | 
				
			||||||
        if (fg != null) forRepositories(fg.repository)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        filter {
 | 
					        filter {
 | 
				
			||||||
            includeGroup("cc.tweaked")
 | 
					            includeGroup("cc.tweaked")
 | 
				
			||||||
            // Things we mirror
 | 
					            // Things we mirror
 | 
				
			||||||
            includeGroup("com.simibubi.create")
 | 
					 | 
				
			||||||
            includeGroup("commoble.morered")
 | 
					            includeGroup("commoble.morered")
 | 
				
			||||||
            includeGroup("dev.architectury")
 | 
					            includeGroup("dev.architectury")
 | 
				
			||||||
            includeGroup("dev.emi")
 | 
					            includeGroup("dev.emi")
 | 
				
			||||||
@@ -64,7 +56,6 @@ repositories {
 | 
				
			|||||||
            includeGroup("mezz.jei")
 | 
					            includeGroup("mezz.jei")
 | 
				
			||||||
            includeGroup("org.teavm")
 | 
					            includeGroup("org.teavm")
 | 
				
			||||||
            includeModule("com.terraformersmc", "modmenu")
 | 
					            includeModule("com.terraformersmc", "modmenu")
 | 
				
			||||||
            includeModule("me.lucko", "fabric-permissions-api")
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -86,8 +77,16 @@ dependencies {
 | 
				
			|||||||
// Configure default JavaCompile tasks with our arguments.
 | 
					// Configure default JavaCompile tasks with our arguments.
 | 
				
			||||||
sourceSets.all {
 | 
					sourceSets.all {
 | 
				
			||||||
    tasks.named(compileJavaTaskName, JavaCompile::class.java) {
 | 
					    tasks.named(compileJavaTaskName, JavaCompile::class.java) {
 | 
				
			||||||
        // Processing just gives us "No processor claimed any of these annotations", so skip that!
 | 
					
 | 
				
			||||||
        options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing"))
 | 
					        options.compilerArgs.addAll(
 | 
				
			||||||
 | 
					            listOf(
 | 
				
			||||||
 | 
					                "-Xlint",
 | 
				
			||||||
 | 
					                // Processing just gives us "No processor claimed any of these annotations", so skip that!
 | 
				
			||||||
 | 
					                "-Xlint:-processing",
 | 
				
			||||||
 | 
					                // We violate this pattern too often for it to be a helpful warning. Something to improve one day!
 | 
				
			||||||
 | 
					                "-Xlint:-this-escape",
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        options.errorprone {
 | 
					        options.errorprone {
 | 
				
			||||||
            check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
 | 
					            check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
 | 
				
			||||||
@@ -134,8 +133,8 @@ tasks.processResources {
 | 
				
			|||||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
 | 
					tasks.withType(AbstractArchiveTask::class.java).configureEach {
 | 
				
			||||||
    isPreserveFileTimestamps = false
 | 
					    isPreserveFileTimestamps = false
 | 
				
			||||||
    isReproducibleFileOrder = true
 | 
					    isReproducibleFileOrder = true
 | 
				
			||||||
    filePermissions {}
 | 
					    dirMode = Integer.valueOf("755", 8)
 | 
				
			||||||
    dirPermissions {}
 | 
					    fileMode = Integer.valueOf("664", 8)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tasks.jar {
 | 
					tasks.jar {
 | 
				
			||||||
@@ -155,7 +154,7 @@ tasks.javadoc {
 | 
				
			|||||||
    options {
 | 
					    options {
 | 
				
			||||||
        val stdOptions = this as StandardJavadocDocletOptions
 | 
					        val stdOptions = this as StandardJavadocDocletOptions
 | 
				
			||||||
        stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
 | 
					        stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
 | 
				
			||||||
        stdOptions.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
 | 
					        stdOptions.links("https://docs.oracle.com/en/java/javase/21/docs/api/")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ publishing {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
        maven("https://maven.squiddev.cc") {
 | 
					        maven("https://squiddev.cc/maven") {
 | 
				
			||||||
            name = "SquidDev"
 | 
					            name = "SquidDev"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            credentials(PasswordCredentials::class)
 | 
					            credentials(PasswordCredentials::class)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ import org.gradle.api.tasks.SourceSet
 | 
				
			|||||||
import org.gradle.api.tasks.bundling.Jar
 | 
					import org.gradle.api.tasks.bundling.Jar
 | 
				
			||||||
import org.gradle.api.tasks.compile.JavaCompile
 | 
					import org.gradle.api.tasks.compile.JavaCompile
 | 
				
			||||||
import org.gradle.api.tasks.javadoc.Javadoc
 | 
					import org.gradle.api.tasks.javadoc.Javadoc
 | 
				
			||||||
 | 
					import org.gradle.configurationcache.extensions.capitalized
 | 
				
			||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
 | 
					import org.gradle.language.base.plugins.LifecycleBasePlugin
 | 
				
			||||||
import org.gradle.language.jvm.tasks.ProcessResources
 | 
					import org.gradle.language.jvm.tasks.ProcessResources
 | 
				
			||||||
import org.gradle.process.JavaForkOptions
 | 
					import org.gradle.process.JavaForkOptions
 | 
				
			||||||
@@ -180,7 +181,7 @@ abstract class CCTweakedExtension(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
 | 
					    fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
 | 
				
			||||||
        val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
 | 
					        val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
 | 
				
			||||||
        val reportTaskName = "jacoco${task.name.capitalise()}Report"
 | 
					        val reportTaskName = "jacoco${task.name.capitalized()}Report"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
 | 
					        val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
 | 
				
			||||||
        task.configure {
 | 
					        task.configure {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,6 +42,6 @@ class CCTweakedPlugin : Plugin<Project> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
        val JAVA_VERSION = JavaLanguageVersion.of(17)
 | 
					        val JAVA_VERSION = JavaLanguageVersion.of(21)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,15 +155,3 @@ fun getNextVersion(version: String): String {
 | 
				
			|||||||
    if (dashIndex >= 0) out.append(version, dashIndex, version.length)
 | 
					    if (dashIndex >= 0) out.append(version, dashIndex, version.length)
 | 
				
			||||||
    return out.toString()
 | 
					    return out.toString()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Capitalise the first letter of the string.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This is a replacement for the now deprecated [String.capitalize].
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
fun String.capitalise(): String {
 | 
					 | 
				
			||||||
    if (isEmpty()) return this
 | 
					 | 
				
			||||||
    val first = this[0]
 | 
					 | 
				
			||||||
    val firstTitle = first.titlecaseChar()
 | 
					 | 
				
			||||||
    return if (first == firstTitle) this else firstTitle + substring(1)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,23 +4,59 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package cc.tweaked.gradle
 | 
					package cc.tweaked.gradle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.minecraftforge.gradle.common.util.RunConfig
 | 
					import net.neoforged.gradle.common.runs.run.RunImpl
 | 
				
			||||||
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal
 | 
					import net.neoforged.gradle.common.runs.tasks.RunExec
 | 
				
			||||||
 | 
					import net.neoforged.gradle.dsl.common.extensions.RunnableSourceSet
 | 
				
			||||||
 | 
					import net.neoforged.gradle.dsl.common.runs.run.Run
 | 
				
			||||||
import org.gradle.api.plugins.JavaPluginExtension
 | 
					import org.gradle.api.plugins.JavaPluginExtension
 | 
				
			||||||
import org.gradle.api.tasks.JavaExec
 | 
					import org.gradle.api.tasks.JavaExec
 | 
				
			||||||
 | 
					import org.gradle.api.tasks.SourceSet
 | 
				
			||||||
import org.gradle.jvm.toolchain.JavaToolchainService
 | 
					import org.gradle.jvm.toolchain.JavaToolchainService
 | 
				
			||||||
 | 
					import org.gradle.kotlin.dsl.assign
 | 
				
			||||||
 | 
					import org.gradle.kotlin.dsl.create
 | 
				
			||||||
 | 
					import org.gradle.kotlin.dsl.findByType
 | 
				
			||||||
import java.nio.file.Files
 | 
					import java.nio.file.Files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Set [JavaExec] task to run a given [RunConfig].
 | 
					 * Set [JavaExec] task to run a given [RunConfig].
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * See also [RunExec].
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
fun JavaExec.setRunConfig(config: RunConfig) {
 | 
					fun JavaExec.setRunConfig(config: Run) {
 | 
				
			||||||
    dependsOn("prepareRuns")
 | 
					    mainClass.set(config.mainClass)
 | 
				
			||||||
    setRunConfigInternal(project, this, config)
 | 
					    workingDir = config.workingDirectory.get().asFile
 | 
				
			||||||
    doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
 | 
					    argumentProviders.add { config.programArguments.get() }
 | 
				
			||||||
 | 
					    jvmArgumentProviders.add { config.jvmArguments.get() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    environment(config.environmentVariables.get())
 | 
				
			||||||
 | 
					    systemProperties(config.systemProperties.get())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    config.modSources.get().forEach { classpath(it.runtimeClasspath) }
 | 
				
			||||||
 | 
					    classpath(config.classpath)
 | 
				
			||||||
 | 
					    classpath(config.dependencies.get().configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (config as RunImpl).taskDependencies.forEach { dependsOn(it) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    javaLauncher.set(
 | 
					    javaLauncher.set(
 | 
				
			||||||
        project.extensions.getByType(JavaToolchainService::class.java)
 | 
					        project.extensions.getByType(JavaToolchainService::class.java)
 | 
				
			||||||
            .launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
 | 
					            .launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Add a new [Run.modSource] with a specific mod id.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					fun Run.modSourceAs(sourceSet: SourceSet, mod: String) {
 | 
				
			||||||
 | 
					    // NeoGradle requires a RunnableSourceSet to be present, so we inject it into other project's source sets.
 | 
				
			||||||
 | 
					    val runnable = sourceSet.extensions.findByType<RunnableSourceSet>() ?: run {
 | 
				
			||||||
 | 
					        val extension = sourceSet.extensions.create<RunnableSourceSet>(RunnableSourceSet.NAME, project)
 | 
				
			||||||
 | 
					        extension.modIdentifier = mod
 | 
				
			||||||
 | 
					        extension.modIdentifier.finalizeValueOnRead()
 | 
				
			||||||
 | 
					        extension
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (runnable.modIdentifier.get() != mod) throw IllegalArgumentException("Multiple mod identifiers")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    modSource(sourceSet)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package cc.tweaked.gradle
 | 
					package cc.tweaked.gradle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.minecraftforge.gradle.common.util.RunConfig
 | 
					 | 
				
			||||||
import org.gradle.api.GradleException
 | 
					import org.gradle.api.GradleException
 | 
				
			||||||
import org.gradle.api.file.FileSystemOperations
 | 
					import org.gradle.api.file.FileSystemOperations
 | 
				
			||||||
import org.gradle.api.invocation.Gradle
 | 
					import org.gradle.api.invocation.Gradle
 | 
				
			||||||
@@ -65,14 +64,6 @@ abstract class ClientJavaExec : JavaExec() {
 | 
				
			|||||||
        setTestProperties()
 | 
					        setTestProperties()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Set this task to run a given [RunConfig].
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    fun setRunConfig(config: RunConfig) {
 | 
					 | 
				
			||||||
        (this as JavaExec).setRunConfig(config)
 | 
					 | 
				
			||||||
        setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Copy configuration from a task with the given name.
 | 
					     * Copy configuration from a task with the given name.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,51 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package net.minecraftforge.gradle.common.util.runs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import net.minecraftforge.gradle.common.util.RunConfig
 | 
					 | 
				
			||||||
import org.gradle.api.Project
 | 
					 | 
				
			||||||
import org.gradle.process.CommandLineArgumentProvider
 | 
					 | 
				
			||||||
import org.gradle.process.JavaExecSpec
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.util.function.Supplier
 | 
					 | 
				
			||||||
import java.util.stream.Collectors
 | 
					 | 
				
			||||||
import java.util.stream.Stream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Set up a [JavaExecSpec] to execute a [RunConfig].
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * [MinecraftRunTask] sets up all its properties when the task is executed, rather than when configured. As such, it's
 | 
					 | 
				
			||||||
 * not possible to use [cc.tweaked.gradle.copyToFull] like we do for Fabric. Instead, we set up the task manually.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Unfortunately most of the functionality we need is package-private, and so we have to put our code into the package.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
internal fun setRunConfigInternal(project: Project, spec: JavaExecSpec, config: RunConfig) {
 | 
					 | 
				
			||||||
    spec.workingDir = File(config.workingDirectory)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    spec.mainClass.set(config.main)
 | 
					 | 
				
			||||||
    for (source in config.allSources) spec.classpath(source.runtimeClasspath)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val originalTask = project.tasks.named(config.taskName, MinecraftRunTask::class.java)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Add argument and JVM argument via providers, to be as lazy as possible with fetching artifacts.
 | 
					 | 
				
			||||||
    val lazyTokens = RunConfigGenerator.configureTokensLazy(
 | 
					 | 
				
			||||||
        project, config, RunConfigGenerator.mapModClassesToGradle(project, config),
 | 
					 | 
				
			||||||
        originalTask.get().minecraftArtifacts,
 | 
					 | 
				
			||||||
        originalTask.get().runtimeClasspathArtifacts,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    spec.argumentProviders.add(
 | 
					 | 
				
			||||||
        CommandLineArgumentProvider {
 | 
					 | 
				
			||||||
            RunConfigGenerator.getArgsStream(config, lazyTokens, false).toList()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    spec.jvmArgumentProviders.add(
 | 
					 | 
				
			||||||
        CommandLineArgumentProvider {
 | 
					 | 
				
			||||||
            (if (config.isClient) config.jvmArgs + originalTask.get().additionalClientArgs.get() else config.jvmArgs).map { config.replace(lazyTokens, it) } +
 | 
					 | 
				
			||||||
                config.properties.map { (k, v) -> "-D${k}=${config.replace(lazyTokens, v)}" }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for ((key, value) in config.environment) spec.environment(key, config.replace(lazyTokens, value))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -22,4 +22,7 @@ SPDX-License-Identifier: MPL-2.0
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <!-- Allow underscores in our test classes. -->
 | 
					    <!-- Allow underscores in our test classes. -->
 | 
				
			||||||
    <suppress checks="MethodName" files=".*(Contract|Test).java" />
 | 
					    <suppress checks="MethodName" files=".*(Contract|Test).java" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Allow underscores in Mixin classes -->
 | 
				
			||||||
 | 
					    <suppress checks="TypeName" files=".*[\\/]mixin[\\/].*.java" />
 | 
				
			||||||
</suppressions>
 | 
					</suppressions>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,7 +191,7 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
> [Confused?][!NOTE]
 | 
					> [Confused?][!NOTE]
 | 
				
			||||||
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
 | 
					> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
 | 
				
			||||||
> cover. That said, don't be afraid to ask [the community for help][community].
 | 
					> cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
 | 
					It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
 | 
				
			||||||
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
 | 
					the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
 | 
				
			||||||
@@ -205,4 +205,5 @@ This is, I'm afraid, left as an exercise to the reader.
 | 
				
			|||||||
[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
 | 
					[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
 | 
				
			||||||
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
 | 
					[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
 | 
				
			||||||
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
 | 
					[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
 | 
				
			||||||
[Community]: /#community
 | 
					[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
				
			||||||
 | 
					[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,11 +50,7 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
 | 
				
			|||||||
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
 | 
					Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
 | 
				
			||||||
various APIs and peripherals provided by the mod.
 | 
					various APIs and peripherals provided by the mod.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<h2 id="community">Community</h2>
 | 
					If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
 | 
				
			||||||
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
 | 
					 | 
				
			||||||
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
 | 
					 | 
				
			||||||
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
 | 
					 | 
				
			||||||
desktop client, or online using [KiwiIRC].
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Get Involved
 | 
					## Get Involved
 | 
				
			||||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
 | 
					CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
 | 
				
			||||||
@@ -69,5 +65,4 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
 | 
				
			|||||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
 | 
					[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
 | 
				
			||||||
[lua]: https://www.lua.org/ "Lua's main website"
 | 
					[lua]: https://www.lua.org/ "Lua's main website"
 | 
				
			||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
					[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
				
			||||||
[EsperNet]: https://www.esper.net/
 | 
					[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
				
			||||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,11 +50,7 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
 | 
				
			|||||||
Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the
 | 
					Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the
 | 
				
			||||||
various APIs and peripherals provided by the mod.
 | 
					various APIs and peripherals provided by the mod.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Community
 | 
					If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
 | 
				
			||||||
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
 | 
					 | 
				
			||||||
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
 | 
					 | 
				
			||||||
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
 | 
					 | 
				
			||||||
desktop client, or online using [KiwiIRC].
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Get Involved
 | 
					## Get Involved
 | 
				
			||||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
 | 
					CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
 | 
				
			||||||
@@ -64,5 +60,4 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
 | 
				
			|||||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
 | 
					[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
 | 
				
			||||||
[lua]: https://www.lua.org/ "Lua's main website"
 | 
					[lua]: https://www.lua.org/ "Lua's main website"
 | 
				
			||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
					[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
				
			||||||
[EsperNet]: https://www.esper.net/
 | 
					[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
				
			||||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,13 +25,13 @@ as documentation for breaking changes and "gotchas" one should look out for betw
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 - Update to Lua 5.2:
 | 
					 - Update to Lua 5.2:
 | 
				
			||||||
   - Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
 | 
					   - Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
 | 
				
			||||||
   - Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. [`getfenv`]/[`setfenv`]
 | 
					   - Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
 | 
				
			||||||
     now only work on Lua functions with an `_ENV` upvalue. [`getfenv`] will return the global environment when called
 | 
					     now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
 | 
				
			||||||
     with other functions, and [`setfenv`] will have no effect.
 | 
					     with other functions, and `setfenv` will have no effect.
 | 
				
			||||||
   - [`load`]/[`loadstring`] defaults to using the global environment (`_G`) rather than the current coroutine's
 | 
					   - `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
 | 
				
			||||||
     environment.
 | 
					     environment.
 | 
				
			||||||
   - Support for dumping functions ([`string.dump`]) and loading binary chunks has been removed.
 | 
					   - Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
 | 
				
			||||||
   - [`math.random`] now uses Lua 5.4's random number generator.
 | 
					   - `math.random` now uses Lua 5.4's random number generator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
 | 
					 - File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,7 +44,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
 | 
				
			|||||||
   `keys.enter` constant was queued when the key was pressed)
 | 
					   `keys.enter` constant was queued when the key was pressed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
 | 
					 - Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
 | 
				
			||||||
   result [`turtle.inspect`] no longer provides block metadata, and [`turtle.getItemDetail`] no longer provides damage.
 | 
					   result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   - Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
 | 
					   - Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
 | 
				
			||||||
     more understandable format.
 | 
					     more understandable format.
 | 
				
			||||||
@@ -70,7 +70,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
 | 
				
			|||||||
 - Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
 | 
					 - Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## ComputerCraft 1.80pr1 {#cc-1.80}
 | 
					## ComputerCraft 1.80pr1 {#cc-1.80}
 | 
				
			||||||
 - Programs run via [`shell.run`] are now started in their own isolated environment. This means globals set by programs
 | 
					 - Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
 | 
				
			||||||
   will not be accessible outside of this program.
 | 
					   will not be accessible outside of this program.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
 | 
					 - Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,8 @@ kotlin.stdlib.default.dependency=false
 | 
				
			|||||||
kotlin.jvm.target.validation.mode=error
 | 
					kotlin.jvm.target.validation.mode=error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Mod properties
 | 
					# Mod properties
 | 
				
			||||||
isUnstable=false
 | 
					isUnstable=true
 | 
				
			||||||
modVersion=1.112.0
 | 
					modVersion=1.111.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
 | 
					# Minecraft properties: We want to configure this here so we can read it in settings.gradle
 | 
				
			||||||
mcVersion=1.20.1
 | 
					mcVersion=1.20.6
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
#This file is generated by updateDaemonJvm
 | 
					 | 
				
			||||||
toolchainVersion=17
 | 
					 | 
				
			||||||
@@ -6,27 +6,27 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Minecraft
 | 
					# Minecraft
 | 
				
			||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
 | 
					# MC version is specified in gradle.properties, as we need that in settings.gradle.
 | 
				
			||||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
 | 
					# Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml
 | 
				
			||||||
fabric-api = "0.86.1+1.20.1"
 | 
					fabric-api = "0.98.0+1.20.6"
 | 
				
			||||||
fabric-loader = "0.14.21"
 | 
					fabric-loader = "0.15.10"
 | 
				
			||||||
forge = "47.1.0"
 | 
					neoForge = "20.6.48-beta"
 | 
				
			||||||
forgeSpi = "7.0.1"
 | 
					neoForgeSpi = "8.0.1"
 | 
				
			||||||
mixin = "0.8.5"
 | 
					mixin = "0.8.5"
 | 
				
			||||||
parchment = "2023.08.20"
 | 
					parchment = "2024.05.01"
 | 
				
			||||||
parchmentMc = "1.20.1"
 | 
					parchmentMc = "1.20.6"
 | 
				
			||||||
yarn = "1.20.1+build.10"
 | 
					yarn = "1.20.6+build.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Core dependencies (these versions are tied to the version Minecraft uses)
 | 
					# Core dependencies (these versions are tied to the version Minecraft uses)
 | 
				
			||||||
fastutil = "8.5.9"
 | 
					fastutil = "8.5.12"
 | 
				
			||||||
guava = "31.1-jre"
 | 
					guava = "32.1.2-jre"
 | 
				
			||||||
netty = "4.1.82.Final"
 | 
					netty = "4.1.97.Final"
 | 
				
			||||||
slf4j = "2.0.1"
 | 
					slf4j = "2.0.9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Core dependencies (independent of Minecraft)
 | 
					# Core dependencies (independent of Minecraft)
 | 
				
			||||||
asm = "9.6"
 | 
					asm = "9.6"
 | 
				
			||||||
autoService = "1.1.1"
 | 
					autoService = "1.1.1"
 | 
				
			||||||
checkerFramework = "3.42.0"
 | 
					checkerFramework = "3.42.0"
 | 
				
			||||||
cobalt = "0.9.3"
 | 
					cobalt = { strictly = "0.9.3" }
 | 
				
			||||||
commonsCli = "1.6.0"
 | 
					commonsCli = "1.6.0"
 | 
				
			||||||
jetbrainsAnnotations = "24.1.0"
 | 
					jetbrainsAnnotations = "24.1.0"
 | 
				
			||||||
jsr305 = "3.0.2"
 | 
					jsr305 = "3.0.2"
 | 
				
			||||||
@@ -36,18 +36,17 @@ kotlin-coroutines = "1.7.3"
 | 
				
			|||||||
nightConfig = "3.6.7"
 | 
					nightConfig = "3.6.7"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Minecraft mods
 | 
					# Minecraft mods
 | 
				
			||||||
emi = "1.0.8+1.20.1"
 | 
					emi = "1.1.5+1.20.6"
 | 
				
			||||||
fabricPermissions = "0.3.20230723"
 | 
					fabricPermissions = "0.3.1"
 | 
				
			||||||
iris = "1.6.4+1.20"
 | 
					iris = "1.6.14+1.20.4"
 | 
				
			||||||
jei = "15.2.0.22"
 | 
					jei = "17.3.0.48"
 | 
				
			||||||
modmenu = "7.1.0"
 | 
					modmenu = "9.0.0"
 | 
				
			||||||
moreRed = "4.0.0.4"
 | 
					moreRed = "4.0.0.4"
 | 
				
			||||||
oculus = "1.2.5"
 | 
					oculus = "1.2.5"
 | 
				
			||||||
rei = "12.0.626"
 | 
					rei = "15.0.728"
 | 
				
			||||||
rubidium = "0.6.1"
 | 
					rubidium = "0.6.1"
 | 
				
			||||||
sodium = "mc1.20-0.4.10"
 | 
					sodium = "mc1.20-0.4.10"
 | 
				
			||||||
create-forge = "0.5.1.f-33"
 | 
					mixinExtra = "0.3.5"
 | 
				
			||||||
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Testing
 | 
					# Testing
 | 
				
			||||||
hamcrest = "2.2"
 | 
					hamcrest = "2.2"
 | 
				
			||||||
@@ -58,22 +57,21 @@ jmh = "1.37"
 | 
				
			|||||||
# Build tools
 | 
					# Build tools
 | 
				
			||||||
cctJavadoc = "1.8.2"
 | 
					cctJavadoc = "1.8.2"
 | 
				
			||||||
checkstyle = "10.14.1"
 | 
					checkstyle = "10.14.1"
 | 
				
			||||||
curseForgeGradle = "1.0.14"
 | 
					curseForgeGradle = "1.1.18"
 | 
				
			||||||
errorProne-core = "2.27.0"
 | 
					errorProne-core = "2.27.0"
 | 
				
			||||||
errorProne-plugin = "3.1.0"
 | 
					errorProne-plugin = "3.1.0"
 | 
				
			||||||
fabric-loom = "1.7.1"
 | 
					fabric-loom = "1.6.7"
 | 
				
			||||||
forgeGradle = "6.0.21"
 | 
					 | 
				
			||||||
githubRelease = "2.5.2"
 | 
					githubRelease = "2.5.2"
 | 
				
			||||||
gradleVersions = "0.50.0"
 | 
					gradleVersions = "0.50.0"
 | 
				
			||||||
ideaExt = "1.1.7"
 | 
					ideaExt = "1.1.7"
 | 
				
			||||||
illuaminate = "0.1.0-73-g43ee16c"
 | 
					illuaminate = "0.1.0-73-g43ee16c"
 | 
				
			||||||
librarian = "1.+"
 | 
					 | 
				
			||||||
lwjgl = "3.3.3"
 | 
					lwjgl = "3.3.3"
 | 
				
			||||||
minotaur = "2.+"
 | 
					minotaur = "2.8.7"
 | 
				
			||||||
 | 
					neoGradle = "7.0.116"
 | 
				
			||||||
nullAway = "0.10.25"
 | 
					nullAway = "0.10.25"
 | 
				
			||||||
spotless = "6.23.3"
 | 
					spotless = "6.23.3"
 | 
				
			||||||
taskTree = "2.1.1"
 | 
					taskTree = "2.1.1"
 | 
				
			||||||
teavm = "0.11.0-SQUID.1"
 | 
					teavm = "0.10.0-SQUID.4"
 | 
				
			||||||
vanillaExtract = "0.1.3"
 | 
					vanillaExtract = "0.1.3"
 | 
				
			||||||
versionCatalogUpdate = "0.8.1"
 | 
					versionCatalogUpdate = "0.8.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,7 +84,7 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
 | 
				
			|||||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
 | 
					cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
 | 
				
			||||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
 | 
					commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
 | 
				
			||||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
 | 
					fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
 | 
				
			||||||
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
 | 
					neoForgeSpi = { module = "net.neoforged:neoforgespi", version.ref = "neoForgeSpi" }
 | 
				
			||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
 | 
					guava = { module = "com.google.guava:guava", version.ref = "guava" }
 | 
				
			||||||
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
 | 
					jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
 | 
				
			||||||
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
 | 
					jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
 | 
				
			||||||
@@ -102,18 +100,17 @@ nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref
 | 
				
			|||||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
 | 
					slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Minecraft mods
 | 
					# Minecraft mods
 | 
				
			||||||
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
 | 
					 | 
				
			||||||
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
 | 
					 | 
				
			||||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
 | 
					 | 
				
			||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
 | 
					fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
 | 
				
			||||||
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
 | 
					 | 
				
			||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
 | 
					fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
 | 
				
			||||||
 | 
					fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
 | 
				
			||||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
 | 
					fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
 | 
				
			||||||
 | 
					emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
 | 
				
			||||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
 | 
					iris = { module = "maven.modrinth:iris", version.ref = "iris" }
 | 
				
			||||||
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
 | 
					jei-api = { module = "mezz.jei:jei-1.20.4-common-api", version.ref = "jei" }
 | 
				
			||||||
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
 | 
					jei-fabric = { module = "mezz.jei:jei-1.20.4-fabric", version.ref = "jei" }
 | 
				
			||||||
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
 | 
					jei-forge = { module = "mezz.jei:jei-1.20.4-forge", version.ref = "jei" }
 | 
				
			||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
 | 
					mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
 | 
				
			||||||
 | 
					mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
 | 
				
			||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
 | 
					modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
 | 
				
			||||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
 | 
					moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
 | 
				
			||||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
 | 
					oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
 | 
				
			||||||
@@ -150,11 +147,10 @@ errorProne-core = { module = "com.google.errorprone:error_prone_core", version.r
 | 
				
			|||||||
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
 | 
					errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
 | 
				
			||||||
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
 | 
					errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
 | 
				
			||||||
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
 | 
					fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
 | 
				
			||||||
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
 | 
					 | 
				
			||||||
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
 | 
					ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
 | 
				
			||||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
 | 
					kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
 | 
				
			||||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
 | 
					 | 
				
			||||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
 | 
					minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
 | 
				
			||||||
 | 
					neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "neoGradle" }
 | 
				
			||||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
 | 
					nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
 | 
				
			||||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
 | 
					spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
 | 
				
			||||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
 | 
					teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
 | 
				
			||||||
@@ -170,11 +166,9 @@ vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = "
 | 
				
			|||||||
yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
 | 
					yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[plugins]
 | 
					[plugins]
 | 
				
			||||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
 | 
					 | 
				
			||||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
 | 
					githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
 | 
				
			||||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
 | 
					gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
 | 
				
			||||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
 | 
					kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
 | 
				
			||||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
 | 
					 | 
				
			||||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
 | 
					taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
 | 
				
			||||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
 | 
					versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -185,9 +179,9 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
 | 
				
			|||||||
# Minecraft
 | 
					# Minecraft
 | 
				
			||||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
 | 
					externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
 | 
				
			||||||
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
 | 
					externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
 | 
				
			||||||
externalMods-forge-runtime = ["jei-forge"]
 | 
					externalMods-forge-runtime = []
 | 
				
			||||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
 | 
					externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
 | 
				
			||||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
 | 
					externalMods-fabric-runtime = [] # ["jei-fabric", "modmenu"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Testing
 | 
					# Testing
 | 
				
			||||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
 | 
					test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
distributionBase=GRADLE_USER_HOME
 | 
					distributionBase=GRADLE_USER_HOME
 | 
				
			||||||
distributionPath=wrapper/dists
 | 
					distributionPath=wrapper/dists
 | 
				
			||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
 | 
				
			||||||
networkTimeout=10000
 | 
					networkTimeout=10000
 | 
				
			||||||
validateDistributionUrl=true
 | 
					validateDistributionUrl=true
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							@@ -55,7 +55,7 @@
 | 
				
			|||||||
#       Darwin, MinGW, and NonStop.
 | 
					#       Darwin, MinGW, and NonStop.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#   (3) This script is generated from the Groovy template
 | 
					#   (3) This script is generated from the Groovy template
 | 
				
			||||||
#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 | 
					#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 | 
				
			||||||
#       within the Gradle project.
 | 
					#       within the Gradle project.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#       You can find Gradle at https://github.com/gradle/gradle/.
 | 
					#       You can find Gradle at https://github.com/gradle/gradle/.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,4 +21,16 @@ tasks.javadoc {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
 | 
					    // Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
 | 
				
			||||||
    source(project(":core-api").sourceSets.main.map { it.allJava })
 | 
					    source(project(":core-api").sourceSets.main.map { it.allJava })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    options {
 | 
				
			||||||
 | 
					        this as StandardJavadocDocletOptions
 | 
				
			||||||
 | 
					        addBooleanOption("-allow-script-in-comments", true)
 | 
				
			||||||
 | 
					        bottom(
 | 
				
			||||||
 | 
					            """
 | 
				
			||||||
 | 
					            <script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
 | 
				
			||||||
 | 
					            <script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
 | 
				
			||||||
 | 
					            <link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
 | 
				
			||||||
 | 
					            """.trimIndent(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,43 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * The public API for client-only code.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see dan200.computercraft.api.ComputerCraftAPI The main API
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public final class ComputerCraftAPIClient {
 | 
					 | 
				
			||||||
    private ComputerCraftAPIClient() {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This may be called at any point after registry creation, though it is recommended to call it within your client
 | 
					 | 
				
			||||||
     * setup step.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param serialiser The turtle upgrade serialiser.
 | 
					 | 
				
			||||||
     * @param modeller   The upgrade modeller.
 | 
					 | 
				
			||||||
     * @param <T>        The type of the turtle upgrade.
 | 
					 | 
				
			||||||
     * @deprecated This method can lead to confusing load behaviour on Forge. Use
 | 
					 | 
				
			||||||
     * {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} on Fabric, or
 | 
					 | 
				
			||||||
     * {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} on Forge.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated(forRemoval = true)
 | 
					 | 
				
			||||||
    public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
 | 
					 | 
				
			||||||
        // TODO(1.20.4): Remove this
 | 
					 | 
				
			||||||
        getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static ComputerCraftAPIClientService getInstance() {
 | 
					 | 
				
			||||||
        return ComputerCraftAPIClientService.get();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
package dan200.computercraft.api.client.turtle;
 | 
					package dan200.computercraft.api.client.turtle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					import dan200.computercraft.api.upgrades.UpgradeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
 | 
					 * A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
 | 
				
			||||||
@@ -18,9 +18,9 @@ public interface RegisterTurtleUpgradeModeller {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Register a {@link TurtleUpgradeModeller}.
 | 
					     * Register a {@link TurtleUpgradeModeller}.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param serialiser The turtle upgrade serialiser.
 | 
					     * @param type     The turtle upgrade type.
 | 
				
			||||||
     * @param modeller   The upgrade modeller.
 | 
					     * @param modeller The upgrade modeller.
 | 
				
			||||||
     * @param <T>        The type of the turtle upgrade.
 | 
					     * @param <T>      The type of the turtle upgrade.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
 | 
					    <T extends ITurtleUpgrade> void register(UpgradeType<T> type, TurtleUpgradeModeller<T> modeller);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,14 +8,12 @@ import dan200.computercraft.api.client.TransformedModel;
 | 
				
			|||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
					import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
					import dan200.computercraft.api.turtle.TurtleSide;
 | 
				
			||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.client.resources.model.UnbakedModel;
 | 
					import net.minecraft.client.resources.model.UnbakedModel;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Provides models for a {@link ITurtleUpgrade}.
 | 
					 * Provides models for a {@link ITurtleUpgrade}.
 | 
				
			||||||
@@ -31,47 +29,32 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Obtain the model to be used when rendering a turtle peripheral.
 | 
					     * Obtain the model to be used when rendering a turtle peripheral.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
 | 
					     * When the current turtle is {@literal null}, this function should be constant for a given upgrade, side and data.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param upgrade The upgrade that you're getting the model for.
 | 
					     * @param upgrade The upgrade that you're getting the model for.
 | 
				
			||||||
     * @param turtle  Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
 | 
					     * @param turtle  Access to the turtle that the upgrade resides on. This will be null when getting item models.
 | 
				
			||||||
     *                {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
 | 
					 | 
				
			||||||
     * @param side    Which side of the turtle (left or right) the upgrade resides on.
 | 
					     * @param side    Which side of the turtle (left or right) the upgrade resides on.
 | 
				
			||||||
     * @return The model that you wish to be used to render your upgrade.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Obtain the model to be used when rendering a turtle peripheral.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param upgrade The upgrade that you're getting the model for.
 | 
					 | 
				
			||||||
     * @param data    Upgrade data instance for current turtle side.
 | 
					     * @param data    Upgrade data instance for current turtle side.
 | 
				
			||||||
     * @param side    Which side of the turtle (left or right) the upgrade resides on.
 | 
					 | 
				
			||||||
     * @return The model that you wish to be used to render your upgrade.
 | 
					     * @return The model that you wish to be used to render your upgrade.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
 | 
					    TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data);
 | 
				
			||||||
        return getModel(upgrade, (ITurtleAccess) null, side);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get a list of models that this turtle modeller depends on.
 | 
					     * Get the models that this turtle modeller depends on.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * Models included in this list will be loaded and baked alongside item and block models, and so may be referenced
 | 
					     * Models included in this stream will be loaded and baked alongside item and block models, and so may be referenced
 | 
				
			||||||
     * by {@link TransformedModel#of(ResourceLocation)}. You do not need to override this method if you will load models
 | 
					     * by {@link TransformedModel#of(ResourceLocation)}. You do not need to override this method if you will load models
 | 
				
			||||||
     * by other means.
 | 
					     * by other means.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return A list of models that this modeller depends on.
 | 
					     * @return A list of models that this modeller depends on.
 | 
				
			||||||
     * @see UnbakedModel#getDependencies()
 | 
					     * @see UnbakedModel#getDependencies()
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default Collection<ResourceLocation> getDependencies() {
 | 
					    default Stream<ResourceLocation> getDependencies() {
 | 
				
			||||||
        return List.of();
 | 
					        return Stream.of();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(CompoundTag)}
 | 
					     * A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(DataComponentPatch)}
 | 
				
			||||||
     * upgrade item}.
 | 
					     * upgrade item}.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
 | 
					     * This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
 | 
				
			||||||
@@ -85,19 +68,6 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
 | 
				
			|||||||
        return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
 | 
					        return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param left  The model to use on the left.
 | 
					 | 
				
			||||||
     * @param right The model to use on the right.
 | 
					 | 
				
			||||||
     * @param <T>   The type of the turtle upgrade.
 | 
					 | 
				
			||||||
     * @return The constructed modeller.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
 | 
					 | 
				
			||||||
        // TODO(1.21.0): Remove this.
 | 
					 | 
				
			||||||
        return sided((ResourceLocation) left, right);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
 | 
					     * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -109,13 +79,13 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
 | 
				
			|||||||
    static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
 | 
					    static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
 | 
				
			||||||
        return new TurtleUpgradeModeller<>() {
 | 
					        return new TurtleUpgradeModeller<>() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
 | 
					            public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
 | 
				
			||||||
                return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
 | 
					                return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public Collection<ResourceLocation> getDependencies() {
 | 
					            public Stream<ResourceLocation> getDependencies() {
 | 
				
			||||||
                return List.of(left, right);
 | 
					                return Stream.of(left, right);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			|||||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
					import dan200.computercraft.api.turtle.TurtleSide;
 | 
				
			||||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
 | 
					import dan200.computercraft.impl.client.ClientPlatformHelper;
 | 
				
			||||||
import net.minecraft.client.Minecraft;
 | 
					import net.minecraft.client.Minecraft;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					 | 
				
			||||||
import org.joml.Matrix4f;
 | 
					import org.joml.Matrix4f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
@@ -37,16 +36,8 @@ final class TurtleUpgradeModellers {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
 | 
					    private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
 | 
					        public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
 | 
				
			||||||
            return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
 | 
					            var stack = upgrade.getUpgradeItem(data);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
 | 
					 | 
				
			||||||
            return getModel(upgrade.getUpgradeItem(data), side);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private TransformedModel getModel(ItemStack stack, TurtleSide side) {
 | 
					 | 
				
			||||||
            var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
 | 
					            var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
 | 
				
			||||||
            if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
 | 
					            if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
 | 
				
			||||||
            return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
 | 
					            return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api;
 | 
					package dan200.computercraft.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.component.ComputerComponent;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.filesystem.Mount;
 | 
					import dan200.computercraft.api.filesystem.Mount;
 | 
				
			||||||
import dan200.computercraft.api.filesystem.WritableMount;
 | 
					import dan200.computercraft.api.filesystem.WritableMount;
 | 
				
			||||||
import dan200.computercraft.api.lua.GenericSource;
 | 
					import dan200.computercraft.api.lua.GenericSource;
 | 
				
			||||||
import dan200.computercraft.api.lua.IComputerSystem;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.lua.ILuaAPI;
 | 
					import dan200.computercraft.api.lua.ILuaAPI;
 | 
				
			||||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
 | 
					import dan200.computercraft.api.lua.ILuaAPIFactory;
 | 
				
			||||||
import dan200.computercraft.api.media.IMedia;
 | 
					import dan200.computercraft.api.media.IMedia;
 | 
				
			||||||
@@ -167,20 +165,7 @@ public final class ComputerCraftAPI {
 | 
				
			|||||||
     * Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
 | 
					     * Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
 | 
					     * Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
 | 
				
			||||||
     * to use peripherals to provide functionality to users. If an API is <em>required</em>, you may want to consider
 | 
					     * to use peripherals to provide functionality to users.
 | 
				
			||||||
     * using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
 | 
					 | 
				
			||||||
     * computers. For example, one can add an additional API just to turtles with the following code:
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * <pre>{@code
 | 
					 | 
				
			||||||
     * ComputerCraftAPI.registerAPIFactory(computer -> {
 | 
					 | 
				
			||||||
     *   // Read the turtle component.
 | 
					 | 
				
			||||||
     *   var turtle = computer.getComponent(ComputerComponents.TURTLE);
 | 
					 | 
				
			||||||
     *   // If present then add our API.
 | 
					 | 
				
			||||||
     *   return turtle == null ? null : new MyCustomTurtleApi(turtle);
 | 
					 | 
				
			||||||
     * });
 | 
					 | 
				
			||||||
     * }</pre>
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param factory The factory for your API subclass.
 | 
					     * @param factory The factory for your API subclass.
 | 
				
			||||||
     * @see ILuaAPIFactory
 | 
					     * @see ILuaAPIFactory
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,12 @@ package dan200.computercraft.api;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import net.minecraft.core.registries.Registries;
 | 
					import net.minecraft.core.registries.Registries;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
 | 
					import net.minecraft.tags.ItemTags;
 | 
				
			||||||
import net.minecraft.tags.TagKey;
 | 
					import net.minecraft.tags.TagKey;
 | 
				
			||||||
import net.minecraft.world.InteractionHand;
 | 
					import net.minecraft.world.InteractionHand;
 | 
				
			||||||
import net.minecraft.world.entity.player.Player;
 | 
					import net.minecraft.world.entity.player.Player;
 | 
				
			||||||
import net.minecraft.world.item.Item;
 | 
					import net.minecraft.world.item.Item;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
import net.minecraft.world.item.context.UseOnContext;
 | 
					import net.minecraft.world.item.context.UseOnContext;
 | 
				
			||||||
import net.minecraft.world.level.Level;
 | 
					import net.minecraft.world.level.Level;
 | 
				
			||||||
import net.minecraft.world.level.block.Block;
 | 
					import net.minecraft.world.level.block.Block;
 | 
				
			||||||
@@ -35,6 +37,14 @@ public class ComputerCraftTags {
 | 
				
			|||||||
         */
 | 
					         */
 | 
				
			||||||
        public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
 | 
					        public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Items which can be dyed.
 | 
				
			||||||
 | 
					         * <p>
 | 
				
			||||||
 | 
					         * This is similar to {@link ItemTags#DYEABLE}, but allows cleaning the item with a sponge, rather than in a
 | 
				
			||||||
 | 
					         * cauldron.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public static final TagKey<Item> DYEABLE = make("dyeable");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static TagKey<Item> make(String name) {
 | 
					        private static TagKey<Item> make(String name) {
 | 
				
			||||||
            return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
 | 
					            return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -75,8 +85,8 @@ public class ComputerCraftTags {
 | 
				
			|||||||
        public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
 | 
					        public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Block which can be {@linkplain BlockState#use(Level, Player, InteractionHand, BlockHitResult) used} when
 | 
					         * Block which can be {@linkplain BlockState#useItemOn(ItemStack, Level, Player, InteractionHand, BlockHitResult) used}
 | 
				
			||||||
         * calling {@code turtle.place()}.
 | 
					         * when calling {@code turtle.place()}.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
 | 
					        public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.component;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import net.minecraft.commands.CommandSourceStack;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A computer which has permission to perform administrative/op commands, such as the command computer.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ApiStatus.NonExtendable
 | 
					 | 
				
			||||||
public interface AdminComputer {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The permission level that this computer can operate at.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The permission level for this computer.
 | 
					 | 
				
			||||||
     * @see CommandSourceStack#hasPermission(int)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    default int permissionLevel() {
 | 
					 | 
				
			||||||
        return 2;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,48 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.component;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.lua.IComputerSystem;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A component attached to a computer.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * Components provide a mechanism to attach additional data to a computer, that can then be queried with
 | 
					 | 
				
			||||||
 * {@link IComputerSystem#getComponent(ComputerComponent)}.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * This is largely designed for {@linkplain ILuaAPIFactory custom APIs}, allowing APIs to read additional properties
 | 
					 | 
				
			||||||
 * of the computer, such as its position.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The type of this component.
 | 
					 | 
				
			||||||
 * @see ComputerComponents The built-in components.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@SuppressWarnings("UnusedTypeParameter")
 | 
					 | 
				
			||||||
public final class ComputerComponent<T> {
 | 
					 | 
				
			||||||
    private final String id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private ComputerComponent(String id) {
 | 
					 | 
				
			||||||
        this.id = id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create a new computer component.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * Mods typically will not need to create their own components.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param namespace The namespace of this component. This should be the mod id.
 | 
					 | 
				
			||||||
     * @param id        The unique id of this component.
 | 
					 | 
				
			||||||
     * @param <T>       The component
 | 
					 | 
				
			||||||
     * @return The newly created component.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static <T> ComputerComponent<T> create(String namespace, String id) {
 | 
					 | 
				
			||||||
        return new ComputerComponent<>(namespace + ":" + id);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String toString() {
 | 
					 | 
				
			||||||
        return "ComputerComponent(" + id + ")";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.component;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.pocket.IPocketAccess;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * The {@link ComputerComponent}s provided by ComputerCraft.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class ComputerComponents {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The {@link ITurtleAccess} associated with a turtle.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static final ComputerComponent<ITurtleAccess> TURTLE = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "turtle");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The {@link IPocketAccess} associated with a pocket computer.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static final ComputerComponent<IPocketAccess> POCKET = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "pocket");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * This component is only present on "command computers", and other computers with admin capabilities.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static final ComputerComponent<AdminComputer> ADMIN_COMPUTER = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "admin_computer");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,59 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.lua;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.component.ComputerComponent;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
					 | 
				
			||||||
import net.minecraft.core.BlockPos;
 | 
					 | 
				
			||||||
import net.minecraft.server.level.ServerLevel;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * An interface passed to {@link ILuaAPIFactory} in order to provide additional information
 | 
					 | 
				
			||||||
 * about a computer.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ApiStatus.NonExtendable
 | 
					 | 
				
			||||||
public interface IComputerSystem extends IComputerAccess {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the level this computer is currently in.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This method is not guaranteed to remain the same (even for stationary computers).
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The computer's current level.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    ServerLevel getLevel();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the position this computer is currently at.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This method is not guaranteed to remain the same (even for stationary computers).
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The computer's current position.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    BlockPos getPosition();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the label for this computer.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return This computer's label, or {@code null} if it is not set.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    String getLabel();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get a component attached to this computer.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * No component is guaranteed to be on a computer, and so this method should always be guarded with a null check.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This method will always return the same value for a given component, and so may be cached.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param component The component to query.
 | 
					 | 
				
			||||||
     * @param <T>       The type of the component.
 | 
					 | 
				
			||||||
     * @return The component, if present.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    <T> @Nullable T getComponent(ComputerComponent<T> component);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,92 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2018 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.network.wired;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A wired network is composed of one of more {@link WiredNode}s, a set of connections between them, and a series
 | 
					 | 
				
			||||||
 * of peripherals.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
 | 
					 | 
				
			||||||
 * there is some path between two nodes then they must be on the same network. {@link WiredNetwork} will automatically
 | 
					 | 
				
			||||||
 * handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
 | 
					 | 
				
			||||||
 * change.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
 | 
					 | 
				
			||||||
 * it is generally preferred to use the methods provided by {@link WiredNode}.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see WiredNode#getNetwork()
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ApiStatus.NonExtendable
 | 
					 | 
				
			||||||
public interface WiredNetwork {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create a connection between two nodes.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This should only be used on the server thread.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param left  The first node to connect
 | 
					 | 
				
			||||||
     * @param right The second node to connect
 | 
					 | 
				
			||||||
     * @return {@code true} if a connection was created or {@code false} if the connection already exists.
 | 
					 | 
				
			||||||
     * @throws IllegalStateException    If neither node is on the network.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If {@code left} and {@code right} are equal.
 | 
					 | 
				
			||||||
     * @see WiredNode#connectTo(WiredNode)
 | 
					 | 
				
			||||||
     * @see WiredNetwork#connect(WiredNode, WiredNode)
 | 
					 | 
				
			||||||
     * @deprecated Use {@link WiredNode#connectTo(WiredNode)}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated
 | 
					 | 
				
			||||||
    boolean connect(WiredNode left, WiredNode right);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Destroy a connection between this node and another.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This should only be used on the server thread.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param left  The first node in the connection.
 | 
					 | 
				
			||||||
     * @param right The second node in the connection.
 | 
					 | 
				
			||||||
     * @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If either node is not on the network.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If {@code left} and {@code right} are equal.
 | 
					 | 
				
			||||||
     * @see WiredNode#disconnectFrom(WiredNode)
 | 
					 | 
				
			||||||
     * @see WiredNetwork#connect(WiredNode, WiredNode)
 | 
					 | 
				
			||||||
     * @deprecated Use {@link WiredNode#disconnectFrom(WiredNode)}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated
 | 
					 | 
				
			||||||
    boolean disconnect(WiredNode left, WiredNode right);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Sever all connections this node has, removing it from this network.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This should only be used on the server thread. You should only call this on nodes
 | 
					 | 
				
			||||||
     * that your network element owns.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param node The node to remove
 | 
					 | 
				
			||||||
     * @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
 | 
					 | 
				
			||||||
     * only element.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If the node is not in the network.
 | 
					 | 
				
			||||||
     * @see WiredNode#remove()
 | 
					 | 
				
			||||||
     * @deprecated Use {@link WiredNode#remove()}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated
 | 
					 | 
				
			||||||
    boolean remove(WiredNode node);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Update the peripherals a node provides.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This should only be used on the server thread. You should only call this on nodes
 | 
					 | 
				
			||||||
     * that your network element owns.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param node        The node to attach peripherals for.
 | 
					 | 
				
			||||||
     * @param peripherals The new peripherals for this node.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If the node is not in the network.
 | 
					 | 
				
			||||||
     * @see WiredNode#updatePeripherals(Map)
 | 
					 | 
				
			||||||
     * @deprecated Use {@link WiredNode#updatePeripherals(Map)}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated
 | 
					 | 
				
			||||||
    void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -11,7 +11,7 @@ import org.jetbrains.annotations.ApiStatus;
 | 
				
			|||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Wired nodes act as a layer between {@link WiredElement}s and {@link WiredNetwork}s.
 | 
					 * A single node on a wired network.
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
 | 
					 * Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
 | 
				
			||||||
 * methods may be safely used on any thread.
 | 
					 * methods may be safely used on any thread.
 | 
				
			||||||
@@ -32,18 +32,6 @@ public interface WiredNode extends PacketNetwork {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    WiredElement getElement();
 | 
					    WiredElement getElement();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The network this node is currently connected to. Note that this may change
 | 
					 | 
				
			||||||
     * after any network operation, so it should not be cached.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This should only be used on the server thread.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return This node's network.
 | 
					 | 
				
			||||||
     * @deprecated Use the connect/disconnect/remove methods on {@link WiredNode}.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated
 | 
					 | 
				
			||||||
    WiredNetwork getNetwork();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Create a connection from this node to another.
 | 
					     * Create a connection from this node to another.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,12 @@ import dan200.computercraft.api.network.PacketSender;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * An object on a {@link WiredNetwork} capable of sending packets.
 | 
					 * An object on a wired network capable of sending packets.
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
 | 
					 * Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
 | 
				
			||||||
 * to send the packet from.
 | 
					 * to send the packet from.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see WiredElement
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public interface WiredSender extends PacketSender {
 | 
					public interface WiredSender extends PacketSender {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api.pocket;
 | 
					package dan200.computercraft.api.pocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					import net.minecraft.network.chat.Component;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,27 +14,20 @@ import net.minecraft.world.item.ItemStack;
 | 
				
			|||||||
 * One does not have to use this, but it does provide a convenient template.
 | 
					 * One does not have to use this, but it does provide a convenient template.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
 | 
					public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
 | 
				
			||||||
    private final ResourceLocation id;
 | 
					    private final Component adjective;
 | 
				
			||||||
    private final String adjective;
 | 
					 | 
				
			||||||
    private final ItemStack stack;
 | 
					    private final ItemStack stack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected AbstractPocketUpgrade(ResourceLocation id, String adjective, ItemStack stack) {
 | 
					    protected AbstractPocketUpgrade(Component adjective, ItemStack stack) {
 | 
				
			||||||
        this.id = id;
 | 
					 | 
				
			||||||
        this.adjective = adjective;
 | 
					        this.adjective = adjective;
 | 
				
			||||||
        this.stack = stack;
 | 
					        this.stack = stack;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected AbstractPocketUpgrade(ResourceLocation id, ItemStack stack) {
 | 
					    protected AbstractPocketUpgrade(String adjective, ItemStack stack) {
 | 
				
			||||||
        this(id, UpgradeBase.getDefaultAdjective(id), stack);
 | 
					        this(Component.translatable(adjective), stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public final ResourceLocation getUpgradeID() {
 | 
					    public final Component getAdjective() {
 | 
				
			||||||
        return id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final String getUnlocalisedAdjective() {
 | 
					 | 
				
			||||||
        return adjective;
 | 
					        return adjective;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,39 +4,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api.pocket;
 | 
					package dan200.computercraft.api.pocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeData;
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.server.level.ServerLevel;
 | 
					 | 
				
			||||||
import net.minecraft.world.entity.Entity;
 | 
					import net.minecraft.world.entity.Entity;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
import net.minecraft.world.phys.Vec3;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Wrapper class for pocket computers.
 | 
					 * Wrapper class for pocket computers.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ApiStatus.NonExtendable
 | 
					 | 
				
			||||||
public interface IPocketAccess {
 | 
					public interface IPocketAccess {
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the level in which the pocket computer exists.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The pocket computer's level.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    ServerLevel getLevel();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the position of the pocket computer.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The pocket computer's position.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    Vec3 getPosition();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Gets the entity holding this item.
 | 
					     * Gets the entity holding this item.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
@@ -83,45 +61,25 @@ public interface IPocketAccess {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    void setLight(int colour);
 | 
					    void setLight(int colour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the currently equipped upgrade.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The currently equipped upgrade.
 | 
					 | 
				
			||||||
     * @see #getUpgradeNBTData()
 | 
					 | 
				
			||||||
     * @see #setUpgrade(UpgradeData)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    UpgradeData<IPocketUpgrade> getUpgrade();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Set the upgrade for this pocket computer, also updating the item stack.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * Note this method is not thread safe - it must be called from the server thread.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param upgrade The new upgrade to set it to, may be {@code null}.
 | 
					 | 
				
			||||||
     * @see #getUpgrade()
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the upgrade-specific NBT.
 | 
					     * Get the upgrade-specific NBT.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * This is persisted between computer reboots and chunk loads.
 | 
					     * This is persisted between computer reboots and chunk loads.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return The upgrade's NBT.
 | 
					     * @return The upgrade's NBT.
 | 
				
			||||||
     * @see #updateUpgradeNBTData()
 | 
					     * @see #setUpgradeData(DataComponentPatch)
 | 
				
			||||||
     * @see UpgradeBase#getUpgradeItem(CompoundTag)
 | 
					     * @see UpgradeBase#getUpgradeItem(DataComponentPatch)
 | 
				
			||||||
     * @see UpgradeBase#getUpgradeData(ItemStack)
 | 
					     * @see UpgradeBase#getUpgradeData(ItemStack)
 | 
				
			||||||
     * @see #getUpgrade()
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    CompoundTag getUpgradeNBTData();
 | 
					    DataComponentPatch getUpgradeData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Mark the upgrade-specific NBT as dirty.
 | 
					     * Update the upgrade-specific data.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @see #getUpgradeNBTData()
 | 
					     * @param data The new upgrade data.
 | 
				
			||||||
 | 
					     * @see #getUpgradeData()
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void updateUpgradeNBTData();
 | 
					    void setUpgradeData(DataComponentPatch data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Remove the current peripheral and create a new one.
 | 
					     * Remove the current peripheral and create a new one.
 | 
				
			||||||
@@ -130,13 +88,4 @@ public interface IPocketAccess {
 | 
				
			|||||||
     * entity} changes.
 | 
					     * entity} changes.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void invalidatePeripheral();
 | 
					    void invalidatePeripheral();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get a list of all upgrades for the pocket computer.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return A collection of all upgrade names.
 | 
					 | 
				
			||||||
     * @deprecated This is a relic of a previous API, which no longer makes sense with newer versions of ComputerCraft.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated(forRemoval = true)
 | 
					 | 
				
			||||||
    Map<ResourceLocation, IPeripheral> getUpgrades();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api.pocket;
 | 
					package dan200.computercraft.api.pocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dan200.computercraft.api.ComputerCraftAPI;
 | 
				
			||||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
					import dan200.computercraft.api.peripheral.IPeripheral;
 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.upgrades.UpgradeType;
 | 
				
			||||||
 | 
					import dan200.computercraft.impl.ComputerCraftAPIService;
 | 
				
			||||||
 | 
					import net.minecraft.core.Registry;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceKey;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import net.minecraft.world.level.Level;
 | 
					import net.minecraft.world.level.Level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
@@ -13,16 +19,54 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * A peripheral which can be equipped to the back side of a pocket computer.
 | 
					 * A peripheral which can be equipped to the back side of a pocket computer.
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding
 | 
					 * Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
 | 
				
			||||||
 * {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry.
 | 
					 * {@link UpgradeType} instance, which are then registered in a registry.
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
 | 
					 * You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
 | 
				
			||||||
 * the upgrade registered internally. See the documentation in {@link PocketUpgradeSerialiser} for details on this process
 | 
					 * the upgrade automatically registered. It is recommended this is done via
 | 
				
			||||||
 * and where files should be located.
 | 
					 * <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @see PocketUpgradeSerialiser For how to register a pocket computer upgrade.
 | 
					 * <h2>Example</h2>
 | 
				
			||||||
 | 
					 * {@snippet lang="java" :
 | 
				
			||||||
 | 
					 * // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
 | 
				
			||||||
 | 
					 * static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * // Register a new upgrade upgrade type called "my_upgrade".
 | 
				
			||||||
 | 
					 * public static final RegistryObject<UpgradeType<MyUpgrade>> MY_UPGRADE =
 | 
				
			||||||
 | 
					 *     POCKET_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(new MyUpgrade()));
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * // Then in your constructor
 | 
				
			||||||
 | 
					 * POCKET_UPGRADES.register(bus);
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * We can then define a new upgrade using JSON by placing the following in
 | 
				
			||||||
 | 
					 * {@code data/<my_mod>/computercraft/pocket_upgrade/<my_upgrade_id>.json}.
 | 
				
			||||||
 | 
					 * {@snippet lang="json" :
 | 
				
			||||||
 | 
					 * {
 | 
				
			||||||
 | 
					 *     "type": "my_mod:my_upgrade"
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public interface IPocketUpgrade extends UpgradeBase {
 | 
					public interface IPocketUpgrade extends UpgradeBase {
 | 
				
			||||||
 | 
					    ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The registry key for pocket upgrade types.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The registry key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static ResourceKey<Registry<UpgradeType<? extends IPocketUpgrade>>> typeRegistry() {
 | 
				
			||||||
 | 
					        return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the type of this upgrade.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The type of this upgrade.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    UpgradeType<? extends IPocketUpgrade> getType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates a peripheral for the pocket computer.
 | 
					     * Creates a peripheral for the pocket computer.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.pocket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
 | 
					 | 
				
			||||||
import net.minecraft.data.DataGenerator;
 | 
					 | 
				
			||||||
import net.minecraft.data.PackOutput;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.function.Consumer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A data provider to generate pocket computer upgrades.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
 | 
					 | 
				
			||||||
 * {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
 | 
					 | 
				
			||||||
 * generate them.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see PocketUpgradeSerialiser
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {
 | 
					 | 
				
			||||||
    public PocketUpgradeDataProvider(PackOutput output) {
 | 
					 | 
				
			||||||
        super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,79 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.pocket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.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.SimpleCraftingRecipeSerializer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.function.BiFunction;
 | 
					 | 
				
			||||||
import java.util.function.Function;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Reads a {@link IPocketUpgrade} from disk and reads/writes it to a network packet.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * This follows the same format as {@link dan200.computercraft.api.turtle.TurtleUpgradeSerialiser} - consult the
 | 
					 | 
				
			||||||
 * documentation there for more information.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The type of pocket computer upgrade this is responsible for serialising.
 | 
					 | 
				
			||||||
 * @see IPocketUpgrade
 | 
					 | 
				
			||||||
 * @see PocketUpgradeDataProvider
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The ID for the associated registry.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The registry key.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static ResourceKey<Registry<PocketUpgradeSerialiser<?>>> registryId() {
 | 
					 | 
				
			||||||
        return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
 | 
					 | 
				
			||||||
     * but for upgrades.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param factory Generate a new upgrade with a specific ID.
 | 
					 | 
				
			||||||
     * @param <T>     The type of the generated upgrade.
 | 
					 | 
				
			||||||
     * @return The serialiser for this upgrade
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
 | 
					 | 
				
			||||||
        final class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
            private Impl(Function<ResourceLocation, T> constructor) {
 | 
					 | 
				
			||||||
                super(constructor);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new Impl(factory);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
 | 
					 | 
				
			||||||
     *                {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
 | 
					 | 
				
			||||||
     * @param <T>     The type of the generated upgrade.
 | 
					 | 
				
			||||||
     * @return The serialiser for this upgrade.
 | 
					 | 
				
			||||||
     * @see #simple(Function)  For upgrades whose crafting stack should not vary.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
 | 
					 | 
				
			||||||
        final class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
            private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
 | 
					 | 
				
			||||||
                super(factory);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new Impl(factory);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -4,8 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api.turtle;
 | 
					package dan200.computercraft.api.turtle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					import net.minecraft.network.chat.Component;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,34 +14,27 @@ import net.minecraft.world.item.ItemStack;
 | 
				
			|||||||
 * One does not have to use this, but it does provide a convenient template.
 | 
					 * One does not have to use this, but it does provide a convenient template.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade {
 | 
					public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade {
 | 
				
			||||||
    private final ResourceLocation id;
 | 
					 | 
				
			||||||
    private final TurtleUpgradeType type;
 | 
					    private final TurtleUpgradeType type;
 | 
				
			||||||
    private final String adjective;
 | 
					    private final Component adjective;
 | 
				
			||||||
    private final ItemStack stack;
 | 
					    private final ItemStack stack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack) {
 | 
					    protected AbstractTurtleUpgrade(TurtleUpgradeType type, Component adjective, ItemStack stack) {
 | 
				
			||||||
        this.id = id;
 | 
					 | 
				
			||||||
        this.type = type;
 | 
					        this.type = type;
 | 
				
			||||||
        this.adjective = adjective;
 | 
					        this.adjective = adjective;
 | 
				
			||||||
        this.stack = stack;
 | 
					        this.stack = stack;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, ItemStack stack) {
 | 
					    protected AbstractTurtleUpgrade(TurtleUpgradeType type, String adjective, ItemStack stack) {
 | 
				
			||||||
        this(id, type, UpgradeBase.getDefaultAdjective(id), stack);
 | 
					        this(type, Component.translatable(adjective), stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public final ResourceLocation getUpgradeID() {
 | 
					    public final Component getAdjective() {
 | 
				
			||||||
        return id;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final String getUnlocalisedAdjective() {
 | 
					 | 
				
			||||||
        return adjective;
 | 
					        return adjective;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public final TurtleUpgradeType getType() {
 | 
					    public final TurtleUpgradeType getUpgradeType() {
 | 
				
			||||||
        return type;
 | 
					        return type;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@ import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
				
			|||||||
import dan200.computercraft.api.upgrades.UpgradeData;
 | 
					import dan200.computercraft.api.upgrades.UpgradeData;
 | 
				
			||||||
import net.minecraft.core.BlockPos;
 | 
					import net.minecraft.core.BlockPos;
 | 
				
			||||||
import net.minecraft.core.Direction;
 | 
					import net.minecraft.core.Direction;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.world.Container;
 | 
					import net.minecraft.world.Container;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
import net.minecraft.world.level.Level;
 | 
					import net.minecraft.world.level.Level;
 | 
				
			||||||
@@ -229,37 +229,22 @@ public interface ITurtleAccess {
 | 
				
			|||||||
     * @param side The side to get the upgrade from.
 | 
					     * @param side The side to get the upgrade from.
 | 
				
			||||||
     * @return The upgrade on the specified side of the turtle, if there is one.
 | 
					     * @return The upgrade on the specified side of the turtle, if there is one.
 | 
				
			||||||
     * @see #getUpgradeWithData(TurtleSide)
 | 
					     * @see #getUpgradeWithData(TurtleSide)
 | 
				
			||||||
     * @see #setUpgradeWithData(TurtleSide, UpgradeData)
 | 
					     * @see #setUpgrade(TurtleSide, UpgradeData)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    ITurtleUpgrade getUpgrade(TurtleSide side);
 | 
					    ITurtleUpgrade getUpgrade(TurtleSide side);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeNBTData(TurtleSide)
 | 
					     * Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeData(TurtleSide)
 | 
				
			||||||
     * update data}.
 | 
					     * update data}.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param side The side to get the upgrade from.
 | 
					     * @param side The side to get the upgrade from.
 | 
				
			||||||
     * @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
 | 
					     * @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
 | 
				
			||||||
     * @see #getUpgradeWithData(TurtleSide)
 | 
					     * @see #getUpgradeWithData(TurtleSide)
 | 
				
			||||||
     * @see #setUpgradeWithData(TurtleSide, UpgradeData)
 | 
					     * @see #setUpgrade(TurtleSide, UpgradeData)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side) {
 | 
					    @Nullable
 | 
				
			||||||
        var upgrade = getUpgrade(side);
 | 
					    UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side);
 | 
				
			||||||
        return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData(side));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param side    The side to set the upgrade on.
 | 
					 | 
				
			||||||
     * @param upgrade The upgrade to set, may be {@code null} to clear.
 | 
					 | 
				
			||||||
     * @see #getUpgrade(TurtleSide)
 | 
					 | 
				
			||||||
     * @deprecated Use {@link #setUpgradeWithData(TurtleSide, UpgradeData)}
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Deprecated
 | 
					 | 
				
			||||||
    default void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
 | 
					 | 
				
			||||||
        setUpgradeWithData(side, upgrade == null ? null : UpgradeData.ofDefault(upgrade));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Set the upgrade for a given side and its upgrade data.
 | 
					     * Set the upgrade for a given side and its upgrade data.
 | 
				
			||||||
@@ -268,7 +253,7 @@ public interface ITurtleAccess {
 | 
				
			|||||||
     * @param upgrade The upgrade to set, may be {@code null} to clear.
 | 
					     * @param upgrade The upgrade to set, may be {@code null} to clear.
 | 
				
			||||||
     * @see #getUpgradeWithData(TurtleSide)
 | 
					     * @see #getUpgradeWithData(TurtleSide)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void setUpgradeWithData(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
 | 
					    void setUpgrade(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
 | 
					     * Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
 | 
				
			||||||
@@ -282,23 +267,23 @@ public interface ITurtleAccess {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
 | 
					     * Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You must
 | 
					     * This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You can
 | 
				
			||||||
     * call {@link #updateUpgradeNBTData(TurtleSide)} after modifying it.
 | 
					     * call {@link #setUpgrade(TurtleSide, UpgradeData)} to modify it.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param side The side to get the upgrade data for.
 | 
					     * @param side The side to get the upgrade data for.
 | 
				
			||||||
     * @return The upgrade-specific data.
 | 
					     * @return The upgrade-specific data.
 | 
				
			||||||
     * @see #updateUpgradeNBTData(TurtleSide)
 | 
					     * @see #setUpgradeData(TurtleSide, DataComponentPatch)
 | 
				
			||||||
     * @see UpgradeBase#getUpgradeItem(CompoundTag)
 | 
					     * @see UpgradeBase#getUpgradeItem(DataComponentPatch)
 | 
				
			||||||
     * @see UpgradeBase#getUpgradeData(ItemStack)
 | 
					     * @see UpgradeBase#getUpgradeData(ItemStack)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    CompoundTag getUpgradeNBTData(TurtleSide side);
 | 
					    DataComponentPatch getUpgradeData(TurtleSide side);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the
 | 
					     * Update the upgrade-specific data.
 | 
				
			||||||
     * client and persisted.
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param side The side to mark dirty.
 | 
					     * @param side The side to set the upgrade data for.
 | 
				
			||||||
     * @see #updateUpgradeNBTData(TurtleSide)
 | 
					     * @param data The new upgrade data.
 | 
				
			||||||
 | 
					     * @see #getUpgradeData(TurtleSide)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void updateUpgradeNBTData(TurtleSide side);
 | 
					    void setUpgradeData(TurtleSide side, DataComponentPatch data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api.turtle;
 | 
					package dan200.computercraft.api.turtle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dan200.computercraft.api.ComputerCraftAPI;
 | 
				
			||||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
					import dan200.computercraft.api.peripheral.IPeripheral;
 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.upgrades.UpgradeType;
 | 
				
			||||||
 | 
					import dan200.computercraft.impl.ComputerCraftAPIService;
 | 
				
			||||||
import net.minecraft.core.Direction;
 | 
					import net.minecraft.core.Direction;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.Registry;
 | 
				
			||||||
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceKey;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,22 +22,79 @@ import javax.annotation.Nullable;
 | 
				
			|||||||
 * peripheral.
 | 
					 * peripheral.
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
 | 
					 * Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
 | 
				
			||||||
 * {@link TurtleUpgradeSerialiser} instance, which are then registered in a Forge registry.
 | 
					 * {@link UpgradeType} instance, which are then registered in a registry.
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
 | 
					 * You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
 | 
				
			||||||
 * the upgrade registered internally. See the documentation in {@link TurtleUpgradeSerialiser} for details on this process
 | 
					 * the upgrade automatically registered. It is recommended this is done via
 | 
				
			||||||
 * and where files should be located.
 | 
					 * <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @see TurtleUpgradeSerialiser For how to register a turtle upgrade.
 | 
					 * <h2>Example</h2>
 | 
				
			||||||
 | 
					 * {@snippet lang="java" :
 | 
				
			||||||
 | 
					 * // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
 | 
				
			||||||
 | 
					 * static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * // Register a new upgrade type called "my_upgrade".
 | 
				
			||||||
 | 
					 * public static final RegistryObject<UpgradeType<MyUpgrade>> MY_UPGRADE =
 | 
				
			||||||
 | 
					 *     TURTLE_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(MyUpgrade::new));
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * // Then in your constructor
 | 
				
			||||||
 | 
					 * TURTLE_UPGRADES.register(bus);
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * We can then define a new upgrade using JSON by placing the following in
 | 
				
			||||||
 | 
					 * {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * {@snippet lang="json" :
 | 
				
			||||||
 | 
					 * {
 | 
				
			||||||
 | 
					 *     "type": "my_mod:my_upgrade"
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Finally, we need to register a model for our upgrade, see
 | 
				
			||||||
 | 
					 * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public interface ITurtleUpgrade extends UpgradeBase {
 | 
					public interface ITurtleUpgrade extends UpgradeBase {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The registry in which turtle upgrades are stored.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ResourceKey<Registry<ITurtleUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a {@link ResourceKey} for a turtle upgrade given a {@link ResourceLocation}.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This should only be called from within data generation code. Do not hard code references to your upgrades!
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param id The id of the turtle upgrade.
 | 
				
			||||||
 | 
					     * @return The upgrade registry key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static ResourceKey<ITurtleUpgrade> createKey(ResourceLocation id) {
 | 
				
			||||||
 | 
					        return ResourceKey.create(REGISTRY, id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The registry key for turtle upgrade types.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The registry key.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static ResourceKey<Registry<UpgradeType<? extends ITurtleUpgrade>>> typeRegistry() {
 | 
				
			||||||
 | 
					        return ComputerCraftAPIService.get().turtleUpgradeRegistryId();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the type of this upgrade.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The type of this upgrade.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    UpgradeType<? extends ITurtleUpgrade> getType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Return whether this turtle adds a tool or a peripheral to the turtle.
 | 
					     * Return whether this turtle adds a tool or a peripheral to the turtle.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return The type of upgrade this is.
 | 
					     * @return The type of upgrade this is.
 | 
				
			||||||
     * @see TurtleUpgradeType for the differences between them.
 | 
					     * @see TurtleUpgradeType for the differences between them.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    TurtleUpgradeType getType();
 | 
					    TurtleUpgradeType getUpgradeType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
 | 
					     * Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
 | 
				
			||||||
@@ -90,7 +153,7 @@ public interface ITurtleUpgrade extends UpgradeBase {
 | 
				
			|||||||
     * @param upgradeData Data that currently stored for this upgrade
 | 
					     * @param upgradeData Data that currently stored for this upgrade
 | 
				
			||||||
     * @return Filtered version of this data.
 | 
					     * @return Filtered version of this data.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default CompoundTag getPersistedData(CompoundTag upgradeData) {
 | 
					    default DataComponentPatch getPersistedData(DataComponentPatch upgradeData) {
 | 
				
			||||||
        return upgradeData;
 | 
					        return upgradeData;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dan200.computercraft.api.turtle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dan200.computercraft.api.ComputerCraftTags;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
				
			||||||
 | 
					import dan200.computercraft.impl.ComputerCraftAPIService;
 | 
				
			||||||
 | 
					import dan200.computercraft.impl.upgrades.TurtleToolSpec;
 | 
				
			||||||
 | 
					import net.minecraft.core.component.DataComponents;
 | 
				
			||||||
 | 
					import net.minecraft.data.worldgen.BootstrapContext;
 | 
				
			||||||
 | 
					import net.minecraft.network.chat.Component;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceKey;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
 | 
					import net.minecraft.tags.TagKey;
 | 
				
			||||||
 | 
					import net.minecraft.world.entity.ai.attributes.Attributes;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.Item;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					import net.minecraft.world.level.block.Block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A builder for custom turtle tool upgrades.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * This can be used from your <a href="../upgrades/UpgradeType.html#datagen">data generator</a> code in order to
 | 
				
			||||||
 | 
					 * register turtle tools for your mod's tools.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <h2>Example:</h2>
 | 
				
			||||||
 | 
					 * {@snippet lang = "java":
 | 
				
			||||||
 | 
					 * import net.minecraft.data.worldgen.BootstrapContext;
 | 
				
			||||||
 | 
					 * import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
 | 
					 * import net.minecraft.world.item.Items;
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * public void registerTool(BootstrapContext<ITurtleUpgrade> upgrades) {
 | 
				
			||||||
 | 
					 *   TurtleToolBuilder.tool(new ResourceLocation("my_mod", "wooden_pickaxe"), Items.WOODEN_PICKAXE).register(upgrades);
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 *}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class TurtleToolBuilder {
 | 
				
			||||||
 | 
					    private final ResourceKey<ITurtleUpgrade> id;
 | 
				
			||||||
 | 
					    private final Item item;
 | 
				
			||||||
 | 
					    private Component adjective;
 | 
				
			||||||
 | 
					    private float damageMultiplier = TurtleToolSpec.DEFAULT_DAMAGE_MULTIPLIER;
 | 
				
			||||||
 | 
					    private @Nullable TagKey<Block> breakable;
 | 
				
			||||||
 | 
					    private boolean allowEnchantments = false;
 | 
				
			||||||
 | 
					    private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TurtleToolBuilder(ResourceKey<ITurtleUpgrade> id, Item item) {
 | 
				
			||||||
 | 
					        this.id = id;
 | 
				
			||||||
 | 
					        adjective = Component.translatable(UpgradeBase.getDefaultAdjective(id.location()));
 | 
				
			||||||
 | 
					        this.item = item;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static TurtleToolBuilder tool(ResourceLocation id, Item item) {
 | 
				
			||||||
 | 
					        return new TurtleToolBuilder(ITurtleUpgrade.createKey(id), item);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static TurtleToolBuilder tool(ResourceKey<ITurtleUpgrade> id, Item item) {
 | 
				
			||||||
 | 
					        return new TurtleToolBuilder(id, item);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the id for this turtle tool.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The upgrade id.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ResourceKey<ITurtleUpgrade> id() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Specify a custom adjective for this tool. By default this takes its adjective from the upgrade id.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param adjective The new adjective to use.
 | 
				
			||||||
 | 
					     * @return The tool builder, for further use.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TurtleToolBuilder adjective(Component adjective) {
 | 
				
			||||||
 | 
					        this.adjective = adjective;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
 | 
				
			||||||
 | 
					     * get the final damage.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param damageMultiplier The damage multiplier.
 | 
				
			||||||
 | 
					     * @return The tool builder, for further use.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TurtleToolBuilder damageMultiplier(float damageMultiplier) {
 | 
				
			||||||
 | 
					        this.damageMultiplier = damageMultiplier;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
 | 
				
			||||||
 | 
					     * {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The tool builder, for further use.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TurtleToolBuilder allowEnchantments() {
 | 
				
			||||||
 | 
					        allowEnchantments = true;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set when the tool will consume durability.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param durability The durability predicate.
 | 
				
			||||||
 | 
					     * @return The tool builder, for further use.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TurtleToolBuilder consumeDurability(TurtleToolDurability durability) {
 | 
				
			||||||
 | 
					        consumeDurability = durability;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
 | 
				
			||||||
 | 
					     * in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
 | 
				
			||||||
 | 
					     * be broken.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param breakable The tag containing all blocks breakable by this item.
 | 
				
			||||||
 | 
					     * @return The tool builder, for further use.
 | 
				
			||||||
 | 
					     * @see ComputerCraftTags.Blocks
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TurtleToolBuilder breakable(TagKey<Block> breakable) {
 | 
				
			||||||
 | 
					        this.breakable = breakable;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Build the turtle tool upgrade.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The constructed upgrade.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ITurtleUpgrade build() {
 | 
				
			||||||
 | 
					        return ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec(
 | 
				
			||||||
 | 
					            adjective,
 | 
				
			||||||
 | 
					            item,
 | 
				
			||||||
 | 
					            damageMultiplier,
 | 
				
			||||||
 | 
					            allowEnchantments,
 | 
				
			||||||
 | 
					            consumeDurability,
 | 
				
			||||||
 | 
					            Optional.ofNullable(breakable)
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Build this upgrade and register it for datagen.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param upgrades The registry this upgrade should be added to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void register(BootstrapContext<ITurtleUpgrade> upgrades) {
 | 
				
			||||||
 | 
					        upgrades.register(id(), build());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,14 +4,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.api.turtle;
 | 
					package dan200.computercraft.api.turtle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.minecraft.core.component.DataComponents;
 | 
				
			||||||
import net.minecraft.util.StringRepresentable;
 | 
					import net.minecraft.util.StringRepresentable;
 | 
				
			||||||
import net.minecraft.world.entity.EquipmentSlot;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Indicates if an equipped turtle item will consume durability.
 | 
					 * Indicates if an equipped turtle item will consume durability.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @see TurtleUpgradeDataProvider.ToolBuilder#consumeDurability(TurtleToolDurability)
 | 
					 * @see TurtleToolBuilder#consumeDurability(TurtleToolDurability)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public enum TurtleToolDurability implements StringRepresentable {
 | 
					public enum TurtleToolDurability implements StringRepresentable {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -21,7 +21,7 @@ public enum TurtleToolDurability implements StringRepresentable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
 | 
					     * The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
 | 
				
			||||||
     * {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
 | 
					     * {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    WHEN_ENCHANTED("when_enchanted"),
 | 
					    WHEN_ENCHANTED("when_enchanted"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,168 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.turtle;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.ComputerCraftTags;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.PlatformHelper;
 | 
					 | 
				
			||||||
import net.minecraft.core.registries.Registries;
 | 
					 | 
				
			||||||
import net.minecraft.data.DataGenerator;
 | 
					 | 
				
			||||||
import net.minecraft.data.PackOutput;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.tags.TagKey;
 | 
					 | 
				
			||||||
import net.minecraft.world.entity.EquipmentSlot;
 | 
					 | 
				
			||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.Item;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					 | 
				
			||||||
import net.minecraft.world.level.block.Block;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
import java.util.function.Consumer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A data provider to generate turtle upgrades.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
 | 
					 | 
				
			||||||
 * {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
 | 
					 | 
				
			||||||
 * generate them.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see TurtleUpgradeSerialiser
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> {
 | 
					 | 
				
			||||||
    private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public TurtleUpgradeDataProvider(PackOutput output) {
 | 
					 | 
				
			||||||
        super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create a new turtle tool upgrade, such as a pickaxe or shovel.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @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(TagKey)}.
 | 
					 | 
				
			||||||
     * @return A tool builder,
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public final ToolBuilder tool(ResourceLocation id, Item item) {
 | 
					 | 
				
			||||||
        return new ToolBuilder(id, existingSerialiser(TOOL_ID), item);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * A builder for custom turtle tool upgrades.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @see #tool(ResourceLocation, Item)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static class ToolBuilder {
 | 
					 | 
				
			||||||
        private final ResourceLocation id;
 | 
					 | 
				
			||||||
        private final TurtleUpgradeSerialiser<?> serialiser;
 | 
					 | 
				
			||||||
        private final Item toolItem;
 | 
					 | 
				
			||||||
        private @Nullable String adjective;
 | 
					 | 
				
			||||||
        private @Nullable Item craftingItem;
 | 
					 | 
				
			||||||
        private @Nullable Float damageMultiplier = null;
 | 
					 | 
				
			||||||
        private @Nullable TagKey<Block> breakable;
 | 
					 | 
				
			||||||
        private boolean allowEnchantments = false;
 | 
					 | 
				
			||||||
        private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
 | 
					 | 
				
			||||||
            this.id = id;
 | 
					 | 
				
			||||||
            this.serialiser = serialiser;
 | 
					 | 
				
			||||||
            this.toolItem = toolItem;
 | 
					 | 
				
			||||||
            craftingItem = null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Specify a custom adjective for this tool. By default this takes its adjective from the tool item.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param adjective The new adjective to use.
 | 
					 | 
				
			||||||
         * @return The tool builder, for further use.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public ToolBuilder adjective(String adjective) {
 | 
					 | 
				
			||||||
            this.adjective = adjective;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Specify a custom item which is used to craft this upgrade. By default this is the same as the provided tool
 | 
					 | 
				
			||||||
         * item, but you may wish to override it.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param craftingItem The item used to craft this upgrade.
 | 
					 | 
				
			||||||
         * @return The tool builder, for further use.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public ToolBuilder craftingItem(Item craftingItem) {
 | 
					 | 
				
			||||||
            this.craftingItem = craftingItem;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
 | 
					 | 
				
			||||||
         * get the final damage.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param damageMultiplier The damage multiplier.
 | 
					 | 
				
			||||||
         * @return The tool builder, for further use.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public ToolBuilder damageMultiplier(float damageMultiplier) {
 | 
					 | 
				
			||||||
            this.damageMultiplier = damageMultiplier;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
 | 
					 | 
				
			||||||
         * {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @return The tool builder, for further use.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public ToolBuilder allowEnchantments() {
 | 
					 | 
				
			||||||
            allowEnchantments = true;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Set when the tool will consume durability.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param durability The durability predicate.
 | 
					 | 
				
			||||||
         * @return The tool builder, for further use.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public ToolBuilder consumeDurability(TurtleToolDurability durability) {
 | 
					 | 
				
			||||||
            consumeDurability = durability;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
 | 
					 | 
				
			||||||
         * in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
 | 
					 | 
				
			||||||
         * be broken.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param breakable The tag containing all blocks breakable by this item.
 | 
					 | 
				
			||||||
         * @return The tool builder, for further use.
 | 
					 | 
				
			||||||
         * @see ComputerCraftTags.Blocks
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public ToolBuilder breakable(TagKey<Block> breakable) {
 | 
					 | 
				
			||||||
            this.breakable = breakable;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Register this as an upgrade.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param add The callback given to {@link #addUpgrades(Consumer)}.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public void add(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) {
 | 
					 | 
				
			||||||
            add.accept(new Upgrade<>(id, serialiser, s -> {
 | 
					 | 
				
			||||||
                s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString());
 | 
					 | 
				
			||||||
                if (adjective != null) s.addProperty("adjective", adjective);
 | 
					 | 
				
			||||||
                if (craftingItem != null) {
 | 
					 | 
				
			||||||
                    s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
 | 
					 | 
				
			||||||
                if (breakable != null) s.addProperty("breakable", breakable.location().toString());
 | 
					 | 
				
			||||||
                if (allowEnchantments) s.addProperty("allowEnchantments", true);
 | 
					 | 
				
			||||||
                if (consumeDurability != TurtleToolDurability.NEVER) {
 | 
					 | 
				
			||||||
                    s.addProperty("consumeDurability", consumeDurability.getSerializedName());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,109 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.turtle;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.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;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.function.BiFunction;
 | 
					 | 
				
			||||||
import java.util.function.Function;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
 | 
					 | 
				
			||||||
 * {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * <h2>Example (Forge)</h2>
 | 
					 | 
				
			||||||
 * <pre>{@code
 | 
					 | 
				
			||||||
 * static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * // Register a new upgrade serialiser called "my_upgrade".
 | 
					 | 
				
			||||||
 * public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
 | 
					 | 
				
			||||||
 *     SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * // Then in your constructor
 | 
					 | 
				
			||||||
 * SERIALISERS.register( bus );
 | 
					 | 
				
			||||||
 * }</pre>
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * We can then define a new upgrade using JSON by placing the following in
 | 
					 | 
				
			||||||
 * {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * <pre>{@code
 | 
					 | 
				
			||||||
 * {
 | 
					 | 
				
			||||||
 *     "type": my_mod:my_upgrade",
 | 
					 | 
				
			||||||
 * }
 | 
					 | 
				
			||||||
 * }</pre>
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * Finally, we need to register a model for our upgrade. The way to do this varies on mod loader, see
 | 
					 | 
				
			||||||
 * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The type of turtle upgrade this is responsible for serialising.
 | 
					 | 
				
			||||||
 * @see ITurtleUpgrade
 | 
					 | 
				
			||||||
 * @see TurtleUpgradeDataProvider
 | 
					 | 
				
			||||||
 * @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The ID for the associated registry.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The registry key.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> registryId() {
 | 
					 | 
				
			||||||
        return ComputerCraftAPIService.get().turtleUpgradeRegistryId();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
 | 
					 | 
				
			||||||
     * but for upgrades.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param factory Generate a new upgrade with a specific ID.
 | 
					 | 
				
			||||||
     * @param <T>     The type of the generated upgrade.
 | 
					 | 
				
			||||||
     * @return The serialiser for this upgrade
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
 | 
					 | 
				
			||||||
        final class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
            private Impl(Function<ResourceLocation, T> constructor) {
 | 
					 | 
				
			||||||
                super(constructor);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new Impl(factory);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
 | 
					 | 
				
			||||||
     *                {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
 | 
					 | 
				
			||||||
     * @param <T>     The type of the generated upgrade.
 | 
					 | 
				
			||||||
     * @return The serialiser for this upgrade.
 | 
					 | 
				
			||||||
     * @see #simple(Function)  For upgrades whose crafting stack should not vary.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
 | 
					 | 
				
			||||||
        final class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
            private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
 | 
					 | 
				
			||||||
                super(factory);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new Impl(factory);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -9,37 +9,34 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
 | 
				
			|||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
					import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
					import dan200.computercraft.api.turtle.TurtleSide;
 | 
				
			||||||
import dan200.computercraft.impl.PlatformHelper;
 | 
					 | 
				
			||||||
import net.minecraft.Util;
 | 
					import net.minecraft.Util;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
 | 
					import net.minecraft.network.chat.Component;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
 | 
					 * Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public interface UpgradeBase {
 | 
					public interface UpgradeBase {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem"
 | 
					     * Get the type of this upgrade.
 | 
				
			||||||
     * or "my_mod:my_upgrade".
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * You should use a unique resource domain to ensure this upgrade is uniquely identified.
 | 
					 | 
				
			||||||
     * The upgrade will fail registration if an already used ID is specified.
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return The unique ID for this upgrade.
 | 
					     * @return The type of this upgrade.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    ResourceLocation getUpgradeID();
 | 
					    UpgradeType<?> getType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Return an unlocalised string to describe this type of computer in item names.
 | 
					     * A description of this upgrade for use in item names.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This should typically be a {@linkplain Component#translatable(String) translation key}, rather than a hard coded
 | 
				
			||||||
 | 
					     * string.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
 | 
					     * Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return The localisation key for this upgrade's adjective.
 | 
					     * @return The text component for this upgrade's adjective.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    String getUnlocalisedAdjective();
 | 
					    Component getAdjective();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Return an item stack representing the type of item that a computer must be crafted
 | 
					     * Return an item stack representing the type of item that a computer must be crafted
 | 
				
			||||||
@@ -57,8 +54,8 @@ public interface UpgradeBase {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns the item stack representing a currently equipped turtle upgrade.
 | 
					     * Returns the item stack representing a currently equipped turtle upgrade.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} and
 | 
					     * While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeData(TurtleSide)} and
 | 
				
			||||||
     * {@link IPocketAccess#getUpgradeNBTData()}}, by default this data is discarded when an upgrade is unequipped,
 | 
					     * {@link IPocketAccess#getUpgradeData()}}, by default this data is discarded when an upgrade is unequipped,
 | 
				
			||||||
     * and the original item stack is returned.
 | 
					     * and the original item stack is returned.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * By overriding this method, you can create a new {@link ItemStack} which contains enough data to
 | 
					     * By overriding this method, you can create a new {@link ItemStack} which contains enough data to
 | 
				
			||||||
@@ -70,24 +67,24 @@ public interface UpgradeBase {
 | 
				
			|||||||
     * @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
 | 
					     * @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
 | 
				
			||||||
     * @return The item stack returned when unequipping.
 | 
					     * @return The item stack returned when unequipping.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default ItemStack getUpgradeItem(CompoundTag upgradeData) {
 | 
					    default ItemStack getUpgradeItem(DataComponentPatch upgradeData) {
 | 
				
			||||||
        return getCraftingItem();
 | 
					        return getCraftingItem();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Extract upgrade data from an {@link ItemStack}.
 | 
					     * Extract upgrade data from an {@link ItemStack}.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * This upgrade data will be available with {@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} or
 | 
					     * This upgrade data will be available with {@link ITurtleAccess#getUpgradeData(TurtleSide)} or
 | 
				
			||||||
     * {@link IPocketAccess#getUpgradeNBTData()}.
 | 
					     * {@link IPocketAccess#getUpgradeData()}.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * This should be an inverse to {@link #getUpgradeItem(CompoundTag)}.
 | 
					     * This should be an inverse to {@link #getUpgradeItem(DataComponentPatch)}.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
 | 
					     * @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
 | 
				
			||||||
     *              {@link #getCraftingItem()}.
 | 
					     *              {@link #getCraftingItem()}.
 | 
				
			||||||
     * @return The upgrade data that should be set on the turtle or pocket computer.
 | 
					     * @return The upgrade data that should be set on the turtle or pocket computer.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default CompoundTag getUpgradeData(ItemStack stack) {
 | 
					    default DataComponentPatch getUpgradeData(ItemStack stack) {
 | 
				
			||||||
        return new CompoundTag();
 | 
					        return DataComponentPatch.EMPTY;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -97,26 +94,15 @@ public interface UpgradeBase {
 | 
				
			|||||||
     * the original stack. In order to prevent people losing items with enchantments (or
 | 
					     * the original stack. In order to prevent people losing items with enchantments (or
 | 
				
			||||||
     * repairing items with non-0 damage), we impose additional checks on the item.
 | 
					     * repairing items with non-0 damage), we impose additional checks on the item.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * The default check requires that any non-capability NBT is exactly the same as the
 | 
					     * The default check requires that any NBT is exactly the same as the crafting item,
 | 
				
			||||||
     * crafting item, but this may be relaxed for your upgrade.
 | 
					     * but this may be relaxed for your upgrade.
 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * This is based on {@code net.minecraftforge.common.crafting.StrictNBTIngredient}'s check.
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
 | 
					     * @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
 | 
				
			||||||
     *              {@link #getCraftingItem()}.
 | 
					     *              {@link #getCraftingItem()}.
 | 
				
			||||||
     * @return If this stack may be used to equip this upgrade.
 | 
					     * @return If this stack may be used to equip this upgrade.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default boolean isItemSuitable(ItemStack stack) {
 | 
					    default boolean isItemSuitable(ItemStack stack) {
 | 
				
			||||||
        var crafting = getCraftingItem();
 | 
					        return ItemStack.isSameItemSameComponents(getCraftingItem(), stack);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
 | 
					 | 
				
			||||||
        // null one.
 | 
					 | 
				
			||||||
        var shareTag = PlatformHelper.get().getShareTag(stack);
 | 
					 | 
				
			||||||
        var craftingShareTag = PlatformHelper.get().getShareTag(crafting);
 | 
					 | 
				
			||||||
        if (shareTag == craftingShareTag) return true;
 | 
					 | 
				
			||||||
        if (shareTag == null) return Objects.requireNonNull(craftingShareTag).isEmpty();
 | 
					 | 
				
			||||||
        if (craftingShareTag == null) return shareTag.isEmpty();
 | 
					 | 
				
			||||||
        return shareTag.equals(craftingShareTag);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -125,7 +111,7 @@ public interface UpgradeBase {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param id The upgrade ID.
 | 
					     * @param id The upgrade ID.
 | 
				
			||||||
     * @return The  generated adjective.
 | 
					     * @return The  generated adjective.
 | 
				
			||||||
     * @see #getUnlocalisedAdjective()
 | 
					     * @see #getAdjective()
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    static String getDefaultAdjective(ResourceLocation id) {
 | 
					    static String getDefaultAdjective(ResourceLocation id) {
 | 
				
			||||||
        return Util.makeDescriptionId("upgrade", id) + ".adjective";
 | 
					        return Util.makeDescriptionId("upgrade", id) + ".adjective";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,60 +6,57 @@ package dan200.computercraft.api.upgrades;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
 | 
					import dan200.computercraft.api.pocket.IPocketUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.Holder;
 | 
				
			||||||
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
import org.jetbrains.annotations.Contract;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
 | 
					 * An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * <strong>IMPORTANT:</strong> The {@link #data()} in an upgrade data is often a reference to the original upgrade data.
 | 
					 | 
				
			||||||
 * Be careful to take a {@linkplain #copy() defensive copy} if you plan to use the data in this upgrade.
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param upgrade The current upgrade.
 | 
					 * @param holder The current upgrade holder.
 | 
				
			||||||
 * @param data    The upgrade's data.
 | 
					 * @param data   The upgrade's data.
 | 
				
			||||||
 * @param <T>     The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
 | 
					 * @param <T>    The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
 | 
					public record UpgradeData<T extends UpgradeBase>(Holder.Reference<T> holder, DataComponentPatch data) {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * A utility method to construct a new {@link UpgradeData} instance.
 | 
					     * A utility method to construct a new {@link UpgradeData} instance.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param upgrade An upgrade.
 | 
					     * @param holder An upgrade.
 | 
				
			||||||
     * @param data    The upgrade's data.
 | 
					     * @param data   The upgrade's data.
 | 
				
			||||||
     * @param <T>     The type of upgrade.
 | 
					     * @param <T>    The type of upgrade.
 | 
				
			||||||
     * @return The new {@link UpgradeData} instance.
 | 
					     * @return The new {@link UpgradeData} instance.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static <T extends UpgradeBase> UpgradeData<T> of(T upgrade, CompoundTag data) {
 | 
					    public static <T extends UpgradeBase> UpgradeData<T> of(Holder.Reference<T> holder, DataComponentPatch data) {
 | 
				
			||||||
        return new UpgradeData<>(upgrade, data);
 | 
					        return new UpgradeData<>(holder, data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Create an {@link UpgradeData} containing the default {@linkplain #data() data} for an upgrade.
 | 
					     * Create an {@link UpgradeData} containing the default {@linkplain #data() data} for an upgrade.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param upgrade The upgrade instance.
 | 
					     * @param holder The upgrade instance.
 | 
				
			||||||
     * @param <T>     The type of upgrade.
 | 
					     * @param <T>    The type of upgrade.
 | 
				
			||||||
     * @return The default upgrade data.
 | 
					     * @return The default upgrade data.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static <T extends UpgradeBase> UpgradeData<T> ofDefault(T upgrade) {
 | 
					    public static <T extends UpgradeBase> UpgradeData<T> ofDefault(Holder.Reference<T> holder) {
 | 
				
			||||||
        return of(upgrade, upgrade.getUpgradeData(upgrade.getCraftingItem()));
 | 
					        var upgrade = holder.value();
 | 
				
			||||||
 | 
					        return of(holder, upgrade.getUpgradeData(upgrade.getCraftingItem()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UpgradeData {
 | 
				
			||||||
 | 
					        if (!holder.isBound()) throw new IllegalArgumentException("Holder is not bound");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Take a copy of a (possibly {@code null}) {@link UpgradeData} instance.
 | 
					     * Get the current upgrade.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param upgrade The copied upgrade data.
 | 
					     * @return The current upgrade.
 | 
				
			||||||
     * @param <T>     The type of upgrade.
 | 
					 | 
				
			||||||
     * @return The newly created upgrade data.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Contract("!null -> !null; null -> null")
 | 
					    public T upgrade() {
 | 
				
			||||||
    public static <T extends UpgradeBase> @Nullable UpgradeData<T> copyOf(@Nullable UpgradeData<T> upgrade) {
 | 
					        return holder().value();
 | 
				
			||||||
        return upgrade == null ? null : upgrade.copy();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the {@linkplain UpgradeBase#getUpgradeItem(CompoundTag) upgrade item} for this upgrade.
 | 
					     * Get the {@linkplain UpgradeBase#getUpgradeItem(DataComponentPatch) upgrade item} for this upgrade.
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
 | 
					     * This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
 | 
				
			||||||
     * {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
 | 
					     * {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
 | 
				
			||||||
@@ -67,16 +64,6 @@ public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
 | 
				
			|||||||
     * @return This upgrade's item.
 | 
					     * @return This upgrade's item.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public ItemStack getUpgradeItem() {
 | 
					    public ItemStack getUpgradeItem() {
 | 
				
			||||||
        return upgrade.getUpgradeItem(data).copy();
 | 
					        return upgrade().getUpgradeItem(data).copy();
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Take a copy of this {@link UpgradeData}. This returns a new instance with the same upgrade and a fresh copy of
 | 
					 | 
				
			||||||
     * the upgrade data.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return A copy of the current instance.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public UpgradeData<T> copy() {
 | 
					 | 
				
			||||||
        return new UpgradeData<>(upgrade(), data().copy());
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,179 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.upgrades;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
import com.google.gson.JsonParseException;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.PlatformHelper;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
 | 
					 | 
				
			||||||
import net.minecraft.Util;
 | 
					 | 
				
			||||||
import net.minecraft.core.Registry;
 | 
					 | 
				
			||||||
import net.minecraft.core.registries.Registries;
 | 
					 | 
				
			||||||
import net.minecraft.data.CachedOutput;
 | 
					 | 
				
			||||||
import net.minecraft.data.DataProvider;
 | 
					 | 
				
			||||||
import net.minecraft.data.PackOutput;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceKey;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.Item;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
import java.util.*;
 | 
					 | 
				
			||||||
import java.util.concurrent.CompletableFuture;
 | 
					 | 
				
			||||||
import java.util.function.Consumer;
 | 
					 | 
				
			||||||
import java.util.function.Function;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A data generator/provider for turtle and pocket computer upgrades. This should not be extended directly, instead see
 | 
					 | 
				
			||||||
 * the other subclasses.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The base class of upgrades.
 | 
					 | 
				
			||||||
 * @param <R> The upgrade serialiser to register for.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
 | 
					 | 
				
			||||||
    private final PackOutput output;
 | 
					 | 
				
			||||||
    private final String name;
 | 
					 | 
				
			||||||
    private final String folder;
 | 
					 | 
				
			||||||
    private final ResourceKey<Registry<R>> registry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private @Nullable List<T> upgrades;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<R>> registry) {
 | 
					 | 
				
			||||||
        this.output = output;
 | 
					 | 
				
			||||||
        this.name = name;
 | 
					 | 
				
			||||||
        this.folder = folder;
 | 
					 | 
				
			||||||
        this.registry = registry;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param id         The ID of the upgrade to create.
 | 
					 | 
				
			||||||
     * @param serialiser The simple serialiser.
 | 
					 | 
				
			||||||
     * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public final Upgrade<R> simple(ResourceLocation id, R serialiser) {
 | 
					 | 
				
			||||||
        if (!(serialiser instanceof SimpleSerialiser)) {
 | 
					 | 
				
			||||||
            throw new IllegalStateException(serialiser + " must be a simple() seriaiser.");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new Upgrade<>(id, serialiser, s -> {
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param id         The ID of the upgrade to create.
 | 
					 | 
				
			||||||
     * @param serialiser The simple serialiser.
 | 
					 | 
				
			||||||
     * @param item       The crafting upgrade for this item.
 | 
					 | 
				
			||||||
     * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public final Upgrade<R> simpleWithCustomItem(ResourceLocation id, R serialiser, Item item) {
 | 
					 | 
				
			||||||
        if (!(serialiser instanceof SerialiserWithCraftingItem)) {
 | 
					 | 
				
			||||||
            throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser.");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new Upgrade<>(id, serialiser, s ->
 | 
					 | 
				
			||||||
            s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, item).toString())
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Add all turtle or pocket computer upgrades.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * <strong>Example usage:</strong>
 | 
					 | 
				
			||||||
     * <pre>{@code
 | 
					 | 
				
			||||||
     * protected void addUpgrades(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade) {
 | 
					 | 
				
			||||||
     *     simple(new ResourceLocation("mymod", "speaker"), SPEAKER_SERIALISER.get()).add(addUpgrade);
 | 
					 | 
				
			||||||
     * }
 | 
					 | 
				
			||||||
     * }</pre>
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param addUpgrade A callback used to register an upgrade.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public CompletableFuture<?> run(CachedOutput cache) {
 | 
					 | 
				
			||||||
        var base = output.getOutputFolder().resolve("data");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Set<ResourceLocation> seen = new HashSet<>();
 | 
					 | 
				
			||||||
        List<T> upgrades = new ArrayList<>();
 | 
					 | 
				
			||||||
        List<CompletableFuture<?>> futures = new ArrayList<>();
 | 
					 | 
				
			||||||
        addUpgrades(upgrade -> {
 | 
					 | 
				
			||||||
            if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var json = new JsonObject();
 | 
					 | 
				
			||||||
            json.addProperty("type", PlatformHelper.get().getRegistryKey(registry, upgrade.serialiser()).toString());
 | 
					 | 
				
			||||||
            upgrade.serialise().accept(json);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json")));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                var result = upgrade.serialiser().fromJson(upgrade.id(), json);
 | 
					 | 
				
			||||||
                upgrades.add(result);
 | 
					 | 
				
			||||||
            } catch (IllegalArgumentException | JsonParseException e) {
 | 
					 | 
				
			||||||
                LOGGER.error("Failed to parse {} {}", name, upgrade.id(), e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.upgrades = Collections.unmodifiableList(upgrades);
 | 
					 | 
				
			||||||
        return Util.sequenceFailFast(futures);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final String getName() {
 | 
					 | 
				
			||||||
        return name;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public final R existingSerialiser(ResourceLocation id) {
 | 
					 | 
				
			||||||
        var result = PlatformHelper.get().getRegistryObject(registry, id);
 | 
					 | 
				
			||||||
        if (result == null) throw new IllegalArgumentException("No such serialiser " + registry);
 | 
					 | 
				
			||||||
        return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public List<T> getGeneratedUpgrades() {
 | 
					 | 
				
			||||||
        if (upgrades == null) throw new IllegalStateException("Upgrades have not been generated yet");
 | 
					 | 
				
			||||||
        return upgrades;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * A constructed upgrade instance, produced {@link #addUpgrades(Consumer)}.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param id         The ID for this upgrade.
 | 
					 | 
				
			||||||
     * @param serialiser The serialiser which reads and writes this upgrade.
 | 
					 | 
				
			||||||
     * @param serialise  Augment the generated JSON with additional fields.
 | 
					 | 
				
			||||||
     * @param <R>        The type of upgrade serialiser.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public record Upgrade<R extends UpgradeSerialiser<?>>(
 | 
					 | 
				
			||||||
        ResourceLocation id, R serialiser, Consumer<JsonObject> serialise
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Convenience method for registering an upgrade.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param add The callback given to {@link #addUpgrades(Consumer)}
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public void add(Consumer<Upgrade<R>> add) {
 | 
					 | 
				
			||||||
            add.accept(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Return a new {@link Upgrade} which requires the given mod to be present.
 | 
					 | 
				
			||||||
         * <p>
 | 
					 | 
				
			||||||
         * This uses mod-loader-specific hooks (Forge's crafting conditions and Fabric's resource conditions). If using
 | 
					 | 
				
			||||||
         * this in a multi-loader setup, you must generate resources separately for the two loaders.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param modId The id of the mod.
 | 
					 | 
				
			||||||
         * @return A new upgrade instance.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public Upgrade<R> requireMod(String modId) {
 | 
					 | 
				
			||||||
            return new Upgrade<>(id, serialiser, json -> {
 | 
					 | 
				
			||||||
                PlatformHelper.get().addRequiredModCondition(json, modId);
 | 
					 | 
				
			||||||
                serialise.accept(json);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,52 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.api.upgrades;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					 | 
				
			||||||
import net.minecraft.network.FriendlyByteBuf;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one
 | 
					 | 
				
			||||||
 * of {@link TurtleUpgradeSerialiser} or {@link PocketUpgradeSerialiser}.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * However, it may sometimes be useful to implement this if you have some shared logic between upgrade types.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The upgrade that this class can serialise and deserialise.
 | 
					 | 
				
			||||||
 * @see TurtleUpgradeSerialiser
 | 
					 | 
				
			||||||
 * @see PocketUpgradeSerialiser
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public interface UpgradeSerialiser<T extends UpgradeBase> {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Read this upgrade from a JSON file in a datapack.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param id     The ID of this upgrade.
 | 
					 | 
				
			||||||
     * @param object The JSON object to load this upgrade from.
 | 
					 | 
				
			||||||
     * @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}.
 | 
					 | 
				
			||||||
     * @see net.minecraft.util.GsonHelper For additional JSON helper methods.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    T fromJson(ResourceLocation id, JsonObject object);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Read this upgrade from a network packet, sent from the server.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param id     The ID of this upgrade.
 | 
					 | 
				
			||||||
     * @param buffer The buffer object to read this upgrade from.
 | 
					 | 
				
			||||||
     * @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Write this upgrade to a network packet, to be sent to the client.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param buffer  The buffer object to write this upgrade to
 | 
					 | 
				
			||||||
     * @param upgrade The upgrade to write.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void toNetwork(FriendlyByteBuf buffer, T upgrade);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dan200.computercraft.api.upgrades;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.mojang.serialization.MapCodec;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.pocket.IPocketUpgrade;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
 | 
					import dan200.computercraft.impl.upgrades.UpgradeTypeImpl;
 | 
				
			||||||
 | 
					import net.minecraft.core.registries.BuiltInRegistries;
 | 
				
			||||||
 | 
					import net.minecraft.data.registries.RegistryPatchGenerator;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.crafting.Recipe;
 | 
				
			||||||
 | 
					import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The type of a {@linkplain ITurtleUpgrade turtle} or {@linkplain IPocketUpgrade pocket} upgrade.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Turtle and pocket computer upgrades are registered using Minecraft's dynamic registry system. As a result, they
 | 
				
			||||||
 | 
					 * follow a similar design to other dynamic content, such as {@linkplain Recipe recipes} or {@link LootItemFunction
 | 
				
			||||||
 | 
					 * loot functions}.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * First, one adds a new class implementing {@link ITurtleUpgrade} or {@link IPocketUpgrade}). This is responsible for
 | 
				
			||||||
 | 
					 * handling all the logic of your upgrade.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * However, the upgrades are not registered directly. Instead, each upgrade class should have a corresponding
 | 
				
			||||||
 | 
					 * {@link UpgradeType}, which is responsible for loading the upgrade from a datapack. The upgrade type should then be
 | 
				
			||||||
 | 
					 * registered in its appropriate registry ({@link ITurtleUpgrade#typeRegistry()},
 | 
				
			||||||
 | 
					 * {@link IPocketUpgrade#typeRegistry()}).
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It
 | 
				
			||||||
 | 
					 * is recommended to do this via the data generators.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <h2 id="datagen">Data Generation</h2>
 | 
				
			||||||
 | 
					 * As turtle and pocket upgrades are just loaded using vanilla's dynamic loaders, one may use the same data generation
 | 
				
			||||||
 | 
					 * tools as you would for any other dynamic registry.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * This can typically be done by extending vanilla's built-in registries using {@link RegistryPatchGenerator}, and then
 | 
				
			||||||
 | 
					 * writing out the new registries using {@code net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider}
 | 
				
			||||||
 | 
					 * on Fabric or {@code net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider} on Forge.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * {@snippet lang="java" :
 | 
				
			||||||
 | 
					 * import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
 | 
					 * import net.minecraft.Util;
 | 
				
			||||||
 | 
					 * import net.minecraft.core.HolderLookup;
 | 
				
			||||||
 | 
					 * import net.minecraft.core.RegistrySetBuilder;
 | 
				
			||||||
 | 
					 * import net.minecraft.data.DataGenerator;
 | 
				
			||||||
 | 
					 * import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * import java.util.concurrent.CompletableFuture;
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * public void generate(DataGenerator.PackGenerator output, CompletableFuture<HolderLookup.Provider> registries) {
 | 
				
			||||||
 | 
					 *     var newRegistries = RegistryPatchGenerator.createLookup(registries, Util.make(new RegistrySetBuilder(), builder -> {
 | 
				
			||||||
 | 
					 *         builder.add(ITurtleUpgrade.REGISTRY, upgrades -> {
 | 
				
			||||||
 | 
					 *             upgrades.register(ITurtleUpgrade.createKey(new ResourceLocation("my_mod", "my_upgrade")), new MyUpgrade());
 | 
				
			||||||
 | 
					 *         });
 | 
				
			||||||
 | 
					 *     }));
 | 
				
			||||||
 | 
					 *     output.addProvider(o -> new DatapackBuiltinEntriesProvider(o, newRegistries, Set.of("my_mod")));
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 * }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param <T> The upgrade subclass that this upgrade type represents.
 | 
				
			||||||
 | 
					 * @see ITurtleUpgrade
 | 
				
			||||||
 | 
					 * @see IPocketUpgrade
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface UpgradeType<T extends UpgradeBase> {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The codec to read and write this upgrade from a datapack.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The codec for this upgrade.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    MapCodec<T> codec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new upgrade type.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param codec The codec
 | 
				
			||||||
 | 
					     * @param <T>   The type of the generated upgrade.
 | 
				
			||||||
 | 
					     * @return The newly created upgrade type.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static <T extends UpgradeBase> UpgradeType<T> create(MapCodec<T> codec) {
 | 
				
			||||||
 | 
					        return new UpgradeTypeImpl<>(codec);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create an upgrade type for an upgrade that takes no arguments.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(Function)} instead.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param instance Generate a new upgrade with a specific ID.
 | 
				
			||||||
 | 
					     * @param <T>      The type of the generated upgrade.
 | 
				
			||||||
 | 
					     * @return A new upgrade type.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static <T extends UpgradeBase> UpgradeType<T> simple(T instance) {
 | 
				
			||||||
 | 
					        return create(MapCodec.unit(instance));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create an upgrade type for a simple upgrade whose crafting item can be specified.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
 | 
				
			||||||
 | 
					     *                {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
 | 
				
			||||||
 | 
					     * @param <T>     The type of the generated upgrade.
 | 
				
			||||||
 | 
					     * @return A new upgrade type.
 | 
				
			||||||
 | 
					     * @see #simple(UpgradeBase)  For upgrades whose crafting stack should not vary.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static <T extends UpgradeBase> UpgradeType<T> simpleWithCustomItem(Function<ItemStack, T> factory) {
 | 
				
			||||||
 | 
					        return create(BuiltInRegistries.ITEM.byNameCodec()
 | 
				
			||||||
 | 
					            .xmap(x -> factory.apply(new ItemStack(x)), x -> x.getCraftingItem().getItem())
 | 
				
			||||||
 | 
					            .fieldOf("item"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,6 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.impl;
 | 
					package dan200.computercraft.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.mojang.serialization.Codec;
 | 
				
			||||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
					import dan200.computercraft.api.ComputerCraftAPI;
 | 
				
			||||||
import dan200.computercraft.api.detail.BlockReference;
 | 
					import dan200.computercraft.api.detail.BlockReference;
 | 
				
			||||||
import dan200.computercraft.api.detail.DetailRegistry;
 | 
					import dan200.computercraft.api.detail.DetailRegistry;
 | 
				
			||||||
@@ -15,10 +16,12 @@ import dan200.computercraft.api.media.MediaProvider;
 | 
				
			|||||||
import dan200.computercraft.api.network.PacketNetwork;
 | 
					import dan200.computercraft.api.network.PacketNetwork;
 | 
				
			||||||
import dan200.computercraft.api.network.wired.WiredElement;
 | 
					import dan200.computercraft.api.network.wired.WiredElement;
 | 
				
			||||||
import dan200.computercraft.api.network.wired.WiredNode;
 | 
					import dan200.computercraft.api.network.wired.WiredNode;
 | 
				
			||||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
 | 
					import dan200.computercraft.api.pocket.IPocketUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
 | 
					import dan200.computercraft.api.redstone.BundledRedstoneProvider;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
 | 
					import dan200.computercraft.api.turtle.TurtleRefuelHandler;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					import dan200.computercraft.api.upgrades.UpgradeType;
 | 
				
			||||||
 | 
					import dan200.computercraft.impl.upgrades.TurtleToolSpec;
 | 
				
			||||||
import net.minecraft.core.BlockPos;
 | 
					import net.minecraft.core.BlockPos;
 | 
				
			||||||
import net.minecraft.core.Direction;
 | 
					import net.minecraft.core.Direction;
 | 
				
			||||||
import net.minecraft.core.Registry;
 | 
					import net.minecraft.core.Registry;
 | 
				
			||||||
@@ -67,9 +70,15 @@ public interface ComputerCraftAPIService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void registerRefuelHandler(TurtleRefuelHandler handler);
 | 
					    void registerRefuelHandler(TurtleRefuelHandler handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId();
 | 
					    ResourceKey<Registry<UpgradeType<? extends ITurtleUpgrade>>> turtleUpgradeRegistryId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId();
 | 
					    Codec<ITurtleUpgrade> turtleUpgradeCodec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ResourceKey<Registry<UpgradeType<? extends IPocketUpgrade>>> pocketUpgradeRegistryId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ITurtleUpgrade createTurtleTool(TurtleToolSpec spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Codec<IPocketUpgrade> pocketUpgradeCodec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DetailRegistry<ItemStack> getItemStackDetailRegistry();
 | 
					    DetailRegistry<ItemStack> getItemStackDetailRegistry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,92 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.impl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
 | 
					 | 
				
			||||||
import net.minecraft.core.Registry;
 | 
					 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceKey;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Abstraction layer for Forge and Fabric. See implementations for more details.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ApiStatus.Internal
 | 
					 | 
				
			||||||
public interface PlatformHelper {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the current {@link PlatformHelper} instance.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return The current instance.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    static PlatformHelper get() {
 | 
					 | 
				
			||||||
        var instance = Instance.INSTANCE;
 | 
					 | 
				
			||||||
        return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the unique ID for a registered object.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param registry The registry to look up this object in.
 | 
					 | 
				
			||||||
     * @param object   The object to look up.
 | 
					 | 
				
			||||||
     * @param <T>      The type of object the registry stores.
 | 
					 | 
				
			||||||
     * @return The registered object's ID.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If the registry or object are not registered.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    <T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Look up an ID in a registry, returning the registered object.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param registry The registry to look up this object in.
 | 
					 | 
				
			||||||
     * @param id       The ID to look up.
 | 
					 | 
				
			||||||
     * @param <T>      The type of object the registry stores.
 | 
					 | 
				
			||||||
     * @return The resolved registry object.
 | 
					 | 
				
			||||||
     * @throws IllegalArgumentException If the registry or object are not registered.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    <T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Get the subset of an {@link ItemStack}'s {@linkplain ItemStack#getTag() tag} which is synced to the client.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param item The stack.
 | 
					 | 
				
			||||||
     * @return The item's tag.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Nullable
 | 
					 | 
				
			||||||
    default CompoundTag getShareTag(ItemStack item) {
 | 
					 | 
				
			||||||
        return item.getTag();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
 | 
					 | 
				
			||||||
     * {@link UpgradeDataProvider}.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param object The JSON object we're generating.
 | 
					 | 
				
			||||||
     * @param modId  The mod ID that we require.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void addRequiredModCondition(JsonObject object, String modId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    final class Instance {
 | 
					 | 
				
			||||||
        static final @Nullable PlatformHelper INSTANCE;
 | 
					 | 
				
			||||||
        static final @Nullable Throwable ERROR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        static {
 | 
					 | 
				
			||||||
            // We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
 | 
					 | 
				
			||||||
            // the error and rethrow it when accessing. This should be JITted away in the common case.
 | 
					 | 
				
			||||||
            var helper = Services.tryLoad(PlatformHelper.class);
 | 
					 | 
				
			||||||
            INSTANCE = helper.instance();
 | 
					 | 
				
			||||||
            ERROR = helper.error();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Instance() {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,49 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.impl.upgrades;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
 | 
					 | 
				
			||||||
import net.minecraft.network.FriendlyByteBuf;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import net.minecraft.util.GsonHelper;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.function.BiFunction;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Simple serialiser which returns a constant upgrade with a custom crafting item.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The upgrade that this class can serialise and deserialise.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ApiStatus.Internal
 | 
					 | 
				
			||||||
public abstract class SerialiserWithCraftingItem<T extends UpgradeBase> implements UpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
    private final BiFunction<ResourceLocation, ItemStack, T> factory;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
 | 
					 | 
				
			||||||
        this.factory = factory;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final T fromJson(ResourceLocation id, JsonObject object) {
 | 
					 | 
				
			||||||
        var item = GsonHelper.getAsItem(object, "item");
 | 
					 | 
				
			||||||
        return factory.apply(id, new ItemStack(item));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
 | 
					 | 
				
			||||||
        var item = buffer.readItem();
 | 
					 | 
				
			||||||
        return factory.apply(id, item);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
 | 
					 | 
				
			||||||
        buffer.writeItem(upgrade.getCraftingItem());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,44 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.impl.upgrades;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
 | 
					 | 
				
			||||||
import net.minecraft.network.FriendlyByteBuf;
 | 
					 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.ApiStatus;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.function.Function;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Simple serialiser which returns a constant upgrade.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param <T> The upgrade that this class can serialise and deserialise.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ApiStatus.Internal
 | 
					 | 
				
			||||||
public abstract class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSerialiser<T> {
 | 
					 | 
				
			||||||
    private final Function<ResourceLocation, T> constructor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public SimpleSerialiser(Function<ResourceLocation, T> constructor) {
 | 
					 | 
				
			||||||
        this.constructor = constructor;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final T fromJson(ResourceLocation id, JsonObject object) {
 | 
					 | 
				
			||||||
        return constructor.apply(id);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
 | 
					 | 
				
			||||||
        return constructor.apply(id);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dan200.computercraft.impl.upgrades;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.mojang.serialization.Codec;
 | 
				
			||||||
 | 
					import com.mojang.serialization.MapCodec;
 | 
				
			||||||
 | 
					import com.mojang.serialization.codecs.RecordCodecBuilder;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.turtle.TurtleToolDurability;
 | 
				
			||||||
 | 
					import net.minecraft.core.registries.BuiltInRegistries;
 | 
				
			||||||
 | 
					import net.minecraft.core.registries.Registries;
 | 
				
			||||||
 | 
					import net.minecraft.network.chat.Component;
 | 
				
			||||||
 | 
					import net.minecraft.network.chat.ComponentSerialization;
 | 
				
			||||||
 | 
					import net.minecraft.tags.TagKey;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.Item;
 | 
				
			||||||
 | 
					import net.minecraft.world.level.block.Block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The template for a turtle tool.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param adjective         The adjective for this tool.
 | 
				
			||||||
 | 
					 * @param item              The tool used.
 | 
				
			||||||
 | 
					 * @param damageMultiplier  The damage multiplier for this tool.
 | 
				
			||||||
 | 
					 * @param allowEnchantments Whether to allow enchantments.
 | 
				
			||||||
 | 
					 * @param consumeDurability When to consume durability.
 | 
				
			||||||
 | 
					 * @param breakable         The items breakable by this tool.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public record TurtleToolSpec(
 | 
				
			||||||
 | 
					    Component adjective,
 | 
				
			||||||
 | 
					    Item item,
 | 
				
			||||||
 | 
					    float damageMultiplier,
 | 
				
			||||||
 | 
					    boolean allowEnchantments,
 | 
				
			||||||
 | 
					    TurtleToolDurability consumeDurability,
 | 
				
			||||||
 | 
					    Optional<TagKey<Block>> breakable
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    public static final float DEFAULT_DAMAGE_MULTIPLIER = 3.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final MapCodec<TurtleToolSpec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
 | 
				
			||||||
 | 
					        ComponentSerialization.CODEC.fieldOf("adjective").forGetter(TurtleToolSpec::adjective),
 | 
				
			||||||
 | 
					        BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::item),
 | 
				
			||||||
 | 
					        Codec.FLOAT.optionalFieldOf("damageMultiplier", DEFAULT_DAMAGE_MULTIPLIER).forGetter(TurtleToolSpec::damageMultiplier),
 | 
				
			||||||
 | 
					        Codec.BOOL.optionalFieldOf("allowEnchantments", false).forGetter(TurtleToolSpec::allowEnchantments),
 | 
				
			||||||
 | 
					        TurtleToolDurability.CODEC.optionalFieldOf("consumeDurability", TurtleToolDurability.NEVER).forGetter(TurtleToolSpec::consumeDurability),
 | 
				
			||||||
 | 
					        TagKey.codec(Registries.BLOCK).optionalFieldOf("breakable").forGetter(TurtleToolSpec::breakable)
 | 
				
			||||||
 | 
					    ).apply(instance, TurtleToolSpec::new));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dan200.computercraft.impl.upgrades;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.mojang.serialization.MapCodec;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
				
			||||||
 | 
					import dan200.computercraft.api.upgrades.UpgradeType;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.ApiStatus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Simple implementation of {@link UpgradeType}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param codec The codec to read/write upgrades with.
 | 
				
			||||||
 | 
					 * @param <T>   The upgrade subclass that this upgrade type represents.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@ApiStatus.Internal
 | 
				
			||||||
 | 
					public record UpgradeTypeImpl<T extends UpgradeBase>(MapCodec<T> codec) implements UpgradeType<T> {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -29,7 +29,7 @@ configurations {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
repositories {
 | 
					repositories {
 | 
				
			||||||
    maven("https://maven.minecraftforge.net/") {
 | 
					    maven("https://maven.neoforged.net/") {
 | 
				
			||||||
        content {
 | 
					        content {
 | 
				
			||||||
            includeModule("org.spongepowered", "mixin")
 | 
					            includeModule("org.spongepowered", "mixin")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -42,8 +42,9 @@ dependencies {
 | 
				
			|||||||
    implementation(commonClasses(project(":common-api")))
 | 
					    implementation(commonClasses(project(":common-api")))
 | 
				
			||||||
    clientImplementation(clientClasses(project(":common-api")))
 | 
					    clientImplementation(clientClasses(project(":common-api")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    compileOnly(libs.mixin)
 | 
				
			||||||
 | 
					    compileOnly(libs.mixinExtra)
 | 
				
			||||||
    compileOnly(libs.bundles.externalMods.common)
 | 
					    compileOnly(libs.bundles.externalMods.common)
 | 
				
			||||||
    compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
 | 
					 | 
				
			||||||
    clientCompileOnly(variantOf(libs.emi) { classifier("api") })
 | 
					    clientCompileOnly(variantOf(libs.emi) { classifier("api") })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    annotationProcessorEverywhere(libs.autoService)
 | 
					    annotationProcessorEverywhere(libs.autoService)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,7 +108,7 @@ public final class ClientHooks {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public static void addBlockDebugInfo(Consumer<String> addText) {
 | 
					    public static void addBlockDebugInfo(Consumer<String> addText) {
 | 
				
			||||||
        var minecraft = Minecraft.getInstance();
 | 
					        var minecraft = Minecraft.getInstance();
 | 
				
			||||||
        if (!minecraft.options.renderDebug || minecraft.level == null) return;
 | 
					        if (!minecraft.getDebugOverlay().showDebugScreen() || minecraft.level == null) return;
 | 
				
			||||||
        if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
 | 
					        if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
 | 
					        var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
 | 
				
			||||||
@@ -128,8 +128,8 @@ public final class ClientHooks {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static void addTurtleUpgrade(Consumer<String> out, TurtleBlockEntity turtle, TurtleSide side) {
 | 
					    private static void addTurtleUpgrade(Consumer<String> out, TurtleBlockEntity turtle, TurtleSide side) {
 | 
				
			||||||
        var upgrade = turtle.getUpgrade(side);
 | 
					        var upgrade = turtle.getAccess().getUpgradeWithData(side);
 | 
				
			||||||
        if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID()));
 | 
					        if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -138,7 +138,7 @@ public final class ClientHooks {
 | 
				
			|||||||
     * @param addText A callback which adds a single line of text.
 | 
					     * @param addText A callback which adds a single line of text.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static void addGameDebugInfo(Consumer<String> addText) {
 | 
					    public static void addGameDebugInfo(Consumer<String> addText) {
 | 
				
			||||||
        if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().options.renderDebug) {
 | 
					        if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().getDebugOverlay().showDebugScreen()) {
 | 
				
			||||||
            addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer());
 | 
					            addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,16 +21,17 @@ import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
 | 
				
			|||||||
import dan200.computercraft.core.util.Colour;
 | 
					import dan200.computercraft.core.util.Colour;
 | 
				
			||||||
import dan200.computercraft.shared.ModRegistry;
 | 
					import dan200.computercraft.shared.ModRegistry;
 | 
				
			||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
 | 
					import dan200.computercraft.shared.command.CommandComputerCraft;
 | 
				
			||||||
import dan200.computercraft.shared.common.IColouredItem;
 | 
					 | 
				
			||||||
import dan200.computercraft.shared.computer.core.ComputerState;
 | 
					import dan200.computercraft.shared.computer.core.ComputerState;
 | 
				
			||||||
import dan200.computercraft.shared.computer.core.ServerContext;
 | 
					import dan200.computercraft.shared.computer.core.ServerContext;
 | 
				
			||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
 | 
					import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
 | 
				
			||||||
import dan200.computercraft.shared.media.items.DiskItem;
 | 
					import dan200.computercraft.shared.media.items.DiskItem;
 | 
				
			||||||
import dan200.computercraft.shared.media.items.TreasureDiskItem;
 | 
					 | 
				
			||||||
import net.minecraft.Util;
 | 
					import net.minecraft.Util;
 | 
				
			||||||
import net.minecraft.client.Minecraft;
 | 
					import net.minecraft.client.Minecraft;
 | 
				
			||||||
import net.minecraft.client.color.item.ItemColor;
 | 
					import net.minecraft.client.color.item.ItemColor;
 | 
				
			||||||
import net.minecraft.client.gui.screens.MenuScreens;
 | 
					import net.minecraft.client.gui.screens.MenuScreens;
 | 
				
			||||||
 | 
					import net.minecraft.client.gui.screens.Screen;
 | 
				
			||||||
 | 
					import net.minecraft.client.gui.screens.inventory.MenuAccess;
 | 
				
			||||||
import net.minecraft.client.multiplayer.ClientLevel;
 | 
					import net.minecraft.client.multiplayer.ClientLevel;
 | 
				
			||||||
import net.minecraft.client.renderer.ShaderInstance;
 | 
					import net.minecraft.client.renderer.ShaderInstance;
 | 
				
			||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
 | 
					import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
 | 
				
			||||||
@@ -41,9 +42,13 @@ import net.minecraft.network.chat.Component;
 | 
				
			|||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
 | 
					import net.minecraft.server.packs.resources.PreparableReloadListener;
 | 
				
			||||||
import net.minecraft.server.packs.resources.ResourceProvider;
 | 
					import net.minecraft.server.packs.resources.ResourceProvider;
 | 
				
			||||||
 | 
					import net.minecraft.util.FastColor;
 | 
				
			||||||
import net.minecraft.world.entity.LivingEntity;
 | 
					import net.minecraft.world.entity.LivingEntity;
 | 
				
			||||||
 | 
					import net.minecraft.world.inventory.AbstractContainerMenu;
 | 
				
			||||||
 | 
					import net.minecraft.world.inventory.MenuType;
 | 
				
			||||||
import net.minecraft.world.item.Item;
 | 
					import net.minecraft.world.item.Item;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.component.DyedItemColor;
 | 
				
			||||||
import net.minecraft.world.level.ItemLike;
 | 
					import net.minecraft.world.level.ItemLike;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
@@ -81,14 +86,6 @@ public final class ClientRegistry {
 | 
				
			|||||||
     * @param itemProperties Callback to register item properties.
 | 
					     * @param itemProperties Callback to register item properties.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static void registerMainThread(RegisterItemProperty itemProperties) {
 | 
					    public static void registerMainThread(RegisterItemProperty itemProperties) {
 | 
				
			||||||
        MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
 | 
					 | 
				
			||||||
        MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
 | 
					 | 
				
			||||||
        MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        MenuScreens.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
 | 
					 | 
				
			||||||
        MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
 | 
					 | 
				
			||||||
        MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        registerItemProperty(itemProperties, "state",
 | 
					        registerItemProperty(itemProperties, "state",
 | 
				
			||||||
            new UnclampedPropertyFunction((stack, world, player, random) -> {
 | 
					            new UnclampedPropertyFunction((stack, world, player, random) -> {
 | 
				
			||||||
                var computer = ClientPocketComputers.get(stack);
 | 
					                var computer = ClientPocketComputers.get(stack);
 | 
				
			||||||
@@ -97,23 +94,39 @@ public final class ClientRegistry {
 | 
				
			|||||||
            ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
 | 
					            ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        registerItemProperty(itemProperties, "coloured",
 | 
					        registerItemProperty(itemProperties, "coloured",
 | 
				
			||||||
            (stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
 | 
					            (stack, world, player, random) -> DyedItemColor.getOrDefault(stack, -1) != -1 ? 1 : 0,
 | 
				
			||||||
            ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
 | 
					            ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void registerMenuScreens(RegisterMenuScreen register) {
 | 
				
			||||||
 | 
					        register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
 | 
				
			||||||
 | 
					        register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
 | 
				
			||||||
 | 
					        register.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
 | 
				
			||||||
 | 
					        register.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        register.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
 | 
				
			||||||
 | 
					        register.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
 | 
				
			||||||
 | 
					        register.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        register.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public interface RegisterMenuScreen {
 | 
				
			||||||
 | 
					        <M extends AbstractContainerMenu, U extends Screen & MenuAccess<M>> void register(MenuType<? extends M> type, MenuScreens.ScreenConstructor<M, U> factory);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
 | 
					    public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
 | 
				
			||||||
        register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
 | 
					        register.register(ModRegistry.TurtleUpgradeTypes.SPEAKER.get(), TurtleUpgradeModeller.sided(
 | 
				
			||||||
            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
 | 
					            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
 | 
				
			||||||
            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
 | 
					            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
 | 
				
			||||||
        ));
 | 
					        ));
 | 
				
			||||||
        register.register(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
 | 
					        register.register(ModRegistry.TurtleUpgradeTypes.WORKBENCH.get(), TurtleUpgradeModeller.sided(
 | 
				
			||||||
            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
 | 
					            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
 | 
				
			||||||
            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
 | 
					            new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
 | 
				
			||||||
        ));
 | 
					        ));
 | 
				
			||||||
        register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
 | 
					        register.register(ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM.get(), new TurtleModemModeller());
 | 
				
			||||||
        register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
 | 
					        register.register(ModRegistry.TurtleUpgradeTypes.TOOL.get(), TurtleUpgradeModeller.flatItem());
 | 
				
			||||||
        register.register(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SafeVarargs
 | 
					    @SafeVarargs
 | 
				
			||||||
@@ -148,12 +161,12 @@ public final class ClientRegistry {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
 | 
					    public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
 | 
				
			||||||
        register.accept(
 | 
					        register.accept(
 | 
				
			||||||
            (stack, layer) -> layer == 1 ? ((DiskItem) stack.getItem()).getColour(stack) : 0xFFFFFF,
 | 
					            (stack, layer) -> layer == 1 ? DiskItem.getColour(stack) : -1,
 | 
				
			||||||
            ModRegistry.Items.DISK.get()
 | 
					            ModRegistry.Items.DISK.get()
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register.accept(
 | 
					        register.accept(
 | 
				
			||||||
            (stack, layer) -> layer == 1 ? TreasureDiskItem.getColour(stack) : 0xFFFFFF,
 | 
					            (stack, layer) -> layer == 1 ? DyedItemColor.getOrDefault(stack, Colour.BLUE.getARGB()) : -1,
 | 
				
			||||||
            ModRegistry.Items.TREASURE_DISK.get()
 | 
					            ModRegistry.Items.TREASURE_DISK.get()
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -166,17 +179,17 @@ public final class ClientRegistry {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static int getPocketColour(ItemStack stack, int layer) {
 | 
					    private static int getPocketColour(ItemStack stack, int layer) {
 | 
				
			||||||
        return switch (layer) {
 | 
					        return switch (layer) {
 | 
				
			||||||
            default -> 0xFFFFFF;
 | 
					            default -> -1;
 | 
				
			||||||
            case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
 | 
					            case 1 -> DyedItemColor.getOrDefault(stack, -1); // Frame colour
 | 
				
			||||||
            case 2 -> { // Light colour
 | 
					            case 2 -> { // Light colour
 | 
				
			||||||
                var computer = ClientPocketComputers.get(stack);
 | 
					                var computer = ClientPocketComputers.get(stack);
 | 
				
			||||||
                yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
 | 
					                yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getARGB() : FastColor.ARGB32.opaque(computer.getLightState());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static int getTurtleColour(ItemStack stack, int layer) {
 | 
					    private static int getTurtleColour(ItemStack stack, int layer) {
 | 
				
			||||||
        return layer == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF;
 | 
					        return layer == 0 ? DyedItemColor.getOrDefault(stack, -1) : -1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
 | 
					    public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,7 @@ public class ClientTableFormatter implements TableFormatter {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        var tag = createTag(table.getId());
 | 
					        var tag = createTag(table.getId());
 | 
				
			||||||
        if (chat.allMessages.removeIf(guiMessage -> guiMessage.tag() != null && Objects.equals(guiMessage.tag().logTag(), tag.logTag()))) {
 | 
					        if (chat.allMessages.removeIf(guiMessage -> guiMessage.tag() != null && Objects.equals(guiMessage.tag().logTag(), tag.logTag()))) {
 | 
				
			||||||
            chat.refreshTrimmedMessage();
 | 
					            chat.rescaleChat();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TableFormatter.super.display(table);
 | 
					        TableFormatter.super.display(table);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MPL-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package dan200.computercraft.client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.auto.service.AutoService;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					 | 
				
			||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@AutoService(ComputerCraftAPIClientService.class)
 | 
					 | 
				
			||||||
public final class ComputerCraftAPIClientImpl implements ComputerCraftAPIClientService {
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
 | 
					 | 
				
			||||||
        TurtleUpgradeModellers.register(serialiser, modeller);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -127,7 +127,6 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
				
			||||||
        renderBackground(graphics);
 | 
					 | 
				
			||||||
        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
					        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
				
			||||||
        renderTooltip(graphics, mouseX, mouseY);
 | 
					        renderTooltip(graphics, mouseX, mouseY);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,6 @@ public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
				
			||||||
        renderBackground(graphics);
 | 
					 | 
				
			||||||
        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
					        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
				
			||||||
        renderTooltip(graphics, mouseX, mouseY);
 | 
					        renderTooltip(graphics, mouseX, mouseY);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import net.minecraft.client.gui.GuiGraphics;
 | 
				
			|||||||
import net.minecraft.client.gui.components.toasts.Toast;
 | 
					import net.minecraft.client.gui.components.toasts.Toast;
 | 
				
			||||||
import net.minecraft.client.gui.components.toasts.ToastComponent;
 | 
					import net.minecraft.client.gui.components.toasts.ToastComponent;
 | 
				
			||||||
import net.minecraft.network.chat.Component;
 | 
					import net.minecraft.network.chat.Component;
 | 
				
			||||||
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import net.minecraft.util.FormattedCharSequence;
 | 
					import net.minecraft.util.FormattedCharSequence;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,6 +19,7 @@ import java.util.List;
 | 
				
			|||||||
 * A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}.
 | 
					 * A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ItemToast implements Toast {
 | 
					public class ItemToast implements Toast {
 | 
				
			||||||
 | 
					    private static final ResourceLocation TEXTURE = new ResourceLocation("toast/recipe");
 | 
				
			||||||
    public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object();
 | 
					    public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final long DISPLAY_TIME = 7000L;
 | 
					    private static final long DISPLAY_TIME = 7000L;
 | 
				
			||||||
@@ -79,7 +81,7 @@ public class ItemToast implements Toast {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (width == 160 && message.size() <= 1) {
 | 
					        if (width == 160 && message.size() <= 1) {
 | 
				
			||||||
            graphics.blit(TEXTURE, 0, 0, 0, 64, width, height());
 | 
					            graphics.blitSprite(TEXTURE, 0, 0, width, height());
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var height = height();
 | 
					            var height = height();
 | 
				
			||||||
@@ -109,14 +111,14 @@ public class ItemToast implements Toast {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
 | 
					    private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
 | 
				
			||||||
        var leftOffset = 5;
 | 
					        var leftOffset = u == 0 ? 20 : 5;
 | 
				
			||||||
        var rightOffset = Math.min(60, x - leftOffset);
 | 
					        var rightOffset = Math.min(60, x - leftOffset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height);
 | 
					        graphics.blitSprite(TEXTURE, 160, 32, 0, u, 0, y, leftOffset, height);
 | 
				
			||||||
        for (var k = leftOffset; k < x - rightOffset; k += 64) {
 | 
					        for (var k = leftOffset; k < x - rightOffset; k += 64) {
 | 
				
			||||||
            graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
 | 
					            graphics.blitSprite(TEXTURE, 160, 32, 32, u, k, y, Math.min(64, x - k - rightOffset), height);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
 | 
					        graphics.blitSprite(TEXTURE, 160, 32, 160 - rightOffset, u, x - rightOffset, y, rightOffset, height);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,9 +66,9 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
 | 
					    public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
 | 
				
			||||||
        Objects.requireNonNull(minecraft().player).getInventory().swapPaint(pDelta);
 | 
					        Objects.requireNonNull(minecraft().player).getInventory().swapPaint(scrollY);
 | 
				
			||||||
        return super.mouseScrolled(pMouseX, pMouseY, pDelta);
 | 
					        return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -105,6 +105,11 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
 | 
				
			||||||
 | 
					        // Skip rendering the background.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Minecraft minecraft() {
 | 
					    private Minecraft minecraft() {
 | 
				
			||||||
        return Nullability.assertNonNull(minecraft);
 | 
					        return Nullability.assertNonNull(minecraft);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,8 +86,6 @@ public final class OptionScreen extends Screen {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
				
			||||||
        renderBackground(graphics);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Render the actual texture.
 | 
					        // Render the actual texture.
 | 
				
			||||||
        graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
 | 
					        graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
 | 
				
			||||||
        graphics.blit(BACKGROUND,
 | 
					        graphics.blit(BACKGROUND,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,6 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
				
			||||||
        renderBackground(graphics);
 | 
					 | 
				
			||||||
        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
					        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
				
			||||||
        renderTooltip(graphics, mouseX, mouseY);
 | 
					        renderTooltip(graphics, mouseX, mouseY);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,9 @@ package dan200.computercraft.client.gui;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.mojang.blaze3d.vertex.Tesselator;
 | 
					import com.mojang.blaze3d.vertex.Tesselator;
 | 
				
			||||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
					import dan200.computercraft.core.terminal.TextBuffer;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.ModRegistry;
 | 
				
			||||||
import dan200.computercraft.shared.common.HeldItemMenu;
 | 
					import dan200.computercraft.shared.common.HeldItemMenu;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.media.items.PrintoutData;
 | 
				
			||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
					import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
				
			||||||
import net.minecraft.client.gui.GuiGraphics;
 | 
					import net.minecraft.client.gui.GuiGraphics;
 | 
				
			||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
 | 
					import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
 | 
				
			||||||
@@ -35,16 +37,17 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        imageHeight = Y_SIZE;
 | 
					        imageHeight = Y_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var text = PrintoutItem.getText(container.getStack());
 | 
					        var printout = container.getStack().getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
 | 
				
			||||||
        this.text = new TextBuffer[text.length];
 | 
					        this.text = new TextBuffer[printout.lines().size()];
 | 
				
			||||||
        for (var i = 0; i < this.text.length; i++) this.text[i] = new TextBuffer(text[i]);
 | 
					        this.colours = new TextBuffer[printout.lines().size()];
 | 
				
			||||||
 | 
					        for (var i = 0; i < this.text.length; i++) {
 | 
				
			||||||
        var colours = PrintoutItem.getColours(container.getStack());
 | 
					            var line = printout.lines().get(i);
 | 
				
			||||||
        this.colours = new TextBuffer[colours.length];
 | 
					            this.text[i] = new TextBuffer(line.text());
 | 
				
			||||||
        for (var i = 0; i < this.colours.length; i++) this.colours[i] = new TextBuffer(colours[i]);
 | 
					            this.colours[i] = new TextBuffer(line.foreground());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        page = 0;
 | 
					        page = 0;
 | 
				
			||||||
        pages = Math.max(this.text.length / PrintoutItem.LINES_PER_PAGE, 1);
 | 
					        pages = Math.max(this.text.length / PrintoutData.LINES_PER_PAGE, 1);
 | 
				
			||||||
        book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
 | 
					        book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,15 +67,15 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean mouseScrolled(double x, double y, double delta) {
 | 
					    public boolean mouseScrolled(double x, double y, double deltaX, double deltaY) {
 | 
				
			||||||
        if (super.mouseScrolled(x, y, delta)) return true;
 | 
					        if (super.mouseScrolled(x, y, deltaX, deltaY)) return true;
 | 
				
			||||||
        if (delta < 0) {
 | 
					        if (deltaY < 0) {
 | 
				
			||||||
            // Scroll up goes to the next page
 | 
					            // Scroll up goes to the next page
 | 
				
			||||||
            if (page < pages - 1) page++;
 | 
					            if (page < pages - 1) page++;
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (delta > 0) {
 | 
					        if (deltaY > 0) {
 | 
				
			||||||
            // Scroll down goes to the previous page
 | 
					            // Scroll down goes to the previous page
 | 
				
			||||||
            if (page > 0) page--;
 | 
					            if (page > 0) page--;
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
@@ -83,22 +86,16 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
 | 
					    protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
 | 
				
			||||||
        // Draw the printout
 | 
					        // Push the printout slightly forward, to avoid clipping into the background.
 | 
				
			||||||
 | 
					        graphics.pose().pushPose();
 | 
				
			||||||
 | 
					        graphics.pose().translate(0, 0, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
 | 
					        var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
 | 
				
			||||||
        drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
 | 
					        drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
 | 
				
			||||||
        drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
 | 
					        drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutData.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
 | 
				
			||||||
        renderer.endBatch();
 | 
					        renderer.endBatch();
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					 | 
				
			||||||
        // We must take the background further back in order to not overlap with our printed pages.
 | 
					 | 
				
			||||||
        graphics.pose().pushPose();
 | 
					 | 
				
			||||||
        graphics.pose().translate(0, 0, -1);
 | 
					 | 
				
			||||||
        renderBackground(graphics);
 | 
					 | 
				
			||||||
        graphics.pose().popPose();
 | 
					        graphics.pose().popPose();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,10 @@ public class DynamicImageButton extends Button {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					    public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
				
			||||||
 | 
					        var message = this.message.get();
 | 
				
			||||||
 | 
					        setMessage(message.message());
 | 
				
			||||||
 | 
					        setTooltip(message.tooltip());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var texture = this.texture.get(isHoveredOrFocused());
 | 
					        var texture = this.texture.get(isHoveredOrFocused());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        RenderSystem.disableDepthTest();
 | 
					        RenderSystem.disableDepthTest();
 | 
				
			||||||
@@ -50,14 +54,6 @@ public class DynamicImageButton extends Button {
 | 
				
			|||||||
        RenderSystem.enableDepthTest();
 | 
					        RenderSystem.enableDepthTest();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
					 | 
				
			||||||
        var message = this.message.get();
 | 
					 | 
				
			||||||
        setMessage(message.message());
 | 
					 | 
				
			||||||
        setTooltip(message.tooltip());
 | 
					 | 
				
			||||||
        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public record HintedMessage(Component message, Tooltip tooltip) {
 | 
					    public record HintedMessage(Component message, Tooltip tooltip) {
 | 
				
			||||||
        public HintedMessage(Component message, @Nullable Component hint) {
 | 
					        public HintedMessage(Component message, @Nullable Component hint) {
 | 
				
			||||||
            this(
 | 
					            this(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,16 +195,16 @@ public class TerminalWidget extends AbstractWidget {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
 | 
					    public boolean mouseScrolled(double mouseX, double mouseY, double deltaX, double deltaY) {
 | 
				
			||||||
        if (!inTermRegion(mouseX, mouseY)) return false;
 | 
					        if (!inTermRegion(mouseX, mouseY)) return false;
 | 
				
			||||||
        if (!hasMouseSupport() || delta == 0) return false;
 | 
					        if (!hasMouseSupport() || deltaY == 0) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
 | 
					        var charX = (int) ((mouseX - innerX) / FONT_WIDTH);
 | 
				
			||||||
        var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
 | 
					        var charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
 | 
				
			||||||
        charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
 | 
					        charX = Math.min(Math.max(charX, 0), terminal.getWidth() - 1);
 | 
				
			||||||
        charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
 | 
					        charY = Math.min(Math.max(charY, 0), terminal.getHeight() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        computer.mouseScroll(delta < 0 ? 1 : -1, charX + 1, charY + 1);
 | 
					        computer.mouseScroll(deltaY < 0 ? 1 : -1, charX + 1, charY + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        lastMouseX = charX;
 | 
					        lastMouseX = charX;
 | 
				
			||||||
        lastMouseY = charY;
 | 
					        lastMouseY = charY;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,12 +6,15 @@ package dan200.computercraft.client.integration.emi;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
					import dan200.computercraft.api.turtle.TurtleSide;
 | 
				
			||||||
import dan200.computercraft.shared.ModRegistry;
 | 
					import dan200.computercraft.shared.ModRegistry;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.integration.RecipeModHelpers;
 | 
				
			||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
					import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
				
			||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
 | 
					import dan200.computercraft.shared.turtle.items.TurtleItem;
 | 
				
			||||||
import dev.emi.emi.api.EmiEntrypoint;
 | 
					import dev.emi.emi.api.EmiEntrypoint;
 | 
				
			||||||
import dev.emi.emi.api.EmiPlugin;
 | 
					import dev.emi.emi.api.EmiPlugin;
 | 
				
			||||||
import dev.emi.emi.api.EmiRegistry;
 | 
					import dev.emi.emi.api.EmiRegistry;
 | 
				
			||||||
import dev.emi.emi.api.stack.Comparison;
 | 
					import dev.emi.emi.api.stack.Comparison;
 | 
				
			||||||
 | 
					import dev.emi.emi.api.stack.EmiStack;
 | 
				
			||||||
 | 
					import net.minecraft.client.Minecraft;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.function.BiPredicate;
 | 
					import java.util.function.BiPredicate;
 | 
				
			||||||
@@ -25,15 +28,17 @@ public class EMIComputerCraft implements EmiPlugin {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
 | 
					        registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
 | 
				
			||||||
        registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
 | 
					        registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (var stack : RecipeModHelpers.getExtraStacks(Minecraft.getInstance().level.registryAccess())) {
 | 
				
			||||||
 | 
					            registry.addEmiStack(EmiStack.of(stack));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final Comparison turtleComparison = compareStacks((left, right) ->
 | 
					    private static final Comparison turtleComparison = compareStacks((left, right)
 | 
				
			||||||
        left.getItem() instanceof TurtleItem turtle
 | 
					        -> TurtleItem.getUpgrade(left, TurtleSide.LEFT) == TurtleItem.getUpgrade(right, TurtleSide.LEFT)
 | 
				
			||||||
            && turtle.getUpgrade(left, TurtleSide.LEFT) == turtle.getUpgrade(right, TurtleSide.LEFT)
 | 
					        && TurtleItem.getUpgrade(left, TurtleSide.RIGHT) == TurtleItem.getUpgrade(right, TurtleSide.RIGHT));
 | 
				
			||||||
            && turtle.getUpgrade(left, TurtleSide.RIGHT) == turtle.getUpgrade(right, TurtleSide.RIGHT));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final Comparison pocketComparison = compareStacks((left, right) ->
 | 
					    private static final Comparison pocketComparison = compareStacks((left, right) -> PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
 | 
				
			||||||
        left.getItem() instanceof PocketComputerItem && PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
 | 
					    private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
 | 
				
			||||||
        return Comparison.of((left, right) -> {
 | 
					        return Comparison.of((left, right) -> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,9 +15,11 @@ import dan200.computercraft.client.platform.ClientPlatformHelper;
 | 
				
			|||||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
 | 
					import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
 | 
				
			||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
 | 
					import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
 | 
				
			||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
 | 
					import dan200.computercraft.shared.turtle.items.TurtleItem;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.util.DataComponentUtil;
 | 
				
			||||||
import dan200.computercraft.shared.util.Holiday;
 | 
					import dan200.computercraft.shared.util.Holiday;
 | 
				
			||||||
import net.minecraft.client.Minecraft;
 | 
					import net.minecraft.client.Minecraft;
 | 
				
			||||||
import net.minecraft.client.resources.model.BakedModel;
 | 
					import net.minecraft.client.resources.model.BakedModel;
 | 
				
			||||||
 | 
					import net.minecraft.core.component.DataComponents;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,13 +56,6 @@ public final class TurtleModelParts<T> {
 | 
				
			|||||||
        boolean christmas,
 | 
					        boolean christmas,
 | 
				
			||||||
        boolean flip
 | 
					        boolean flip
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        Combination copy() {
 | 
					 | 
				
			||||||
            if (leftUpgrade == null && rightUpgrade == null) return this;
 | 
					 | 
				
			||||||
            return new Combination(
 | 
					 | 
				
			||||||
                colour, UpgradeData.copyOf(leftUpgrade), UpgradeData.copyOf(rightUpgrade),
 | 
					 | 
				
			||||||
                overlay, christmas, flip
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final BakedModel familyModel;
 | 
					    private final BakedModel familyModel;
 | 
				
			||||||
@@ -96,31 +91,18 @@ public final class TurtleModelParts<T> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public T getModel(ItemStack stack) {
 | 
					    public T getModel(ItemStack stack) {
 | 
				
			||||||
        var combination = getCombination(stack);
 | 
					        var combination = getCombination(stack);
 | 
				
			||||||
        var existing = modelCache.get(combination);
 | 
					        return modelCache.computeIfAbsent(combination, buildModel);
 | 
				
			||||||
        if (existing != null) return existing;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Take a defensive copy of the upgrade data, and add it to the cache.
 | 
					 | 
				
			||||||
        var newCombination = combination.copy();
 | 
					 | 
				
			||||||
        var newModel = buildModel.apply(newCombination);
 | 
					 | 
				
			||||||
        modelCache.put(newCombination, newModel);
 | 
					 | 
				
			||||||
        return newModel;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Combination getCombination(ItemStack stack) {
 | 
					    private Combination getCombination(ItemStack stack) {
 | 
				
			||||||
        var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
 | 
					        var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
 | 
				
			||||||
 | 
					        var leftUpgrade = TurtleItem.getUpgradeWithData(stack, TurtleSide.LEFT);
 | 
				
			||||||
        if (!(stack.getItem() instanceof TurtleItem turtle)) {
 | 
					        var rightUpgrade = TurtleItem.getUpgradeWithData(stack, TurtleSide.RIGHT);
 | 
				
			||||||
            return new Combination(false, null, null, null, christmas, false);
 | 
					        var overlay = TurtleItem.getOverlay(stack);
 | 
				
			||||||
        }
 | 
					        var label = DataComponentUtil.getCustomName(stack);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        var colour = turtle.getColour(stack);
 | 
					 | 
				
			||||||
        var leftUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.LEFT);
 | 
					 | 
				
			||||||
        var rightUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.RIGHT);
 | 
					 | 
				
			||||||
        var overlay = turtle.getOverlay(stack);
 | 
					 | 
				
			||||||
        var label = turtle.getLabel(stack);
 | 
					 | 
				
			||||||
        var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
 | 
					        var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new Combination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
 | 
					        return new Combination(stack.has(DataComponents.DYED_COLOR), leftUpgrade, rightUpgrade, overlay, christmas, flip);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private List<BakedModel> buildModel(Combination combo) {
 | 
					    private List<BakedModel> buildModel(Combination combo) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.client.network;
 | 
					package dan200.computercraft.client.network;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
 | 
					 | 
				
			||||||
import dan200.computercraft.shared.network.NetworkMessage;
 | 
					import dan200.computercraft.shared.network.NetworkMessage;
 | 
				
			||||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
 | 
					import dan200.computercraft.shared.network.server.ServerNetworkContext;
 | 
				
			||||||
import net.minecraft.client.Minecraft;
 | 
					import net.minecraft.client.Minecraft;
 | 
				
			||||||
 | 
					import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Methods for sending packets from clients to the server.
 | 
					 * Methods for sending packets from clients to the server.
 | 
				
			||||||
@@ -23,6 +23,6 @@ public final class ClientNetworking {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public static void sendToServer(NetworkMessage<ServerNetworkContext> message) {
 | 
					    public static void sendToServer(NetworkMessage<ServerNetworkContext> message) {
 | 
				
			||||||
        var connection = Minecraft.getInstance().getConnection();
 | 
					        var connection = Minecraft.getInstance().getConnection();
 | 
				
			||||||
        if (connection != null) connection.send(ClientPlatformHelper.get().createPacket(message));
 | 
					        if (connection != null) connection.send(new ServerboundCustomPayloadPacket(message));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,9 @@
 | 
				
			|||||||
package dan200.computercraft.client.platform;
 | 
					package dan200.computercraft.client.platform;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.mojang.blaze3d.vertex.PoseStack;
 | 
					import com.mojang.blaze3d.vertex.PoseStack;
 | 
				
			||||||
import dan200.computercraft.shared.network.NetworkMessage;
 | 
					 | 
				
			||||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
 | 
					 | 
				
			||||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
					import net.minecraft.client.renderer.MultiBufferSource;
 | 
				
			||||||
import net.minecraft.client.resources.model.BakedModel;
 | 
					import net.minecraft.client.resources.model.BakedModel;
 | 
				
			||||||
import net.minecraft.core.BlockPos;
 | 
					import net.minecraft.core.BlockPos;
 | 
				
			||||||
import net.minecraft.network.protocol.Packet;
 | 
					 | 
				
			||||||
import net.minecraft.network.protocol.game.ServerGamePacketListener;
 | 
					 | 
				
			||||||
import net.minecraft.sounds.SoundEvent;
 | 
					import net.minecraft.sounds.SoundEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
@@ -21,14 +17,6 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
 | 
				
			|||||||
        return (ClientPlatformHelper) dan200.computercraft.impl.client.ClientPlatformHelper.get();
 | 
					        return (ClientPlatformHelper) dan200.computercraft.impl.client.ClientPlatformHelper.get();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Convert a serverbound {@link NetworkMessage} to a Minecraft {@link Packet}.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param message The messsge to convert.
 | 
					 | 
				
			||||||
     * @return The converted message.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Render a {@link BakedModel}, using any loader-specific hooks.
 | 
					     * Render a {@link BakedModel}, using any loader-specific hooks.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package dan200.computercraft.client.pocket;
 | 
					package dan200.computercraft.client.pocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.ModRegistry;
 | 
				
			||||||
import dan200.computercraft.shared.computer.core.ComputerState;
 | 
					import dan200.computercraft.shared.computer.core.ComputerState;
 | 
				
			||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
 | 
					import dan200.computercraft.shared.computer.core.ServerComputer;
 | 
				
			||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
 | 
					import dan200.computercraft.shared.computer.terminal.TerminalState;
 | 
				
			||||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
 | 
					import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
 | 
				
			||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
					 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
@@ -53,7 +53,7 @@ public final class ClientPocketComputers {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static @Nullable PocketComputerData get(ItemStack stack) {
 | 
					    public static @Nullable PocketComputerData get(ItemStack stack) {
 | 
				
			||||||
        var id = PocketComputerItem.getInstanceID(stack);
 | 
					        var id = stack.get(ModRegistry.DataComponents.COMPUTER.get());
 | 
				
			||||||
        return id == null ? null : instances.get(id);
 | 
					        return id == null ? null : instances.get(id.instance());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,7 +51,6 @@ public final class CableHighlightRenderer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        var buffer = bufferSource.getBuffer(RenderType.lines());
 | 
					        var buffer = bufferSource.getBuffer(RenderType.lines());
 | 
				
			||||||
        var matrix4f = transform.last().pose();
 | 
					        var matrix4f = transform.last().pose();
 | 
				
			||||||
        var normal = transform.last().normal();
 | 
					 | 
				
			||||||
        // TODO: Can we just accesstransformer out LevelRenderer.renderShape?
 | 
					        // TODO: Can we just accesstransformer out LevelRenderer.renderShape?
 | 
				
			||||||
        shape.forAllEdges((x1, y1, z1, x2, y2, z2) -> {
 | 
					        shape.forAllEdges((x1, y1, z1, x2, y2, z2) -> {
 | 
				
			||||||
            var xDelta = (float) (x2 - x1);
 | 
					            var xDelta = (float) (x2 - x1);
 | 
				
			||||||
@@ -65,12 +64,12 @@ public final class CableHighlightRenderer {
 | 
				
			|||||||
            buffer
 | 
					            buffer
 | 
				
			||||||
                .vertex(matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset))
 | 
					                .vertex(matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset))
 | 
				
			||||||
                .color(0, 0, 0, 0.4f)
 | 
					                .color(0, 0, 0, 0.4f)
 | 
				
			||||||
                .normal(normal, xDelta, yDelta, zDelta)
 | 
					                .normal(transform.last(), xDelta, yDelta, zDelta)
 | 
				
			||||||
                .endVertex();
 | 
					                .endVertex();
 | 
				
			||||||
            buffer
 | 
					            buffer
 | 
				
			||||||
                .vertex(matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset))
 | 
					                .vertex(matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset))
 | 
				
			||||||
                .color(0, 0, 0, 0.4f)
 | 
					                .color(0, 0, 0, 0.4f)
 | 
				
			||||||
                .normal(normal, xDelta, yDelta, zDelta)
 | 
					                .normal(transform.last(), xDelta, yDelta, zDelta)
 | 
				
			||||||
                .endVertex();
 | 
					                .endVertex();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ import dan200.computercraft.shared.config.Config;
 | 
				
			|||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
					import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
				
			||||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
					import net.minecraft.client.renderer.MultiBufferSource;
 | 
				
			||||||
import net.minecraft.world.item.ItemStack;
 | 
					import net.minecraft.world.item.ItemStack;
 | 
				
			||||||
 | 
					import net.minecraft.world.item.component.DyedItemColor;
 | 
				
			||||||
import org.joml.Matrix4f;
 | 
					import org.joml.Matrix4f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
 | 
					import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
 | 
				
			||||||
@@ -61,7 +62,7 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
 | 
				
			|||||||
        // Render the main frame
 | 
					        // Render the main frame
 | 
				
			||||||
        var item = (PocketComputerItem) stack.getItem();
 | 
					        var item = (PocketComputerItem) stack.getItem();
 | 
				
			||||||
        var family = item.getFamily();
 | 
					        var family = item.getFamily();
 | 
				
			||||||
        var frameColour = item.getColour(stack);
 | 
					        var frameColour = DyedItemColor.getOrDefault(stack, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var matrix = transform.last().pose();
 | 
					        var matrix = transform.last().pose();
 | 
				
			||||||
        renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
 | 
					        renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ package dan200.computercraft.client.render;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.mojang.blaze3d.vertex.PoseStack;
 | 
					import com.mojang.blaze3d.vertex.PoseStack;
 | 
				
			||||||
import com.mojang.math.Axis;
 | 
					import com.mojang.math.Axis;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.ModRegistry;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.media.items.PrintoutData;
 | 
				
			||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
					import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
				
			||||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
					import net.minecraft.client.renderer.MultiBufferSource;
 | 
				
			||||||
import net.minecraft.world.entity.EntityType;
 | 
					import net.minecraft.world.entity.EntityType;
 | 
				
			||||||
@@ -15,8 +17,8 @@ import net.minecraft.world.item.ItemStack;
 | 
				
			|||||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
 | 
					import static dan200.computercraft.client.render.PrintoutRenderer.*;
 | 
				
			||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
 | 
					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.text.FixedWidthFontRenderer.FONT_WIDTH;
 | 
				
			||||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINES_PER_PAGE;
 | 
					import static dan200.computercraft.shared.media.items.PrintoutData.LINES_PER_PAGE;
 | 
				
			||||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINE_MAX_LENGTH;
 | 
					import static dan200.computercraft.shared.media.items.PrintoutData.LINE_LENGTH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Emulates map and item-frame rendering for printouts.
 | 
					 * Emulates map and item-frame rendering for printouts.
 | 
				
			||||||
@@ -37,8 +39,6 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int packedLight) {
 | 
					    public static void onRenderInFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int packedLight) {
 | 
				
			||||||
        if (!(stack.getItem() instanceof PrintoutItem)) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Move a little bit forward to ensure we're not clipping with the frame
 | 
					        // Move a little bit forward to ensure we're not clipping with the frame
 | 
				
			||||||
        transform.translate(0.0f, 0.0f, -0.001f);
 | 
					        transform.translate(0.0f, 0.0f, -0.001f);
 | 
				
			||||||
        transform.mulPose(Axis.ZP.rotationDegrees(180f));
 | 
					        transform.mulPose(Axis.ZP.rotationDegrees(180f));
 | 
				
			||||||
@@ -50,10 +50,12 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static void drawPrintout(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
 | 
					    private static void drawPrintout(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
 | 
				
			||||||
        var pages = PrintoutItem.getPageCount(stack);
 | 
					        var pageData = stack.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var pages = pageData.pages();
 | 
				
			||||||
        var book = ((PrintoutItem) stack.getItem()).getType() == PrintoutItem.Type.BOOK;
 | 
					        var book = ((PrintoutItem) stack.getItem()).getType() == PrintoutItem.Type.BOOK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
 | 
					        double width = LINE_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
 | 
				
			||||||
        double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
 | 
					        double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Non-books will be left aligned
 | 
					        // Non-books will be left aligned
 | 
				
			||||||
@@ -75,9 +77,6 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
 | 
				
			|||||||
        transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
 | 
					        transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        drawBorder(transform, render, 0, 0, -0.01f, 0, pages, book, light);
 | 
					        drawBorder(transform, render, 0, 0, -0.01f, 0, pages, book, light);
 | 
				
			||||||
        drawText(
 | 
					        drawText(transform, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, pageData.lines());
 | 
				
			||||||
            transform, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light,
 | 
					 | 
				
			||||||
            PrintoutItem.getText(stack), PrintoutItem.getColours(stack)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,11 +9,14 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
 | 
				
			|||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
 | 
					import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
 | 
				
			||||||
import dan200.computercraft.core.terminal.Palette;
 | 
					import dan200.computercraft.core.terminal.Palette;
 | 
				
			||||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
					import dan200.computercraft.core.terminal.TextBuffer;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.media.items.PrintoutData;
 | 
				
			||||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
					import net.minecraft.client.renderer.MultiBufferSource;
 | 
				
			||||||
import org.joml.Matrix4f;
 | 
					import org.joml.Matrix4f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
 | 
					import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
 | 
				
			||||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINES_PER_PAGE;
 | 
					import static dan200.computercraft.shared.media.items.PrintoutData.LINES_PER_PAGE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Renders printed pages or books, either for a GUI ({@link dan200.computercraft.client.gui.PrintoutScreen}) or
 | 
					 * Renders printed pages or books, either for a GUI ({@link dan200.computercraft.client.gui.PrintoutScreen}) or
 | 
				
			||||||
@@ -69,13 +72,14 @@ public final class PrintoutRenderer {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void drawText(PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, String[] text, String[] colours) {
 | 
					    public static void drawText(PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, List<PrintoutData.Line> lines) {
 | 
				
			||||||
        var buffer = bufferSource.getBuffer(RenderTypes.PRINTOUT_TEXT);
 | 
					        var buffer = bufferSource.getBuffer(RenderTypes.PRINTOUT_TEXT);
 | 
				
			||||||
        var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, buffer);
 | 
					        var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, buffer);
 | 
				
			||||||
        for (var line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
 | 
					        for (var line = 0; line < LINES_PER_PAGE && line < lines.size(); line++) {
 | 
				
			||||||
 | 
					            var lineContents = lines.get(start + line);
 | 
				
			||||||
            FixedWidthFontRenderer.drawString(emitter,
 | 
					            FixedWidthFontRenderer.drawString(emitter,
 | 
				
			||||||
                x, y + line * FONT_HEIGHT,
 | 
					                x, y + line * FONT_HEIGHT,
 | 
				
			||||||
                new TextBuffer(text[start + line]), new TextBuffer(colours[start + line]),
 | 
					                new TextBuffer(lineContents.text()), new TextBuffer(lineContents.foreground()),
 | 
				
			||||||
                Palette.DEFAULT, light
 | 
					                Palette.DEFAULT, light
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -125,10 +125,10 @@ public class SpriteRenderer {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static float u(TextureAtlasSprite sprite, int x, int width) {
 | 
					    public static float u(TextureAtlasSprite sprite, int x, int width) {
 | 
				
			||||||
        return sprite.getU((double) x / width * 16);
 | 
					        return sprite.getU((float) x / width);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static float v(TextureAtlasSprite sprite, int y, int height) {
 | 
					    public static float v(TextureAtlasSprite sprite, int y, int height) {
 | 
				
			||||||
        return sprite.getV((double) y / height * 16);
 | 
					        return sprite.getV((float) y / height);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import com.mojang.blaze3d.platform.MemoryTracker;
 | 
				
			|||||||
import com.mojang.blaze3d.systems.RenderSystem;
 | 
					import com.mojang.blaze3d.systems.RenderSystem;
 | 
				
			||||||
import com.mojang.blaze3d.vertex.*;
 | 
					import com.mojang.blaze3d.vertex.*;
 | 
				
			||||||
import com.mojang.math.Axis;
 | 
					import com.mojang.math.Axis;
 | 
				
			||||||
 | 
					import dan200.computercraft.annotations.ForgeOverride;
 | 
				
			||||||
import dan200.computercraft.client.FrameInfo;
 | 
					import dan200.computercraft.client.FrameInfo;
 | 
				
			||||||
import dan200.computercraft.client.integration.ShaderMod;
 | 
					import dan200.computercraft.client.integration.ShaderMod;
 | 
				
			||||||
import dan200.computercraft.client.render.RenderTypes;
 | 
					import dan200.computercraft.client.render.RenderTypes;
 | 
				
			||||||
@@ -25,7 +26,7 @@ import dan200.computercraft.shared.util.DirectionUtil;
 | 
				
			|||||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
					import net.minecraft.client.renderer.MultiBufferSource;
 | 
				
			||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
 | 
					import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
 | 
				
			||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
 | 
					import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
 | 
				
			||||||
import org.joml.Matrix3f;
 | 
					import net.minecraft.world.phys.AABB;
 | 
				
			||||||
import org.joml.Matrix4f;
 | 
					import org.joml.Matrix4f;
 | 
				
			||||||
import org.lwjgl.opengl.GL11;
 | 
					import org.lwjgl.opengl.GL11;
 | 
				
			||||||
import org.lwjgl.opengl.GL20;
 | 
					import org.lwjgl.opengl.GL20;
 | 
				
			||||||
@@ -46,8 +47,6 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    private static final float MARGIN = (float) (MonitorBlockEntity.RENDER_MARGIN * 1.1);
 | 
					    private static final float MARGIN = (float) (MonitorBlockEntity.RENDER_MARGIN * 1.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final Matrix3f IDENTITY_NORMAL = new Matrix3f().identity();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static @Nullable ByteBuffer backingBuffer;
 | 
					    private static @Nullable ByteBuffer backingBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static long lastFrame = -1;
 | 
					    private static long lastFrame = -1;
 | 
				
			||||||
@@ -189,17 +188,23 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
 | 
				
			|||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Our VBO doesn't transform its vertices with the provided pose stack, which means that the inverse view
 | 
					                // Our VBO renders coordinates in monitor-space rather than world space. A full sized monitor (8x6) will
 | 
				
			||||||
                // rotation matrix gives entirely wrong numbers for fog distances. We just set it to the identity which
 | 
					                // use positions from (0, 0) to (164*FONT_WIDTH, 81*FONT_HEIGHT) = (984, 729). This is far outside the
 | 
				
			||||||
                // gives a good enough approximation.
 | 
					                // normal render distance (~200), and the edges of the monitor fade out due to fog.
 | 
				
			||||||
                var oldInverseRotation = RenderSystem.getInverseViewRotationMatrix();
 | 
					                // There's not really a good way around this, at least without using a custom render type (which the VBO
 | 
				
			||||||
                RenderSystem.setInverseViewRotationMatrix(IDENTITY_NORMAL);
 | 
					                // renderer is trying to avoid!). Instead, we just disable fog entirely by setting the fog start to an
 | 
				
			||||||
 | 
					                // absurdly high value.
 | 
				
			||||||
 | 
					                var oldFogStart = RenderSystem.getShaderFogStart();
 | 
				
			||||||
 | 
					                RenderSystem.setShaderFogStart(1e4f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                RenderTypes.TERMINAL.setupRenderState();
 | 
					                RenderTypes.TERMINAL.setupRenderState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Compose the existing model view matrix with our transformation matrix.
 | 
				
			||||||
 | 
					                var modelView = new Matrix4f(RenderSystem.getModelViewMatrix()).mul(matrix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Render background geometry
 | 
					                // Render background geometry
 | 
				
			||||||
                backgroundBuffer.bind();
 | 
					                backgroundBuffer.bind();
 | 
				
			||||||
                backgroundBuffer.drawWithShader(matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader());
 | 
					                backgroundBuffer.drawWithShader(modelView, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Render foreground geometry with glPolygonOffset enabled.
 | 
					                // Render foreground geometry with glPolygonOffset enabled.
 | 
				
			||||||
                RenderSystem.polygonOffset(-1.0f, -10.0f);
 | 
					                RenderSystem.polygonOffset(-1.0f, -10.0f);
 | 
				
			||||||
@@ -207,7 +212,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                foregroundBuffer.bind();
 | 
					                foregroundBuffer.bind();
 | 
				
			||||||
                foregroundBuffer.drawWithShader(
 | 
					                foregroundBuffer.drawWithShader(
 | 
				
			||||||
                    matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(),
 | 
					                    modelView, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(),
 | 
				
			||||||
                    // As mentioned in the above comment, render the extra cursor quad if it is visible this frame. Each
 | 
					                    // 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.
 | 
					                    // // quad has an index count of 6.
 | 
				
			||||||
                    FixedWidthFontRenderer.isCursorVisible(terminal) && FrameInfo.getGlobalCursorBlink()
 | 
					                    FixedWidthFontRenderer.isCursorVisible(terminal) && FrameInfo.getGlobalCursorBlink()
 | 
				
			||||||
@@ -220,7 +225,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
 | 
				
			|||||||
                RenderTypes.TERMINAL.clearRenderState();
 | 
					                RenderTypes.TERMINAL.clearRenderState();
 | 
				
			||||||
                VertexBuffer.unbind();
 | 
					                VertexBuffer.unbind();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                RenderSystem.setInverseViewRotationMatrix(oldInverseRotation);
 | 
					                RenderSystem.setShaderFogStart(oldFogStart);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case BEST -> throw new IllegalStateException("Impossible: Should never use BEST renderer");
 | 
					            case BEST -> throw new IllegalStateException("Impossible: Should never use BEST renderer");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -255,6 +260,11 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
 | 
				
			|||||||
        return Config.monitorDistance;
 | 
					        return Config.monitorDistance;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ForgeOverride
 | 
				
			||||||
 | 
					    public AABB getRenderBoundingBox(MonitorBlockEntity monitor) {
 | 
				
			||||||
 | 
					        return monitor.getRenderBoundingBox();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Determine if any monitors were rendered this frame.
 | 
					     * Determine if any monitors were rendered this frame.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ import net.minecraft.client.renderer.MultiBufferSource;
 | 
				
			|||||||
import net.minecraft.client.renderer.RenderType;
 | 
					import net.minecraft.client.renderer.RenderType;
 | 
				
			||||||
import net.minecraft.core.Direction;
 | 
					import net.minecraft.core.Direction;
 | 
				
			||||||
import net.minecraft.world.phys.BlockHitResult;
 | 
					import net.minecraft.world.phys.BlockHitResult;
 | 
				
			||||||
import org.joml.Matrix3f;
 | 
					 | 
				
			||||||
import org.joml.Matrix4f;
 | 
					import org.joml.Matrix4f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.EnumSet;
 | 
					import java.util.EnumSet;
 | 
				
			||||||
@@ -53,7 +52,7 @@ public final class MonitorHighlightRenderer {
 | 
				
			|||||||
        // I wish I could think of a better way to do this
 | 
					        // I wish I could think of a better way to do this
 | 
				
			||||||
        var buffer = bufferSource.getBuffer(RenderType.lines());
 | 
					        var buffer = bufferSource.getBuffer(RenderType.lines());
 | 
				
			||||||
        var transform = transformStack.last().pose();
 | 
					        var transform = transformStack.last().pose();
 | 
				
			||||||
        var normal = transformStack.last().normal();
 | 
					        var normal = transformStack.last();
 | 
				
			||||||
        if (faces.contains(NORTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 0, UP);
 | 
					        if (faces.contains(NORTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 0, UP);
 | 
				
			||||||
        if (faces.contains(SOUTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 1, UP);
 | 
					        if (faces.contains(SOUTH) || faces.contains(WEST)) line(buffer, transform, normal, 0, 0, 1, UP);
 | 
				
			||||||
        if (faces.contains(NORTH) || faces.contains(EAST)) line(buffer, transform, normal, 1, 0, 0, UP);
 | 
					        if (faces.contains(NORTH) || faces.contains(EAST)) line(buffer, transform, normal, 1, 0, 0, UP);
 | 
				
			||||||
@@ -71,7 +70,7 @@ public final class MonitorHighlightRenderer {
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static void line(VertexConsumer buffer, Matrix4f transform, Matrix3f normal, float x, float y, float z, Direction direction) {
 | 
					    private static void line(VertexConsumer buffer, Matrix4f transform, PoseStack.Pose normal, float x, float y, float z, Direction direction) {
 | 
				
			||||||
        buffer
 | 
					        buffer
 | 
				
			||||||
            .vertex(transform, x, y, z)
 | 
					            .vertex(transform, x, y, z)
 | 
				
			||||||
            .color(0, 0, 0, 0.4f)
 | 
					            .color(0, 0, 0, 0.4f)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,51 +9,52 @@ import dan200.computercraft.api.client.TransformedModel;
 | 
				
			|||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
 | 
					import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
					import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
					import dan200.computercraft.api.turtle.TurtleSide;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.ModRegistry;
 | 
				
			||||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
 | 
					import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
 | 
				
			||||||
 | 
					import dan200.computercraft.shared.util.DataComponentUtil;
 | 
				
			||||||
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A {@link TurtleUpgradeModeller} for modems, providing different models depending on if the modem is on/off.
 | 
					 * A {@link TurtleUpgradeModeller} for modems, providing different models depending on if the modem is on/off.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
 | 
					public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
 | 
				
			||||||
    private final ResourceLocation leftOffModel;
 | 
					 | 
				
			||||||
    private final ResourceLocation rightOffModel;
 | 
					 | 
				
			||||||
    private final ResourceLocation leftOnModel;
 | 
					 | 
				
			||||||
    private final ResourceLocation rightOnModel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public TurtleModemModeller(boolean advanced) {
 | 
					 | 
				
			||||||
        if (advanced) {
 | 
					 | 
				
			||||||
            leftOffModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_advanced_off_left");
 | 
					 | 
				
			||||||
            rightOffModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_advanced_off_right");
 | 
					 | 
				
			||||||
            leftOnModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_advanced_on_left");
 | 
					 | 
				
			||||||
            rightOnModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_advanced_on_right");
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            leftOffModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_normal_off_left");
 | 
					 | 
				
			||||||
            rightOffModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_normal_off_right");
 | 
					 | 
				
			||||||
            leftOnModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_normal_on_left");
 | 
					 | 
				
			||||||
            rightOnModel = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_normal_on_right");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
 | 
					    public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
 | 
				
			||||||
        var active = false;
 | 
					        var active = DataComponentUtil.isPresent(data, ModRegistry.DataComponents.ON.get(), x -> x);
 | 
				
			||||||
        if (turtle != null) {
 | 
					 | 
				
			||||||
            var turtleNBT = turtle.getUpgradeNBTData(side);
 | 
					 | 
				
			||||||
            active = turtleNBT.contains("active") && turtleNBT.getBoolean("active");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var models = upgrade.advanced() ? ModemModels.ADVANCED : ModemModels.NORMAL;
 | 
				
			||||||
        return side == TurtleSide.LEFT
 | 
					        return side == TurtleSide.LEFT
 | 
				
			||||||
            ? TransformedModel.of(active ? leftOnModel : leftOffModel)
 | 
					            ? TransformedModel.of(active ? models.leftOnModel() : models.leftOffModel())
 | 
				
			||||||
            : TransformedModel.of(active ? rightOnModel : rightOffModel);
 | 
					            : TransformedModel.of(active ? models.rightOnModel() : models.rightOffModel());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Collection<ResourceLocation> getDependencies() {
 | 
					    public Stream<ResourceLocation> getDependencies() {
 | 
				
			||||||
        return List.of(leftOffModel, rightOffModel, leftOnModel, rightOnModel);
 | 
					        return Stream.of(ModemModels.NORMAL, ModemModels.ADVANCED).flatMap(ModemModels::getDependencies);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private record ModemModels(
 | 
				
			||||||
 | 
					        ResourceLocation leftOffModel, ResourceLocation rightOffModel,
 | 
				
			||||||
 | 
					        ResourceLocation leftOnModel, ResourceLocation rightOnModel
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        private static final ModemModels NORMAL = create("normal");
 | 
				
			||||||
 | 
					        private static final ModemModels ADVANCED = create("advanced");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static ModemModels create(String type) {
 | 
				
			||||||
 | 
					            return new ModemModels(
 | 
				
			||||||
 | 
					                new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_off_left"),
 | 
				
			||||||
 | 
					                new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_off_right"),
 | 
				
			||||||
 | 
					                new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_on_left"),
 | 
				
			||||||
 | 
					                new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_on_right")
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Stream<ResourceLocation> getDependencies() {
 | 
				
			||||||
 | 
					            return Stream.of(leftOffModel, rightOffModel, leftOnModel, rightOnModel);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,18 +10,13 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
 | 
				
			|||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
					import dan200.computercraft.api.turtle.ITurtleAccess;
 | 
				
			||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
					import dan200.computercraft.api.turtle.ITurtleUpgrade;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
					import dan200.computercraft.api.turtle.TurtleSide;
 | 
				
			||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
 | 
					import dan200.computercraft.api.upgrades.UpgradeType;
 | 
				
			||||||
import dan200.computercraft.impl.PlatformHelper;
 | 
					import dan200.computercraft.shared.util.RegistryHelper;
 | 
				
			||||||
import dan200.computercraft.impl.TurtleUpgrades;
 | 
					 | 
				
			||||||
import dan200.computercraft.impl.UpgradeManager;
 | 
					 | 
				
			||||||
import net.minecraft.client.Minecraft;
 | 
					import net.minecraft.client.Minecraft;
 | 
				
			||||||
import net.minecraft.nbt.CompoundTag;
 | 
					import net.minecraft.core.component.DataComponentPatch;
 | 
				
			||||||
import net.minecraft.resources.ResourceLocation;
 | 
					import net.minecraft.resources.ResourceLocation;
 | 
				
			||||||
import org.slf4j.Logger;
 | 
					 | 
				
			||||||
import org.slf4j.LoggerFactory;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.WeakHashMap;
 | 
					 | 
				
			||||||
import java.util.concurrent.ConcurrentHashMap;
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
import java.util.stream.Stream;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,63 +24,44 @@ import java.util.stream.Stream;
 | 
				
			|||||||
 * A registry of {@link TurtleUpgradeModeller}s.
 | 
					 * A registry of {@link TurtleUpgradeModeller}s.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public final class TurtleUpgradeModellers {
 | 
					public final class TurtleUpgradeModellers {
 | 
				
			||||||
    private static final Logger LOG = LoggerFactory.getLogger(TurtleUpgradeModellers.class);
 | 
					    private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side, data) ->
 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side) ->
 | 
					 | 
				
			||||||
        new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
 | 
					        new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final Map<TurtleUpgradeSerialiser<?>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
 | 
					    private static final Map<UpgradeType<? extends ITurtleUpgrade>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
 | 
				
			||||||
    private static volatile boolean fetchedModels;
 | 
					    private static volatile boolean fetchedModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
 | 
					 | 
				
			||||||
     * {@link TurtleUpgradeModeller}, we maintain a cache here.
 | 
					 | 
				
			||||||
     * <p>
 | 
					 | 
				
			||||||
     * Turtle upgrades may be removed as part of datapack reloads, so we use a weak map to avoid the memory leak.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private static final WeakHashMap<ITurtleUpgrade, TurtleUpgradeModeller<?>> modelCache = new WeakHashMap<>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private TurtleUpgradeModellers() {
 | 
					    private TurtleUpgradeModellers() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
 | 
					    public static <T extends ITurtleUpgrade> void register(UpgradeType<T> type, TurtleUpgradeModeller<T> modeller) {
 | 
				
			||||||
        if (fetchedModels) {
 | 
					        if (fetchedModels) {
 | 
				
			||||||
            // TODO(1.20.4): Replace with an error.
 | 
					            throw new IllegalStateException(String.format(
 | 
				
			||||||
            LOG.warn(
 | 
					                "Turtle upgrade type %s must be registered before models are baked.",
 | 
				
			||||||
                "Turtle upgrade serialiser {} was registered too late, its models may not be loaded correctly. If you are " +
 | 
					                RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.typeRegistry()), type)
 | 
				
			||||||
                    "the mod author, you may be using a deprecated API - see https://github.com/cc-tweaked/CC-Tweaked/pull/1684 " +
 | 
					            ));
 | 
				
			||||||
                    "for further information.",
 | 
					 | 
				
			||||||
                PlatformHelper.get().getRegistryKey(TurtleUpgradeSerialiser.registryId(), serialiser)
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (turtleModels.putIfAbsent(serialiser, modeller) != null) {
 | 
					        if (turtleModels.putIfAbsent(type, modeller) != null) {
 | 
				
			||||||
            throw new IllegalStateException("Modeller already registered for serialiser");
 | 
					            throw new IllegalStateException("Modeller already registered for serialiser");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
 | 
					    public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
 | 
				
			||||||
        @SuppressWarnings("unchecked")
 | 
					        return getModeller(upgrade).getModel(upgrade, access, side, access.getUpgradeData(side));
 | 
				
			||||||
        var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
 | 
					 | 
				
			||||||
        return modeller.getModel(upgrade, access, side);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
 | 
					    public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) {
 | 
				
			||||||
        @SuppressWarnings("unchecked")
 | 
					        return getModeller(upgrade).getModel(upgrade, null, side, data);
 | 
				
			||||||
        var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
 | 
					 | 
				
			||||||
        return modeller.getModel(upgrade, data, side);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) {
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
        var wrapper = TurtleUpgrades.instance().getWrapper(upgradeA);
 | 
					    private static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> getModeller(T upgrade) {
 | 
				
			||||||
        if (wrapper == null) return NULL_TURTLE_MODELLER;
 | 
					        var modeller = turtleModels.get(upgrade.getType());
 | 
				
			||||||
 | 
					        return (TurtleUpgradeModeller<T>) (modeller == null ? NULL_TURTLE_MODELLER : modeller);
 | 
				
			||||||
        var modeller = turtleModels.get(wrapper.serialiser());
 | 
					 | 
				
			||||||
        return modeller == null ? NULL_TURTLE_MODELLER : modeller;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Stream<ResourceLocation> getDependencies() {
 | 
					    public static Stream<ResourceLocation> getDependencies() {
 | 
				
			||||||
        fetchedModels = true;
 | 
					        fetchedModels = true;
 | 
				
			||||||
        return turtleModels.values().stream().flatMap(x -> x.getDependencies().stream());
 | 
					        return turtleModels.values().stream().flatMap(TurtleUpgradeModeller::getDependencies);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -206,8 +206,10 @@
 | 
				
			|||||||
  "item.computercraft.treasure_disk": "Floppy Disk",
 | 
					  "item.computercraft.treasure_disk": "Floppy Disk",
 | 
				
			||||||
  "itemGroup.computercraft": "ComputerCraft",
 | 
					  "itemGroup.computercraft": "ComputerCraft",
 | 
				
			||||||
  "tag.item.computercraft.computer": "Computers",
 | 
					  "tag.item.computercraft.computer": "Computers",
 | 
				
			||||||
 | 
					  "tag.item.computercraft.dyeable": "Dyable items",
 | 
				
			||||||
  "tag.item.computercraft.monitor": "Monitors",
 | 
					  "tag.item.computercraft.monitor": "Monitors",
 | 
				
			||||||
  "tag.item.computercraft.turtle": "Turtles",
 | 
					  "tag.item.computercraft.turtle": "Turtles",
 | 
				
			||||||
 | 
					  "tag.item.computercraft.turtle_can_place": "Turtle-placeable items",
 | 
				
			||||||
  "tag.item.computercraft.wired_modem": "Wired modems",
 | 
					  "tag.item.computercraft.wired_modem": "Wired modems",
 | 
				
			||||||
  "tracking_field.computercraft.avg": "%s (avg)",
 | 
					  "tracking_field.computercraft.avg": "%s (avg)",
 | 
				
			||||||
  "tracking_field.computercraft.computer_tasks.name": "Tasks",
 | 
					  "tracking_field.computercraft.computer_tasks.name": "Tasks",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  "parent": "minecraft:recipes/root",
 | 
					  "parent": "minecraft:recipes/root",
 | 
				
			||||||
  "criteria": {
 | 
					  "criteria": {
 | 
				
			||||||
    "has_monitor": {
 | 
					    "has_monitor": {
 | 
				
			||||||
      "conditions": {"items": [{"items": ["computercraft:monitor_normal"]}]},
 | 
					      "conditions": {"items": [{"items": "computercraft:monitor_normal"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_the_recipe": {
 | 
					    "has_the_recipe": {
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
      "trigger": "minecraft:recipe_unlocked"
 | 
					      "trigger": "minecraft:recipe_unlocked"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "requirements": [["has_monitor", "has_the_recipe"]],
 | 
					  "requirements": [["has_the_recipe", "has_monitor"]],
 | 
				
			||||||
  "rewards": {"recipes": ["computercraft:skull_cloudy"]},
 | 
					  "rewards": {"recipes": ["computercraft:skull_cloudy"]}
 | 
				
			||||||
  "sends_telemetry_event": false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  "parent": "minecraft:recipes/root",
 | 
					  "parent": "minecraft:recipes/root",
 | 
				
			||||||
  "criteria": {
 | 
					  "criteria": {
 | 
				
			||||||
    "has_computer": {
 | 
					    "has_computer": {
 | 
				
			||||||
      "conditions": {"items": [{"items": ["computercraft:computer_advanced"]}]},
 | 
					      "conditions": {"items": [{"items": "computercraft:computer_advanced"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_the_recipe": {
 | 
					    "has_the_recipe": {
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
      "trigger": "minecraft:recipe_unlocked"
 | 
					      "trigger": "minecraft:recipe_unlocked"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "requirements": [["has_computer", "has_the_recipe"]],
 | 
					  "requirements": [["has_the_recipe", "has_computer"]],
 | 
				
			||||||
  "rewards": {"recipes": ["computercraft:skull_dan200"]},
 | 
					  "rewards": {"recipes": ["computercraft:skull_dan200"]}
 | 
				
			||||||
  "sends_telemetry_event": false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,16 +2,15 @@
 | 
				
			|||||||
  "parent": "minecraft:recipes/root",
 | 
					  "parent": "minecraft:recipes/root",
 | 
				
			||||||
  "criteria": {
 | 
					  "criteria": {
 | 
				
			||||||
    "has_computer": {
 | 
					    "has_computer": {
 | 
				
			||||||
      "conditions": {"items": [{"tag": "computercraft:computer"}]},
 | 
					      "conditions": {"items": [{"items": "#computercraft:computer"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_modem": {
 | 
					    "has_modem": {
 | 
				
			||||||
      "conditions": {"items": [{"tag": "computercraft:wired_modem"}]},
 | 
					      "conditions": {"items": [{"items": "#computercraft:wired_modem"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_the_recipe": {"conditions": {"recipe": "computercraft:cable"}, "trigger": "minecraft:recipe_unlocked"}
 | 
					    "has_the_recipe": {"conditions": {"recipe": "computercraft:cable"}, "trigger": "minecraft:recipe_unlocked"}
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "requirements": [["has_computer", "has_modem", "has_the_recipe"]],
 | 
					  "requirements": [["has_the_recipe", "has_computer", "has_modem"]],
 | 
				
			||||||
  "rewards": {"recipes": ["computercraft:cable"]},
 | 
					  "rewards": {"recipes": ["computercraft:cable"]}
 | 
				
			||||||
  "sends_telemetry_event": false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  "parent": "minecraft:recipes/root",
 | 
					  "parent": "minecraft:recipes/root",
 | 
				
			||||||
  "criteria": {
 | 
					  "criteria": {
 | 
				
			||||||
    "has_components": {
 | 
					    "has_components": {
 | 
				
			||||||
      "conditions": {"items": [{"tag": "c:redstone_dusts"}, {"tag": "c:gold_ingots"}]},
 | 
					      "conditions": {"items": [{"items": "#c:dusts/redstone"}, {"items": "#c:ingots/gold"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_the_recipe": {
 | 
					    "has_the_recipe": {
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
      "trigger": "minecraft:recipe_unlocked"
 | 
					      "trigger": "minecraft:recipe_unlocked"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "requirements": [["has_components", "has_the_recipe"]],
 | 
					  "requirements": [["has_the_recipe", "has_components"]],
 | 
				
			||||||
  "rewards": {"recipes": ["computercraft:computer_advanced"]},
 | 
					  "rewards": {"recipes": ["computercraft:computer_advanced"]}
 | 
				
			||||||
  "sends_telemetry_event": false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  "parent": "minecraft:recipes/root",
 | 
					  "parent": "minecraft:recipes/root",
 | 
				
			||||||
  "criteria": {
 | 
					  "criteria": {
 | 
				
			||||||
    "has_components": {
 | 
					    "has_components": {
 | 
				
			||||||
      "conditions": {"items": [{"items": ["computercraft:computer_normal"]}, {"tag": "c:gold_ingots"}]},
 | 
					      "conditions": {"items": [{"items": "computercraft:computer_normal"}, {"items": "#c:ingots/gold"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_the_recipe": {
 | 
					    "has_the_recipe": {
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
      "trigger": "minecraft:recipe_unlocked"
 | 
					      "trigger": "minecraft:recipe_unlocked"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "requirements": [["has_components", "has_the_recipe"]],
 | 
					  "requirements": [["has_the_recipe", "has_components"]],
 | 
				
			||||||
  "rewards": {"recipes": ["computercraft:computer_advanced_upgrade"]},
 | 
					  "rewards": {"recipes": ["computercraft:computer_advanced_upgrade"]}
 | 
				
			||||||
  "sends_telemetry_event": false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  "parent": "minecraft:recipes/root",
 | 
					  "parent": "minecraft:recipes/root",
 | 
				
			||||||
  "criteria": {
 | 
					  "criteria": {
 | 
				
			||||||
    "has_components": {
 | 
					    "has_components": {
 | 
				
			||||||
      "conditions": {"items": [{"items": ["minecraft:command_block"]}]},
 | 
					      "conditions": {"items": [{"items": "minecraft:command_block"}]},
 | 
				
			||||||
      "trigger": "minecraft:inventory_changed"
 | 
					      "trigger": "minecraft:inventory_changed"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "has_the_recipe": {
 | 
					    "has_the_recipe": {
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
      "trigger": "minecraft:recipe_unlocked"
 | 
					      "trigger": "minecraft:recipe_unlocked"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "requirements": [["has_components", "has_the_recipe"]],
 | 
					  "requirements": [["has_the_recipe", "has_components"]],
 | 
				
			||||||
  "rewards": {"recipes": ["computercraft:computer_command"]},
 | 
					  "rewards": {"recipes": ["computercraft:computer_command"]}
 | 
				
			||||||
  "sends_telemetry_event": false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user