mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-11-04 07:32:59 +00:00 
			
		
		
		
	Compare commits
	
		
			117 Commits
		
	
	
		
			v1.20.4-1.
			...
			v1.20.1-1.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d24984c1d5 | ||
| 
						 | 
					8080dcdd9e | ||
| 
						 | 
					d7cea55e2a | ||
| 
						 | 
					9b2f974a81 | ||
| 
						 | 
					43770fa9bd | ||
| 
						 | 
					80c7a54ad4 | ||
| 
						 | 
					e57b6fede2 | ||
| 
						 | 
					34a2fd039f | ||
| 
						 | 
					3299d0e72a | ||
| 
						 | 
					b89e2615db | ||
| 
						 | 
					cdcd82679c | ||
| 
						 | 
					cdfa866760 | ||
| 
						 | 
					aa8078ddeb | ||
| 
						 | 
					7e53c19d74 | ||
| 
						 | 
					b7a8432cfb | ||
| 
						 | 
					356c8e8aeb | ||
| 
						 | 
					ed283155f7 | ||
| 
						 | 
					87dfad026e | ||
| 
						 | 
					bb97c465d9 | ||
| 
						 | 
					9484315d37 | ||
| 
						 | 
					be59f1a875 | ||
| 
						 | 
					bfb28b4710 | ||
| 
						 | 
					216f0adb3c | ||
| 
						 | 
					77af4bc213 | ||
| 
						 | 
					5abab982c7 | ||
| 
						 | 
					764e1aa332 | ||
| 
						 | 
					c47718b09d | ||
| 
						 | 
					08d4f91c8b | ||
| 
						 | 
					b9eac4e509 | ||
| 
						 | 
					dc3d8ea198 | ||
| 
						 | 
					cbe075b001 | ||
| 
						 | 
					ed0b156e05 | ||
| 
						 | 
					4dd0735066 | ||
| 
						 | 
					38e516d7c7 | ||
| 
						 | 
					70a31855ac | ||
| 
						 | 
					6c8e64ffcd | ||
| 
						 | 
					7285c32d58 | ||
| 
						 | 
					99c60ac54b | ||
| 
						 | 
					63e40cf3cb | ||
| 
						 | 
					1d45935a25 | ||
| 
						 | 
					f80373e7a2 | ||
| 
						 | 
					63185629b7 | ||
| 
						 | 
					4bfb9ac323 | ||
| 
						 | 
					5926b6c994 | ||
| 
						 | 
					f5ed43584d | ||
| 
						 | 
					d77f5f135f | ||
| 
						 | 
					7744d2663b | ||
| 
						 | 
					4566cb8273 | ||
| 
						 | 
					052e7a7ae5 | ||
| 
						 | 
					0895200681 | ||
| 
						 | 
					09d0f563b7 | ||
| 
						 | 
					e188f1d3fa | ||
| 
						 | 
					819a4f7231 | ||
| 
						 | 
					898cb2a95d | ||
| 
						 | 
					03a8f83191 | ||
| 
						 | 
					aef92c8ebc | ||
| 
						 | 
					571ea794a8 | ||
| 
						 | 
					e81af93043 | ||
| 
						 | 
					25b8a65c5c | ||
| 
						 | 
					e4236824d7 | ||
| 
						 | 
					cfd11ffa92 | ||
| 
						 | 
					ce133a5e66 | ||
| 
						 | 
					038fbc1ed1 | ||
| 
						 | 
					c582fb521c | ||
| 
						 | 
					af21792844 | ||
| 
						 | 
					9fbb1070ef | ||
| 
						 | 
					1944995c33 | ||
| 
						 | 
					ac851a795b | ||
| 
						 | 
					334761788a | ||
| 
						 | 
					5af3e15dd5 | ||
| 
						 | 
					209b1ddbf9 | ||
| 
						 | 
					0c9f9a8652 | ||
| 
						 | 
					862d92785e | ||
| 
						 | 
					d48b85d50c | ||
| 
						 | 
					4d619de357 | ||
| 
						 | 
					57c289f173 | ||
| 
						 | 
					f63f85921f | ||
| 
						 | 
					c7e49d1929 | ||
| 
						 | 
					d9b0cc7075 | ||
| 
						 | 
					1e214f329e | ||
| 
						 | 
					de930c8d09 | ||
| 
						 | 
					735e7ce09b | ||
| 
						 | 
					6e9799316a | ||
| 
						 | 
					4e90240922 | ||
| 
						 | 
					1a87d1bf45 | ||
| 
						 | 
					00e2e2bd2d | ||
| 
						 | 
					7c1f40031b | ||
| 
						 | 
					929debd382 | ||
| 
						 | 
					4980b7355d | ||
| 
						 | 
					925092add3 | ||
| 
						 | 
					550296edc5 | ||
| 
						 | 
					0771c4891b | ||
| 
						 | 
					776fa00b94 | ||
| 
						 | 
					03bb279206 | ||
| 
						 | 
					fabd77132d | ||
| 
						 | 
					95be0a25bf | ||
| 
						 | 
					ad49325376 | ||
| 
						 | 
					825d45eb26 | ||
| 
						 | 
					8b2516abb5 | ||
| 
						 | 
					bce099ef32 | ||
| 
						 | 
					6d14ce625f | ||
| 
						 | 
					c8eadf4011 | ||
| 
						 | 
					0c1ab780bb | ||
| 
						 | 
					0f623c2cca | ||
| 
						 | 
					b9ba2534a4 | ||
| 
						 | 
					c764981a40 | ||
| 
						 | 
					6363164f2b | ||
| 
						 | 
					63580b4acb | ||
| 
						 | 
					9af1aa1ecf | ||
| 
						 | 
					ad0f551204 | ||
| 
						 | 
					0d3e00cc41 | ||
| 
						 | 
					836d6b939e | ||
| 
						 | 
					0e5248e5e6 | ||
| 
						 | 
					e154b0db2a | ||
| 
						 | 
					ae767eb5be | ||
| 
						 | 
					777aa34bb0 | ||
| 
						 | 
					286f969f94 | 
							
								
								
									
										6
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							@@ -8,10 +8,8 @@ body:
 | 
			
		||||
    label: Minecraft Version
 | 
			
		||||
    description: What version of Minecraft are you using?
 | 
			
		||||
    options:
 | 
			
		||||
      - 1.16.x
 | 
			
		||||
      - 1.18.x
 | 
			
		||||
      - 1.19.x
 | 
			
		||||
      - 1.20.x
 | 
			
		||||
      - 1.20.1
 | 
			
		||||
      - 1.21.x
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: input
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								.github/workflows/main-ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/main-ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,16 +9,16 @@ jobs:
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: 📥 Clone repository
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: 📥 Set up Java
 | 
			
		||||
      uses: actions/setup-java@v3
 | 
			
		||||
      uses: actions/setup-java@v4
 | 
			
		||||
      with:
 | 
			
		||||
        java-version: 17
 | 
			
		||||
        distribution: 'temurin'
 | 
			
		||||
 | 
			
		||||
    - name: 📥 Setup Gradle
 | 
			
		||||
      uses: gradle/gradle-build-action@v2
 | 
			
		||||
      uses: gradle/actions/setup-gradle@v3
 | 
			
		||||
      with:
 | 
			
		||||
        cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
 | 
			
		||||
 | 
			
		||||
@@ -58,13 +58,13 @@ jobs:
 | 
			
		||||
        find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
 | 
			
		||||
 | 
			
		||||
    - name: 📤 Upload Jar
 | 
			
		||||
      uses: actions/upload-artifact@v3
 | 
			
		||||
      uses: actions/upload-artifact@v4
 | 
			
		||||
      with:
 | 
			
		||||
        name: CC-Tweaked
 | 
			
		||||
        path: ./jars
 | 
			
		||||
 | 
			
		||||
    - name: 📤 Upload coverage
 | 
			
		||||
      uses: codecov/codecov-action@v3
 | 
			
		||||
      uses: codecov/codecov-action@v4
 | 
			
		||||
 | 
			
		||||
  build-core:
 | 
			
		||||
    strategy:
 | 
			
		||||
@@ -81,24 +81,28 @@ jobs:
 | 
			
		||||
    runs-on: ${{ matrix.uses }}
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Clone repository
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
    - name: 📥 Clone repository
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Set up Java
 | 
			
		||||
      uses: actions/setup-java@v3
 | 
			
		||||
    - name: 📥 Set up Java
 | 
			
		||||
      uses: actions/setup-java@v4
 | 
			
		||||
      with:
 | 
			
		||||
        java-version: 17
 | 
			
		||||
        distribution: 'temurin'
 | 
			
		||||
 | 
			
		||||
    - name: Setup Gradle
 | 
			
		||||
      uses: gradle/gradle-build-action@v2
 | 
			
		||||
    - name: 📥 Setup Gradle
 | 
			
		||||
      uses: gradle/actions/setup-gradle@v3
 | 
			
		||||
      with:
 | 
			
		||||
        cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
 | 
			
		||||
 | 
			
		||||
    - name: Run tests
 | 
			
		||||
    - name: ⚒️ Build
 | 
			
		||||
      run: |
 | 
			
		||||
        ./gradlew --configure-on-demand :core:assemble
 | 
			
		||||
 | 
			
		||||
    - name: 🧪 Run tests
 | 
			
		||||
      run: |
 | 
			
		||||
        ./gradlew --configure-on-demand :core:test
 | 
			
		||||
 | 
			
		||||
    - name: Parse test reports
 | 
			
		||||
    - name: 🧪 Parse test reports
 | 
			
		||||
      run: python3 ./tools/parse-reports.py
 | 
			
		||||
      if: ${{ failure() }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								.github/workflows/make-doc.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/make-doc.sh
									
									
									
									
										vendored
									
									
								
							@@ -1,19 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -eu
 | 
			
		||||
 | 
			
		||||
DEST="${GITHUB_REF#refs/*/}"
 | 
			
		||||
echo "Uploading docs to https://tweaked.cc/$DEST"
 | 
			
		||||
 | 
			
		||||
# Setup ssh key
 | 
			
		||||
mkdir -p "$HOME/.ssh/"
 | 
			
		||||
echo "$SSH_KEY" > "$HOME/.ssh/key"
 | 
			
		||||
chmod 600 "$HOME/.ssh/key"
 | 
			
		||||
 | 
			
		||||
# And upload
 | 
			
		||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
 | 
			
		||||
      "$GITHUB_WORKSPACE/projects/web/build/site/" \
 | 
			
		||||
      "$SSH_USER@$SSH_HOST:/$DEST"
 | 
			
		||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
 | 
			
		||||
      "$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
 | 
			
		||||
      "$SSH_USER@$SSH_HOST:/$DEST/javadoc"
 | 
			
		||||
							
								
								
									
										34
									
								
								.github/workflows/make-doc.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								.github/workflows/make-doc.yml
									
									
									
									
										vendored
									
									
								
							@@ -3,8 +3,7 @@ name: Build documentation
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
    - mc-1.19.x
 | 
			
		||||
    - mc-1.20.x
 | 
			
		||||
    - mc-*
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  make_doc:
 | 
			
		||||
@@ -12,30 +11,25 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Clone repository
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
    - name: 📥 Clone repository
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Set up Java
 | 
			
		||||
      uses: actions/setup-java@v1
 | 
			
		||||
    - name: 📥 Set up Java
 | 
			
		||||
      uses: actions/setup-java@v4
 | 
			
		||||
      with:
 | 
			
		||||
        java-version: 17
 | 
			
		||||
        distribution: 'temurin'
 | 
			
		||||
 | 
			
		||||
    - name: Setup Gradle
 | 
			
		||||
      uses: gradle/gradle-build-action@v2
 | 
			
		||||
    - name: 📥 Setup Gradle
 | 
			
		||||
      uses: gradle/actions/setup-gradle@v3
 | 
			
		||||
      with:
 | 
			
		||||
        cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
 | 
			
		||||
 | 
			
		||||
    - name: Build with Gradle
 | 
			
		||||
      run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
 | 
			
		||||
    - name: ⚒️ Generate documentation
 | 
			
		||||
      run: ./gradlew docWebsite --no-daemon
 | 
			
		||||
 | 
			
		||||
    - name: Generate documentation
 | 
			
		||||
      run: ./gradlew docWebsite :common-api:javadoc  --no-daemon
 | 
			
		||||
 | 
			
		||||
    - name: Upload documentation
 | 
			
		||||
      run: .github/workflows/make-doc.sh 2> /dev/null
 | 
			
		||||
      env:
 | 
			
		||||
        SSH_KEY:  ${{ secrets.SSH_KEY  }}
 | 
			
		||||
        SSH_USER: ${{ secrets.SSH_USER }}
 | 
			
		||||
        SSH_HOST: ${{ secrets.SSH_HOST }}
 | 
			
		||||
        SSH_PORT: ${{ secrets.SSH_PORT }}
 | 
			
		||||
    - name: 📤 Upload Jar
 | 
			
		||||
      uses: actions/upload-artifact@v4
 | 
			
		||||
      with:
 | 
			
		||||
        name: Documentation
 | 
			
		||||
        path: ./projects/web/build/site/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								.gitpod.yml
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								.gitpod.yml
									
									
									
									
									
								
							@@ -1,26 +0,0 @@
 | 
			
		||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
image:
 | 
			
		||||
  file: config/gitpod/Dockerfile
 | 
			
		||||
 | 
			
		||||
ports:
 | 
			
		||||
  - port: 25565
 | 
			
		||||
    onOpen: notify
 | 
			
		||||
 | 
			
		||||
vscode:
 | 
			
		||||
  extensions:
 | 
			
		||||
    - eamodio.gitlens
 | 
			
		||||
    - github.vscode-pull-request-github
 | 
			
		||||
    - ms-azuretools.vscode-docker
 | 
			
		||||
    - redhat.java
 | 
			
		||||
    - richardwillis.vscode-gradle
 | 
			
		||||
    - vscjava.vscode-java-debug
 | 
			
		||||
    - vscode.github
 | 
			
		||||
 | 
			
		||||
tasks:
 | 
			
		||||
  - name: Setup pre-commit hool
 | 
			
		||||
    init: pre-commit install --allow-missing-config
 | 
			
		||||
  - name: Install npm packages
 | 
			
		||||
    init: npm ci
 | 
			
		||||
@@ -27,7 +27,7 @@ repos:
 | 
			
		||||
    exclude: "^(.*\\.(bat)|LICENSE)$"
 | 
			
		||||
 | 
			
		||||
- repo: https://github.com/fsfe/reuse-tool
 | 
			
		||||
  rev: v2.1.0
 | 
			
		||||
  rev: v4.0.3
 | 
			
		||||
  hooks:
 | 
			
		||||
  - id: reuse
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								.reuse/dep5
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								.reuse/dep5
									
									
									
									
									
								
							@@ -1,100 +0,0 @@
 | 
			
		||||
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_upgrades/*
 | 
			
		||||
  projects/common/src/testMod/resources/data/cctest/structures/*
 | 
			
		||||
  projects/fabric/src/generated/*
 | 
			
		||||
  projects/forge/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/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,7 +12,6 @@ If you've any other questions, [just ask the community][community] or [open an i
 | 
			
		||||
 | 
			
		||||
## Table of Contents
 | 
			
		||||
 - [Reporting issues](#reporting-issues)
 | 
			
		||||
 - [Translations](#translations)
 | 
			
		||||
 - [Setting up a development environment](#setting-up-a-development-environment)
 | 
			
		||||
 - [Developing CC: Tweaked](#developing-cc-tweaked)
 | 
			
		||||
 - [Writing documentation](#writing-documentation)
 | 
			
		||||
@@ -21,17 +20,13 @@ 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
 | 
			
		||||
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
 | 
			
		||||
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:
 | 
			
		||||
   - Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
 | 
			
		||||
   - Java Development Kit 17 (JDK). This can be downloaded from [Adoptium].
 | 
			
		||||
   - [Git](https://git-scm.com/).
 | 
			
		||||
   - If you want to work on documentation, [NodeJS][node].
 | 
			
		||||
   - [NodeJS 20 or later][node].
 | 
			
		||||
 | 
			
		||||
 - Download CC: Tweaked's source code:
 | 
			
		||||
   ```
 | 
			
		||||
@@ -101,7 +96,6 @@ about how you can build on that until you've covered everything!
 | 
			
		||||
[community]: README.md#community "Get in touch with the community."
 | 
			
		||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
 | 
			
		||||
[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"
 | 
			
		||||
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
 | 
			
		||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								README.md
									
									
									
									
									
								
							@@ -26,8 +26,9 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
 | 
			
		||||
 | 
			
		||||
## Community
 | 
			
		||||
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 [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
 | 
			
		||||
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
 | 
			
		||||
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].
 | 
			
		||||
 | 
			
		||||
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +40,7 @@ on is present.
 | 
			
		||||
```groovy
 | 
			
		||||
repositories {
 | 
			
		||||
  maven {
 | 
			
		||||
    url "https://squiddev.cc/maven/"
 | 
			
		||||
    url "https://maven.squiddev.cc"
 | 
			
		||||
    content {
 | 
			
		||||
      includeGroup("cc.tweaked")
 | 
			
		||||
    }
 | 
			
		||||
@@ -86,6 +87,6 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
 | 
			
		||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
 | 
			
		||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
 | 
			
		||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
 | 
			
		||||
[forum]: https://forums.computercraft.cc/
 | 
			
		||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
			
		||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
			
		||||
[EsperNet]: https://www.esper.net/
 | 
			
		||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										110
									
								
								REUSE.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								REUSE.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
# 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/**"]
 | 
			
		||||
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"
 | 
			
		||||
@@ -5,9 +5,8 @@
 | 
			
		||||
import cc.tweaked.gradle.JUnitExt
 | 
			
		||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
 | 
			
		||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
 | 
			
		||||
import org.jetbrains.gradle.ext.compiler
 | 
			
		||||
import org.jetbrains.gradle.ext.runConfigurations
 | 
			
		||||
import org.jetbrains.gradle.ext.settings
 | 
			
		||||
import org.jetbrains.gradle.ext.*
 | 
			
		||||
import org.jetbrains.gradle.ext.Application
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    publishing
 | 
			
		||||
@@ -86,6 +85,19 @@ idea.project.settings.runConfigurations {
 | 
			
		||||
        moduleName = "${idea.project.name}.forge.test"
 | 
			
		||||
        packageName = ""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    register<Application>("Standalone") {
 | 
			
		||||
        moduleName = "${idea.project.name}.standalone.main"
 | 
			
		||||
        mainClass = "cc.tweaked.standalone.Main"
 | 
			
		||||
        programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in
 | 
			
		||||
// "Project Structure".
 | 
			
		||||
idea.project.settings.delegateActions {
 | 
			
		||||
    delegateBuildRunToGradle = false
 | 
			
		||||
    testRunner = ActionDelegationConfig.TestRunner.PLATFORM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
idea.project.settings.compiler.javac {
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ repositories {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    maven("https://squiddev.cc/maven") {
 | 
			
		||||
    maven("https://maven.squiddev.cc") {
 | 
			
		||||
        name = "SquidDev"
 | 
			
		||||
        content {
 | 
			
		||||
            includeGroup("cc.tweaked.vanilla-extract")
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ java {
 | 
			
		||||
repositories {
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
 | 
			
		||||
    val mainMaven = maven("https://squiddev.cc/maven") {
 | 
			
		||||
    val mainMaven = maven("https://maven.squiddev.cc/mirror") {
 | 
			
		||||
        name = "SquidDev"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -54,6 +54,7 @@ repositories {
 | 
			
		||||
        filter {
 | 
			
		||||
            includeGroup("cc.tweaked")
 | 
			
		||||
            // Things we mirror
 | 
			
		||||
            includeGroup("com.simibubi.create")
 | 
			
		||||
            includeGroup("commoble.morered")
 | 
			
		||||
            includeGroup("dev.architectury")
 | 
			
		||||
            includeGroup("dev.emi")
 | 
			
		||||
@@ -94,9 +95,8 @@ sourceSets.all {
 | 
			
		||||
            check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
 | 
			
		||||
            // Too many false positives right now. Maybe we need an indirection for it later on.
 | 
			
		||||
            check("ReferenceEquality", CheckSeverity.OFF)
 | 
			
		||||
            check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
 | 
			
		||||
            check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
 | 
			
		||||
            check("OperatorPrecedence", CheckSeverity.OFF) // For now.
 | 
			
		||||
            check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
 | 
			
		||||
            check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
 | 
			
		||||
            check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
 | 
			
		||||
 | 
			
		||||
@@ -134,8 +134,8 @@ tasks.processResources {
 | 
			
		||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
 | 
			
		||||
    isPreserveFileTimestamps = false
 | 
			
		||||
    isReproducibleFileOrder = true
 | 
			
		||||
    dirMode = Integer.valueOf("755", 8)
 | 
			
		||||
    fileMode = Integer.valueOf("664", 8)
 | 
			
		||||
    filePermissions {}
 | 
			
		||||
    dirPermissions {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks.jar {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ publishing {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    repositories {
 | 
			
		||||
        maven("https://squiddev.cc/maven") {
 | 
			
		||||
        maven("https://maven.squiddev.cc") {
 | 
			
		||||
            name = "SquidDev"
 | 
			
		||||
 | 
			
		||||
            credentials(PasswordCredentials::class)
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ import org.gradle.api.tasks.SourceSet
 | 
			
		||||
import org.gradle.api.tasks.bundling.Jar
 | 
			
		||||
import org.gradle.api.tasks.compile.JavaCompile
 | 
			
		||||
import org.gradle.api.tasks.javadoc.Javadoc
 | 
			
		||||
import org.gradle.configurationcache.extensions.capitalized
 | 
			
		||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
 | 
			
		||||
import org.gradle.language.jvm.tasks.ProcessResources
 | 
			
		||||
import org.gradle.process.JavaForkOptions
 | 
			
		||||
@@ -35,7 +34,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.net.URI
 | 
			
		||||
import java.net.URL
 | 
			
		||||
import java.util.regex.Pattern
 | 
			
		||||
 | 
			
		||||
abstract class CCTweakedExtension(
 | 
			
		||||
@@ -182,7 +180,7 @@ abstract class CCTweakedExtension(
 | 
			
		||||
 | 
			
		||||
    fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
 | 
			
		||||
        val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
 | 
			
		||||
        val reportTaskName = "jacoco${task.name.capitalized()}Report"
 | 
			
		||||
        val reportTaskName = "jacoco${task.name.capitalise()}Report"
 | 
			
		||||
 | 
			
		||||
        val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
 | 
			
		||||
        task.configure {
 | 
			
		||||
@@ -226,12 +224,12 @@ abstract class CCTweakedExtension(
 | 
			
		||||
     * where possible.
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadFile(label: String, url: String): File {
 | 
			
		||||
        val url = URL(url)
 | 
			
		||||
        val path = File(url.path)
 | 
			
		||||
        val uri = URI(url)
 | 
			
		||||
        val path = File(uri.path)
 | 
			
		||||
 | 
			
		||||
        project.repositories.ivy {
 | 
			
		||||
            name = label
 | 
			
		||||
            setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
 | 
			
		||||
            setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
 | 
			
		||||
            patternLayout {
 | 
			
		||||
                artifact("[artifact].[ext]")
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -143,7 +143,7 @@ fun getNextVersion(version: String): String {
 | 
			
		||||
    val lastIndex = mainVersion.lastIndexOf('.')
 | 
			
		||||
    if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
 | 
			
		||||
    val lastVersion = try {
 | 
			
		||||
        version.substring(lastIndex + 1).toInt()
 | 
			
		||||
        mainVersion.substring(lastIndex + 1).toInt()
 | 
			
		||||
    } catch (e: NumberFormatException) {
 | 
			
		||||
        throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
 | 
			
		||||
    }
 | 
			
		||||
@@ -155,3 +155,15 @@ fun getNextVersion(version: String): String {
 | 
			
		||||
    if (dashIndex >= 0) out.append(version, dashIndex, version.length)
 | 
			
		||||
    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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/MergeTrees.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/MergeTrees.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package cc.tweaked.gradle
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.vanillaextract.core.util.MoreFiles
 | 
			
		||||
import org.gradle.api.Action
 | 
			
		||||
import org.gradle.api.DefaultTask
 | 
			
		||||
import org.gradle.api.GradleException
 | 
			
		||||
import org.gradle.api.file.*
 | 
			
		||||
import org.gradle.api.model.ObjectFactory
 | 
			
		||||
import org.gradle.api.provider.ListProperty
 | 
			
		||||
import org.gradle.api.tasks.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Merge common files across multiple directories into one destination directory.
 | 
			
		||||
 *
 | 
			
		||||
 * This is intended for merging the generated resources from the Forge and Fabric projects. Files common between the two
 | 
			
		||||
 * are written to the global [output] directory, while distinct files are written to the per-source
 | 
			
		||||
 * [MergeTrees.Source.output] directory.
 | 
			
		||||
 */
 | 
			
		||||
abstract class MergeTrees : DefaultTask() {
 | 
			
		||||
    /**
 | 
			
		||||
     * A source directory to read from.
 | 
			
		||||
     */
 | 
			
		||||
    interface Source {
 | 
			
		||||
        /**
 | 
			
		||||
         * The folder contianing all input files.
 | 
			
		||||
         */
 | 
			
		||||
        @get:InputFiles
 | 
			
		||||
        @get:PathSensitive(PathSensitivity.RELATIVE)
 | 
			
		||||
        val input: ConfigurableFileTree
 | 
			
		||||
 | 
			
		||||
        fun input(configure: Action<ConfigurableFileTree>) {
 | 
			
		||||
            configure.execute(input)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The folder to write files unique to this folder to.
 | 
			
		||||
         */
 | 
			
		||||
        @get:OutputDirectory
 | 
			
		||||
        val output: DirectoryProperty
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The list of sources.
 | 
			
		||||
     */
 | 
			
		||||
    @get:Nested
 | 
			
		||||
    abstract val sources: ListProperty<Source>
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add and configure a new source.
 | 
			
		||||
     */
 | 
			
		||||
    fun source(configure: Action<Source>) {
 | 
			
		||||
        val instance = objectFactory.newInstance(Source::class.java)
 | 
			
		||||
        configure.execute(instance)
 | 
			
		||||
        instance.output.disallowChanges()
 | 
			
		||||
        sources.add(instance)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The directory to write common files to.
 | 
			
		||||
     */
 | 
			
		||||
    @get:OutputDirectory
 | 
			
		||||
    abstract val output: DirectoryProperty
 | 
			
		||||
 | 
			
		||||
    @get:Inject
 | 
			
		||||
    protected abstract val objectFactory: ObjectFactory
 | 
			
		||||
 | 
			
		||||
    @get:Inject
 | 
			
		||||
    protected abstract val fsOperations: FileSystemOperations
 | 
			
		||||
 | 
			
		||||
    @TaskAction
 | 
			
		||||
    fun run() {
 | 
			
		||||
        val sources = this.sources.get()
 | 
			
		||||
        if (sources.isEmpty()) throw GradleException("Cannot have an empty list of sources")
 | 
			
		||||
 | 
			
		||||
        val files = mutableMapOf<String, SharedFile>()
 | 
			
		||||
        for (source in sources) {
 | 
			
		||||
            source.input.visit(
 | 
			
		||||
                object : FileVisitor {
 | 
			
		||||
                    override fun visitDir(dirDetails: FileVisitDetails) = Unit
 | 
			
		||||
                    override fun visitFile(fileDetails: FileVisitDetails) {
 | 
			
		||||
                        val path = fileDetails.file.toRelativeString(source.input.dir)
 | 
			
		||||
                        val hash = MoreFiles.computeSha1(fileDetails.file.toPath())
 | 
			
		||||
 | 
			
		||||
                        val existing = files[path]
 | 
			
		||||
                        if (existing == null) {
 | 
			
		||||
                            files[path] = SharedFile(hash, 1)
 | 
			
		||||
                        } else if (existing.hash == hash) {
 | 
			
		||||
                            existing.found++
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
 | 
			
		||||
 | 
			
		||||
        // Copy shared files to the common directory
 | 
			
		||||
        fsOperations.sync {
 | 
			
		||||
            from(sources[0].input)
 | 
			
		||||
            into(output)
 | 
			
		||||
            include(sharedFiles)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // And all other files to their per-source directory
 | 
			
		||||
        for (source in sources) {
 | 
			
		||||
            fsOperations.sync {
 | 
			
		||||
                from(source.input)
 | 
			
		||||
                into(source.output)
 | 
			
		||||
                exclude(sharedFiles)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class SharedFile(val hash: String, var found: Int)
 | 
			
		||||
}
 | 
			
		||||
@@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() {
 | 
			
		||||
    @TaskAction
 | 
			
		||||
    fun install() {
 | 
			
		||||
        project.exec {
 | 
			
		||||
            commandLine("npm", "ci")
 | 
			
		||||
            commandLine(ProcessHelpers.getExecutable("npm"), "ci")
 | 
			
		||||
            workingDir = projectRoot.get().asFile
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -59,6 +59,6 @@ abstract class NpmInstall : DefaultTask() {
 | 
			
		||||
abstract class NpxExecToDir : ExecToDir() {
 | 
			
		||||
    init {
 | 
			
		||||
        dependsOn(NpmInstall.TASK_NAME)
 | 
			
		||||
        executable = "npx"
 | 
			
		||||
        executable = ProcessHelpers.getExecutable("npx")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import org.gradle.api.GradleException
 | 
			
		||||
import java.io.BufferedReader
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.InputStreamReader
 | 
			
		||||
import java.nio.charset.StandardCharsets
 | 
			
		||||
 | 
			
		||||
internal object ProcessHelpers {
 | 
			
		||||
    fun startProcess(vararg command: String): Process {
 | 
			
		||||
@@ -34,7 +35,7 @@ internal object ProcessHelpers {
 | 
			
		||||
        val process = startProcess(*command)
 | 
			
		||||
        process.outputStream.close()
 | 
			
		||||
 | 
			
		||||
        val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
 | 
			
		||||
        val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader ->
 | 
			
		||||
            reader.lines().filter { it.isNotEmpty() }.toList()
 | 
			
		||||
        }
 | 
			
		||||
        ProcessGroovyMethods.closeStreams(process)
 | 
			
		||||
@@ -46,6 +47,28 @@ internal object ProcessHelpers {
 | 
			
		||||
        val path = System.getenv("PATH") ?: return false
 | 
			
		||||
        return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Search for an executable on the `PATH` if required.
 | 
			
		||||
     *
 | 
			
		||||
     * [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
 | 
			
		||||
     * Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
 | 
			
		||||
     */
 | 
			
		||||
    fun getExecutable(name: String): String {
 | 
			
		||||
        if (!System.getProperty("os.name").lowercase().contains("windows")) return name
 | 
			
		||||
 | 
			
		||||
        val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
 | 
			
		||||
        val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
 | 
			
		||||
 | 
			
		||||
        for (pathEntry in path) {
 | 
			
		||||
            for (ext in pathExt) {
 | 
			
		||||
                val resolved = File(pathEntry, name + ext)
 | 
			
		||||
                if (resolved.exists()) return resolved.getAbsolutePath()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return name
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal fun Process.waitForOrThrow(message: String) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
FROM gitpod/workspace-base
 | 
			
		||||
 | 
			
		||||
USER gitpod
 | 
			
		||||
 | 
			
		||||
RUN sudo apt-get -q update \
 | 
			
		||||
 && sudo apt-get install -yq openjdk-16-jdk python3-pip npm \
 | 
			
		||||
 && sudo pip3 install pre-commit \
 | 
			
		||||
 && sudo update-java-alternatives --set java-1.16.0-openjdk-amd64
 | 
			
		||||
@@ -131,7 +131,7 @@ different.
 | 
			
		||||
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
 | 
			
		||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
 | 
			
		||||
 | 
			
		||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
 | 
			
		||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each
 | 
			
		||||
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
 | 
			
		||||
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
 | 
			
		||||
[`fs.ReadHandle.read`] if you prefer.
 | 
			
		||||
@@ -191,7 +191,7 @@ end
 | 
			
		||||
 | 
			
		||||
> [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
 | 
			
		||||
> cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
 | 
			
		||||
> cover. That said, don't be afraid to ask [the community for help][community].
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
@@ -205,5 +205,4 @@ 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"
 | 
			
		||||
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
 | 
			
		||||
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
 | 
			
		||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
			
		||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
			
		||||
[Community]: /#community
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								doc/index.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								doc/index.md
									
									
									
									
									
								
							@@ -4,7 +4,14 @@ SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
 | 
			
		||||
SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
# 
 | 
			
		||||
<h1>
 | 
			
		||||
    <picture>
 | 
			
		||||
        <source media="(prefers-color-scheme: dark)" srcset="logo-darkmode.png">
 | 
			
		||||
        <source media="(prefers-color-scheme: light)" srcset="logo.png">
 | 
			
		||||
        <img alt="CC: Tweaked" src="logo.png">
 | 
			
		||||
    </picture>
 | 
			
		||||
</h1>
 | 
			
		||||
 | 
			
		||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
 | 
			
		||||
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
 | 
			
		||||
new features.
 | 
			
		||||
@@ -38,12 +45,16 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
 | 
			
		||||
 | 
			
		||||
 - [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
 | 
			
		||||
 - [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
 | 
			
		||||
 - [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
 | 
			
		||||
 - [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
 | 
			
		||||
<h2 id="community">Community</h2>
 | 
			
		||||
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
 | 
			
		||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
 | 
			
		||||
@@ -58,4 +69,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
 | 
			
		||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
 | 
			
		||||
[lua]: https://www.lua.org/ "Lua's main website"
 | 
			
		||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
			
		||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
			
		||||
[EsperNet]: https://www.esper.net/
 | 
			
		||||
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
 | 
			
		||||
 
 | 
			
		||||
@@ -45,12 +45,16 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
 | 
			
		||||
 | 
			
		||||
 - [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
 | 
			
		||||
 - [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
 | 
			
		||||
 - [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
 | 
			
		||||
 - [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
 | 
			
		||||
## Community
 | 
			
		||||
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
 | 
			
		||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
 | 
			
		||||
@@ -60,4 +64,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
 | 
			
		||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
 | 
			
		||||
[lua]: https://www.lua.org/ "Lua's main website"
 | 
			
		||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
 | 
			
		||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
 | 
			
		||||
[EsperNet]: https://www.esper.net/
 | 
			
		||||
[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:
 | 
			
		||||
   - 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`
 | 
			
		||||
     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.
 | 
			
		||||
   - `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
 | 
			
		||||
   - 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
 | 
			
		||||
     with other functions, and [`setfenv`] will have no effect.
 | 
			
		||||
   - [`load`]/[`loadstring`] defaults to using the global environment (`_G`) rather than the current coroutine's
 | 
			
		||||
     environment.
 | 
			
		||||
   - Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
 | 
			
		||||
   - `math.random` now uses Lua 5.4's random number generator.
 | 
			
		||||
   - Support for dumping functions ([`string.dump`]) and loading binary chunks has been removed.
 | 
			
		||||
   - [`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.
 | 
			
		||||
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
 - 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
 | 
			
		||||
     more understandable format.
 | 
			
		||||
@@ -70,12 +70,12 @@ 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.
 | 
			
		||||
 | 
			
		||||
## 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.
 | 
			
		||||
 | 
			
		||||
 - Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
 | 
			
		||||
   you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
 | 
			
		||||
 | 
			
		||||
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
 | 
			
		||||
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
 | 
			
		||||
[legal_data_pack]: https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#Legal_characters
 | 
			
		||||
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
org.gradle.jvmargs=-Xmx3G
 | 
			
		||||
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8
 | 
			
		||||
org.gradle.parallel=true
 | 
			
		||||
 | 
			
		||||
kotlin.stdlib.default.dependency=false
 | 
			
		||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
 | 
			
		||||
 | 
			
		||||
# Mod properties
 | 
			
		||||
isUnstable=false
 | 
			
		||||
modVersion=1.110.0
 | 
			
		||||
modVersion=1.113.0
 | 
			
		||||
 | 
			
		||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
 | 
			
		||||
mcVersion=1.20.1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								gradle/gradle-daemon-jvm.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								gradle/gradle-daemon-jvm.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
#This file is generated by updateDaemonJvm
 | 
			
		||||
toolchainVersion=17
 | 
			
		||||
@@ -26,7 +26,7 @@ slf4j = "2.0.1"
 | 
			
		||||
asm = "9.6"
 | 
			
		||||
autoService = "1.1.1"
 | 
			
		||||
checkerFramework = "3.42.0"
 | 
			
		||||
cobalt = "0.9.2"
 | 
			
		||||
cobalt = "0.9.3"
 | 
			
		||||
commonsCli = "1.6.0"
 | 
			
		||||
jetbrainsAnnotations = "24.1.0"
 | 
			
		||||
jsr305 = "3.0.2"
 | 
			
		||||
@@ -46,6 +46,8 @@ oculus = "1.2.5"
 | 
			
		||||
rei = "12.0.626"
 | 
			
		||||
rubidium = "0.6.1"
 | 
			
		||||
sodium = "mc1.20-0.4.10"
 | 
			
		||||
create-forge = "0.5.1.f-33"
 | 
			
		||||
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
 | 
			
		||||
 | 
			
		||||
# Testing
 | 
			
		||||
hamcrest = "2.2"
 | 
			
		||||
@@ -57,22 +59,22 @@ jmh = "1.37"
 | 
			
		||||
cctJavadoc = "1.8.2"
 | 
			
		||||
checkstyle = "10.14.1"
 | 
			
		||||
curseForgeGradle = "1.0.14"
 | 
			
		||||
errorProne-core = "2.23.0"
 | 
			
		||||
errorProne-core = "2.27.0"
 | 
			
		||||
errorProne-plugin = "3.1.0"
 | 
			
		||||
fabric-loom = "1.5.7"
 | 
			
		||||
forgeGradle = "6.0.20"
 | 
			
		||||
fabric-loom = "1.7.1"
 | 
			
		||||
forgeGradle = "6.0.21"
 | 
			
		||||
githubRelease = "2.5.2"
 | 
			
		||||
gradleVersions = "0.50.0"
 | 
			
		||||
ideaExt = "1.1.7"
 | 
			
		||||
illuaminate = "0.1.0-69-gf294ab2"
 | 
			
		||||
illuaminate = "0.1.0-73-g43ee16c"
 | 
			
		||||
librarian = "1.+"
 | 
			
		||||
lwjgl = "3.3.3"
 | 
			
		||||
minotaur = "2.+"
 | 
			
		||||
nullAway = "0.9.9"
 | 
			
		||||
nullAway = "0.10.25"
 | 
			
		||||
spotless = "6.23.3"
 | 
			
		||||
taskTree = "2.1.1"
 | 
			
		||||
teavm = "0.10.0-SQUID.3"
 | 
			
		||||
vanillaExtract = "0.1.2"
 | 
			
		||||
teavm = "0.11.0-SQUID.1"
 | 
			
		||||
vanillaExtract = "0.1.3"
 | 
			
		||||
versionCatalogUpdate = "0.8.1"
 | 
			
		||||
 | 
			
		||||
[libraries]
 | 
			
		||||
@@ -100,11 +102,13 @@ nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref
 | 
			
		||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
 | 
			
		||||
 | 
			
		||||
# Minecraft mods
 | 
			
		||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
 | 
			
		||||
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" }
 | 
			
		||||
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-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
 | 
			
		||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
 | 
			
		||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
 | 
			
		||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
 | 
			
		||||
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
 | 
			
		||||
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
 | 
			
		||||
@@ -154,6 +158,7 @@ minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur"
 | 
			
		||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
 | 
			
		||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
 | 
			
		||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
 | 
			
		||||
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
 | 
			
		||||
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
 | 
			
		||||
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
 | 
			
		||||
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
distributionBase=GRADLE_USER_HOME
 | 
			
		||||
distributionPath=wrapper/dists
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
 | 
			
		||||
networkTimeout=10000
 | 
			
		||||
validateDistributionUrl=true
 | 
			
		||||
zipStoreBase=GRADLE_USER_HOME
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							@@ -15,6 +15,8 @@
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
##############################################################################
 | 
			
		||||
#
 | 
			
		||||
@@ -55,7 +57,7 @@
 | 
			
		||||
#       Darwin, MinGW, and NonStop.
 | 
			
		||||
#
 | 
			
		||||
#   (3) This script is generated from the Groovy template
 | 
			
		||||
#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 | 
			
		||||
#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 | 
			
		||||
#       within the Gradle project.
 | 
			
		||||
#
 | 
			
		||||
#       You can find Gradle at https://github.com/gradle/gradle/.
 | 
			
		||||
@@ -84,7 +86,8 @@ done
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
APP_BASE_NAME=${0##*/}
 | 
			
		||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
 | 
			
		||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
 | 
			
		||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
 | 
			
		||||
' "$PWD" ) || exit
 | 
			
		||||
 | 
			
		||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
 | 
			
		||||
MAX_FD=maximum
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							@@ -13,6 +13,8 @@
 | 
			
		||||
@rem See the License for the specific language governing permissions and
 | 
			
		||||
@rem limitations under the License.
 | 
			
		||||
@rem
 | 
			
		||||
@rem SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
@rem
 | 
			
		||||
 | 
			
		||||
@if "%DEBUG%"=="" @echo off
 | 
			
		||||
@rem ##########################################################################
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,11 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.api;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.component.ComputerComponent;
 | 
			
		||||
import dan200.computercraft.api.filesystem.Mount;
 | 
			
		||||
import dan200.computercraft.api.filesystem.WritableMount;
 | 
			
		||||
import dan200.computercraft.api.lua.GenericSource;
 | 
			
		||||
import dan200.computercraft.api.lua.IComputerSystem;
 | 
			
		||||
import dan200.computercraft.api.lua.ILuaAPI;
 | 
			
		||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
 | 
			
		||||
import dan200.computercraft.api.media.IMedia;
 | 
			
		||||
@@ -165,7 +167,20 @@ public final class ComputerCraftAPI {
 | 
			
		||||
     * Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
 | 
			
		||||
     * to use peripherals to provide functionality to users.
 | 
			
		||||
     * to use peripherals to provide functionality to users. If an API is <em>required</em>, you may want to consider
 | 
			
		||||
     * 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.
 | 
			
		||||
     * @see ILuaAPIFactory
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
// 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
// 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 + ")";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
// 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");
 | 
			
		||||
}
 | 
			
		||||
@@ -13,7 +13,7 @@ import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An item detail provider for {@link ItemStack}'s whose {@link Item} has a specific type.
 | 
			
		||||
 * An item detail provider for {@link ItemStack}s whose {@link Item} has a specific type.
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T> The type the stack's item must have.
 | 
			
		||||
 */
 | 
			
		||||
@@ -22,7 +22,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
 | 
			
		||||
    private final @Nullable String namespace;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new item detail provider. Meta will be inserted into a new sub-map named as per {@code namespace}.
 | 
			
		||||
     * Create a new item detail provider. Details will be inserted into a new sub-map named as per {@code namespace}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param itemType  The type the stack's item must have.
 | 
			
		||||
     * @param namespace The namespace to use for this provider.
 | 
			
		||||
@@ -34,7 +34,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new item detail provider. Meta will be inserted directly into the results.
 | 
			
		||||
     * Create a new item detail provider. Details will be inserted directly into the results.
 | 
			
		||||
     *
 | 
			
		||||
     * @param itemType The type the stack's item must have.
 | 
			
		||||
     */
 | 
			
		||||
@@ -53,21 +53,18 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
 | 
			
		||||
     * @param stack The item stack to provide details for.
 | 
			
		||||
     * @param item  The item to provide details for.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract void provideDetails(
 | 
			
		||||
        Map<? super String, Object> data, ItemStack stack, T item
 | 
			
		||||
    );
 | 
			
		||||
    public abstract void provideDetails(Map<? super String, Object> data, ItemStack stack, T item);
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
 | 
			
		||||
    public final void provideDetails(Map<? super String, Object> data, ItemStack stack) {
 | 
			
		||||
        var item = stack.getItem();
 | 
			
		||||
        if (!itemType.isInstance(item)) return;
 | 
			
		||||
 | 
			
		||||
        // If `namespace` is specified, insert into a new data map instead of the existing one.
 | 
			
		||||
        Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
 | 
			
		||||
 | 
			
		||||
        provideDetails(child, stack, itemType.cast(item));
 | 
			
		||||
 | 
			
		||||
        if (namespace != null) {
 | 
			
		||||
        if (namespace == null) {
 | 
			
		||||
            provideDetails(data, stack, itemType.cast(item));
 | 
			
		||||
        } else {
 | 
			
		||||
            Map<? super String, Object> child = new HashMap<>();
 | 
			
		||||
            provideDetails(child, stack, itemType.cast(item));
 | 
			
		||||
            data.put(namespace, child);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ public interface DetailRegistry<T> {
 | 
			
		||||
     * @param provider The detail provider to register.
 | 
			
		||||
     * @see DetailProvider
 | 
			
		||||
     */
 | 
			
		||||
    void addProvider(DetailProvider<T> provider);
 | 
			
		||||
    void addProvider(DetailProvider<? super T> provider);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
// 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);
 | 
			
		||||
}
 | 
			
		||||
@@ -4,13 +4,15 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.api.lua;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Construct an {@link ILuaAPI} for a specific computer.
 | 
			
		||||
 * Construct an {@link ILuaAPI} for a computer.
 | 
			
		||||
 *
 | 
			
		||||
 * @see ILuaAPI
 | 
			
		||||
 * @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
 | 
			
		||||
 * @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
 | 
			
		||||
 */
 | 
			
		||||
@FunctionalInterface
 | 
			
		||||
public interface ILuaAPIFactory {
 | 
			
		||||
@@ -6,10 +6,14 @@ package dan200.computercraft.api.pocket;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
import dan200.computercraft.api.upgrades.UpgradeBase;
 | 
			
		||||
import dan200.computercraft.api.upgrades.UpgradeData;
 | 
			
		||||
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.item.ItemStack;
 | 
			
		||||
import net.minecraft.world.phys.Vec3;
 | 
			
		||||
import org.jetbrains.annotations.ApiStatus;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@@ -17,7 +21,22 @@ import java.util.Map;
 | 
			
		||||
/**
 | 
			
		||||
 * Wrapper class for pocket computers.
 | 
			
		||||
 */
 | 
			
		||||
@ApiStatus.NonExtendable
 | 
			
		||||
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.
 | 
			
		||||
     * <p>
 | 
			
		||||
@@ -64,6 +83,26 @@ public interface IPocketAccess {
 | 
			
		||||
     */
 | 
			
		||||
    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.
 | 
			
		||||
     * <p>
 | 
			
		||||
@@ -73,6 +112,7 @@ public interface IPocketAccess {
 | 
			
		||||
     * @see #updateUpgradeNBTData()
 | 
			
		||||
     * @see UpgradeBase#getUpgradeItem(CompoundTag)
 | 
			
		||||
     * @see UpgradeBase#getUpgradeData(ItemStack)
 | 
			
		||||
     * @see #getUpgrade()
 | 
			
		||||
     */
 | 
			
		||||
    CompoundTag getUpgradeNBTData();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
package dan200.computercraft.api.turtle;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An enum representing the two sides of the turtle that a turtle turtle might reside.
 | 
			
		||||
 * An enum representing the two sides of the turtle that a turtle upgrade might reside.
 | 
			
		||||
 */
 | 
			
		||||
public enum TurtleSide {
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,12 @@ plugins {
 | 
			
		||||
    id("cc-tweaked.publishing")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sourceSets {
 | 
			
		||||
    main {
 | 
			
		||||
        resources.srcDir("src/generated/resources")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
minecraft {
 | 
			
		||||
    accessWideners(
 | 
			
		||||
        "src/main/resources/computercraft.accesswidener",
 | 
			
		||||
@@ -37,6 +43,7 @@ dependencies {
 | 
			
		||||
    clientImplementation(clientClasses(project(":common-api")))
 | 
			
		||||
 | 
			
		||||
    compileOnly(libs.bundles.externalMods.common)
 | 
			
		||||
    compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
 | 
			
		||||
    clientCompileOnly(variantOf(libs.emi) { classifier("api") })
 | 
			
		||||
 | 
			
		||||
    annotationProcessorEverywhere(libs.autoService)
 | 
			
		||||
@@ -105,3 +112,21 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
 | 
			
		||||
    doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
 | 
			
		||||
    doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val runData by tasks.registering(MergeTrees::class) {
 | 
			
		||||
    output = layout.projectDirectory.dir("src/generated/resources")
 | 
			
		||||
 | 
			
		||||
    for (loader in listOf("forge", "fabric")) {
 | 
			
		||||
        mustRunAfter(":$loader:runData")
 | 
			
		||||
        source {
 | 
			
		||||
            input {
 | 
			
		||||
                from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
 | 
			
		||||
                exclude(".cache")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
 | 
			
		||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
 | 
			
		||||
import dan200.computercraft.client.gui.*;
 | 
			
		||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
 | 
			
		||||
import dan200.computercraft.client.render.CustomLecternRenderer;
 | 
			
		||||
import dan200.computercraft.client.render.RenderTypes;
 | 
			
		||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
 | 
			
		||||
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
 | 
			
		||||
@@ -25,7 +26,6 @@ import dan200.computercraft.shared.common.IColouredItem;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ComputerState;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ServerContext;
 | 
			
		||||
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.TreasureDiskItem;
 | 
			
		||||
import net.minecraft.Util;
 | 
			
		||||
@@ -74,6 +74,7 @@ public final class ClientRegistry {
 | 
			
		||||
        BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
 | 
			
		||||
        BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
 | 
			
		||||
        BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
 | 
			
		||||
        BlockEntityRenderers.register(ModRegistry.BlockEntities.LECTERN.get(), CustomLecternRenderer::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -83,7 +84,6 @@ public final class ClientRegistry {
 | 
			
		||||
     */
 | 
			
		||||
    public static void registerMainThread(RegisterItemProperty itemProperties) {
 | 
			
		||||
        MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
 | 
			
		||||
        MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_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);
 | 
			
		||||
 | 
			
		||||
@@ -91,8 +91,6 @@ public final class ClientRegistry {
 | 
			
		||||
        MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
 | 
			
		||||
        MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
 | 
			
		||||
 | 
			
		||||
        MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
 | 
			
		||||
 | 
			
		||||
        registerItemProperty(itemProperties, "state",
 | 
			
		||||
            new UnclampedPropertyFunction((stack, world, player, random) -> {
 | 
			
		||||
                var computer = ClientPocketComputers.get(stack);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import dan200.computercraft.client.gui.widgets.DynamicImageButton;
 | 
			
		||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
 | 
			
		||||
import dan200.computercraft.client.network.ClientNetworking;
 | 
			
		||||
import dan200.computercraft.core.terminal.Terminal;
 | 
			
		||||
import dan200.computercraft.core.util.Nullability;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.InputHandler;
 | 
			
		||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
 | 
			
		||||
@@ -18,6 +19,7 @@ import dan200.computercraft.shared.config.Config;
 | 
			
		||||
import dan200.computercraft.shared.network.server.UploadFileMessage;
 | 
			
		||||
import net.minecraft.ChatFormatting;
 | 
			
		||||
import net.minecraft.Util;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.gui.GuiGraphics;
 | 
			
		||||
import net.minecraft.client.gui.components.events.GuiEventListener;
 | 
			
		||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
 | 
			
		||||
@@ -96,8 +98,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
 | 
			
		||||
        getTerminal().update();
 | 
			
		||||
 | 
			
		||||
        if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
 | 
			
		||||
            new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
 | 
			
		||||
                .showOrReplace(minecraft.getToasts());
 | 
			
		||||
            new ItemToast(minecraft(), displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
 | 
			
		||||
                .showOrReplace(minecraft().getToasts());
 | 
			
		||||
            uploadNagDeadline = Long.MAX_VALUE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -207,7 +209,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
 | 
			
		||||
        if (!toUpload.isEmpty()) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void uploadResult(UploadResult result, @Nullable Component message) {
 | 
			
		||||
@@ -223,9 +225,13 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void alert(Component title, Component message) {
 | 
			
		||||
        OptionScreen.show(minecraft, title, message,
 | 
			
		||||
            List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
 | 
			
		||||
            () -> minecraft.setScreen(this)
 | 
			
		||||
        OptionScreen.show(minecraft(), title, message,
 | 
			
		||||
            List.of(OptionScreen.newButton(OK, b -> minecraft().setScreen(this))),
 | 
			
		||||
            () -> minecraft().setScreen(this)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Minecraft minecraft() {
 | 
			
		||||
        return Nullability.assertNonNull(minecraft);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -49,31 +49,31 @@ public final class ClientInputHandler implements InputHandler {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void keyDown(int key, boolean repeat) {
 | 
			
		||||
        ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
 | 
			
		||||
        ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void keyUp(int key) {
 | 
			
		||||
        ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
 | 
			
		||||
        ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void mouseClick(int button, int x, int y) {
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void mouseUp(int button, int x, int y) {
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.UP, button, x, y));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void mouseDrag(int button, int x, int y) {
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.DRAG, button, x, y));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void mouseScroll(int direction, int x, int y) {
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
 | 
			
		||||
        ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.SCROLL, direction, x, y));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,10 @@ package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
 | 
			
		||||
import dan200.computercraft.core.terminal.Terminal;
 | 
			
		||||
import dan200.computercraft.core.util.Nullability;
 | 
			
		||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
 | 
			
		||||
import net.minecraft.client.KeyMapping;
 | 
			
		||||
import net.minecraft.client.Minecraft;
 | 
			
		||||
import net.minecraft.client.gui.GuiGraphics;
 | 
			
		||||
import net.minecraft.client.gui.screens.Screen;
 | 
			
		||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
 | 
			
		||||
@@ -16,6 +18,7 @@ import net.minecraft.world.entity.player.Inventory;
 | 
			
		||||
import org.lwjgl.glfw.GLFW;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
 | 
			
		||||
 | 
			
		||||
@@ -44,8 +47,8 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
 | 
			
		||||
    protected void init() {
 | 
			
		||||
        // First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
 | 
			
		||||
        // grabbing unsets.
 | 
			
		||||
        minecraft.mouseHandler.grabMouse();
 | 
			
		||||
        minecraft.screen = this;
 | 
			
		||||
        minecraft().mouseHandler.grabMouse();
 | 
			
		||||
        minecraft().screen = this;
 | 
			
		||||
        KeyMapping.releaseAll();
 | 
			
		||||
 | 
			
		||||
        super.init();
 | 
			
		||||
@@ -64,13 +67,13 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
 | 
			
		||||
        minecraft.player.getInventory().swapPaint(pDelta);
 | 
			
		||||
        Objects.requireNonNull(minecraft().player).getInventory().swapPaint(pDelta);
 | 
			
		||||
        return super.mouseScrolled(pMouseX, pMouseY, pDelta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onClose() {
 | 
			
		||||
        minecraft.player.closeContainer();
 | 
			
		||||
        Objects.requireNonNull(minecraft().player).closeContainer();
 | 
			
		||||
        super.onClose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -93,12 +96,16 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
 | 
			
		||||
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
 | 
			
		||||
        super.render(graphics, mouseX, mouseY, partialTicks);
 | 
			
		||||
 | 
			
		||||
        var font = minecraft.font;
 | 
			
		||||
        var font = minecraft().font;
 | 
			
		||||
        var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
 | 
			
		||||
        var y = 10;
 | 
			
		||||
        for (var line : lines) {
 | 
			
		||||
            graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
 | 
			
		||||
            graphics.drawString(font, line, (width / 2) - (font.width(line) / 2), y, 0xFFFFFF, true);
 | 
			
		||||
            y += 9;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Minecraft minecraft() {
 | 
			
		||||
        return Nullability.assertNonNull(minecraft);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,15 +6,22 @@ package dan200.computercraft.client.gui;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.vertex.Tesselator;
 | 
			
		||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
			
		||||
import dan200.computercraft.shared.common.HeldItemMenu;
 | 
			
		||||
import dan200.computercraft.shared.ModRegistry;
 | 
			
		||||
import dan200.computercraft.shared.media.PrintoutMenu;
 | 
			
		||||
import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
			
		||||
import net.minecraft.client.gui.GuiGraphics;
 | 
			
		||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
 | 
			
		||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
			
		||||
import net.minecraft.network.chat.Component;
 | 
			
		||||
import net.minecraft.world.entity.player.Inventory;
 | 
			
		||||
import net.minecraft.world.inventory.AbstractContainerMenu;
 | 
			
		||||
import net.minecraft.world.inventory.ContainerListener;
 | 
			
		||||
import net.minecraft.world.item.ItemStack;
 | 
			
		||||
import org.lwjgl.glfw.GLFW;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
 | 
			
		||||
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
 | 
			
		||||
 | 
			
		||||
@@ -23,40 +30,75 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA
 | 
			
		||||
 *
 | 
			
		||||
 * @see dan200.computercraft.client.render.PrintoutRenderer
 | 
			
		||||
 */
 | 
			
		||||
public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
			
		||||
    private final boolean book;
 | 
			
		||||
    private final int pages;
 | 
			
		||||
    private final TextBuffer[] text;
 | 
			
		||||
    private final TextBuffer[] colours;
 | 
			
		||||
    private int page;
 | 
			
		||||
public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu> implements ContainerListener {
 | 
			
		||||
    private PrintoutInfo printout = PrintoutInfo.DEFAULT;
 | 
			
		||||
    private int page = 0;
 | 
			
		||||
 | 
			
		||||
    public PrintoutScreen(HeldItemMenu container, Inventory player, Component title) {
 | 
			
		||||
    public PrintoutScreen(PrintoutMenu container, Inventory player, Component title) {
 | 
			
		||||
        super(container, player, title);
 | 
			
		||||
 | 
			
		||||
        imageHeight = Y_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        var text = PrintoutItem.getText(container.getStack());
 | 
			
		||||
        this.text = new TextBuffer[text.length];
 | 
			
		||||
        for (var i = 0; i < this.text.length; i++) this.text[i] = new TextBuffer(text[i]);
 | 
			
		||||
    private void setPrintout(ItemStack stack) {
 | 
			
		||||
        var text = PrintoutItem.getText(stack);
 | 
			
		||||
        var textBuffers = new TextBuffer[text.length];
 | 
			
		||||
        for (var i = 0; i < textBuffers.length; i++) textBuffers[i] = new TextBuffer(text[i]);
 | 
			
		||||
 | 
			
		||||
        var colours = PrintoutItem.getColours(container.getStack());
 | 
			
		||||
        this.colours = new TextBuffer[colours.length];
 | 
			
		||||
        for (var i = 0; i < this.colours.length; i++) this.colours[i] = new TextBuffer(colours[i]);
 | 
			
		||||
        var colours = PrintoutItem.getColours(stack);
 | 
			
		||||
        var colourBuffers = new TextBuffer[colours.length];
 | 
			
		||||
        for (var i = 0; i < colours.length; i++) colourBuffers[i] = new TextBuffer(colours[i]);
 | 
			
		||||
 | 
			
		||||
        page = 0;
 | 
			
		||||
        pages = Math.max(this.text.length / PrintoutItem.LINES_PER_PAGE, 1);
 | 
			
		||||
        book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
 | 
			
		||||
        var pages = Math.max(text.length / PrintoutItem.LINES_PER_PAGE, 1);
 | 
			
		||||
        var book = stack.is(ModRegistry.Items.PRINTED_BOOK.get());
 | 
			
		||||
 | 
			
		||||
        printout = new PrintoutInfo(pages, book, textBuffers, colourBuffers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void init() {
 | 
			
		||||
        super.init();
 | 
			
		||||
        menu.addSlotListener(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void removed() {
 | 
			
		||||
        menu.removeSlotListener(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void slotChanged(AbstractContainerMenu menu, int slot, ItemStack stack) {
 | 
			
		||||
        if (slot == 0) setPrintout(stack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void dataChanged(AbstractContainerMenu menu, int slot, int data) {
 | 
			
		||||
        if (slot == PrintoutMenu.DATA_CURRENT_PAGE) page = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setPage(int page) {
 | 
			
		||||
        this.page = page;
 | 
			
		||||
 | 
			
		||||
        var gameMode = Objects.requireNonNull(Objects.requireNonNull(minecraft).gameMode);
 | 
			
		||||
        gameMode.handleInventoryButtonClick(menu.containerId, PrintoutMenu.PAGE_BUTTON_OFFSET + page);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void previousPage() {
 | 
			
		||||
        if (page > 0) setPage(page - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void nextPage() {
 | 
			
		||||
        if (page < printout.pages() - 1) setPage(page + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean keyPressed(int key, int scancode, int modifiers) {
 | 
			
		||||
        if (key == GLFW.GLFW_KEY_RIGHT) {
 | 
			
		||||
            if (page < pages - 1) page++;
 | 
			
		||||
            nextPage();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key == GLFW.GLFW_KEY_LEFT) {
 | 
			
		||||
            if (page > 0) page--;
 | 
			
		||||
            previousPage();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -68,13 +110,13 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
			
		||||
        if (super.mouseScrolled(x, y, delta)) return true;
 | 
			
		||||
        if (delta < 0) {
 | 
			
		||||
            // Scroll up goes to the next page
 | 
			
		||||
            if (page < pages - 1) page++;
 | 
			
		||||
            nextPage();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (delta > 0) {
 | 
			
		||||
            // Scroll down goes to the previous page
 | 
			
		||||
            if (page > 0) page--;
 | 
			
		||||
            previousPage();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -85,8 +127,9 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
			
		||||
    protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
 | 
			
		||||
        // Draw the printout
 | 
			
		||||
        var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, printout.pages(), printout.book(), FULL_BRIGHT_LIGHTMAP);
 | 
			
		||||
        drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, printout.text(), printout.colour());
 | 
			
		||||
        renderer.endBatch();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -105,4 +148,18 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
 | 
			
		||||
    protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
 | 
			
		||||
        // Skip rendering labels.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    record PrintoutInfo(int pages, boolean book, TextBuffer[] text, TextBuffer[] colour) {
 | 
			
		||||
        public static final PrintoutInfo DEFAULT;
 | 
			
		||||
 | 
			
		||||
        static {
 | 
			
		||||
            var textLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
 | 
			
		||||
            Arrays.fill(textLines, new TextBuffer(" ".repeat(PrintoutItem.LINE_MAX_LENGTH)));
 | 
			
		||||
 | 
			
		||||
            var colourLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
 | 
			
		||||
            Arrays.fill(colourLines, new TextBuffer("f".repeat(PrintoutItem.LINE_MAX_LENGTH)));
 | 
			
		||||
 | 
			
		||||
            DEFAULT = new PrintoutInfo(1, false, textLines, colourLines);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.integration.jei;
 | 
			
		||||
package dan200.computercraft.client.integration.jei;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.integration.jei;
 | 
			
		||||
package dan200.computercraft.client.integration.jei;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
 | 
			
		||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
			
		||||
@@ -0,0 +1,117 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.client.model;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.vertex.PoseStack;
 | 
			
		||||
import com.mojang.blaze3d.vertex.VertexConsumer;
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.client.render.CustomLecternRenderer;
 | 
			
		||||
import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
			
		||||
import net.minecraft.client.model.geom.ModelPart;
 | 
			
		||||
import net.minecraft.client.model.geom.PartPose;
 | 
			
		||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
 | 
			
		||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
 | 
			
		||||
import net.minecraft.client.resources.model.Material;
 | 
			
		||||
import net.minecraft.resources.ResourceLocation;
 | 
			
		||||
import net.minecraft.world.inventory.InventoryMenu;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A model for {@linkplain PrintoutItem printouts} placed on a lectern.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
 | 
			
		||||
 * number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
 | 
			
		||||
 *
 | 
			
		||||
 * @see CustomLecternRenderer
 | 
			
		||||
 */
 | 
			
		||||
public class LecternPrintoutModel {
 | 
			
		||||
    public static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/printout");
 | 
			
		||||
    public static final Material MATERIAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE);
 | 
			
		||||
 | 
			
		||||
    private static final int TEXTURE_WIDTH = 32;
 | 
			
		||||
    private static final int TEXTURE_HEIGHT = 32;
 | 
			
		||||
 | 
			
		||||
    private static final String PAGE_1 = "page_1";
 | 
			
		||||
    private static final String PAGE_2 = "page_2";
 | 
			
		||||
    private static final String PAGE_3 = "page_3";
 | 
			
		||||
    private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
 | 
			
		||||
 | 
			
		||||
    private final ModelPart pagesRoot;
 | 
			
		||||
    private final ModelPart bookRoot;
 | 
			
		||||
    private final ModelPart[] pages;
 | 
			
		||||
 | 
			
		||||
    public LecternPrintoutModel() {
 | 
			
		||||
        pagesRoot = buildPages();
 | 
			
		||||
        bookRoot = buildBook();
 | 
			
		||||
        pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ModelPart buildPages() {
 | 
			
		||||
        var mesh = new MeshDefinition();
 | 
			
		||||
        var parts = mesh.getRoot();
 | 
			
		||||
        parts.addOrReplaceChild(
 | 
			
		||||
            PAGE_1,
 | 
			
		||||
            CubeListBuilder.create().texOffs(0, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
 | 
			
		||||
            PartPose.ZERO
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        parts.addOrReplaceChild(
 | 
			
		||||
            PAGE_2,
 | 
			
		||||
            CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
 | 
			
		||||
            PartPose.offsetAndRotation(-0.125f, 0, 1.5f, (float) Math.PI * (1f / 16), 0, 0)
 | 
			
		||||
        );
 | 
			
		||||
        parts.addOrReplaceChild(
 | 
			
		||||
            PAGE_3,
 | 
			
		||||
            CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
 | 
			
		||||
            PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ModelPart buildBook() {
 | 
			
		||||
        var mesh = new MeshDefinition();
 | 
			
		||||
        var parts = mesh.getRoot();
 | 
			
		||||
 | 
			
		||||
        parts.addOrReplaceChild(
 | 
			
		||||
            "spine",
 | 
			
		||||
            CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
 | 
			
		||||
            PartPose.ZERO
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        var angle = (float) Math.toRadians(5);
 | 
			
		||||
        parts.addOrReplaceChild(
 | 
			
		||||
            "left",
 | 
			
		||||
            CubeListBuilder.create()
 | 
			
		||||
                .texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
 | 
			
		||||
                .texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
 | 
			
		||||
            PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        parts.addOrReplaceChild(
 | 
			
		||||
            "right",
 | 
			
		||||
            CubeListBuilder.create()
 | 
			
		||||
                .texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
 | 
			
		||||
                .texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
 | 
			
		||||
            PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void renderBook(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay) {
 | 
			
		||||
        bookRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
 | 
			
		||||
        if (pageCount > pages.length) pageCount = pages.length;
 | 
			
		||||
        var i = 0;
 | 
			
		||||
        for (; i < pageCount; i++) pages[i].visible = true;
 | 
			
		||||
        for (; i < pages.length; i++) pages[i].visible = false;
 | 
			
		||||
 | 
			
		||||
        pagesRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -49,7 +49,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void handleMonitorData(BlockPos pos, TerminalState terminal) {
 | 
			
		||||
    public void handleMonitorData(BlockPos pos, @Nullable TerminalState terminal) {
 | 
			
		||||
        var player = Minecraft.getInstance().player;
 | 
			
		||||
        if (player == null) return;
 | 
			
		||||
 | 
			
		||||
@@ -67,7 +67,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal) {
 | 
			
		||||
    public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, @Nullable TerminalState terminal) {
 | 
			
		||||
        ClientPocketComputers.setState(instanceId, state, lightState, terminal);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ package dan200.computercraft.client.pocket;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ComputerState;
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ServerComputer;
 | 
			
		||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
 | 
			
		||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
 | 
			
		||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
 | 
			
		||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
 | 
			
		||||
@@ -44,16 +43,13 @@ public final class ClientPocketComputers {
 | 
			
		||||
     * @param lightColour  The current colour of the modem light.
 | 
			
		||||
     * @param terminalData The current terminal contents.
 | 
			
		||||
     */
 | 
			
		||||
    public static void setState(UUID instanceId, ComputerState state, int lightColour, TerminalState terminalData) {
 | 
			
		||||
    public static void setState(UUID instanceId, ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
 | 
			
		||||
        var computer = instances.get(instanceId);
 | 
			
		||||
        if (computer == null) {
 | 
			
		||||
            var terminal = new NetworkedTerminal(terminalData.width, terminalData.height, terminalData.colour);
 | 
			
		||||
            instances.put(instanceId, computer = new PocketComputerData(state, lightColour, terminal));
 | 
			
		||||
            instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData));
 | 
			
		||||
        } else {
 | 
			
		||||
            computer.setState(state, lightColour);
 | 
			
		||||
            computer.setState(state, lightColour, terminalData);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (terminalData.hasTerminal()) terminalData.apply(computer.getTerminal());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static @Nullable PocketComputerData get(ItemStack stack) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,11 @@ package dan200.computercraft.client.pocket;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.shared.computer.core.ComputerState;
 | 
			
		||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
 | 
			
		||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
 | 
			
		||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Clientside data about a pocket computer.
 | 
			
		||||
 * <p>
 | 
			
		||||
@@ -19,21 +22,21 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
 | 
			
		||||
 * @see PocketServerComputer The server-side pocket computer.
 | 
			
		||||
 */
 | 
			
		||||
public final class PocketComputerData {
 | 
			
		||||
    private final NetworkedTerminal terminal;
 | 
			
		||||
    private @Nullable NetworkedTerminal terminal;
 | 
			
		||||
    private ComputerState state;
 | 
			
		||||
    private int lightColour;
 | 
			
		||||
 | 
			
		||||
    PocketComputerData(ComputerState state, int lightColour, NetworkedTerminal terminal) {
 | 
			
		||||
    PocketComputerData(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
 | 
			
		||||
        this.state = state;
 | 
			
		||||
        this.lightColour = lightColour;
 | 
			
		||||
        this.terminal = terminal;
 | 
			
		||||
        if (terminalData != null) terminal = terminalData.create();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getLightState() {
 | 
			
		||||
        return state != ComputerState.OFF ? lightColour : -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NetworkedTerminal getTerminal() {
 | 
			
		||||
    public @Nullable NetworkedTerminal getTerminal() {
 | 
			
		||||
        return terminal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -41,8 +44,16 @@ public final class PocketComputerData {
 | 
			
		||||
        return state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setState(ComputerState state, int lightColour) {
 | 
			
		||||
    void setState(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
 | 
			
		||||
        this.state = state;
 | 
			
		||||
        this.lightColour = lightColour;
 | 
			
		||||
 | 
			
		||||
        if (terminalData != null) {
 | 
			
		||||
            if (terminal == null) {
 | 
			
		||||
                terminal = terminalData.create();
 | 
			
		||||
            } else {
 | 
			
		||||
                terminalData.apply(terminal);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,51 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.client.render;
 | 
			
		||||
 | 
			
		||||
import com.mojang.blaze3d.vertex.PoseStack;
 | 
			
		||||
import com.mojang.math.Axis;
 | 
			
		||||
import dan200.computercraft.client.model.LecternPrintoutModel;
 | 
			
		||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
 | 
			
		||||
import dan200.computercraft.shared.media.items.PrintoutItem;
 | 
			
		||||
import net.minecraft.client.renderer.MultiBufferSource;
 | 
			
		||||
import net.minecraft.client.renderer.RenderType;
 | 
			
		||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
 | 
			
		||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
 | 
			
		||||
import net.minecraft.client.renderer.blockentity.LecternRenderer;
 | 
			
		||||
import net.minecraft.world.level.block.LecternBlock;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This largely follows {@link LecternRenderer}, but with support for multiple types of item.
 | 
			
		||||
 */
 | 
			
		||||
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
 | 
			
		||||
    private final LecternPrintoutModel printoutModel;
 | 
			
		||||
 | 
			
		||||
    public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
 | 
			
		||||
        printoutModel = new LecternPrintoutModel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
 | 
			
		||||
        poseStack.pushPose();
 | 
			
		||||
        poseStack.translate(0.5f, 1.0625f, 0.5f);
 | 
			
		||||
        poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
 | 
			
		||||
        poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
 | 
			
		||||
        poseStack.translate(0, -0.125f, 0);
 | 
			
		||||
 | 
			
		||||
        var item = lectern.getItem();
 | 
			
		||||
        if (item.getItem() instanceof PrintoutItem printout) {
 | 
			
		||||
            var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
 | 
			
		||||
            if (printout.getType() == PrintoutItem.Type.BOOK) {
 | 
			
		||||
                printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
 | 
			
		||||
            } else {
 | 
			
		||||
                printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        poseStack.popPose();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -17,6 +17,8 @@ import net.minecraft.world.entity.player.Player;
 | 
			
		||||
import net.minecraft.world.item.ItemDisplayContext;
 | 
			
		||||
import net.minecraft.world.item.ItemStack;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A base class for items which have map-like rendering when held in the hand.
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,7 +37,7 @@ public abstract class ItemMapLikeRenderer {
 | 
			
		||||
    protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
 | 
			
		||||
 | 
			
		||||
    public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
 | 
			
		||||
        Player player = Minecraft.getInstance().player;
 | 
			
		||||
        Player player = Objects.requireNonNull(Minecraft.getInstance().player);
 | 
			
		||||
 | 
			
		||||
        transform.pushPose();
 | 
			
		||||
        if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
 | 
			
		||||
        // Render the label
 | 
			
		||||
        var label = turtle.getLabel();
 | 
			
		||||
        var hit = renderer.cameraHitResult;
 | 
			
		||||
        if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
 | 
			
		||||
        if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
 | 
			
		||||
            var mc = Minecraft.getInstance();
 | 
			
		||||
            var font = this.font;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
package dan200.computercraft.data.client;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.client.gui.GuiSprites;
 | 
			
		||||
import dan200.computercraft.client.model.LecternPrintoutModel;
 | 
			
		||||
import dan200.computercraft.data.DataProviders;
 | 
			
		||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
 | 
			
		||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
 | 
			
		||||
@@ -30,7 +31,8 @@ public final class ClientDataProviders {
 | 
			
		||||
        generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
 | 
			
		||||
            out.accept(new ResourceLocation("blocks"), List.of(
 | 
			
		||||
                new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
 | 
			
		||||
                new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
 | 
			
		||||
                new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()),
 | 
			
		||||
                new SingleFile(LecternPrintoutModel.TEXTURE, Optional.empty())
 | 
			
		||||
            ));
 | 
			
		||||
            out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
 | 
			
		||||
                // Buttons
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "variants": {
 | 
			
		||||
    "facing=east": {"model": "minecraft:block/lectern", "y": 90},
 | 
			
		||||
    "facing=north": {"model": "minecraft:block/lectern", "y": 0},
 | 
			
		||||
    "facing=south": {"model": "minecraft:block/lectern", "y": 180},
 | 
			
		||||
    "facing=west": {"model": "minecraft:block/lectern", "y": 270}
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user