mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-22 17:37:38 +00:00
Compare commits
50 Commits
v1.15.2-1.
...
v1.15.2-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1650b72edb | ||
![]() |
88f41314c7 | ||
![]() |
0b65d56ab0 | ||
![]() |
a256b70685 | ||
![]() |
f16d1499fe | ||
![]() |
79ca851e4f | ||
![]() |
d5c54d64a6 | ||
![]() |
5cfdd2339f | ||
![]() |
46c9840d00 | ||
![]() |
b3f2f14e96 | ||
![]() |
3ace49d27f | ||
![]() |
c489d4bc4f | ||
![]() |
2b029bd506 | ||
![]() |
2227845658 | ||
![]() |
a735f23e1f | ||
![]() |
de6f27ceaf | ||
![]() |
2fab1a3054 | ||
![]() |
d4745ae47e | ||
![]() |
dc21e2dbc9 | ||
![]() |
75dfa71275 | ||
![]() |
d71bf225cc | ||
![]() |
8644c4ebf6 | ||
![]() |
b323db30ee | ||
![]() |
53efd6b303 | ||
![]() |
97faa1b3bc | ||
![]() |
7404133d40 | ||
![]() |
e18e24407e | ||
![]() |
026afa7f73 | ||
![]() |
29cc5bb86b | ||
![]() |
aa9d3c8269 | ||
![]() |
f8074636bc | ||
![]() |
0f6db63020 | ||
![]() |
51fcd83b87 | ||
![]() |
c2190e1318 | ||
![]() |
c40a13558c | ||
![]() |
02695aea51 | ||
![]() |
d5be1aca0e | ||
![]() |
8ff8b78ed8 | ||
![]() |
7fc55aa9a0 | ||
![]() |
38335ca187 | ||
![]() |
e0e194099c | ||
![]() |
8063059764 | ||
![]() |
f96d923b2a | ||
![]() |
9142ccfc93 | ||
![]() |
9f7cc00fcb | ||
![]() |
b129ae627b | ||
![]() |
f9fb0619fa | ||
![]() |
58ea7a275e | ||
![]() |
8487a13764 | ||
![]() |
5d0daf9b2d |
12
.gitattributes
vendored
12
.gitattributes
vendored
@@ -1,3 +1,15 @@
|
||||
# Ignore changes in generated files
|
||||
src/generated/resources/data/** linguist-generated
|
||||
src/test/server-files/structures linguist-generated
|
||||
|
||||
* text=auto
|
||||
|
||||
*.gradle eol=lf diff=java
|
||||
*.java eol=lf diff=java
|
||||
*.kt eol=lf diff=java
|
||||
*.lua eol=lf
|
||||
*.md eol=lf diff=markdown
|
||||
*.txt eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jar binary
|
||||
|
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,16 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report some misbehaviour in the mod
|
||||
labels: bug
|
||||
---
|
||||
|
||||
<!--
|
||||
## Before reporting
|
||||
- Search for the bug on the issue tracker. Make sure to look at closed issues too!
|
||||
-->
|
||||
|
||||
## Useful information to include:
|
||||
- Minecraft version
|
||||
- CC: Tweaked version
|
||||
- Logs: These will be located in the `logs/` directory of your Minecraft instance. Please upload them as a gist or directly into this editor.
|
||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
33
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
33
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Bug report
|
||||
description: Report some misbehaviour in the mod
|
||||
labels: [ bug ]
|
||||
body:
|
||||
- type: dropdown
|
||||
id: mc-version
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
description: What version of Minecraft are you using?
|
||||
options:
|
||||
- 1.15.x
|
||||
- 1.16.x
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: "What version of CC: Tweaked are you using?"
|
||||
placeholder: "e.g. 1.96.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: details
|
||||
attributes:
|
||||
label: Details
|
||||
description: |
|
||||
Description of the bug. Please include the following:
|
||||
- Logs: These will be located in the `logs/` directory of your Minecraft
|
||||
instance. Please upload them as a gist or directly into this editor.
|
||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily,
|
||||
but often it's much more obscure. The more information I have to help
|
||||
reproduce it, the quicker it'll get fixed.
|
22
.gitpod.yml
Normal file
22
.gitpod.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
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 --config config/pre-commit/config.yml --allow-missing-config
|
||||
- name: Install npm packages
|
||||
init: npm ci
|
@@ -19,6 +19,10 @@ process. When building on Windows, Use `gradlew.bat` instead of `./gradlew`.
|
||||
- **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked`
|
||||
- **Setup Forge:** `./gradlew build`
|
||||
- **Run Minecraft:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
||||
- **Optionally:** For small PRs (especially those only touching Lua code), it may be easier to use GitPod, which
|
||||
provides a pre-configured environment: [](https://gitpod.io/#https://github.com/SquidDev-CC/CC-Tweaked/)
|
||||
|
||||
Do note you will need to download the mod after compiling to test.
|
||||
|
||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
||||
These commands may take a few minutes to run the first time, as the environment is set up, but should be much faster
|
||||
|
165
build.gradle
165
build.gradle
@@ -1,6 +1,5 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "forge"
|
||||
@@ -9,8 +8,7 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.google.code.gson:gson:2.8.1'
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:4.1.9'
|
||||
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:5.0.6'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +16,11 @@ plugins {
|
||||
id "checkstyle"
|
||||
id "jacoco"
|
||||
id "maven-publish"
|
||||
id "com.github.hierynomus.license" version "0.15.0"
|
||||
id "com.github.hierynomus.license" version "0.16.1"
|
||||
id "com.matthewprenger.cursegradle" version "1.4.0"
|
||||
id "com.github.breadmoirai.github-release" version "2.2.12"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.3.72"
|
||||
id "com.modrinth.minotaur" version "1.2.1"
|
||||
}
|
||||
|
||||
apply plugin: 'net.minecraftforge.gradle'
|
||||
@@ -31,15 +30,30 @@ version = mod_version
|
||||
group = "org.squiddev"
|
||||
archivesBaseName = "cc-tweaked-${mc_version}"
|
||||
|
||||
def javaVersion = JavaLanguageVersion.of(8)
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
languageVersion = javaVersion
|
||||
}
|
||||
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
// Tragically java.toolchain.languageVersion doesn't apply to ForgeGradle's
|
||||
// tasks, so we force all launchers to use the right Java version.
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
javaCompiler = javaToolchains.compilerFor {
|
||||
languageVersion = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaExec).configureEach {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
minecraft {
|
||||
runs {
|
||||
client {
|
||||
@@ -183,59 +197,6 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.*
|
||||
import java.util.zip.*
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonElement
|
||||
import com.hierynomus.gradle.license.tasks.LicenseCheck
|
||||
import com.hierynomus.gradle.license.tasks.LicenseFormat
|
||||
import proguard.gradle.ProGuardTask
|
||||
|
||||
task proguard(type: ProGuardTask, dependsOn: jar) {
|
||||
description "Removes unused shadowed classes from the jar"
|
||||
group "compact"
|
||||
|
||||
injars jar.archivePath
|
||||
outjars "${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar"
|
||||
|
||||
// Add the main runtime jar and all non-shadowed dependencies
|
||||
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
|
||||
libraryjars "${System.getProperty('java.home')}/lib/jce.jar"
|
||||
doFirst {
|
||||
sourceSets.main.compileClasspath
|
||||
.filter { !it.name.contains("Cobalt") }
|
||||
.each { libraryjars it }
|
||||
}
|
||||
|
||||
// We want to avoid as much obfuscation as possible. We're only doing this to shrink code size.
|
||||
dontobfuscate; dontoptimize; keepattributes; keepparameternames
|
||||
|
||||
// Proguard will remove directories by default, but that breaks JarMount.
|
||||
keepdirectories 'data/computercraft/lua**'
|
||||
|
||||
// Preserve ComputerCraft classes - we only want to strip shadowed files.
|
||||
keep 'class dan200.computercraft.** { *; }'
|
||||
|
||||
// LWJGL and Apache bundle Java 9 versions, which is great, but rather breaks Proguard
|
||||
dontwarn 'module-info'
|
||||
dontwarn 'org.apache.**,org.lwjgl.**'
|
||||
}
|
||||
|
||||
task proguardMove(dependsOn: proguard) {
|
||||
description "Replace the original jar with the minified version"
|
||||
group "compact"
|
||||
|
||||
doLast {
|
||||
Files.move(
|
||||
file("${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar").toPath(),
|
||||
file(jar.archivePath).toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", mod_version
|
||||
inputs.property "mcversion", mc_version
|
||||
@@ -253,6 +214,7 @@ processResources {
|
||||
e.printStackTrace()
|
||||
}
|
||||
inputs.property "commithash", hash
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include 'META-INF/mods.toml'
|
||||
@@ -269,49 +231,10 @@ processResources {
|
||||
}
|
||||
}
|
||||
|
||||
task compressJson(dependsOn: jar) {
|
||||
group "compact"
|
||||
description "Minifies all JSON files, stripping whitespace"
|
||||
|
||||
def jarPath = file(jar.archivePath)
|
||||
|
||||
def tempPath = File.createTempFile("input", ".jar", temporaryDir)
|
||||
tempPath.deleteOnExit()
|
||||
|
||||
def gson = new GsonBuilder().create()
|
||||
|
||||
doLast {
|
||||
// Copy over all files in the current jar to the new one, running json files from GSON. As pretty printing
|
||||
// is turned off, they should be minified.
|
||||
new ZipFile(jarPath).withCloseable { inJar ->
|
||||
tempPath.getParentFile().mkdirs()
|
||||
new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar ->
|
||||
inJar.entries().each { entry ->
|
||||
if(entry.directory) {
|
||||
outJar.putNextEntry(entry)
|
||||
} else if(!entry.name.endsWith(".json")) {
|
||||
outJar.putNextEntry(entry)
|
||||
inJar.getInputStream(entry).withCloseable { outJar << it }
|
||||
} else {
|
||||
ZipEntry newEntry = new ZipEntry(entry.name)
|
||||
newEntry.setTime(entry.time)
|
||||
outJar.putNextEntry(newEntry)
|
||||
|
||||
def element = inJar.getInputStream(entry).withCloseable { gson.fromJson(it.newReader("UTF8"), JsonElement.class) }
|
||||
outJar.write(gson.toJson(element).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// And replace the original jar again
|
||||
Files.move(tempPath.toPath(), jarPath.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
sourcesJar {
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
assemble.dependsOn compressJson
|
||||
|
||||
// Web tasks
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
@@ -383,6 +306,10 @@ jacocoTestReport {
|
||||
|
||||
check.dependsOn jacocoTestReport
|
||||
|
||||
|
||||
import com.hierynomus.gradle.license.tasks.LicenseCheck
|
||||
import com.hierynomus.gradle.license.tasks.LicenseFormat
|
||||
|
||||
license {
|
||||
mapping("java", "SLASHSTAR_STYLE")
|
||||
strictCheck true
|
||||
@@ -436,7 +363,7 @@ task setupServer(type: Copy) {
|
||||
tasks.register('testInGame', JavaExec.class).configure {
|
||||
it.group('test server')
|
||||
it.description("Runs tests on a temporary Minecraft server.")
|
||||
it.dependsOn(setupServer, 'prepareRunTestServer')
|
||||
it.dependsOn(setupServer, 'prepareRunTestServer', 'cleanTestInGame')
|
||||
|
||||
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
|
||||
// isn't created until the task is configured (which is no good for us).
|
||||
@@ -490,31 +417,31 @@ task checkRelease {
|
||||
description "Verifies that everything is ready for a release"
|
||||
|
||||
inputs.property "version", mod_version
|
||||
inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.txt")
|
||||
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
|
||||
inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.md")
|
||||
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
||||
|
||||
doLast {
|
||||
def ok = true
|
||||
|
||||
// Check we're targetting the current version
|
||||
def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt").readLines()
|
||||
def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md").readLines()
|
||||
if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
|
||||
ok = false
|
||||
project.logger.error("Expected `whatsnew.txt' to target $mod_version.")
|
||||
project.logger.error("Expected `whatsnew.md' to target $mod_version.")
|
||||
}
|
||||
|
||||
// Check "read more" exists and trim it
|
||||
def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' }
|
||||
if (idx == -1) {
|
||||
ok = false
|
||||
project.logger.error("Must mention the changelog in whatsnew.txt")
|
||||
project.logger.error("Must mention the changelog in whatsnew.md")
|
||||
} else {
|
||||
whatsnew = whatsnew.getAt(0 ..< idx)
|
||||
}
|
||||
|
||||
// Check whatsnew and changelog match.
|
||||
def versionChangelog = "# " + whatsnew.join("\n")
|
||||
def changelog = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/changelog.txt").getText()
|
||||
def changelog = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/changelog.md").getText()
|
||||
if (!changelog.startsWith(versionChangelog)) {
|
||||
ok = false
|
||||
project.logger.error("whatsnew and changelog are not in sync")
|
||||
@@ -538,6 +465,26 @@ curseforge {
|
||||
}
|
||||
}
|
||||
|
||||
import com.modrinth.minotaur.TaskModrinthUpload
|
||||
tasks.register('publishModrinth', TaskModrinthUpload.class).configure {
|
||||
dependsOn('assemble', 'reobfJar')
|
||||
onlyIf {
|
||||
project.hasProperty('modrinthApiKey')
|
||||
}
|
||||
|
||||
token = project.hasProperty('curseForgeApiKey')
|
||||
projectId = 'gu7yAYhd'
|
||||
versionNumber = project.mod_version
|
||||
uploadFile = jar
|
||||
addGameVersion(project.mc_version)
|
||||
addLoader('forge')
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata) {
|
||||
// We can't generate metadata as that includes Forge as a dependency.
|
||||
enabled = false
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
@@ -563,6 +510,8 @@ publishing {
|
||||
url = 'https://github.com/SquidDev-CC/CC-Tweaked/blob/mc-1.15.x/LICENSE'
|
||||
}
|
||||
}
|
||||
|
||||
withXml { asNode().remove(asNode().get("dependencies")) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -605,10 +554,10 @@ githubRelease {
|
||||
prerelease false
|
||||
}
|
||||
|
||||
def uploadTasks = ["publish", "curseforge", "githubRelease"]
|
||||
def uploadTasks = ["publish", "curseforge", "publishModrinth", "githubRelease"]
|
||||
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }
|
||||
|
||||
task uploadAll(dependsOn: uploadTasks) {
|
||||
group "upload"
|
||||
description "Uploads to all repositories (Maven, Curse, GitHub release)"
|
||||
description "Uploads to all repositories (Maven, Curse, Modrinth, GitHub release)"
|
||||
}
|
||||
|
@@ -7,9 +7,6 @@
|
||||
<suppress checks="StaticVariableName" files=".*[\\/]ComputerCraft.java" />
|
||||
<suppress checks="StaticVariableName" files=".*[\\/]ComputerCraftAPI.java" />
|
||||
|
||||
<!-- Do not check for missing package Javadoc. -->
|
||||
<suppress checks="JavadocStyle" files=".*[\\/]package-info.java" />
|
||||
|
||||
<!-- The commands API is documented in Lua. -->
|
||||
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
|
||||
</suppressions>
|
||||
|
8
config/gitpod/Dockerfile
Normal file
8
config/gitpod/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM gitpod/workspace-base
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN sudo apt-get -q update \
|
||||
&& sudo apt-get install -yq openjdk-8-jdk openjdk-16-jdk python3-pip npm \
|
||||
&& sudo pip3 install pre-commit \
|
||||
&& sudo update-java-alternatives --set java-1.8.0-openjdk-amd64
|
@@ -1,5 +1,5 @@
|
||||
# Mod properties
|
||||
mod_version=1.96.0
|
||||
mod_version=1.97.0
|
||||
|
||||
# Minecraft properties (update mods.toml when changing)
|
||||
mc_version=1.15.2
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -36,7 +36,6 @@ public final class ComputerCraft
|
||||
public static int maximumFilesOpen = 128;
|
||||
public static boolean disableLua51Features = false;
|
||||
public static String defaultComputerSettings = "";
|
||||
public static boolean debugEnable = true;
|
||||
public static boolean logComputerErrors = true;
|
||||
public static boolean commandRequireCreative = true;
|
||||
|
||||
|
@@ -32,7 +32,7 @@ public final class TransformedModel
|
||||
public TransformedModel( @Nonnull IBakedModel model )
|
||||
{
|
||||
this.model = Objects.requireNonNull( model );
|
||||
this.matrix = TransformationMatrix.identity();
|
||||
matrix = TransformationMatrix.identity();
|
||||
}
|
||||
|
||||
public static TransformedModel of( @Nonnull ModelResourceLocation location )
|
||||
|
@@ -30,7 +30,7 @@ public class FileOperationException extends IOException
|
||||
public FileOperationException( @Nonnull String message )
|
||||
{
|
||||
super( Objects.requireNonNull( message, "message cannot be null" ) );
|
||||
this.filename = null;
|
||||
filename = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@@ -19,14 +19,14 @@ public class LuaException extends Exception
|
||||
public LuaException( @Nullable String message )
|
||||
{
|
||||
super( message );
|
||||
this.hasLevel = false;
|
||||
this.level = 1;
|
||||
hasLevel = false;
|
||||
level = 1;
|
||||
}
|
||||
|
||||
public LuaException( @Nullable String message, int level )
|
||||
{
|
||||
super( message );
|
||||
this.hasLevel = true;
|
||||
hasLevel = true;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
|
@@ -30,14 +30,14 @@ public final class MethodResult
|
||||
|
||||
private MethodResult( Object[] arguments, ILuaCallback callback )
|
||||
{
|
||||
this.result = arguments;
|
||||
result = arguments;
|
||||
this.callback = callback;
|
||||
this.adjust = 0;
|
||||
adjust = 0;
|
||||
}
|
||||
|
||||
private MethodResult( Object[] arguments, ILuaCallback callback, int adjust )
|
||||
{
|
||||
this.result = arguments;
|
||||
result = arguments;
|
||||
this.callback = callback;
|
||||
this.adjust = adjust;
|
||||
}
|
||||
|
40
src/main/java/dan200/computercraft/client/ClientHooks.java
Normal file
40
src/main/java/dan200/computercraft/client/ClientHooks.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public class ClientHooks
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void onWorldUnload( WorldEvent.Unload event )
|
||||
{
|
||||
if( event.getWorld().isClientSide() )
|
||||
{
|
||||
ClientMonitor.destroyAll();
|
||||
SoundManager.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLogIn( ClientPlayerNetworkEvent.LoggedInEvent event )
|
||||
{
|
||||
ComputerCraft.clientComputerRegistry.reset();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLogOut( ClientPlayerNetworkEvent.LoggedOutEvent event )
|
||||
{
|
||||
ComputerCraft.clientComputerRegistry.reset();
|
||||
}
|
||||
}
|
@@ -6,31 +6,34 @@
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
|
||||
import dan200.computercraft.client.render.TurtleModelLoader;
|
||||
import dan200.computercraft.client.render.TurtlePlayerRenderer;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.media.items.ItemDisk;
|
||||
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel;
|
||||
import net.minecraft.client.gui.ScreenManager;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.RenderTypeLookup;
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
|
||||
import net.minecraft.inventory.container.PlayerContainer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ColorHandlerEvent;
|
||||
import net.minecraftforge.client.event.ModelBakeEvent;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.client.model.ModelLoader;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||
import net.minecraftforge.client.model.SimpleModelTransform;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
/**
|
||||
* Registers textures and models for items.
|
||||
@@ -39,6 +42,7 @@ import java.util.Map;
|
||||
public final class ClientRegistry
|
||||
{
|
||||
private static final String[] EXTRA_MODELS = new String[] {
|
||||
// Turtle upgrades
|
||||
"turtle_modem_normal_off_left",
|
||||
"turtle_modem_normal_on_left",
|
||||
"turtle_modem_normal_off_right",
|
||||
@@ -54,56 +58,20 @@ public final class ClientRegistry
|
||||
"turtle_speaker_upgrade_left",
|
||||
"turtle_speaker_upgrade_right",
|
||||
|
||||
// Turtle block renderer
|
||||
"turtle_colour",
|
||||
"turtle_elf_overlay",
|
||||
};
|
||||
|
||||
private static final String[] EXTRA_TEXTURES = new String[] {
|
||||
// TODO: Gather these automatically from the model. Sadly the model loader isn't available
|
||||
// when stitching textures.
|
||||
"block/turtle_colour",
|
||||
"block/turtle_elf_overlay",
|
||||
"block/turtle_crafty_face",
|
||||
"block/turtle_speaker_face",
|
||||
};
|
||||
|
||||
private ClientRegistry() {}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerModels( ModelRegistryEvent event )
|
||||
{
|
||||
ModelLoaderRegistry.registerLoader( new ResourceLocation( ComputerCraft.MOD_ID, "turtle" ), TurtleModelLoader.INSTANCE );
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent( TextureStitchEvent.Pre event )
|
||||
{
|
||||
if( !event.getMap().location().equals( PlayerContainer.BLOCK_ATLAS ) ) return;
|
||||
|
||||
for( String extra : EXTRA_TEXTURES )
|
||||
for( String model : EXTRA_MODELS )
|
||||
{
|
||||
event.addSprite( new ResourceLocation( ComputerCraft.MOD_ID, extra ) );
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onModelBakeEvent( ModelBakeEvent event )
|
||||
{
|
||||
// Load all extra models
|
||||
ModelLoader loader = event.getModelLoader();
|
||||
Map<ResourceLocation, IBakedModel> registry = event.getModelRegistry();
|
||||
|
||||
for( String modelName : EXTRA_MODELS )
|
||||
{
|
||||
ResourceLocation location = new ResourceLocation( ComputerCraft.MOD_ID, "item/" + modelName );
|
||||
IUnbakedModel model = loader.getModel( location );
|
||||
model.getMaterials( loader::getModel, new HashSet<>() );
|
||||
|
||||
IBakedModel baked = model.bake( loader, ModelLoader.defaultTextureGetter(), SimpleModelTransform.IDENTITY, location );
|
||||
if( baked != null )
|
||||
{
|
||||
registry.put( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, modelName ), "inventory" ), baked );
|
||||
}
|
||||
ModelLoader.addSpecialModel( new ModelResourceLocation( new ResourceLocation( ComputerCraft.MOD_ID, model ), "inventory" ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,4 +116,41 @@ public final class ClientRegistry
|
||||
Registry.ModBlocks.TURTLE_NORMAL.get(), Registry.ModBlocks.TURTLE_ADVANCED.get()
|
||||
);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void setupClient( FMLClientSetupEvent event )
|
||||
{
|
||||
registerContainers();
|
||||
|
||||
// While turtles themselves are not transparent, their upgrades may be.
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_NORMAL.get(), RenderType.translucent() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderType.translucent() );
|
||||
|
||||
// Monitors' textures have transparent fronts and so count as cutouts.
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_NORMAL.get(), RenderType.cutout() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderType.cutout() );
|
||||
|
||||
// Setup TESRs
|
||||
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new );
|
||||
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
|
||||
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
|
||||
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
|
||||
}
|
||||
|
||||
private static void registerContainers()
|
||||
{
|
||||
// My IDE doesn't think so, but we do actually need these generics.
|
||||
|
||||
ScreenManager.<ContainerComputer, GuiComputer<ContainerComputer>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
|
||||
ScreenManager.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
|
||||
ScreenManager.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::new );
|
||||
|
||||
ScreenManager.register( Registry.ModContainers.PRINTER.get(), GuiPrinter::new );
|
||||
ScreenManager.register( Registry.ModContainers.DISK_DRIVE.get(), GuiDiskDrive::new );
|
||||
ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new );
|
||||
|
||||
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView );
|
||||
}
|
||||
}
|
||||
|
84
src/main/java/dan200/computercraft/client/SoundManager.java
Normal file
84
src/main/java/dan200/computercraft/client/SoundManager.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.ISound;
|
||||
import net.minecraft.client.audio.ITickableSound;
|
||||
import net.minecraft.client.audio.LocatableSound;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SoundManager
|
||||
{
|
||||
private static final Map<UUID, MoveableSound> sounds = new HashMap<>();
|
||||
|
||||
public static void playSound( UUID source, Vec3d position, SoundEvent event, float volume, float pitch )
|
||||
{
|
||||
SoundHandler soundManager = Minecraft.getInstance().getSoundManager();
|
||||
|
||||
MoveableSound oldSound = sounds.get( source );
|
||||
if( oldSound != null ) soundManager.stop( oldSound );
|
||||
|
||||
MoveableSound newSound = new MoveableSound( event, position, volume, pitch );
|
||||
sounds.put( source, newSound );
|
||||
soundManager.play( newSound );
|
||||
}
|
||||
|
||||
public static void stopSound( UUID source )
|
||||
{
|
||||
ISound sound = sounds.remove( source );
|
||||
if( sound == null ) return;
|
||||
|
||||
Minecraft.getInstance().getSoundManager().stop( sound );
|
||||
}
|
||||
|
||||
public static void moveSound( UUID source, Vec3d position )
|
||||
{
|
||||
MoveableSound sound = sounds.get( source );
|
||||
if( sound != null ) sound.setPosition( position );
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
sounds.clear();
|
||||
}
|
||||
|
||||
private static class MoveableSound extends LocatableSound implements ITickableSound
|
||||
{
|
||||
protected MoveableSound( SoundEvent sound, Vec3d position, float volume, float pitch )
|
||||
{
|
||||
super( sound, SoundCategory.RECORDS );
|
||||
setPosition( position );
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
void setPosition( Vec3d position )
|
||||
{
|
||||
x = (float) position.x();
|
||||
y = (float) position.y();
|
||||
z = (float) position.z();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStopped()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
import net.minecraft.client.gui.widget.Widget;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
public abstract class ComputerScreenBase<T extends ContainerComputerBase> extends ContainerScreen<T>
|
||||
{
|
||||
protected WidgetTerminal terminal;
|
||||
protected final ClientComputer computer;
|
||||
protected final ComputerFamily family;
|
||||
|
||||
protected final int sidebarYOffset;
|
||||
|
||||
public ComputerScreenBase( T container, PlayerInventory player, ITextComponent title, int sidebarYOffset )
|
||||
{
|
||||
super( container, player, title );
|
||||
computer = (ClientComputer) container.getComputer();
|
||||
family = container.getFamily();
|
||||
this.sidebarYOffset = sidebarYOffset;
|
||||
}
|
||||
|
||||
protected abstract WidgetTerminal createTerminal();
|
||||
|
||||
@Override
|
||||
protected final void init()
|
||||
{
|
||||
super.init();
|
||||
minecraft.keyboardHandler.setSendRepeatsToGui( true );
|
||||
|
||||
terminal = addButton( createTerminal() );
|
||||
ComputerSidebar.addButtons( this, computer, this::addButton, leftPos, topPos + sidebarYOffset );
|
||||
setFocused( terminal );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removed()
|
||||
{
|
||||
super.removed();
|
||||
minecraft.keyboardHandler.setSendRepeatsToGui( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void tick()
|
||||
{
|
||||
super.tick();
|
||||
terminal.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean keyPressed( int key, int scancode, int modifiers )
|
||||
{
|
||||
// Forward the tab key to the terminal, rather than moving between controls.
|
||||
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal )
|
||||
{
|
||||
return getFocused().keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
return super.keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void render( int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderTooltip( mouseX, mouseY );
|
||||
|
||||
for( Widget widget : buttons )
|
||||
{
|
||||
if( widget.isHovered() ) widget.renderToolTip( mouseX, mouseY );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
|
||||
{
|
||||
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
|| super.mouseDragged( x, y, button, deltaX, deltaY );
|
||||
}
|
||||
}
|
@@ -7,43 +7,33 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
|
||||
public final class GuiComputer<T extends ContainerComputerBase> extends ContainerScreen<T>
|
||||
public final class GuiComputer<T extends ContainerComputerBase> extends ComputerScreenBase<T>
|
||||
{
|
||||
private final ComputerFamily family;
|
||||
private final ClientComputer computer;
|
||||
private final int termWidth;
|
||||
private final int termHeight;
|
||||
|
||||
private WidgetTerminal terminal;
|
||||
private WidgetWrapper terminalWrapper;
|
||||
|
||||
private GuiComputer(
|
||||
T container, PlayerInventory player, ITextComponent title, int termWidth, int termHeight
|
||||
)
|
||||
{
|
||||
super( container, player, title );
|
||||
family = container.getFamily();
|
||||
computer = (ClientComputer) container.getComputer();
|
||||
super( container, player, title, BORDER );
|
||||
this.termWidth = termWidth;
|
||||
this.termHeight = termHeight;
|
||||
terminal = null;
|
||||
|
||||
imageWidth = WidgetTerminal.getWidth( termWidth ) + BORDER * 2 + ComputerSidebar.WIDTH;
|
||||
imageHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2;
|
||||
}
|
||||
|
||||
public static GuiComputer<ContainerComputer> create( ContainerComputer container, PlayerInventory inventory, ITextComponent component )
|
||||
@@ -70,82 +60,21 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Containe
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void init()
|
||||
protected WidgetTerminal createTerminal()
|
||||
{
|
||||
minecraft.keyboardHandler.setSendRepeatsToGui( true );
|
||||
|
||||
int termPxWidth = termWidth * FixedWidthFontRenderer.FONT_WIDTH;
|
||||
int termPxHeight = termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
|
||||
imageWidth = termPxWidth + MARGIN * 2 + BORDER * 2;
|
||||
imageHeight = termPxHeight + MARGIN * 2 + BORDER * 2;
|
||||
|
||||
super.init();
|
||||
|
||||
terminal = new WidgetTerminal( minecraft, () -> computer, termWidth, termHeight, MARGIN, MARGIN, MARGIN, MARGIN );
|
||||
terminalWrapper = new WidgetWrapper( terminal, MARGIN + BORDER + leftPos, MARGIN + BORDER + topPos, termPxWidth, termPxHeight );
|
||||
|
||||
children.add( terminalWrapper );
|
||||
setFocused( terminalWrapper );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed()
|
||||
{
|
||||
super.removed();
|
||||
children.remove( terminal );
|
||||
terminal = null;
|
||||
minecraft.keyboardHandler.setSendRepeatsToGui( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
super.tick();
|
||||
terminal.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed( int key, int scancode, int modifiers )
|
||||
{
|
||||
// Forward the tab key to the terminal, rather than moving between controls.
|
||||
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
|
||||
{
|
||||
return getFocused().keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
return super.keyPressed( key, scancode, modifiers );
|
||||
return new WidgetTerminal( computer,
|
||||
leftPos + ComputerSidebar.WIDTH + BORDER, topPos + BORDER, termWidth, termHeight
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderBg( float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
// Draw terminal
|
||||
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
|
||||
|
||||
// Draw a border around the terminal
|
||||
RenderSystem.color4f( 1, 1, 1, 1 );
|
||||
minecraft.getTextureManager().bind( ComputerBorderRenderer.getTexture( family ) );
|
||||
ComputerBorderRenderer.render(
|
||||
terminalWrapper.getX() - MARGIN, terminalWrapper.getY() - MARGIN, getBlitOffset(),
|
||||
terminalWrapper.getWidth() + MARGIN * 2, terminalWrapper.getHeight() + MARGIN * 2
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderTooltip( mouseX, mouseY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
|
||||
{
|
||||
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
|| super.mouseDragged( x, y, button, deltaX, deltaY );
|
||||
ComputerBorderRenderer.render( terminal.x, terminal.y, getBlitOffset(), terminal.getWidth(), terminal.getHeight() );
|
||||
ComputerSidebar.renderBackground( leftPos, topPos + sidebarYOffset );
|
||||
}
|
||||
}
|
||||
|
@@ -7,132 +7,67 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
|
||||
import dan200.computercraft.client.gui.widgets.WidgetWrapper;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
|
||||
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
public class GuiTurtle extends ContainerScreen<ContainerTurtle>
|
||||
import static dan200.computercraft.shared.turtle.inventory.ContainerTurtle.*;
|
||||
|
||||
public class GuiTurtle extends ComputerScreenBase<ContainerTurtle>
|
||||
{
|
||||
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/turtle_normal.png" );
|
||||
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/turtle_advanced.png" );
|
||||
|
||||
private final ContainerTurtle container;
|
||||
private static final int TEX_WIDTH = 254;
|
||||
private static final int TEX_HEIGHT = 217;
|
||||
|
||||
private final ComputerFamily family;
|
||||
private final ClientComputer computer;
|
||||
|
||||
private WidgetTerminal terminal;
|
||||
private WidgetWrapper terminalWrapper;
|
||||
|
||||
public GuiTurtle( ContainerTurtle container, PlayerInventory player, ITextComponent title )
|
||||
{
|
||||
super( container, player, title );
|
||||
|
||||
this.container = container;
|
||||
super( container, player, title, BORDER );
|
||||
family = container.getFamily();
|
||||
computer = (ClientComputer) container.getComputer();
|
||||
|
||||
imageWidth = 254;
|
||||
imageHeight = 217;
|
||||
imageWidth = TEX_WIDTH + ComputerSidebar.WIDTH;
|
||||
imageHeight = TEX_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init()
|
||||
protected WidgetTerminal createTerminal()
|
||||
{
|
||||
super.init();
|
||||
minecraft.keyboardHandler.setSendRepeatsToGui( true );
|
||||
|
||||
int termPxWidth = ComputerCraft.turtleTermWidth * FixedWidthFontRenderer.FONT_WIDTH;
|
||||
int termPxHeight = ComputerCraft.turtleTermHeight * FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
|
||||
terminal = new WidgetTerminal(
|
||||
minecraft, () -> computer,
|
||||
ComputerCraft.turtleTermWidth,
|
||||
ComputerCraft.turtleTermHeight,
|
||||
2, 2, 2, 2
|
||||
return new WidgetTerminal(
|
||||
computer, leftPos + BORDER + ComputerSidebar.WIDTH, topPos + BORDER,
|
||||
ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight
|
||||
);
|
||||
terminalWrapper = new WidgetWrapper( terminal, 2 + 8 + leftPos, 2 + 8 + topPos, termPxWidth, termPxHeight );
|
||||
|
||||
children.add( terminalWrapper );
|
||||
setFocused( terminalWrapper );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed()
|
||||
{
|
||||
super.removed();
|
||||
children.remove( terminal );
|
||||
terminal = null;
|
||||
minecraft.keyboardHandler.setSendRepeatsToGui( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
super.tick();
|
||||
terminal.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed( int key, int scancode, int modifiers )
|
||||
{
|
||||
// Forward the tab key to the terminal, rather than moving between controls.
|
||||
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
|
||||
{
|
||||
return getFocused().keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
return super.keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
private void drawSelectionSlot( boolean advanced )
|
||||
{
|
||||
// Draw selection slot
|
||||
int slot = container.getSelectedSlot();
|
||||
if( slot >= 0 )
|
||||
{
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
int slotX = slot % 4;
|
||||
int slotY = slot / 4;
|
||||
minecraft.getTextureManager().bind( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
|
||||
blit( leftPos + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18, topPos + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18, 0, 217, 24, 24 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg( float partialTicks, int mouseX, int mouseY )
|
||||
{
|
||||
// Draw term
|
||||
boolean advanced = family == ComputerFamily.ADVANCED;
|
||||
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
|
||||
|
||||
// Draw border/inventory
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
minecraft.getTextureManager().bind( advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL );
|
||||
blit( leftPos, topPos, 0, 0, imageWidth, imageHeight );
|
||||
blit( leftPos + ComputerSidebar.WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT );
|
||||
|
||||
drawSelectionSlot( advanced );
|
||||
}
|
||||
minecraft.getTextureManager().bind( advanced ? ComputerBorderRenderer.BACKGROUND_ADVANCED : ComputerBorderRenderer.BACKGROUND_NORMAL );
|
||||
ComputerSidebar.renderBackground( leftPos, topPos + sidebarYOffset );
|
||||
|
||||
@Override
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
renderBackground();
|
||||
super.render( mouseX, mouseY, partialTicks );
|
||||
renderTooltip( mouseX, mouseY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
|
||||
{
|
||||
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|
||||
|| super.mouseDragged( x, y, button, deltaX, deltaY );
|
||||
int slot = getMenu().getSelectedSlot();
|
||||
if( slot >= 0 )
|
||||
{
|
||||
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
||||
int slotX = slot % 4;
|
||||
int slotY = slot / 4;
|
||||
blit(
|
||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18,
|
||||
0, 217, 24, 24
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.Widget;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Registers buttons to interact with a computer.
|
||||
*/
|
||||
public final class ComputerSidebar
|
||||
{
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/buttons.png" );
|
||||
|
||||
private static final int TEX_SIZE = 64;
|
||||
|
||||
private static final int ICON_WIDTH = 12;
|
||||
private static final int ICON_HEIGHT = 12;
|
||||
private static final int ICON_MARGIN = 2;
|
||||
|
||||
private static final int ICON_TEX_Y_DIFF = 14;
|
||||
|
||||
private static final int CORNERS_BORDER = 3;
|
||||
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
||||
|
||||
private static final int BUTTONS = 2;
|
||||
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
||||
public static final int WIDTH = 17;
|
||||
|
||||
private ComputerSidebar()
|
||||
{
|
||||
}
|
||||
|
||||
public static void addButtons( Screen screen, ClientComputer computer, Consumer<Widget> add, int x, int y )
|
||||
{
|
||||
x += CORNERS_BORDER + 1;
|
||||
y += CORNERS_BORDER + ICON_MARGIN;
|
||||
|
||||
add.accept( new DynamicImageButton(
|
||||
screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> computer.isOn() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
|
||||
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( computer ),
|
||||
() -> computer.isOn() ? Arrays.asList(
|
||||
I18n.get( "gui.computercraft.tooltip.turn_off" ),
|
||||
TextFormatting.GRAY + I18n.get( "gui.computercraft.tooltip.turn_off.key" )
|
||||
) : Arrays.asList(
|
||||
I18n.get( "gui.computercraft.tooltip.turn_on" ),
|
||||
TextFormatting.GRAY + I18n.get( "gui.computercraft.tooltip.turn_off.key" )
|
||||
)
|
||||
) );
|
||||
|
||||
y += ICON_HEIGHT + ICON_MARGIN * 2;
|
||||
|
||||
add.accept( new DynamicImageButton(
|
||||
screen, x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF,
|
||||
TEXTURE, TEX_SIZE, TEX_SIZE, b -> computer.queueEvent( "terminate" ),
|
||||
Arrays.asList(
|
||||
I18n.get( "gui.computercraft.tooltip.terminate" ),
|
||||
TextFormatting.GRAY + I18n.get( "gui.computercraft.tooltip.terminate.key" )
|
||||
)
|
||||
) );
|
||||
}
|
||||
|
||||
public static void renderBackground( int x, int y )
|
||||
{
|
||||
Screen.blit(
|
||||
x, y, 0, 102, WIDTH, FULL_BORDER,
|
||||
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
||||
);
|
||||
|
||||
Screen.blit(
|
||||
x, y + FULL_BORDER, WIDTH, HEIGHT - FULL_BORDER * 2,
|
||||
0, 107, WIDTH, 4,
|
||||
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
||||
);
|
||||
|
||||
Screen.blit(
|
||||
x, y + HEIGHT - FULL_BORDER, 0, 111, WIDTH, FULL_BORDER,
|
||||
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
private static void toggleComputer( ClientComputer computer )
|
||||
{
|
||||
if( computer.isOn() )
|
||||
{
|
||||
computer.shutdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.turnOn();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.button.Button;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.util.NonNullSupplier;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
/**
|
||||
* Version of {@link net.minecraft.client.gui.widget.button.ImageButton} which allows changing some properties
|
||||
* dynamically.
|
||||
*/
|
||||
public class DynamicImageButton extends Button
|
||||
{
|
||||
private final Screen screen;
|
||||
private final ResourceLocation texture;
|
||||
private final IntSupplier xTexStart;
|
||||
private final int yTexStart;
|
||||
private final int yDiffTex;
|
||||
private final int textureWidth;
|
||||
private final int textureHeight;
|
||||
private final NonNullSupplier<List<String>> tooltip;
|
||||
|
||||
public DynamicImageButton(
|
||||
Screen screen, int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex,
|
||||
ResourceLocation texture, int textureWidth, int textureHeight,
|
||||
IPressable onPress, List<String> tooltip
|
||||
)
|
||||
{
|
||||
this(
|
||||
screen, x, y, width, height, () -> xTexStart, yTexStart, yDiffTex,
|
||||
texture, textureWidth, textureHeight,
|
||||
onPress, () -> tooltip
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public DynamicImageButton(
|
||||
Screen screen, int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex,
|
||||
ResourceLocation texture, int textureWidth, int textureHeight,
|
||||
IPressable onPress, NonNullSupplier<List<String>> tooltip
|
||||
)
|
||||
{
|
||||
super( x, y, width, height, "", onPress );
|
||||
this.screen = screen;
|
||||
this.textureWidth = textureWidth;
|
||||
this.textureHeight = textureHeight;
|
||||
this.xTexStart = xTexStart;
|
||||
this.yTexStart = yTexStart;
|
||||
this.yDiffTex = yDiffTex;
|
||||
this.texture = texture;
|
||||
this.tooltip = tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderButton( int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
minecraft.getTextureManager().bind( texture );
|
||||
RenderSystem.disableDepthTest();
|
||||
|
||||
int yTex = yTexStart;
|
||||
if( isHovered() ) yTex += yDiffTex;
|
||||
|
||||
blit( x, y, xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight );
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
List<String> tooltip = this.tooltip.get();
|
||||
return tooltip.isEmpty() ? "" : tooltip.get( 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderToolTip( int mouseX, int mouseY )
|
||||
{
|
||||
List<String> tooltip = this.tooltip.get();
|
||||
if( !tooltip.isEmpty() ) screen.renderTooltip( tooltip, mouseX, mouseY );
|
||||
}
|
||||
}
|
@@ -8,29 +8,29 @@ package dan200.computercraft.client.gui.widgets;
|
||||
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.IGuiEventListener;
|
||||
import net.minecraft.client.gui.widget.Widget;
|
||||
import net.minecraft.util.SharedConstants;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.BitSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||
|
||||
public class WidgetTerminal implements IGuiEventListener
|
||||
public class WidgetTerminal extends Widget
|
||||
{
|
||||
private static final float TERMINATE_TIME = 0.5f;
|
||||
|
||||
private final Minecraft client;
|
||||
private final ClientComputer computer;
|
||||
|
||||
private boolean focused;
|
||||
|
||||
private final Supplier<ClientComputer> computer;
|
||||
private final int termWidth;
|
||||
private final int termHeight;
|
||||
// The positions of the actual terminal
|
||||
private final int innerX;
|
||||
private final int innerY;
|
||||
private final int innerWidth;
|
||||
private final int innerHeight;
|
||||
|
||||
private float terminateTimer = -1;
|
||||
private float rebootTimer = -1;
|
||||
@@ -40,23 +40,18 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
private int lastMouseX = -1;
|
||||
private int lastMouseY = -1;
|
||||
|
||||
private final int leftMargin;
|
||||
private final int rightMargin;
|
||||
private final int topMargin;
|
||||
private final int bottomMargin;
|
||||
|
||||
private final BitSet keysDown = new BitSet( 256 );
|
||||
|
||||
public WidgetTerminal( Minecraft client, Supplier<ClientComputer> computer, int termWidth, int termHeight, int leftMargin, int rightMargin, int topMargin, int bottomMargin )
|
||||
public WidgetTerminal( @Nonnull ClientComputer computer, int x, int y, int termWidth, int termHeight )
|
||||
{
|
||||
this.client = client;
|
||||
super( x, y, termWidth * FONT_WIDTH + MARGIN * 2, termHeight * FONT_HEIGHT + MARGIN * 2, "" );
|
||||
|
||||
this.computer = computer;
|
||||
this.termWidth = termWidth;
|
||||
this.termHeight = termHeight;
|
||||
this.leftMargin = leftMargin;
|
||||
this.rightMargin = rightMargin;
|
||||
this.topMargin = topMargin;
|
||||
this.bottomMargin = bottomMargin;
|
||||
|
||||
innerX = x + MARGIN;
|
||||
innerY = y + MARGIN;
|
||||
innerWidth = termWidth * FONT_WIDTH;
|
||||
innerHeight = termHeight * FONT_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +60,7 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range
|
||||
{
|
||||
// Queue the "char" event
|
||||
queueEvent( "char", Character.toString( ch ) );
|
||||
computer.queueEvent( "char", new Object[] { Character.toString( ch ) } );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -91,7 +86,7 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
|
||||
case GLFW.GLFW_KEY_V:
|
||||
// Ctrl+V for paste
|
||||
String clipboard = client.keyboardHandler.getClipboard();
|
||||
String clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
||||
if( clipboard != null )
|
||||
{
|
||||
// Clip to the first occurrence of \r or \n
|
||||
@@ -116,7 +111,7 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
{
|
||||
// Clip to 512 characters and queue the event
|
||||
if( clipboard.length() > 512 ) clipboard = clipboard.substring( 0, 512 );
|
||||
queueEvent( "paste", clipboard );
|
||||
computer.queueEvent( "paste", new Object[] { clipboard } );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -129,8 +124,7 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
// Queue the "key" event and add to the down set
|
||||
boolean repeat = keysDown.get( key );
|
||||
keysDown.set( key );
|
||||
IComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.keyDown( key, repeat );
|
||||
computer.keyDown( key, repeat );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -143,8 +137,7 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
if( key >= 0 && keysDown.get( key ) )
|
||||
{
|
||||
keysDown.set( key, false );
|
||||
IComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.keyUp( key );
|
||||
computer.keyUp( key );
|
||||
}
|
||||
|
||||
switch( key )
|
||||
@@ -170,14 +163,14 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
@Override
|
||||
public boolean mouseClicked( double mouseX, double mouseY, int button )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
|
||||
if( !inTermRegion( mouseX, mouseY ) ) return false;
|
||||
if( !computer.isColour() || button < 0 || button > 2 ) return false;
|
||||
|
||||
Terminal term = computer.getTerminal();
|
||||
if( term != null )
|
||||
{
|
||||
int charX = (int) (mouseX / FONT_WIDTH);
|
||||
int charY = (int) (mouseY / FONT_HEIGHT);
|
||||
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
|
||||
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
|
||||
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
|
||||
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
|
||||
|
||||
@@ -194,14 +187,14 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
@Override
|
||||
public boolean mouseReleased( double mouseX, double mouseY, int button )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
|
||||
if( !inTermRegion( mouseX, mouseY ) ) return false;
|
||||
if( !computer.isColour() || button < 0 || button > 2 ) return false;
|
||||
|
||||
Terminal term = computer.getTerminal();
|
||||
if( term != null )
|
||||
{
|
||||
int charX = (int) (mouseX / FONT_WIDTH);
|
||||
int charY = (int) (mouseY / FONT_HEIGHT);
|
||||
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
|
||||
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
|
||||
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
|
||||
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
|
||||
|
||||
@@ -221,14 +214,14 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
@Override
|
||||
public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
|
||||
if( !inTermRegion( mouseX, mouseY ) ) return false;
|
||||
if( !computer.isColour() || button < 0 || button > 2 ) return false;
|
||||
|
||||
Terminal term = computer.getTerminal();
|
||||
if( term != null )
|
||||
{
|
||||
int charX = (int) (mouseX / FONT_WIDTH);
|
||||
int charY = (int) (mouseY / FONT_HEIGHT);
|
||||
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
|
||||
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
|
||||
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
|
||||
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
|
||||
|
||||
@@ -246,14 +239,14 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
@Override
|
||||
public boolean mouseScrolled( double mouseX, double mouseY, double delta )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer == null || !computer.isColour() || delta == 0 ) return false;
|
||||
if( !inTermRegion( mouseX, mouseY ) ) return false;
|
||||
if( !computer.isColour() || delta == 0 ) return false;
|
||||
|
||||
Terminal term = computer.getTerminal();
|
||||
if( term != null )
|
||||
{
|
||||
int charX = (int) (mouseX / FONT_WIDTH);
|
||||
int charY = (int) (mouseY / FONT_HEIGHT);
|
||||
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
|
||||
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
|
||||
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
|
||||
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
|
||||
|
||||
@@ -266,89 +259,74 @@ public class WidgetTerminal implements IGuiEventListener
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean inTermRegion( double mouseX, double mouseY )
|
||||
{
|
||||
return active && visible && mouseX >= innerX && mouseY >= innerY && mouseX < innerX + innerWidth && mouseY < innerY + innerHeight;
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
if( terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME )
|
||||
{
|
||||
queueEvent( "terminate" );
|
||||
computer.queueEvent( "terminate" );
|
||||
}
|
||||
|
||||
if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.shutdown();
|
||||
computer.shutdown();
|
||||
}
|
||||
|
||||
if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.reboot();
|
||||
computer.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeFocus( boolean reversed )
|
||||
public void onFocusedChanged( boolean focused )
|
||||
{
|
||||
if( focused )
|
||||
if( !focused )
|
||||
{
|
||||
// When blurring, we should make all keys go up
|
||||
for( int key = 0; key < keysDown.size(); key++ )
|
||||
{
|
||||
if( keysDown.get( key ) ) queueEvent( "key_up", key );
|
||||
if( keysDown.get( key ) ) computer.keyUp( key );
|
||||
}
|
||||
keysDown.clear();
|
||||
|
||||
// When blurring, we should make the last mouse button go up
|
||||
if( lastMouseButton > 0 )
|
||||
{
|
||||
IComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 );
|
||||
computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 );
|
||||
lastMouseButton = -1;
|
||||
}
|
||||
|
||||
shutdownTimer = terminateTimer = rebootTimer = -1;
|
||||
}
|
||||
focused = !focused;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void draw( int originX, int originY )
|
||||
{
|
||||
synchronized( computer )
|
||||
{
|
||||
// Draw the screen contents
|
||||
ClientComputer computer = this.computer.get();
|
||||
Terminal terminal = computer != null ? computer.getTerminal() : null;
|
||||
if( terminal != null )
|
||||
{
|
||||
FixedWidthFontRenderer.drawTerminal( originX, originY, terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin );
|
||||
}
|
||||
else
|
||||
{
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(
|
||||
originX - leftMargin, originY - rightMargin,
|
||||
termWidth * FONT_WIDTH + leftMargin + rightMargin,
|
||||
termHeight * FONT_HEIGHT + topMargin + bottomMargin
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void queueEvent( String event )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.queueEvent( event );
|
||||
}
|
||||
|
||||
private void queueEvent( String event, Object... args )
|
||||
{
|
||||
ClientComputer computer = this.computer.get();
|
||||
if( computer != null ) computer.queueEvent( event, args );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMouseOver( double x, double y )
|
||||
public void render( int mouseX, int mouseY, float partialTicks )
|
||||
{
|
||||
return true;
|
||||
// Draw the screen contents
|
||||
Terminal terminal = computer.getTerminal();
|
||||
if( terminal != null )
|
||||
{
|
||||
FixedWidthFontRenderer.drawTerminal( innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
|
||||
}
|
||||
else
|
||||
{
|
||||
FixedWidthFontRenderer.drawEmptyTerminal( x, y, width, height );
|
||||
}
|
||||
}
|
||||
|
||||
public static int getWidth( int termWidth )
|
||||
{
|
||||
return termWidth * FONT_WIDTH + MARGIN * 2;
|
||||
}
|
||||
|
||||
public static int getHeight( int termHeight )
|
||||
{
|
||||
return termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||
}
|
||||
}
|
||||
|
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import net.minecraft.client.gui.IGuiEventListener;
|
||||
|
||||
public class WidgetWrapper implements IGuiEventListener
|
||||
{
|
||||
private final IGuiEventListener listener;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public WidgetWrapper( IGuiEventListener listener, int x, int y, int width, int height )
|
||||
{
|
||||
this.listener = listener;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeFocus( boolean b )
|
||||
{
|
||||
return listener.changeFocus( b );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked( double x, double y, int button )
|
||||
{
|
||||
double dx = x - this.x, dy = y - this.y;
|
||||
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseClicked( dx, dy, button );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased( double x, double y, int button )
|
||||
{
|
||||
double dx = x - this.x, dy = y - this.y;
|
||||
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseReleased( dx, dy, button );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
|
||||
{
|
||||
double dx = x - this.x, dy = y - this.y;
|
||||
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseDragged( dx, dy, button, deltaX, deltaY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled( double x, double y, double delta )
|
||||
{
|
||||
double dx = x - this.x, dy = y - this.y;
|
||||
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseScrolled( dx, dy, delta );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed( int key, int scancode, int modifiers )
|
||||
{
|
||||
return listener.keyPressed( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased( int key, int scancode, int modifiers )
|
||||
{
|
||||
return listener.keyReleased( key, scancode, modifiers );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean charTyped( char character, int modifiers )
|
||||
{
|
||||
return listener.charTyped( character, modifiers );
|
||||
}
|
||||
|
||||
public int getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMouseOver( double x, double y )
|
||||
{
|
||||
double dx = x - this.x, dy = y - this.y;
|
||||
return dx >= 0 && dx < width && dy >= 0 && dy < height;
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.client.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
|
||||
import dan200.computercraft.client.render.TurtlePlayerRenderer;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
import net.minecraft.client.gui.ScreenManager;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.RenderTypeLookup;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD )
|
||||
public final class ComputerCraftProxyClient
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void setupClient( FMLClientSetupEvent event )
|
||||
{
|
||||
registerContainers();
|
||||
|
||||
// While turtles themselves are not transparent, their upgrades may be.
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_NORMAL.get(), RenderType.translucent() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderType.translucent() );
|
||||
|
||||
// Monitors' textures have transparent fronts and so count as cutouts.
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_NORMAL.get(), RenderType.cutout() );
|
||||
RenderTypeLookup.setRenderLayer( Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderType.cutout() );
|
||||
|
||||
// Setup TESRs
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
|
||||
}
|
||||
|
||||
private static void registerContainers()
|
||||
{
|
||||
// My IDE doesn't think so, but we do actually need these generics.
|
||||
|
||||
ScreenManager.<ContainerComputer, GuiComputer<ContainerComputer>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
|
||||
ScreenManager.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
|
||||
ScreenManager.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::new );
|
||||
|
||||
ScreenManager.register( Registry.ModContainers.PRINTER.get(), GuiPrinter::new );
|
||||
ScreenManager.register( Registry.ModContainers.DISK_DRIVE.get(), GuiDiskDrive::new );
|
||||
ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new );
|
||||
|
||||
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView );
|
||||
}
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public static final class ForgeHandlers
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void onWorldUnload( WorldEvent.Unload event )
|
||||
{
|
||||
if( event.getWorld().isClientSide() )
|
||||
{
|
||||
ClientMonitor.destroyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -52,7 +52,8 @@ public class ComputerBorderRenderer
|
||||
|
||||
public static final int LIGHT_HEIGHT = 8;
|
||||
|
||||
private static final float TEX_SCALE = 1 / 256.0f;
|
||||
public static final int TEX_SIZE = 256;
|
||||
private static final float TEX_SCALE = 1 / (float) TEX_SIZE;
|
||||
|
||||
private final Matrix4f transform;
|
||||
private final IVertexBuilder builder;
|
||||
|
@@ -37,7 +37,7 @@ public class HTTPAPI implements ILuaAPI
|
||||
{
|
||||
private final IAPIEnvironment apiEnvironment;
|
||||
|
||||
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>();
|
||||
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>( ResourceGroup.DEFAULT );
|
||||
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>( () -> ComputerCraft.httpMaxRequests );
|
||||
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>( () -> ComputerCraft.httpMaxWebsockets );
|
||||
|
||||
@@ -127,7 +127,10 @@ public class HTTPAPI implements ILuaAPI
|
||||
HttpRequest request = new HttpRequest( requests, apiEnvironment, address, postString, headers, binary, redirect );
|
||||
|
||||
// Make the request
|
||||
request.queue( r -> r.request( uri, httpMethod ) );
|
||||
if( !request.queue( r -> r.request( uri, httpMethod ) ) )
|
||||
{
|
||||
throw new LuaException( "Too many ongoing HTTP requests" );
|
||||
}
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
@@ -138,12 +141,15 @@ public class HTTPAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final Object[] checkURL( String address )
|
||||
public final Object[] checkURL( String address ) throws LuaException
|
||||
{
|
||||
try
|
||||
{
|
||||
URI uri = HttpRequest.checkUri( address );
|
||||
new CheckUrl( checkUrls, apiEnvironment, address, uri ).queue( CheckUrl::run );
|
||||
if( !new CheckUrl( checkUrls, apiEnvironment, address, uri ).queue( CheckUrl::run ) )
|
||||
{
|
||||
throw new LuaException( "Too many ongoing checkUrl calls" );
|
||||
}
|
||||
|
||||
return new Object[] { true };
|
||||
}
|
||||
|
@@ -11,12 +11,19 @@ import dan200.computercraft.core.apis.http.options.AddressRule;
|
||||
import dan200.computercraft.core.apis.http.options.Options;
|
||||
import dan200.computercraft.shared.util.ThreadUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ConnectTimeoutException;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
@@ -161,4 +168,29 @@ public final class NetworkUtils
|
||||
buffer.readBytes( bytes );
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String toFriendlyError( @Nonnull Throwable cause )
|
||||
{
|
||||
if( cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException )
|
||||
{
|
||||
return cause.getMessage();
|
||||
}
|
||||
else if( cause instanceof TooLongFrameException )
|
||||
{
|
||||
return "Message is too large";
|
||||
}
|
||||
else if( cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException )
|
||||
{
|
||||
return "Timed out";
|
||||
}
|
||||
else if( cause instanceof SSLHandshakeException || (cause instanceof DecoderException && cause.getCause() instanceof SSLHandshakeException) )
|
||||
{
|
||||
return "Could not create a secure connection";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Could not connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -97,7 +97,7 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
|
||||
tryClose();
|
||||
}
|
||||
|
||||
public boolean queue( Consumer<T> task )
|
||||
public final boolean queue( Consumer<T> task )
|
||||
{
|
||||
@SuppressWarnings( "unchecked" )
|
||||
T thisT = (T) this;
|
||||
|
@@ -18,6 +18,9 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public class ResourceGroup<T extends Resource<T>>
|
||||
{
|
||||
public static final int DEFAULT_LIMIT = 512;
|
||||
public static final IntSupplier DEFAULT = () -> DEFAULT_LIMIT;
|
||||
|
||||
private static final IntSupplier ZERO = () -> 0;
|
||||
|
||||
final IntSupplier limit;
|
||||
|
@@ -38,8 +38,10 @@ public class ResourceQueue<T extends Resource<T>> extends ResourceGroup<T>
|
||||
public synchronized boolean queue( Supplier<T> resource )
|
||||
{
|
||||
if( !active ) return false;
|
||||
if( super.queue( resource ) ) return true;
|
||||
if( pending.size() > DEFAULT_LIMIT ) return false;
|
||||
|
||||
if( !super.queue( resource ) ) pending.add( resource );
|
||||
pending.add( resource );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -19,13 +19,10 @@ import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ConnectTimeoutException;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -190,7 +187,7 @@ public class HttpRequest extends Resource<HttpRequest>
|
||||
.remoteAddress( socketAddress )
|
||||
.connect()
|
||||
.addListener( c -> {
|
||||
if( !c.isSuccess() ) failure( c.cause() );
|
||||
if( !c.isSuccess() ) failure( NetworkUtils.toFriendlyError( c.cause() ) );
|
||||
} );
|
||||
|
||||
// Do an additional check for cancellation
|
||||
@@ -202,7 +199,7 @@ public class HttpRequest extends Resource<HttpRequest>
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
failure( "Could not connect" );
|
||||
failure( NetworkUtils.toFriendlyError( e ) );
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error in HTTP request", e );
|
||||
}
|
||||
}
|
||||
@@ -212,29 +209,6 @@ public class HttpRequest extends Resource<HttpRequest>
|
||||
if( tryClose() ) environment.queueEvent( FAILURE_EVENT, address, message );
|
||||
}
|
||||
|
||||
void failure( Throwable cause )
|
||||
{
|
||||
String message;
|
||||
if( cause instanceof HTTPRequestException )
|
||||
{
|
||||
message = cause.getMessage();
|
||||
}
|
||||
else if( cause instanceof TooLongFrameException )
|
||||
{
|
||||
message = "Response is too large";
|
||||
}
|
||||
else if( cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException )
|
||||
{
|
||||
message = "Timed out";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "Could not connect";
|
||||
}
|
||||
|
||||
failure( message );
|
||||
}
|
||||
|
||||
void failure( String message, HttpResponseHandle object )
|
||||
{
|
||||
if( tryClose() ) environment.queueEvent( FAILURE_EVENT, address, message, object );
|
||||
|
@@ -183,7 +183,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
||||
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause )
|
||||
{
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error handling HTTP response", cause );
|
||||
request.failure( cause );
|
||||
request.failure( NetworkUtils.toFriendlyError( cause ) );
|
||||
}
|
||||
|
||||
private void sendResponse()
|
||||
|
@@ -22,6 +22,7 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
|
||||
@@ -150,8 +151,9 @@ public class Websocket extends Resource<Websocket>
|
||||
p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) );
|
||||
}
|
||||
|
||||
String subprotocol = headers.get( HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL );
|
||||
WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
|
||||
uri, WebSocketVersion.V13, null, true, headers,
|
||||
uri, WebSocketVersion.V13, subprotocol, true, headers,
|
||||
options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage
|
||||
);
|
||||
|
||||
@@ -166,7 +168,7 @@ public class Websocket extends Resource<Websocket>
|
||||
.remoteAddress( socketAddress )
|
||||
.connect()
|
||||
.addListener( c -> {
|
||||
if( !c.isSuccess() ) failure( c.cause().getMessage() );
|
||||
if( !c.isSuccess() ) failure( NetworkUtils.toFriendlyError( c.cause() ) );
|
||||
} );
|
||||
|
||||
// Do an additional check for cancellation
|
||||
@@ -178,7 +180,7 @@ public class Websocket extends Resource<Websocket>
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
failure( "Could not connect" );
|
||||
failure( NetworkUtils.toFriendlyError( e ) );
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error in websocket", e );
|
||||
}
|
||||
}
|
||||
|
@@ -5,17 +5,13 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis.http.websocket;
|
||||
|
||||
import dan200.computercraft.core.apis.http.HTTPRequestException;
|
||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||
import dan200.computercraft.core.apis.http.options.Options;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ConnectTimeoutException;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.websocketx.*;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
|
||||
@@ -97,24 +93,7 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object>
|
||||
{
|
||||
ctx.close();
|
||||
|
||||
String message;
|
||||
if( cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException )
|
||||
{
|
||||
message = cause.getMessage();
|
||||
}
|
||||
else if( cause instanceof TooLongFrameException )
|
||||
{
|
||||
message = "Message is too large";
|
||||
}
|
||||
else if( cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException )
|
||||
{
|
||||
message = "Timed out";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "Could not connect";
|
||||
}
|
||||
|
||||
String message = NetworkUtils.toFriendlyError( cause );
|
||||
if( handshaker.isHandshakeComplete() )
|
||||
{
|
||||
websocket.close( -1, message );
|
||||
|
@@ -11,7 +11,10 @@ import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
@@ -63,7 +66,7 @@ public final class Generator<T>
|
||||
{
|
||||
this.base = base;
|
||||
this.context = context;
|
||||
this.interfaces = new String[] { Type.getInternalName( base ) };
|
||||
interfaces = new String[] { Type.getInternalName( base ) };
|
||||
this.wrap = wrap;
|
||||
|
||||
StringBuilder methodDesc = new StringBuilder().append( "(Ljava/lang/Object;" );
|
||||
|
@@ -251,6 +251,20 @@ final class ComputerExecutor
|
||||
* and then schedule a shutdown.
|
||||
*/
|
||||
void abort()
|
||||
{
|
||||
immediateFail( StateCommand.ABORT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort this whole computer due to an internal error. This will immediately destroy the Lua machine,
|
||||
* and then schedule a shutdown.
|
||||
*/
|
||||
void fastFail()
|
||||
{
|
||||
immediateFail( StateCommand.ERROR );
|
||||
}
|
||||
|
||||
private void immediateFail( StateCommand command )
|
||||
{
|
||||
ILuaMachine machine = this.machine;
|
||||
if( machine != null ) machine.close();
|
||||
@@ -258,7 +272,7 @@ final class ComputerExecutor
|
||||
synchronized( queueLock )
|
||||
{
|
||||
if( closed ) return;
|
||||
command = StateCommand.ABORT;
|
||||
this.command = command;
|
||||
if( isOn ) enqueue();
|
||||
}
|
||||
}
|
||||
@@ -596,6 +610,12 @@ final class ComputerExecutor
|
||||
displayFailure( "Error running computer", TimeoutState.ABORT_MESSAGE );
|
||||
shutdown();
|
||||
break;
|
||||
|
||||
case ERROR:
|
||||
if( !isOn ) return;
|
||||
displayFailure( "Error running computer", "An internal error occurred, see logs." );
|
||||
shutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( event != null )
|
||||
@@ -644,6 +664,7 @@ final class ComputerExecutor
|
||||
SHUTDOWN,
|
||||
REBOOT,
|
||||
ABORT,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
private static final class Event
|
||||
|
@@ -506,6 +506,8 @@ public final class ComputerThread
|
||||
catch( Exception | LinkageError | VirtualMachineError e )
|
||||
{
|
||||
ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e );
|
||||
// Tear down the computer immediately. There's no guarantee it's well behaved from now on.
|
||||
executor.fastFail();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -10,7 +10,6 @@ import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.ObjectSource;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.core.tracking.TrackingField;
|
||||
@@ -53,7 +52,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
private final Computer computer;
|
||||
private final TimeoutState timeout;
|
||||
private final TimeoutDebugHandler debug;
|
||||
private final ILuaContext context = new CobaltLuaContext();
|
||||
private final ILuaContext context;
|
||||
|
||||
private LuaState state;
|
||||
private LuaTable globals;
|
||||
@@ -65,6 +64,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
{
|
||||
this.computer = computer;
|
||||
this.timeout = timeout;
|
||||
context = new LuaContext( computer );
|
||||
debug = new TimeoutDebugHandler();
|
||||
|
||||
// Create an environment to run in
|
||||
@@ -97,7 +97,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
globals.load( state, new CoroutineLib() );
|
||||
globals.load( state, new Bit32Lib() );
|
||||
globals.load( state, new Utf8Lib() );
|
||||
if( ComputerCraft.debugEnable ) globals.load( state, new DebugLib() );
|
||||
globals.load( state, new DebugLib() );
|
||||
|
||||
// Remove globals we don't want to expose
|
||||
globals.rawset( "collectgarbage", Constants.NIL );
|
||||
@@ -509,53 +509,6 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
}
|
||||
}
|
||||
|
||||
private class CobaltLuaContext implements ILuaContext
|
||||
{
|
||||
@Override
|
||||
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
|
||||
{
|
||||
// Issue command
|
||||
final long taskID = MainThread.getUniqueTaskID();
|
||||
final Runnable iTask = () -> {
|
||||
try
|
||||
{
|
||||
Object[] results = task.execute();
|
||||
if( results != null )
|
||||
{
|
||||
Object[] eventArguments = new Object[results.length + 2];
|
||||
eventArguments[0] = taskID;
|
||||
eventArguments[1] = true;
|
||||
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
||||
computer.queueEvent( "task_complete", eventArguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
||||
}
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
|
||||
}
|
||||
catch( Throwable t )
|
||||
{
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
|
||||
computer.queueEvent( "task_complete", new Object[] {
|
||||
taskID, false, "Java Exception Thrown: " + t,
|
||||
} );
|
||||
}
|
||||
};
|
||||
if( computer.queueMainThread( iTask ) )
|
||||
{
|
||||
return taskID;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new LuaException( "Task limit exceeded" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class HardAbortError extends Error
|
||||
{
|
||||
private static final long serialVersionUID = 7954092008586367501L;
|
||||
|
69
src/main/java/dan200/computercraft/core/lua/LuaContext.java
Normal file
69
src/main/java/dan200/computercraft/core/lua/LuaContext.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.core.lua;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaTask;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.core.computer.Computer;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class LuaContext implements ILuaContext
|
||||
{
|
||||
private final Computer computer;
|
||||
|
||||
LuaContext( Computer computer )
|
||||
{
|
||||
this.computer = computer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long issueMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException
|
||||
{
|
||||
// Issue command
|
||||
final long taskID = MainThread.getUniqueTaskID();
|
||||
final Runnable iTask = () -> {
|
||||
try
|
||||
{
|
||||
Object[] results = task.execute();
|
||||
if( results != null )
|
||||
{
|
||||
Object[] eventArguments = new Object[results.length + 2];
|
||||
eventArguments[0] = taskID;
|
||||
eventArguments[1] = true;
|
||||
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
||||
computer.queueEvent( "task_complete", eventArguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
||||
}
|
||||
}
|
||||
catch( LuaException e )
|
||||
{
|
||||
computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
|
||||
}
|
||||
catch( Exception t )
|
||||
{
|
||||
if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running task", t );
|
||||
computer.queueEvent( "task_complete", new Object[] {
|
||||
taskID, false, "Java Exception Thrown: " + t,
|
||||
} );
|
||||
}
|
||||
};
|
||||
if( computer.queueMainThread( iTask ) )
|
||||
{
|
||||
return taskID;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new LuaException( "Task limit exceeded" );
|
||||
}
|
||||
}
|
||||
}
|
@@ -44,9 +44,9 @@ public class Terminal
|
||||
this.height = height;
|
||||
onChanged = changedCallback;
|
||||
|
||||
text = new TextBuffer[this.height];
|
||||
textColour = new TextBuffer[this.height];
|
||||
backgroundColour = new TextBuffer[this.height];
|
||||
text = new TextBuffer[height];
|
||||
textColour = new TextBuffer[height];
|
||||
backgroundColour = new TextBuffer[height];
|
||||
for( int i = 0; i < this.height; i++ )
|
||||
{
|
||||
text[i] = new TextBuffer( ' ', this.width );
|
||||
@@ -93,9 +93,9 @@ public class Terminal
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
text = new TextBuffer[this.height];
|
||||
textColour = new TextBuffer[this.height];
|
||||
backgroundColour = new TextBuffer[this.height];
|
||||
text = new TextBuffer[height];
|
||||
textColour = new TextBuffer[height];
|
||||
backgroundColour = new TextBuffer[height];
|
||||
for( int i = 0; i < this.height; i++ )
|
||||
{
|
||||
if( i >= oldHeight )
|
||||
|
@@ -12,7 +12,7 @@ public class TextBuffer
|
||||
public TextBuffer( char c, int length )
|
||||
{
|
||||
text = new char[length];
|
||||
this.fill( c );
|
||||
fill( c );
|
||||
}
|
||||
|
||||
public TextBuffer( String text )
|
||||
@@ -79,6 +79,7 @@ public class TextBuffer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return new String( text );
|
||||
|
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
@@ -17,7 +17,7 @@ public class Generators
|
||||
@SubscribeEvent
|
||||
public static void gather( GatherDataEvent event )
|
||||
{
|
||||
ComputerCraftProxyCommon.registerLoot();
|
||||
Registry.registerLoot();
|
||||
|
||||
DataGenerator generator = event.getGenerator();
|
||||
generator.addProvider( new Recipes( generator ) );
|
||||
|
@@ -6,11 +6,11 @@
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
||||
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||
import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@@ -46,7 +46,7 @@ public class LootTables extends LootTableProvider
|
||||
computerDrop( add, Registry.ModBlocks.TURTLE_NORMAL );
|
||||
computerDrop( add, Registry.ModBlocks.TURTLE_ADVANCED );
|
||||
|
||||
add.accept( ComputerCraftProxyCommon.ForgeHandlers.LOOT_TREASURE_DISK, LootTable
|
||||
add.accept( CommonHooks.LOOT_TREASURE_DISK, LootTable
|
||||
.lootTable()
|
||||
.setParamSet( LootParameterSets.ALL_PARAMS )
|
||||
.build() );
|
||||
|
123
src/main/java/dan200/computercraft/shared/CommonHooks.java
Normal file
123
src/main/java/dan200/computercraft/shared/CommonHooks.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
||||
import net.minecraft.inventory.container.Container;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.storage.loot.ConstantRange;
|
||||
import net.minecraft.world.storage.loot.LootPool;
|
||||
import net.minecraft.world.storage.loot.LootTables;
|
||||
import net.minecraft.world.storage.loot.TableLootEntry;
|
||||
import net.minecraftforge.event.LootTableLoadEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Miscellaneous hooks which are present on the client and server.
|
||||
*
|
||||
* These should possibly be refactored into separate classes at some point, but are fine here for now.
|
||||
*
|
||||
* @see dan200.computercraft.client.ClientHooks For client-specific ones.
|
||||
*/
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID )
|
||||
public final class CommonHooks
|
||||
{
|
||||
private CommonHooks()
|
||||
{
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerTick( TickEvent.ServerTickEvent event )
|
||||
{
|
||||
if( event.phase == TickEvent.Phase.START )
|
||||
{
|
||||
MainThread.executePendingTasks();
|
||||
ComputerCraft.serverComputerRegistry.update();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onContainerOpen( PlayerContainerEvent.Open event )
|
||||
{
|
||||
// If we're opening a computer container then broadcast the terminal state
|
||||
Container container = event.getContainer();
|
||||
if( container instanceof IContainerComputer )
|
||||
{
|
||||
IComputer computer = ((IContainerComputer) container).getComputer();
|
||||
if( computer instanceof ServerComputer )
|
||||
{
|
||||
((ServerComputer) computer).sendTerminalState( event.getPlayer() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarting( FMLServerStartingEvent event )
|
||||
{
|
||||
CommandComputerCraft.register( event.getCommandDispatcher() );
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarted( FMLServerStartedEvent event )
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStopped( FMLServerStoppedEvent event )
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
|
||||
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation( ComputerCraft.MOD_ID, "treasure_disk" );
|
||||
|
||||
private static final Set<ResourceLocation> TABLES = new HashSet<>( Arrays.asList(
|
||||
LootTables.SIMPLE_DUNGEON,
|
||||
LootTables.ABANDONED_MINESHAFT,
|
||||
LootTables.STRONGHOLD_CORRIDOR,
|
||||
LootTables.STRONGHOLD_CROSSING,
|
||||
LootTables.STRONGHOLD_LIBRARY,
|
||||
LootTables.DESERT_PYRAMID,
|
||||
LootTables.JUNGLE_TEMPLE,
|
||||
LootTables.IGLOO_CHEST,
|
||||
LootTables.WOODLAND_MANSION,
|
||||
LootTables.VILLAGE_CARTOGRAPHER
|
||||
) );
|
||||
|
||||
@SubscribeEvent
|
||||
public static void lootLoad( LootTableLoadEvent event )
|
||||
{
|
||||
ResourceLocation name = event.getName();
|
||||
if( !name.getNamespace().equals( "minecraft" ) || !TABLES.contains( name ) ) return;
|
||||
|
||||
event.getTable().addPool( LootPool.lootPool()
|
||||
.add( TableLootEntry.lootTableReference( LOOT_TREASURE_DISK ) )
|
||||
.setRolls( ConstantRange.exactly( 1 ) )
|
||||
.name( "computercraft_treasure" )
|
||||
.build() );
|
||||
}
|
||||
}
|
@@ -42,7 +42,6 @@ public final class Config
|
||||
private static final ConfigValue<Integer> maximumFilesOpen;
|
||||
private static final ConfigValue<Boolean> disableLua51Features;
|
||||
private static final ConfigValue<String> defaultComputerSettings;
|
||||
private static final ConfigValue<Boolean> debugEnabled;
|
||||
private static final ConfigValue<Boolean> logComputerErrors;
|
||||
private static final ConfigValue<Boolean> commandRequireCreative;
|
||||
|
||||
@@ -120,10 +119,6 @@ public final class Config
|
||||
"autocompletion" )
|
||||
.define( "default_computer_settings", ComputerCraft.defaultComputerSettings );
|
||||
|
||||
debugEnabled = builder
|
||||
.comment( "Enable Lua's debug library. This is sandboxed to each computer, so is generally safe to be used by players." )
|
||||
.define( "debug_enabled", ComputerCraft.debugEnable );
|
||||
|
||||
logComputerErrors = builder
|
||||
.comment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
|
||||
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." )
|
||||
@@ -317,7 +312,6 @@ public final class Config
|
||||
ComputerCraft.maximumFilesOpen = maximumFilesOpen.get();
|
||||
ComputerCraft.disableLua51Features = disableLua51Features.get();
|
||||
ComputerCraft.defaultComputerSettings = defaultComputerSettings.get();
|
||||
ComputerCraft.debugEnable = debugEnabled.get();
|
||||
ComputerCraft.computerThreads = computerThreads.get();
|
||||
ComputerCraft.logComputerErrors = logComputerErrors.get();
|
||||
ComputerCraft.commandRequireCreative = commandRequireCreative.get();
|
||||
|
@@ -7,8 +7,13 @@ package dan200.computercraft.shared;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.command.arguments.ArgumentSerializers;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.common.ContainerHeldItem;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
|
||||
import dan200.computercraft.shared.computer.blocks.TileComputer;
|
||||
@@ -17,11 +22,17 @@ import dan200.computercraft.shared.computer.inventory.ContainerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.computer.items.ItemComputer;
|
||||
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||
import dan200.computercraft.shared.data.ConstantLootConditionSerializer;
|
||||
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
||||
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||
import dan200.computercraft.shared.media.items.ItemDisk;
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout;
|
||||
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
|
||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
||||
import dan200.computercraft.shared.media.recipes.DiskRecipe;
|
||||
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.network.container.ContainerData;
|
||||
import dan200.computercraft.shared.network.container.HeldItemContainerData;
|
||||
@@ -29,6 +40,9 @@ import dan200.computercraft.shared.network.container.ViewComputerContainerData;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.BlockDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.*;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.BlockWirelessModem;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem;
|
||||
@@ -52,29 +66,30 @@ import dan200.computercraft.shared.turtle.items.ItemTurtle;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||
import dan200.computercraft.shared.util.CreativeTabMain;
|
||||
import dan200.computercraft.shared.util.FixedPointTileEntityType;
|
||||
import dan200.computercraft.shared.util.ImpostorRecipe;
|
||||
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
|
||||
import dan200.computercraft.shared.util.*;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.EntityClassification;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.inventory.container.ContainerType;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.item.crafting.IRecipeSerializer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.storage.loot.conditions.LootConditionManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fml.DeferredWorkQueue;
|
||||
import net.minecraftforge.fml.RegistryObject;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
@@ -333,6 +348,68 @@ public final class Registry
|
||||
);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SuppressWarnings( "deprecation" )
|
||||
public static void init( FMLCommonSetupEvent event )
|
||||
{
|
||||
NetworkHandler.setup();
|
||||
|
||||
DeferredWorkQueue.runLater( () -> {
|
||||
registerProviders();
|
||||
ArgumentSerializers.register();
|
||||
registerLoot();
|
||||
} );
|
||||
|
||||
ComputerCraftAPI.registerGenericSource( new InventoryMethods() );
|
||||
ComputerCraftAPI.registerGenericSource( new FluidMethods() );
|
||||
ComputerCraftAPI.registerGenericSource( new EnergyMethods() );
|
||||
}
|
||||
|
||||
private static void registerProviders()
|
||||
{
|
||||
// Register bundled power providers
|
||||
ComputerCraftAPI.registerBundledRedstoneProvider( new DefaultBundledRedstoneProvider() );
|
||||
|
||||
// Register media providers
|
||||
ComputerCraftAPI.registerMediaProvider( stack -> {
|
||||
Item item = stack.getItem();
|
||||
if( item instanceof IMedia ) return (IMedia) item;
|
||||
if( item instanceof MusicDiscItem ) return RecordMedia.INSTANCE;
|
||||
return null;
|
||||
} );
|
||||
|
||||
// Register capabilities
|
||||
CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage<>(), () -> null );
|
||||
CapabilityManager.INSTANCE.register( IPeripheral.class, new NullStorage<>(), () -> null );
|
||||
|
||||
// Register generic capabilities. This can technically be done off-thread, but we need it to happen
|
||||
// after Forge's common setup, so this is easiest.
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityEnergy.ENERGY );
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
|
||||
}
|
||||
|
||||
public static void registerLoot()
|
||||
{
|
||||
LootConditionManager.register( ConstantLootConditionSerializer.of(
|
||||
new ResourceLocation( ComputerCraft.MOD_ID, "block_named" ),
|
||||
BlockNamedEntityLootCondition.class,
|
||||
BlockNamedEntityLootCondition.INSTANCE
|
||||
) );
|
||||
|
||||
LootConditionManager.register( ConstantLootConditionSerializer.of(
|
||||
new ResourceLocation( ComputerCraft.MOD_ID, "player_creative" ),
|
||||
PlayerCreativeLootCondition.class,
|
||||
PlayerCreativeLootCondition.INSTANCE
|
||||
) );
|
||||
|
||||
LootConditionManager.register( ConstantLootConditionSerializer.of(
|
||||
new ResourceLocation( ComputerCraft.MOD_ID, "has_id" ),
|
||||
HasComputerIdLootCondition.class,
|
||||
HasComputerIdLootCondition.INSTANCE
|
||||
) );
|
||||
}
|
||||
|
||||
public static void setup()
|
||||
{
|
||||
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
|
@@ -33,9 +33,9 @@ public final class TurtleUpgrades
|
||||
Wrapper( ITurtleUpgrade upgrade )
|
||||
{
|
||||
this.upgrade = upgrade;
|
||||
this.id = upgrade.getUpgradeID().toString();
|
||||
this.modId = ModLoadingContext.get().getActiveNamespace();
|
||||
this.enabled = true;
|
||||
id = upgrade.getUpgradeID().toString();
|
||||
modId = ModLoadingContext.get().getActiveNamespace();
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.command;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ClientChatEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Basic client-side commands.
|
||||
*
|
||||
* Simply hooks into client chat messages and intercepts matching strings.
|
||||
*/
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public final class ClientCommands
|
||||
{
|
||||
public static final String OPEN_COMPUTER = "/computercraft open-computer ";
|
||||
|
||||
private ClientCommands()
|
||||
{
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientSendMessage( ClientChatEvent event )
|
||||
{
|
||||
// Emulate the command on the client side
|
||||
if( event.getMessage().startsWith( OPEN_COMPUTER ) )
|
||||
{
|
||||
event.setCanceled( true );
|
||||
|
||||
String idStr = event.getMessage().substring( OPEN_COMPUTER.length() ).trim();
|
||||
int id;
|
||||
try
|
||||
{
|
||||
id = Integer.parseInt( idStr );
|
||||
}
|
||||
catch( NumberFormatException ignore )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File( IDAssigner.getDir(), "computer/" + id );
|
||||
if( !file.isDirectory() ) return;
|
||||
|
||||
Util.getPlatform().openFile( file );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -21,6 +21,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
@@ -37,6 +38,7 @@ import net.minecraft.world.World;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import static dan200.computercraft.shared.command.CommandUtils.isPlayer;
|
||||
@@ -320,6 +322,12 @@ public final class CommandComputerCraft
|
||||
) );
|
||||
}
|
||||
|
||||
if( UserLevel.OWNER.test( source ) && isPlayer( source ) )
|
||||
{
|
||||
ITextComponent linkPath = linkStorage( computerId );
|
||||
if( linkPath != null ) out.append( " " ).append( linkPath );
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -339,6 +347,18 @@ public final class CommandComputerCraft
|
||||
}
|
||||
}
|
||||
|
||||
private static ITextComponent linkStorage( int id )
|
||||
{
|
||||
File file = new File( IDAssigner.getDir(), "computer/" + id );
|
||||
if( !file.isDirectory() ) return null;
|
||||
|
||||
return link(
|
||||
text( "\u270E" ),
|
||||
ClientCommands.OPEN_COMPUTER + id,
|
||||
translate( "commands.computercraft.dump.open_path" )
|
||||
);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static TrackingContext getTimingContext( CommandSource source )
|
||||
{
|
||||
|
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.util.text.event.ClickEvent;
|
||||
import net.minecraft.util.text.event.HoverEvent;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ClientChatEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
import static net.minecraft.command.Commands.argument;
|
||||
import static net.minecraft.command.Commands.literal;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public final class CommandCopy
|
||||
{
|
||||
private static final String PREFIX = "/computercraft copy ";
|
||||
|
||||
private CommandCopy()
|
||||
{
|
||||
}
|
||||
|
||||
public static void register( CommandDispatcher<CommandSource> registry )
|
||||
{
|
||||
registry.register( literal( "computercraft" )
|
||||
.then( literal( "copy" ) )
|
||||
.then( argument( "message", StringArgumentType.greedyString() ) )
|
||||
.executes( context -> {
|
||||
Minecraft.getInstance().keyboardHandler.setClipboard( context.getArgument( "message", String.class ) );
|
||||
return 1;
|
||||
} )
|
||||
);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientSendMessage( ClientChatEvent event )
|
||||
{
|
||||
// Emulate the command on the client side
|
||||
if( event.getMessage().startsWith( PREFIX ) )
|
||||
{
|
||||
Minecraft.getInstance().keyboardHandler.setClipboard( event.getMessage().substring( PREFIX.length() ) );
|
||||
event.setCanceled( true );
|
||||
}
|
||||
}
|
||||
|
||||
public static ITextComponent createCopyText( String text )
|
||||
{
|
||||
StringTextComponent name = new StringTextComponent( text );
|
||||
name.getStyle()
|
||||
.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, PREFIX + text ) )
|
||||
.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent( "gui.computercraft.tooltip.copy" ) ) );
|
||||
return name;
|
||||
}
|
||||
}
|
@@ -57,14 +57,15 @@ public enum UserLevel implements Predicate<CommandSource>
|
||||
{
|
||||
if( this == ANYONE ) return true;
|
||||
|
||||
// We *always* allow level 0 stuff, even if the
|
||||
MinecraftServer server = source.getServer();
|
||||
Entity sender = source.getEntity();
|
||||
|
||||
if( server.isSingleplayer() && sender instanceof PlayerEntity &&
|
||||
((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
|
||||
if( this == OWNER || this == OWNER_OP )
|
||||
{
|
||||
if( this == OWNER || this == OWNER_OP ) return true;
|
||||
MinecraftServer server = source.getServer();
|
||||
Entity sender = source.getEntity();
|
||||
if( server.isSingleplayer() && sender instanceof PlayerEntity &&
|
||||
((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return source.hasPermission( toLevel() );
|
||||
|
@@ -71,11 +71,16 @@ public final class ChatHelpers
|
||||
}
|
||||
|
||||
public static ITextComponent link( ITextComponent component, String command, ITextComponent toolTip )
|
||||
{
|
||||
return link( component, new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ), toolTip );
|
||||
}
|
||||
|
||||
public static ITextComponent link( ITextComponent component, ClickEvent click, ITextComponent toolTip )
|
||||
{
|
||||
Style style = component.getStyle();
|
||||
|
||||
if( style.getColor() == null ) style.setColor( TextFormatting.YELLOW );
|
||||
style.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ) );
|
||||
style.setClickEvent( click );
|
||||
style.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) );
|
||||
|
||||
return component;
|
||||
@@ -85,4 +90,13 @@ public final class ChatHelpers
|
||||
{
|
||||
return coloured( text, HEADER );
|
||||
}
|
||||
|
||||
public static ITextComponent copy( String text )
|
||||
{
|
||||
StringTextComponent name = new StringTextComponent( text );
|
||||
name.getStyle()
|
||||
.setClickEvent( new ClickEvent( ClickEvent.Action.COPY_TO_CLIPBOARD, text ) )
|
||||
.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslationTextComponent( "gui.computercraft.tooltip.copy" ) ) );
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@ public class ContainerHeldItem extends Container
|
||||
public Factory( ContainerType<ContainerHeldItem> type, ItemStack stack, Hand hand )
|
||||
{
|
||||
this.type = type;
|
||||
this.name = stack.getHoverName();
|
||||
name = stack.getHoverName();
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
|
@@ -56,7 +56,7 @@ public class TileCommandComputer extends TileComputer
|
||||
@Override
|
||||
public boolean acceptsSuccess()
|
||||
{
|
||||
return getLevel().getGameRules().getBoolean( GameRules.RULE_SENDCOMMANDFEEDBACK );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -175,12 +175,12 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
|
||||
label = computer.getLabel();
|
||||
on = computer.isOn();
|
||||
|
||||
if( computer.hasOutputChanged() ) updateOutput();
|
||||
|
||||
// Update the block state if needed. We don't fire a block update intentionally,
|
||||
// as this only really is needed on the client side.
|
||||
updateBlockState( computer.getState() );
|
||||
|
||||
// TODO: This should ideally be split up into label/id/on (which should save NBT and sync to client) and
|
||||
// redstone (which should update outputs)
|
||||
if( computer.hasOutputChanged() ) updateOutput();
|
||||
}
|
||||
}
|
||||
@@ -376,11 +376,8 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
|
||||
fresh = true;
|
||||
changed = true;
|
||||
}
|
||||
if( changed )
|
||||
{
|
||||
updateBlock();
|
||||
updateInput();
|
||||
}
|
||||
|
||||
if( changed ) updateInput();
|
||||
return ComputerCraft.serverComputerRegistry.get( instanceID );
|
||||
}
|
||||
|
||||
|
@@ -25,14 +25,14 @@ public class ContainerViewComputer extends ContainerComputerBase implements ICon
|
||||
public ContainerViewComputer( int id, ServerComputer computer )
|
||||
{
|
||||
super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player -> canInteractWith( computer, player ), computer, computer.getFamily() );
|
||||
this.width = this.height = 0;
|
||||
width = height = 0;
|
||||
}
|
||||
|
||||
public ContainerViewComputer( int id, PlayerInventory player, ViewComputerContainerData data )
|
||||
{
|
||||
super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player, data );
|
||||
this.width = data.getWidth();
|
||||
this.height = data.getHeight();
|
||||
width = data.getWidth();
|
||||
height = data.getHeight();
|
||||
}
|
||||
|
||||
private static boolean canInteractWith( @Nonnull ServerComputer computer, @Nonnull PlayerEntity player )
|
||||
|
@@ -99,6 +99,7 @@ public class AddTurtleTool implements IUndoableAction
|
||||
return String.format( "Removing turtle upgrade %s.", id );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate( ILogger logger )
|
||||
{
|
||||
TrackingLogger trackLog = new TrackingLogger( logger );
|
||||
|
@@ -41,7 +41,7 @@ public class RemoveTurtleUpgradeByItem implements IUndoableAction
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
if( this.upgrade != null ) TurtleUpgrades.enable( upgrade );
|
||||
if( upgrade != null ) TurtleUpgrades.enable( upgrade );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -40,7 +40,7 @@ public class RemoveTurtleUpgradeByName implements IUndoableAction
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
if( this.upgrade != null ) TurtleUpgrades.enable( upgrade );
|
||||
if( upgrade != null ) TurtleUpgrades.enable( upgrade );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -339,17 +339,17 @@ class RecipeResolver implements IRecipeManagerPlugin
|
||||
UpgradeInfo( ItemStack stack, ITurtleUpgrade turtle )
|
||||
{
|
||||
this.stack = stack;
|
||||
this.ingredient = of( stack );
|
||||
this.upgrade = this.turtle = turtle;
|
||||
this.pocket = null;
|
||||
ingredient = of( stack );
|
||||
upgrade = this.turtle = turtle;
|
||||
pocket = null;
|
||||
}
|
||||
|
||||
UpgradeInfo( ItemStack stack, IPocketUpgrade pocket )
|
||||
{
|
||||
this.stack = stack;
|
||||
this.ingredient = of( stack );
|
||||
this.turtle = null;
|
||||
this.upgrade = this.pocket = pocket;
|
||||
ingredient = of( stack );
|
||||
turtle = null;
|
||||
upgrade = this.pocket = pocket;
|
||||
}
|
||||
|
||||
List<Shaped> getRecipes()
|
||||
|
@@ -68,6 +68,8 @@ public class ItemTreasureDisk extends Item implements IMedia
|
||||
public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world )
|
||||
{
|
||||
IMount rootTreasure = getTreasureMount();
|
||||
if( rootTreasure == null ) return null;
|
||||
|
||||
String subPath = getSubPath( stack );
|
||||
try
|
||||
{
|
||||
@@ -121,7 +123,7 @@ public class ItemTreasureDisk extends Item implements IMedia
|
||||
private static String getTitle( @Nonnull ItemStack stack )
|
||||
{
|
||||
CompoundNBT nbt = stack.getTag();
|
||||
return nbt != null && nbt.contains( NBT_TITLE ) ? nbt.getString( NBT_TITLE ) : "'alongtimeago' by dan200";
|
||||
return nbt != null && nbt.contains( NBT_TITLE ) ? nbt.getString( NBT_TITLE ) : "'missingno' by how did you get this anyway?";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -43,19 +43,22 @@ public final class NetworkHandler
|
||||
.simpleChannel();
|
||||
|
||||
// Server messages
|
||||
registerMainThread( 0, NetworkDirection.PLAY_TO_SERVER, ComputerActionServerMessage::new );
|
||||
registerMainThread( 1, NetworkDirection.PLAY_TO_SERVER, QueueEventServerMessage::new );
|
||||
registerMainThread( 2, NetworkDirection.PLAY_TO_SERVER, RequestComputerMessage::new );
|
||||
registerMainThread( 3, NetworkDirection.PLAY_TO_SERVER, KeyEventServerMessage::new );
|
||||
registerMainThread( 4, NetworkDirection.PLAY_TO_SERVER, MouseEventServerMessage::new );
|
||||
registerMainThread( 0, NetworkDirection.PLAY_TO_SERVER, ComputerActionServerMessage.class, ComputerActionServerMessage::new );
|
||||
registerMainThread( 1, NetworkDirection.PLAY_TO_SERVER, QueueEventServerMessage.class, QueueEventServerMessage::new );
|
||||
registerMainThread( 2, NetworkDirection.PLAY_TO_SERVER, RequestComputerMessage.class, RequestComputerMessage::new );
|
||||
registerMainThread( 3, NetworkDirection.PLAY_TO_SERVER, KeyEventServerMessage.class, KeyEventServerMessage::new );
|
||||
registerMainThread( 4, NetworkDirection.PLAY_TO_SERVER, MouseEventServerMessage.class, MouseEventServerMessage::new );
|
||||
|
||||
// Client messages
|
||||
registerMainThread( 10, NetworkDirection.PLAY_TO_CLIENT, ChatTableClientMessage::new );
|
||||
registerMainThread( 11, NetworkDirection.PLAY_TO_CLIENT, ComputerDataClientMessage::new );
|
||||
registerMainThread( 12, NetworkDirection.PLAY_TO_CLIENT, ComputerDeletedClientMessage::new );
|
||||
registerMainThread( 13, NetworkDirection.PLAY_TO_CLIENT, ComputerTerminalClientMessage::new );
|
||||
registerMainThread( 10, NetworkDirection.PLAY_TO_CLIENT, ChatTableClientMessage.class, ChatTableClientMessage::new );
|
||||
registerMainThread( 11, NetworkDirection.PLAY_TO_CLIENT, ComputerDataClientMessage.class, ComputerDataClientMessage::new );
|
||||
registerMainThread( 12, NetworkDirection.PLAY_TO_CLIENT, ComputerDeletedClientMessage.class, ComputerDeletedClientMessage::new );
|
||||
registerMainThread( 13, NetworkDirection.PLAY_TO_CLIENT, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new );
|
||||
registerMainThread( 14, NetworkDirection.PLAY_TO_CLIENT, PlayRecordClientMessage.class, PlayRecordClientMessage::new );
|
||||
registerMainThread( 15, NetworkDirection.PLAY_TO_CLIENT, MonitorClientMessage.class, MonitorClientMessage::new );
|
||||
registerMainThread( 16, NetworkDirection.PLAY_TO_CLIENT, SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new );
|
||||
registerMainThread( 17, NetworkDirection.PLAY_TO_CLIENT, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new );
|
||||
registerMainThread( 18, NetworkDirection.PLAY_TO_CLIENT, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new );
|
||||
}
|
||||
|
||||
public static void sendToPlayer( PlayerEntity player, NetworkMessage packet )
|
||||
@@ -87,24 +90,6 @@ public final class NetworkHandler
|
||||
network.send( PacketDistributor.TRACKING_CHUNK.with( () -> chunk ), packet );
|
||||
}
|
||||
|
||||
/**
|
||||
* /**
|
||||
* Register packet, and a thread-unsafe handler for it.
|
||||
*
|
||||
* @param <T> The type of the packet to send.
|
||||
* @param id The identifier for this packet type.
|
||||
* @param direction A network direction which will be asserted before any processing of this message occurs.
|
||||
* @param factory The factory for this type of packet.
|
||||
*/
|
||||
private static <T extends NetworkMessage> void registerMainThread( int id, NetworkDirection direction, Supplier<T> factory )
|
||||
{
|
||||
registerMainThread( id, direction, getType( factory ), buf -> {
|
||||
T instance = factory.get();
|
||||
instance.fromBytes( buf );
|
||||
return instance;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* /**
|
||||
* Register packet, and a thread-unsafe handler for it.
|
||||
|
@@ -27,18 +27,6 @@ public interface NetworkMessage
|
||||
*/
|
||||
void toBytes( @Nonnull PacketBuffer buf );
|
||||
|
||||
/**
|
||||
* Read this packet from a buffer.
|
||||
*
|
||||
* This may be called on any thread, so this should be a pure operation.
|
||||
*
|
||||
* @param buf The buffer to read data from.
|
||||
*/
|
||||
default void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
throw new IllegalStateException( "Should have been registered using a \"from bytes\" method" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle this {@link NetworkMessage}.
|
||||
*
|
||||
|
@@ -16,7 +16,7 @@ import javax.annotation.Nonnull;
|
||||
|
||||
public class ChatTableClientMessage implements NetworkMessage
|
||||
{
|
||||
private TableBuilder table;
|
||||
private final TableBuilder table;
|
||||
|
||||
public ChatTableClientMessage( TableBuilder table )
|
||||
{
|
||||
@@ -24,32 +24,7 @@ public class ChatTableClientMessage implements NetworkMessage
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public ChatTableClientMessage()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
buf.writeVarInt( table.getId() );
|
||||
buf.writeVarInt( table.getColumns() );
|
||||
buf.writeBoolean( table.getHeaders() != null );
|
||||
if( table.getHeaders() != null )
|
||||
{
|
||||
for( ITextComponent header : table.getHeaders() ) buf.writeComponent( header );
|
||||
}
|
||||
|
||||
buf.writeVarInt( table.getRows().size() );
|
||||
for( ITextComponent[] row : table.getRows() )
|
||||
{
|
||||
for( ITextComponent column : row ) buf.writeComponent( column );
|
||||
}
|
||||
|
||||
buf.writeVarInt( table.getAdditional() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
public ChatTableClientMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
int id = buf.readVarInt();
|
||||
int columns = buf.readVarInt();
|
||||
@@ -77,6 +52,26 @@ public class ChatTableClientMessage implements NetworkMessage
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
buf.writeVarInt( table.getId() );
|
||||
buf.writeVarInt( table.getColumns() );
|
||||
buf.writeBoolean( table.getHeaders() != null );
|
||||
if( table.getHeaders() != null )
|
||||
{
|
||||
for( ITextComponent header : table.getHeaders() ) buf.writeComponent( header );
|
||||
}
|
||||
|
||||
buf.writeVarInt( table.getRows().size() );
|
||||
for( ITextComponent[] row : table.getRows() )
|
||||
{
|
||||
for( ITextComponent column : row ) buf.writeComponent( column );
|
||||
}
|
||||
|
||||
buf.writeVarInt( table.getAdditional() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
|
@@ -17,15 +17,16 @@ import javax.annotation.Nonnull;
|
||||
*/
|
||||
public abstract class ComputerClientMessage implements NetworkMessage
|
||||
{
|
||||
private int instanceId;
|
||||
private final int instanceId;
|
||||
|
||||
public ComputerClientMessage( int instanceId )
|
||||
{
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public ComputerClientMessage()
|
||||
public ComputerClientMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
instanceId = buf.readVarInt();
|
||||
}
|
||||
|
||||
public int getInstanceId()
|
||||
@@ -39,12 +40,6 @@ public abstract class ComputerClientMessage implements NetworkMessage
|
||||
buf.writeVarInt( instanceId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
instanceId = buf.readVarInt();
|
||||
}
|
||||
|
||||
public ClientComputer getComputer()
|
||||
{
|
||||
ClientComputer computer = ComputerCraft.clientComputerRegistry.get( instanceId );
|
||||
|
@@ -18,8 +18,8 @@ import javax.annotation.Nonnull;
|
||||
*/
|
||||
public class ComputerDataClientMessage extends ComputerClientMessage
|
||||
{
|
||||
private ComputerState state;
|
||||
private CompoundNBT userData;
|
||||
private final ComputerState state;
|
||||
private final CompoundNBT userData;
|
||||
|
||||
public ComputerDataClientMessage( ServerComputer computer )
|
||||
{
|
||||
@@ -28,8 +28,11 @@ public class ComputerDataClientMessage extends ComputerClientMessage
|
||||
userData = computer.getUserData();
|
||||
}
|
||||
|
||||
public ComputerDataClientMessage()
|
||||
public ComputerDataClientMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super( buf );
|
||||
state = buf.readEnum( ComputerState.class );
|
||||
userData = buf.readNbt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,14 +43,6 @@ public class ComputerDataClientMessage extends ComputerClientMessage
|
||||
buf.writeNbt( userData );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super.fromBytes( buf );
|
||||
state = buf.readEnum( ComputerState.class );
|
||||
userData = buf.readNbt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
|
@@ -6,6 +6,7 @@
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
public class ComputerDeletedClientMessage extends ComputerClientMessage
|
||||
@@ -15,8 +16,9 @@ public class ComputerDeletedClientMessage extends ComputerClientMessage
|
||||
super( instanceId );
|
||||
}
|
||||
|
||||
public ComputerDeletedClientMessage()
|
||||
public ComputerDeletedClientMessage( PacketBuffer buffer )
|
||||
{
|
||||
super( buffer );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -12,7 +12,7 @@ import javax.annotation.Nonnull;
|
||||
|
||||
public class ComputerTerminalClientMessage extends ComputerClientMessage
|
||||
{
|
||||
private TerminalState state;
|
||||
private final TerminalState state;
|
||||
|
||||
public ComputerTerminalClientMessage( int instanceId, TerminalState state )
|
||||
{
|
||||
@@ -20,8 +20,10 @@ public class ComputerTerminalClientMessage extends ComputerClientMessage
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public ComputerTerminalClientMessage()
|
||||
public ComputerTerminalClientMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super( buf );
|
||||
state = new TerminalState( buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,13 +33,6 @@ public class ComputerTerminalClientMessage extends ComputerClientMessage
|
||||
state.write( buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super.fromBytes( buf );
|
||||
state = new TerminalState( buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.client.SoundManager;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Starts a sound on the client.
|
||||
*
|
||||
* Used by speakers to play sounds.
|
||||
*
|
||||
* @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker
|
||||
*/
|
||||
public class SpeakerMoveClientMessage implements NetworkMessage
|
||||
{
|
||||
private final UUID source;
|
||||
private final Vec3d pos;
|
||||
|
||||
public SpeakerMoveClientMessage( UUID source, Vec3d pos )
|
||||
{
|
||||
this.source = source;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public SpeakerMoveClientMessage( PacketBuffer buf )
|
||||
{
|
||||
source = buf.readUUID();
|
||||
pos = new Vec3d( buf.readDouble(), buf.readDouble(), buf.readDouble() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
buf.writeUUID( source );
|
||||
buf.writeDouble( pos.x() );
|
||||
buf.writeDouble( pos.y() );
|
||||
buf.writeDouble( pos.z() );
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn( Dist.CLIENT )
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
SoundManager.moveSound( source, pos );
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.client.SoundManager;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Starts a sound on the client.
|
||||
*
|
||||
* Used by speakers to play sounds.
|
||||
*
|
||||
* @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker
|
||||
*/
|
||||
public class SpeakerPlayClientMessage implements NetworkMessage
|
||||
{
|
||||
private final UUID source;
|
||||
private final Vec3d pos;
|
||||
private final ResourceLocation sound;
|
||||
private final float volume;
|
||||
private final float pitch;
|
||||
|
||||
public SpeakerPlayClientMessage( UUID source, Vec3d pos, ResourceLocation event, float volume, float pitch )
|
||||
{
|
||||
this.source = source;
|
||||
this.pos = pos;
|
||||
sound = event;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public SpeakerPlayClientMessage( PacketBuffer buf )
|
||||
{
|
||||
source = buf.readUUID();
|
||||
pos = new Vec3d( buf.readDouble(), buf.readDouble(), buf.readDouble() );
|
||||
sound = buf.readResourceLocation();
|
||||
volume = buf.readFloat();
|
||||
pitch = buf.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
buf.writeUUID( source );
|
||||
buf.writeDouble( pos.x() );
|
||||
buf.writeDouble( pos.y() );
|
||||
buf.writeDouble( pos.z() );
|
||||
buf.writeResourceLocation( sound );
|
||||
buf.writeFloat( volume );
|
||||
buf.writeFloat( pitch );
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn( Dist.CLIENT )
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
SoundEvent sound = ForgeRegistries.SOUND_EVENTS.getValue( this.sound );
|
||||
if( sound != null ) SoundManager.playSound( source, pos, sound, volume, pitch );
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.network.client;
|
||||
|
||||
import dan200.computercraft.client.SoundManager;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Stops a sound on the client
|
||||
*
|
||||
* Called when a speaker is broken.
|
||||
*
|
||||
* @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker
|
||||
*/
|
||||
public class SpeakerStopClientMessage implements NetworkMessage
|
||||
{
|
||||
private final UUID source;
|
||||
|
||||
public SpeakerStopClientMessage( UUID source )
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public SpeakerStopClientMessage( PacketBuffer buf )
|
||||
{
|
||||
source = buf.readUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
buf.writeUUID( source );
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn( Dist.CLIENT )
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
SoundManager.stopSound( source );
|
||||
}
|
||||
}
|
@@ -54,36 +54,36 @@ public class TerminalState
|
||||
|
||||
if( terminal == null )
|
||||
{
|
||||
this.width = this.height = 0;
|
||||
this.buffer = null;
|
||||
width = height = 0;
|
||||
buffer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.width = terminal.getWidth();
|
||||
this.height = terminal.getHeight();
|
||||
width = terminal.getWidth();
|
||||
height = terminal.getHeight();
|
||||
|
||||
ByteBuf buf = this.buffer = Unpooled.buffer();
|
||||
ByteBuf buf = buffer = Unpooled.buffer();
|
||||
terminal.write( new PacketBuffer( buf ) );
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalState( PacketBuffer buf )
|
||||
{
|
||||
this.colour = buf.readBoolean();
|
||||
this.compress = buf.readBoolean();
|
||||
colour = buf.readBoolean();
|
||||
compress = buf.readBoolean();
|
||||
|
||||
if( buf.readBoolean() )
|
||||
{
|
||||
this.width = buf.readVarInt();
|
||||
this.height = buf.readVarInt();
|
||||
width = buf.readVarInt();
|
||||
height = buf.readVarInt();
|
||||
|
||||
int length = buf.readVarInt();
|
||||
this.buffer = readCompressed( buf, length, compress );
|
||||
buffer = readCompressed( buf, length, compress );
|
||||
}
|
||||
else
|
||||
{
|
||||
this.width = this.height = 0;
|
||||
this.buffer = null;
|
||||
width = height = 0;
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -16,14 +16,14 @@ public class ComputerContainerData implements ContainerData
|
||||
|
||||
public ComputerContainerData( ServerComputer computer )
|
||||
{
|
||||
this.id = computer.getInstanceID();
|
||||
this.family = computer.getFamily();
|
||||
id = computer.getInstanceID();
|
||||
family = computer.getFamily();
|
||||
}
|
||||
|
||||
public ComputerContainerData( PacketBuffer buf )
|
||||
{
|
||||
this.id = buf.readInt();
|
||||
this.family = buf.readEnum( ComputerFamily.class );
|
||||
id = buf.readInt();
|
||||
family = buf.readEnum( ComputerFamily.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -13,7 +13,7 @@ import javax.annotation.Nonnull;
|
||||
|
||||
public class ComputerActionServerMessage extends ComputerServerMessage
|
||||
{
|
||||
private Action action;
|
||||
private final Action action;
|
||||
|
||||
public ComputerActionServerMessage( int instanceId, Action action )
|
||||
{
|
||||
@@ -21,8 +21,10 @@ public class ComputerActionServerMessage extends ComputerServerMessage
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public ComputerActionServerMessage()
|
||||
public ComputerActionServerMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super( buf );
|
||||
action = buf.readEnum( Action.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,13 +34,6 @@ public class ComputerActionServerMessage extends ComputerServerMessage
|
||||
buf.writeEnum( action );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super.fromBytes( buf );
|
||||
action = buf.readEnum( Action.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
|
||||
{
|
||||
|
@@ -22,15 +22,16 @@ import javax.annotation.Nonnull;
|
||||
*/
|
||||
public abstract class ComputerServerMessage implements NetworkMessage
|
||||
{
|
||||
private int instanceId;
|
||||
private final int instanceId;
|
||||
|
||||
public ComputerServerMessage( int instanceId )
|
||||
{
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public ComputerServerMessage()
|
||||
public ComputerServerMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
instanceId = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,12 +40,6 @@ public abstract class ComputerServerMessage implements NetworkMessage
|
||||
buf.writeVarInt( instanceId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
instanceId = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
|
@@ -18,8 +18,8 @@ public class KeyEventServerMessage extends ComputerServerMessage
|
||||
public static final int TYPE_REPEAT = 1;
|
||||
public static final int TYPE_UP = 2;
|
||||
|
||||
private int type;
|
||||
private int key;
|
||||
private final int type;
|
||||
private final int key;
|
||||
|
||||
public KeyEventServerMessage( int instanceId, int type, int key )
|
||||
{
|
||||
@@ -28,8 +28,11 @@ public class KeyEventServerMessage extends ComputerServerMessage
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public KeyEventServerMessage()
|
||||
public KeyEventServerMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super( buf );
|
||||
type = buf.readByte();
|
||||
key = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,14 +43,6 @@ public class KeyEventServerMessage extends ComputerServerMessage
|
||||
buf.writeVarInt( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super.fromBytes( buf );
|
||||
type = buf.readByte();
|
||||
key = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
|
||||
{
|
||||
|
@@ -19,10 +19,10 @@ public class MouseEventServerMessage extends ComputerServerMessage
|
||||
public static final int TYPE_UP = 2;
|
||||
public static final int TYPE_SCROLL = 3;
|
||||
|
||||
private int type;
|
||||
private int x;
|
||||
private int y;
|
||||
private int arg;
|
||||
private final int type;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int arg;
|
||||
|
||||
public MouseEventServerMessage( int instanceId, int type, int arg, int x, int y )
|
||||
{
|
||||
@@ -33,8 +33,13 @@ public class MouseEventServerMessage extends ComputerServerMessage
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public MouseEventServerMessage()
|
||||
public MouseEventServerMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super( buf );
|
||||
type = buf.readByte();
|
||||
arg = buf.readVarInt();
|
||||
x = buf.readVarInt();
|
||||
y = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,16 +52,6 @@ public class MouseEventServerMessage extends ComputerServerMessage
|
||||
buf.writeVarInt( y );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super.fromBytes( buf );
|
||||
type = buf.readByte();
|
||||
arg = buf.readVarInt();
|
||||
x = buf.readVarInt();
|
||||
y = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
|
||||
{
|
||||
|
@@ -22,8 +22,8 @@ import javax.annotation.Nullable;
|
||||
*/
|
||||
public class QueueEventServerMessage extends ComputerServerMessage
|
||||
{
|
||||
private String event;
|
||||
private Object[] args;
|
||||
private final String event;
|
||||
private final Object[] args;
|
||||
|
||||
public QueueEventServerMessage( int instanceId, @Nonnull String event, @Nullable Object[] args )
|
||||
{
|
||||
@@ -32,8 +32,13 @@ public class QueueEventServerMessage extends ComputerServerMessage
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public QueueEventServerMessage()
|
||||
public QueueEventServerMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super( buf );
|
||||
event = buf.readUtf( Short.MAX_VALUE );
|
||||
|
||||
CompoundNBT args = buf.readNbt();
|
||||
this.args = args == null ? null : NBTUtil.decodeObjects( args );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,16 +49,6 @@ public class QueueEventServerMessage extends ComputerServerMessage
|
||||
buf.writeNbt( args == null ? null : NBTUtil.encodeObjects( args ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
super.fromBytes( buf );
|
||||
event = buf.readUtf( Short.MAX_VALUE );
|
||||
|
||||
CompoundNBT args = buf.readNbt();
|
||||
this.args = args == null ? null : NBTUtil.decodeObjects( args );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
|
||||
{
|
||||
|
@@ -15,15 +15,16 @@ import javax.annotation.Nonnull;
|
||||
|
||||
public class RequestComputerMessage implements NetworkMessage
|
||||
{
|
||||
private int instance;
|
||||
private final int instance;
|
||||
|
||||
public RequestComputerMessage( int instance )
|
||||
{
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public RequestComputerMessage()
|
||||
public RequestComputerMessage( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
instance = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,12 +33,6 @@ public class RequestComputerMessage implements NetworkMessage
|
||||
buf.writeVarInt( instance );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes( @Nonnull PacketBuffer buf )
|
||||
{
|
||||
instance = buf.readVarInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle( NetworkEvent.Context context )
|
||||
{
|
||||
|
@@ -24,7 +24,7 @@ final class SaturatedMethod
|
||||
SaturatedMethod( Object target, NamedMethod<PeripheralMethod> method )
|
||||
{
|
||||
this.target = target;
|
||||
this.name = method.getName();
|
||||
name = method.getName();
|
||||
this.method = method.getMethod();
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,7 @@ import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.command.CommandCopy;
|
||||
import dan200.computercraft.shared.command.text.ChatHelpers;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
@@ -53,7 +53,7 @@ public class TileCable extends TileGeneric
|
||||
@Override
|
||||
public World getWorld()
|
||||
{
|
||||
return TileCable.this.getLevel();
|
||||
return getLevel();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -268,12 +268,12 @@ public class TileCable extends TileGeneric
|
||||
if( oldName != null )
|
||||
{
|
||||
player.displayClientMessage( new TranslationTextComponent( "chat.computercraft.wired_modem.peripheral_disconnected",
|
||||
CommandCopy.createCopyText( oldName ) ), false );
|
||||
ChatHelpers.copy( oldName ) ), false );
|
||||
}
|
||||
if( newName != null )
|
||||
{
|
||||
player.displayClientMessage( new TranslationTextComponent( "chat.computercraft.wired_modem.peripheral_connected",
|
||||
CommandCopy.createCopyText( newName ) ), false );
|
||||
ChatHelpers.copy( newName ) ), false );
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.network.wired.IWiredNode;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.command.CommandCopy;
|
||||
import dan200.computercraft.shared.command.text.ChatHelpers;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
@@ -218,7 +218,7 @@ public class TileWiredModemFull extends TileGeneric
|
||||
for( int i = 0; i < names.size(); i++ )
|
||||
{
|
||||
if( i > 0 ) base.append( ", " );
|
||||
base.append( CommandCopy.createCopyText( names.get( i ) ) );
|
||||
base.append( ChatHelpers.copy( names.get( i ) ) );
|
||||
}
|
||||
|
||||
player.displayClientMessage( new TranslationTextComponent( kind, base ), false );
|
||||
|
@@ -87,7 +87,7 @@ public class BlockMonitor extends BlockGeneric
|
||||
{
|
||||
TileMonitor monitor = (TileMonitor) entity;
|
||||
// Defer the block update if we're being placed by another TE. See #691
|
||||
if ( livingEntity == null || livingEntity instanceof FakePlayer )
|
||||
if( livingEntity == null || livingEntity instanceof FakePlayer )
|
||||
{
|
||||
monitor.updateNeighborsDeferred();
|
||||
return;
|
||||
|
@@ -148,7 +148,7 @@ public class TileMonitor extends TileGeneric
|
||||
@Override
|
||||
public void blockTick()
|
||||
{
|
||||
if ( needsUpdate )
|
||||
if( needsUpdate )
|
||||
{
|
||||
needsUpdate = false;
|
||||
updateNeighbors();
|
||||
@@ -278,8 +278,6 @@ public class TileMonitor extends TileGeneric
|
||||
|
||||
int oldXIndex = xIndex;
|
||||
int oldYIndex = yIndex;
|
||||
int oldWidth = width;
|
||||
int oldHeight = height;
|
||||
|
||||
xIndex = nbt.getInt( NBT_X );
|
||||
yIndex = nbt.getInt( NBT_Y );
|
||||
@@ -299,13 +297,6 @@ public class TileMonitor extends TileGeneric
|
||||
// If we're the origin terminal then create it.
|
||||
if( clientMonitor == null ) clientMonitor = new ClientMonitor( advanced, this );
|
||||
}
|
||||
|
||||
if( oldXIndex != xIndex || oldYIndex != yIndex ||
|
||||
oldWidth != width || oldHeight != height )
|
||||
{
|
||||
// One of our properties has changed, so ensure we redraw the block
|
||||
updateBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public final void read( TerminalState state )
|
||||
|
@@ -10,17 +10,23 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
||||
import net.minecraft.network.play.server.SPlaySoundPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.ResourceLocationException;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
@@ -32,20 +38,44 @@ import static dan200.computercraft.api.lua.LuaValues.checkFinite;
|
||||
*/
|
||||
public abstract class SpeakerPeripheral implements IPeripheral
|
||||
{
|
||||
private static final int MIN_TICKS_BETWEEN_SOUNDS = 1;
|
||||
|
||||
private long clock = 0;
|
||||
private long lastPlayTime = 0;
|
||||
private final AtomicInteger notesThisTick = new AtomicInteger();
|
||||
|
||||
private long lastPositionTime;
|
||||
private Vec3d lastPosition;
|
||||
|
||||
public void update()
|
||||
{
|
||||
clock++;
|
||||
notesThisTick.set( 0 );
|
||||
|
||||
// Push position updates to any speakers which have ever played a note,
|
||||
// have moved by a non-trivial amount and haven't had a position update
|
||||
// in the last second.
|
||||
if( lastPlayTime > 0 && (clock - lastPositionTime) >= 20 )
|
||||
{
|
||||
Vec3d position = getPosition();
|
||||
if( lastPosition == null || lastPosition.distanceToSqr( position ) >= 0.1 )
|
||||
{
|
||||
lastPosition = position;
|
||||
lastPositionTime = clock;
|
||||
NetworkHandler.sendToAllTracking(
|
||||
new SpeakerMoveClientMessage( getSource(), position ),
|
||||
getWorld().getChunkAt( new BlockPos( position ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract World getWorld();
|
||||
|
||||
public abstract Vec3d getPosition();
|
||||
|
||||
protected abstract UUID getSource();
|
||||
|
||||
public boolean madeSound( long ticks )
|
||||
{
|
||||
return clock - lastPlayTime <= ticks;
|
||||
@@ -135,26 +165,37 @@ public abstract class SpeakerPeripheral implements IPeripheral
|
||||
|
||||
private synchronized boolean playSound( ILuaContext context, ResourceLocation name, float volume, float pitch, boolean isNote ) throws LuaException
|
||||
{
|
||||
if( clock - lastPlayTime < TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS &&
|
||||
(!isNote || clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick) )
|
||||
if( clock - lastPlayTime < MIN_TICKS_BETWEEN_SOUNDS )
|
||||
{
|
||||
// Rate limiting occurs when we've already played a sound within the last tick, or we've
|
||||
// played more notes than allowable within the current tick.
|
||||
return false;
|
||||
// Rate limiting occurs when we've already played a sound within the last tick.
|
||||
if( !isNote ) return false;
|
||||
// Or we've played more notes than allowable within the current tick.
|
||||
if( clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick ) return false;
|
||||
}
|
||||
|
||||
World world = getWorld();
|
||||
Vec3d pos = getPosition();
|
||||
|
||||
float range = MathHelper.clamp( volume, 1.0f, 3.0f ) * 16;
|
||||
|
||||
context.issueMainThreadTask( () -> {
|
||||
MinecraftServer server = world.getServer();
|
||||
if( server == null ) return null;
|
||||
|
||||
float adjVolume = Math.min( volume, 3.0f );
|
||||
server.getPlayerList().broadcast(
|
||||
null, pos.x, pos.y, pos.z, adjVolume > 1.0f ? 16 * adjVolume : 16.0, world.dimension.getType(),
|
||||
new SPlaySoundPacket( name, SoundCategory.RECORDS, pos, adjVolume, pitch )
|
||||
);
|
||||
if( isNote )
|
||||
{
|
||||
server.getPlayerList().broadcast(
|
||||
null, pos.x, pos.y, pos.z, range, world.dimension.getType(),
|
||||
new SPlaySoundPacket( name, SoundCategory.RECORDS, pos, range, pitch )
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkHandler.sendToAllAround(
|
||||
new SpeakerPlayClientMessage( getSource(), pos, name, range, pitch ),
|
||||
world, pos, range
|
||||
);
|
||||
}
|
||||
return null;
|
||||
} );
|
||||
|
||||
|
@@ -7,6 +7,8 @@ package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.common.TileGeneric;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.tileentity.ITickableTileEntity;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
@@ -19,15 +21,15 @@ import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
|
||||
|
||||
public class TileSpeaker extends TileGeneric implements ITickableTileEntity
|
||||
{
|
||||
public static final int MIN_TICKS_BETWEEN_SOUNDS = 1;
|
||||
|
||||
private final SpeakerPeripheral peripheral;
|
||||
private LazyOptional<IPeripheral> peripheralCap;
|
||||
private final UUID source = UUID.randomUUID();
|
||||
|
||||
public TileSpeaker( TileEntityType<TileSpeaker> type )
|
||||
{
|
||||
@@ -41,6 +43,13 @@ public class TileSpeaker extends TileGeneric implements ITickableTileEntity
|
||||
peripheral.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved()
|
||||
{
|
||||
super.setRemoved();
|
||||
NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> cap, @Nullable Direction side )
|
||||
@@ -83,6 +92,12 @@ public class TileSpeaker extends TileGeneric implements ITickableTileEntity
|
||||
return new Vec3d( pos.getX(), pos.getY(), pos.getZ() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getSource()
|
||||
{
|
||||
return speaker.source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( @Nullable IPeripheral other )
|
||||
{
|
||||
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A speaker peripheral which is used on an upgrade, and so is only attached to one computer.
|
||||
*/
|
||||
public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral
|
||||
{
|
||||
private final UUID source = UUID.randomUUID();
|
||||
|
||||
@Override
|
||||
protected final UUID getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach( @Nonnull IComputerAccess computer )
|
||||
{
|
||||
NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) );
|
||||
}
|
||||
}
|
@@ -46,7 +46,7 @@ public final class ContainerPocketComputer extends ContainerComputerBase
|
||||
public Factory( ServerComputer computer, ItemStack stack, ItemPocketComputer item, Hand hand )
|
||||
{
|
||||
this.computer = computer;
|
||||
this.name = stack.getHoverName();
|
||||
name = stack.getHoverName();
|
||||
this.item = item;
|
||||
this.hand = hand;
|
||||
}
|
||||
|
@@ -6,11 +6,11 @@
|
||||
package dan200.computercraft.shared.pocket.peripherals;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class PocketSpeakerPeripheral extends SpeakerPeripheral
|
||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral
|
||||
{
|
||||
private World world = null;
|
||||
private Vec3d position = Vec3d.ZERO;
|
||||
|
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.shared.proxy;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.network.wired.IWiredElement;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.MainThread;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.command.arguments.ArgumentSerializers;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.computer.core.IComputer;
|
||||
import dan200.computercraft.shared.computer.core.IContainerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||
import dan200.computercraft.shared.data.ConstantLootConditionSerializer;
|
||||
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
|
||||
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
||||
import dan200.computercraft.shared.media.items.RecordMedia;
|
||||
import dan200.computercraft.shared.network.NetworkHandler;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.EnergyMethods;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.FluidMethods;
|
||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
||||
import dan200.computercraft.shared.util.NullStorage;
|
||||
import net.minecraft.inventory.container.Container;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.MusicDiscItem;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.storage.loot.ConstantRange;
|
||||
import net.minecraft.world.storage.loot.LootPool;
|
||||
import net.minecraft.world.storage.loot.LootTables;
|
||||
import net.minecraft.world.storage.loot.TableLootEntry;
|
||||
import net.minecraft.world.storage.loot.conditions.LootConditionManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.event.LootTableLoadEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fml.DeferredWorkQueue;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD )
|
||||
public final class ComputerCraftProxyCommon
|
||||
{
|
||||
@SubscribeEvent
|
||||
@SuppressWarnings( "deprecation" )
|
||||
public static void init( FMLCommonSetupEvent event )
|
||||
{
|
||||
NetworkHandler.setup();
|
||||
|
||||
DeferredWorkQueue.runLater( () -> {
|
||||
registerProviders();
|
||||
ArgumentSerializers.register();
|
||||
registerLoot();
|
||||
} );
|
||||
|
||||
ComputerCraftAPI.registerGenericSource( new InventoryMethods() );
|
||||
ComputerCraftAPI.registerGenericSource( new FluidMethods() );
|
||||
ComputerCraftAPI.registerGenericSource( new EnergyMethods() );
|
||||
}
|
||||
|
||||
public static void registerLoot()
|
||||
{
|
||||
LootConditionManager.register( ConstantLootConditionSerializer.of(
|
||||
new ResourceLocation( ComputerCraft.MOD_ID, "block_named" ),
|
||||
BlockNamedEntityLootCondition.class,
|
||||
BlockNamedEntityLootCondition.INSTANCE
|
||||
) );
|
||||
|
||||
LootConditionManager.register( ConstantLootConditionSerializer.of(
|
||||
new ResourceLocation( ComputerCraft.MOD_ID, "player_creative" ),
|
||||
PlayerCreativeLootCondition.class,
|
||||
PlayerCreativeLootCondition.INSTANCE
|
||||
) );
|
||||
|
||||
LootConditionManager.register( ConstantLootConditionSerializer.of(
|
||||
new ResourceLocation( ComputerCraft.MOD_ID, "has_id" ),
|
||||
HasComputerIdLootCondition.class,
|
||||
HasComputerIdLootCondition.INSTANCE
|
||||
) );
|
||||
}
|
||||
|
||||
private static void registerProviders()
|
||||
{
|
||||
// Register bundled power providers
|
||||
ComputerCraftAPI.registerBundledRedstoneProvider( new DefaultBundledRedstoneProvider() );
|
||||
|
||||
// Register media providers
|
||||
ComputerCraftAPI.registerMediaProvider( stack -> {
|
||||
Item item = stack.getItem();
|
||||
if( item instanceof IMedia ) return (IMedia) item;
|
||||
if( item instanceof MusicDiscItem ) return RecordMedia.INSTANCE;
|
||||
return null;
|
||||
} );
|
||||
|
||||
// Register capabilities
|
||||
CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage<>(), () -> null );
|
||||
CapabilityManager.INSTANCE.register( IPeripheral.class, new NullStorage<>(), () -> null );
|
||||
|
||||
// Register generic capabilities. This can technically be done off-thread, but we need it to happen
|
||||
// after Forge's common setup, so this is easiest.
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityEnergy.ENERGY );
|
||||
ComputerCraftAPI.registerGenericCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
|
||||
}
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID )
|
||||
public static final class ForgeHandlers
|
||||
{
|
||||
private ForgeHandlers()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
@SubscribeEvent
|
||||
public static void onConnectionOpened( FMLNetworkEvent.ClientConnectedToServerEvent event )
|
||||
{
|
||||
ComputerCraft.clientComputerRegistry.reset();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onConnectionClosed( FMLNetworkEvent.ClientDisconnectionFromServerEvent event )
|
||||
{
|
||||
ComputerCraft.clientComputerRegistry.reset();
|
||||
}
|
||||
*/
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerTick( TickEvent.ServerTickEvent event )
|
||||
{
|
||||
if( event.phase == TickEvent.Phase.START )
|
||||
{
|
||||
MainThread.executePendingTasks();
|
||||
ComputerCraft.serverComputerRegistry.update();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onContainerOpen( PlayerContainerEvent.Open event )
|
||||
{
|
||||
// If we're opening a computer container then broadcast the terminal state
|
||||
Container container = event.getContainer();
|
||||
if( container instanceof IContainerComputer )
|
||||
{
|
||||
IComputer computer = ((IContainerComputer) container).getComputer();
|
||||
if( computer instanceof ServerComputer )
|
||||
{
|
||||
((ServerComputer) computer).sendTerminalState( event.getPlayer() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarting( FMLServerStartingEvent event )
|
||||
{
|
||||
CommandComputerCraft.register( event.getCommandDispatcher() );
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarted( FMLServerStartedEvent event )
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStopped( FMLServerStoppedEvent event )
|
||||
{
|
||||
ComputerCraft.serverComputerRegistry.reset();
|
||||
WirelessNetwork.resetNetworks();
|
||||
Tracking.reset();
|
||||
}
|
||||
|
||||
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation( ComputerCraft.MOD_ID, "treasure_disk" );
|
||||
|
||||
private static final Set<ResourceLocation> TABLES = new HashSet<>( Arrays.asList(
|
||||
LootTables.SIMPLE_DUNGEON,
|
||||
LootTables.ABANDONED_MINESHAFT,
|
||||
LootTables.STRONGHOLD_CORRIDOR,
|
||||
LootTables.STRONGHOLD_CROSSING,
|
||||
LootTables.STRONGHOLD_LIBRARY,
|
||||
LootTables.DESERT_PYRAMID,
|
||||
LootTables.JUNGLE_TEMPLE,
|
||||
LootTables.IGLOO_CHEST,
|
||||
LootTables.WOODLAND_MANSION,
|
||||
LootTables.VILLAGE_CARTOGRAPHER
|
||||
) );
|
||||
|
||||
@SubscribeEvent
|
||||
public static void lootLoad( LootTableLoadEvent event )
|
||||
{
|
||||
ResourceLocation name = event.getName();
|
||||
if( !name.getNamespace().equals( "minecraft" ) || !TABLES.contains( name ) ) return;
|
||||
|
||||
event.getTable().addPool( LootPool.lootPool()
|
||||
.add( TableLootEntry.lootTableReference( LOOT_TREASURE_DISK ) )
|
||||
.setRolls( ConstantRange.exactly( 1 ) )
|
||||
.name( "computercraft_treasure" )
|
||||
.build() );
|
||||
}
|
||||
}
|
||||
}
|
@@ -715,7 +715,6 @@ public class TurtleAPI implements ILuaAPI
|
||||
* more information about the item at the cost of taking longer to run.
|
||||
* @return The command result.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @see InventoryMethods#getItemDetail Describes the information returned by a detailed query.
|
||||
* @cc.treturn nil|table Information about the given slot, or {@code nil} if it is empty.
|
||||
* @cc.usage Print the current slot, assuming it contains 13 dirt.
|
||||
*
|
||||
@@ -726,6 +725,7 @@ public class TurtleAPI implements ILuaAPI
|
||||
* -- count = 13,
|
||||
* -- }
|
||||
* }</pre>
|
||||
* @see InventoryMethods#getItemDetail Describes the information returned by a detailed query.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult getItemDetail( ILuaContext context, Optional<Integer> slot, Optional<Boolean> detailed ) throws LuaException
|
||||
|
@@ -160,8 +160,8 @@ public class TurtleBrain implements ITurtleAccess
|
||||
overlay = nbt.contains( NBT_OVERLAY ) ? new ResourceLocation( nbt.getString( NBT_OVERLAY ) ) : null;
|
||||
|
||||
// Read upgrades
|
||||
setUpgrade( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_LEFT_UPGRADE ) ) : null );
|
||||
setUpgrade( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null );
|
||||
setUpgradeDirect( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_LEFT_UPGRADE ) ) : null );
|
||||
setUpgradeDirect( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null );
|
||||
|
||||
// NBT
|
||||
upgradeNBTData.clear();
|
||||
@@ -503,20 +503,15 @@ public class TurtleBrain implements ITurtleAccess
|
||||
setFuelLevel( getFuelLevel() + addition );
|
||||
}
|
||||
|
||||
private int issueCommand( ITurtleCommand command )
|
||||
{
|
||||
commandQueue.offer( new TurtleCommandQueueEntry( ++commandsIssued, command ) );
|
||||
return commandsIssued;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MethodResult executeCommand( @Nonnull ITurtleCommand command )
|
||||
{
|
||||
if( getWorld().isClientSide ) throw new UnsupportedOperationException( "Cannot run commands on the client" );
|
||||
if( commandQueue.size() > 16 ) return MethodResult.of( false, "Too many ongoing turtle commands" );
|
||||
|
||||
// Issue command
|
||||
int commandID = issueCommand( command );
|
||||
commandQueue.offer( new TurtleCommandQueueEntry( ++commandsIssued, command ) );
|
||||
int commandID = commandsIssued;
|
||||
return new CommandCallback( commandID ).pull;
|
||||
}
|
||||
|
||||
@@ -618,16 +613,30 @@ public class TurtleBrain implements ITurtleAccess
|
||||
|
||||
@Override
|
||||
public void setUpgrade( @Nonnull TurtleSide side, ITurtleUpgrade upgrade )
|
||||
{
|
||||
if( !setUpgradeDirect( side, upgrade ) ) return;
|
||||
|
||||
// This is a separate function to avoid updating the block when reading the NBT. We don't need to do this as
|
||||
// either the block is newly placed (and so won't have changed) or is being updated with /data, which calls
|
||||
// updateBlock for us.
|
||||
if( owner.getLevel() != null )
|
||||
{
|
||||
owner.updateBlock();
|
||||
owner.updateInput();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setUpgradeDirect( @Nonnull TurtleSide side, ITurtleUpgrade upgrade )
|
||||
{
|
||||
// Remove old upgrade
|
||||
if( upgrades.containsKey( side ) )
|
||||
{
|
||||
if( upgrades.get( side ) == upgrade ) return;
|
||||
if( upgrades.get( side ) == upgrade ) return false;
|
||||
upgrades.remove( side );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( upgrade == null ) return;
|
||||
if( upgrade == null ) return false;
|
||||
}
|
||||
|
||||
upgradeNBTData.remove( side );
|
||||
@@ -639,8 +648,9 @@ public class TurtleBrain implements ITurtleAccess
|
||||
if( owner.getLevel() != null )
|
||||
{
|
||||
updatePeripherals( owner.createServerComputer() );
|
||||
owner.updateBlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -62,7 +62,7 @@ public class TurtleDropCommand implements ITurtleCommand
|
||||
IItemHandler inventory = InventoryUtil.getInventory( world, newPosition, side );
|
||||
|
||||
// Fire the event, restoring the inventory and exiting if it is cancelled.
|
||||
TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
|
||||
TurtlePlayer player = TurtlePlayer.getWithPosition( turtle, oldPosition, direction );
|
||||
TurtleInventoryEvent.Drop event = new TurtleInventoryEvent.Drop( turtle, player, world, newPosition, inventory, stack );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) )
|
||||
{
|
||||
|
@@ -50,7 +50,7 @@ public class TurtleInspectCommand implements ITurtleCommand
|
||||
Map<String, Object> table = BlockData.fill( new HashMap<>(), state );
|
||||
|
||||
// Fire the event, exiting if it is cancelled
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, oldPosition, direction );
|
||||
TurtleBlockEvent.Inspect event = new TurtleBlockEvent.Inspect( turtle, turtlePlayer, world, newPosition, state, table );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) ) return TurtleCommandResult.failure( event.getFailureMessage() );
|
||||
|
||||
|
@@ -47,7 +47,7 @@ public class TurtleMoveCommand implements ITurtleCommand
|
||||
BlockPos oldPosition = turtle.getPosition();
|
||||
BlockPos newPosition = oldPosition.relative( direction );
|
||||
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, oldPosition, direction );
|
||||
TurtleCommandResult canEnterResult = canEnter( turtlePlayer, oldWorld, newPosition );
|
||||
if( !canEnterResult.isSuccess() )
|
||||
{
|
||||
|
@@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleAnimation;
|
||||
import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
import dan200.computercraft.shared.TurtlePermissions;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import dan200.computercraft.shared.util.DropConsumer;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
@@ -20,6 +19,7 @@ import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.network.play.client.CUseEntityPacket;
|
||||
import net.minecraft.tileentity.SignTileEntity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
@@ -34,11 +34,13 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.wrapper.InvWrapper;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraftforge.eventbus.api.Event.Result;
|
||||
|
||||
public class TurtlePlaceCommand implements ITurtleCommand
|
||||
{
|
||||
@@ -57,10 +59,7 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
{
|
||||
// Get thing to place
|
||||
ItemStack stack = turtle.getInventory().getItem( turtle.getSelectedSlot() );
|
||||
if( stack.isEmpty() )
|
||||
{
|
||||
return TurtleCommandResult.failure( "No items to place" );
|
||||
}
|
||||
if( stack.isEmpty() ) return TurtleCommandResult.failure( "No items to place" );
|
||||
|
||||
// Remember old block
|
||||
Direction direction = this.direction.toWorldDir( turtle );
|
||||
@@ -68,144 +67,63 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
|
||||
// Create a fake player, and orient it appropriately
|
||||
BlockPos playerPosition = turtle.getPosition().relative( direction );
|
||||
TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, playerPosition, direction );
|
||||
|
||||
TurtleBlockEvent.Place place = new TurtleBlockEvent.Place( turtle, turtlePlayer, turtle.getWorld(), coordinates, stack );
|
||||
if( MinecraftForge.EVENT_BUS.post( place ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( place.getFailureMessage() );
|
||||
}
|
||||
if( MinecraftForge.EVENT_BUS.post( place ) ) return TurtleCommandResult.failure( place.getFailureMessage() );
|
||||
|
||||
// Do the deploying
|
||||
String[] errorMessage = new String[1];
|
||||
ItemStack remainder = deploy( stack, turtle, turtlePlayer, direction, extraArguments, errorMessage );
|
||||
if( remainder != stack )
|
||||
turtlePlayer.loadInventory( turtle );
|
||||
ErrorMessage message = new ErrorMessage();
|
||||
boolean result = deploy( stack, turtle, turtlePlayer, direction, extraArguments, message );
|
||||
turtlePlayer.unloadInventory( turtle );
|
||||
if( result )
|
||||
{
|
||||
// Put the remaining items back
|
||||
turtle.getInventory().setItem( turtle.getSelectedSlot(), remainder );
|
||||
turtle.getInventory().setChanged();
|
||||
|
||||
// Animate and return success
|
||||
turtle.playAnimation( TurtleAnimation.WAIT );
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
else if( message.message != null )
|
||||
{
|
||||
return TurtleCommandResult.failure( message.message );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( errorMessage[0] != null )
|
||||
{
|
||||
return TurtleCommandResult.failure( errorMessage[0] );
|
||||
}
|
||||
else if( stack.getItem() instanceof BlockItem )
|
||||
{
|
||||
return TurtleCommandResult.failure( "Cannot place block here" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return TurtleCommandResult.failure( "Cannot place item here" );
|
||||
}
|
||||
return TurtleCommandResult.failure( stack.getItem() instanceof BlockItem ? "Cannot place block here" : "Cannot place item here" );
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, Direction direction, Object[] extraArguments, String[] outErrorMessage )
|
||||
public static boolean deployCopiedItem( @Nonnull ItemStack stack, ITurtleAccess turtle, Direction direction, Object[] extraArguments, ErrorMessage outErrorMessage )
|
||||
{
|
||||
// Create a fake player, and orient it appropriately
|
||||
BlockPos playerPosition = turtle.getPosition().relative( direction );
|
||||
TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction );
|
||||
|
||||
return deploy( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, playerPosition, direction );
|
||||
turtlePlayer.loadInventory( stack );
|
||||
boolean result = deploy( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage );
|
||||
turtlePlayer.inventory.clearContent();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, String[] outErrorMessage )
|
||||
private static boolean deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, ErrorMessage outErrorMessage )
|
||||
{
|
||||
// Deploy on an entity
|
||||
ItemStack remainder = deployOnEntity( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
if( deployOnEntity( stack, turtle, turtlePlayer ) ) return true;
|
||||
|
||||
// Deploy on the block immediately in front
|
||||
BlockPos position = turtle.getPosition();
|
||||
BlockPos newPosition = position.relative( direction );
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition, direction.getOpposite(), extraArguments, true, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
|
||||
// Deploy on the block one block away
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition.relative( direction ), direction.getOpposite(), extraArguments, false, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
|
||||
if( direction.getAxis() != Direction.Axis.Y )
|
||||
{
|
||||
// Try to deploy against a block. Tries the following options:
|
||||
// Deploy on the block immediately in front
|
||||
return deployOnBlock( stack, turtle, turtlePlayer, newPosition, direction.getOpposite(), extraArguments, true, outErrorMessage )
|
||||
// Deploy on the block one block away
|
||||
|| deployOnBlock( stack, turtle, turtlePlayer, newPosition.relative( direction ), direction.getOpposite(), extraArguments, false, outErrorMessage )
|
||||
// Deploy down on the block in front
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition.below(), Direction.UP, extraArguments, false, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy back onto the turtle
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, position, direction, extraArguments, false, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
|
||||
// If nothing worked, return the original stack unchanged
|
||||
return stack;
|
||||
|| (direction.getAxis() != Direction.Axis.Y && deployOnBlock( stack, turtle, turtlePlayer, newPosition.below(), Direction.UP, extraArguments, false, outErrorMessage ))
|
||||
// Deploy back onto the turtle
|
||||
|| deployOnBlock( stack, turtle, turtlePlayer, position, direction, extraArguments, false, outErrorMessage );
|
||||
}
|
||||
|
||||
public static TurtlePlayer createPlayer( ITurtleAccess turtle, BlockPos position, Direction direction )
|
||||
{
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.get( turtle );
|
||||
orientPlayer( turtle, turtlePlayer, position, direction );
|
||||
return turtlePlayer;
|
||||
}
|
||||
|
||||
private static void orientPlayer( ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction direction )
|
||||
{
|
||||
double posX = position.getX() + 0.5;
|
||||
double posY = position.getY() + 0.5;
|
||||
double posZ = position.getZ() + 0.5;
|
||||
|
||||
// Stop intersection with the turtle itself
|
||||
if( turtle.getPosition().equals( position ) )
|
||||
{
|
||||
posX += 0.48 * direction.getStepX();
|
||||
posY += 0.48 * direction.getStepY();
|
||||
posZ += 0.48 * direction.getStepZ();
|
||||
}
|
||||
|
||||
if( direction.getAxis() != Direction.Axis.Y )
|
||||
{
|
||||
turtlePlayer.yRot = direction.toYRot();
|
||||
turtlePlayer.xRot = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
turtlePlayer.yRot = turtle.getDirection().toYRot();
|
||||
turtlePlayer.xRot = DirectionUtil.toPitchAngle( direction );
|
||||
}
|
||||
|
||||
turtlePlayer.setPosRaw( posX, posY, posZ );
|
||||
turtlePlayer.xo = posX;
|
||||
turtlePlayer.yo = posY;
|
||||
turtlePlayer.zo = posZ;
|
||||
turtlePlayer.xRotO = turtlePlayer.xRot;
|
||||
turtlePlayer.yRotO = turtlePlayer.yRot;
|
||||
|
||||
turtlePlayer.yHeadRot = turtlePlayer.yRot;
|
||||
turtlePlayer.yHeadRotO = turtlePlayer.yHeadRot;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ItemStack deployOnEntity( @Nonnull ItemStack stack, final ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, String[] outErrorMessage )
|
||||
private static boolean deployOnEntity( @Nonnull ItemStack stack, final ITurtleAccess turtle, TurtlePlayer turtlePlayer )
|
||||
{
|
||||
// See if there is an entity present
|
||||
final World world = turtle.getWorld();
|
||||
@@ -213,81 +131,57 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
Vec3d turtlePos = turtlePlayer.position();
|
||||
Vec3d rayDir = turtlePlayer.getViewVector( 1.0f );
|
||||
Pair<Entity, Vec3d> hit = WorldUtil.rayTraceEntities( world, turtlePos, rayDir, 1.5 );
|
||||
if( hit == null )
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Load up the turtle's inventory
|
||||
ItemStack stackCopy = stack.copy();
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
if( hit == null ) return false;
|
||||
|
||||
// Start claiming entity drops
|
||||
Entity hitEntity = hit.getKey();
|
||||
Vec3d hitPos = hit.getValue();
|
||||
DropConsumer.set(
|
||||
hitEntity,
|
||||
drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() )
|
||||
);
|
||||
|
||||
// Place on the entity
|
||||
boolean placed = false;
|
||||
ActionResultType cancelResult = ForgeHooks.onInteractEntityAt( turtlePlayer, hitEntity, hitPos, Hand.MAIN_HAND );
|
||||
if( cancelResult == null )
|
||||
{
|
||||
cancelResult = hitEntity.interactAt( turtlePlayer, hitPos, Hand.MAIN_HAND );
|
||||
}
|
||||
IItemHandler itemHandler = new InvWrapper( turtlePlayer.inventory );
|
||||
DropConsumer.set( hitEntity, drop -> InventoryUtil.storeItems( drop, itemHandler, 1 ) );
|
||||
|
||||
if( cancelResult == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// See EntityPlayer.interactOn
|
||||
cancelResult = ForgeHooks.onInteractEntity( turtlePlayer, hitEntity, Hand.MAIN_HAND );
|
||||
if( cancelResult == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else if( cancelResult == null )
|
||||
{
|
||||
if( hitEntity.interact( turtlePlayer, Hand.MAIN_HAND ) )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else if( hitEntity instanceof LivingEntity )
|
||||
{
|
||||
placed = stackCopy.interactEnemy( turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND );
|
||||
if( placed ) turtlePlayer.loadInventory( stackCopy );
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean placed = doDeployOnEntity( stack, turtlePlayer, hitEntity, hitPos );
|
||||
|
||||
// Stop claiming drops
|
||||
List<ItemStack> remainingDrops = DropConsumer.clear();
|
||||
for( ItemStack remaining : remainingDrops )
|
||||
{
|
||||
WorldUtil.dropItemStack( remaining, world, position, turtle.getDirection().getOpposite() );
|
||||
}
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
||||
if( !placed && ItemStack.matches( stack, remainder ) )
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
else if( !remainder.isEmpty() )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
DropConsumer.clearAndDrop( world, position, turtle.getDirection().getOpposite() );
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static boolean canDeployOnBlock( @Nonnull BlockItemUseContext context, ITurtleAccess turtle, TurtlePlayer player, BlockPos position, Direction side, boolean allowReplaceable, String[] outErrorMessage )
|
||||
/**
|
||||
* Place a block onto an entity. For instance, feeding cows.
|
||||
*
|
||||
* @param stack The stack we're placing.
|
||||
* @param turtlePlayer The player of the turtle we're placing.
|
||||
* @param hitEntity The entity we're interacting with.
|
||||
* @param hitPos The position our ray trace hit the entity.
|
||||
* @return If this item was deployed.
|
||||
* @see net.minecraft.network.play.ServerPlayNetHandler#handleInteract(CUseEntityPacket)
|
||||
* @see net.minecraft.entity.player.PlayerEntity#interactOn(Entity, Hand)
|
||||
*/
|
||||
private static boolean doDeployOnEntity( @Nonnull ItemStack stack, TurtlePlayer turtlePlayer, @Nonnull Entity hitEntity, @Nonnull Vec3d hitPos )
|
||||
{
|
||||
// Placing "onto" a block follows two flows. First we try to interactAt. If that doesn't succeed, then we try to
|
||||
// call the normal interact path. Cancelling an interactAt *does not* cancel a normal interact path.
|
||||
|
||||
ActionResultType interactAt = ForgeHooks.onInteractEntityAt( turtlePlayer, hitEntity, hitPos, Hand.MAIN_HAND );
|
||||
if( interactAt == null ) interactAt = hitEntity.interactAt( turtlePlayer, hitPos, Hand.MAIN_HAND );
|
||||
if( interactAt.consumesAction() ) return true;
|
||||
|
||||
ActionResultType interact = ForgeHooks.onInteractEntity( turtlePlayer, hitEntity, Hand.MAIN_HAND );
|
||||
if( interact != null ) return interact.consumesAction();
|
||||
|
||||
if( hitEntity.interact( turtlePlayer, Hand.MAIN_HAND ) ) return true;
|
||||
if( hitEntity instanceof LivingEntity )
|
||||
{
|
||||
return stack.interactEnemy( turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canDeployOnBlock(
|
||||
@Nonnull BlockItemUseContext context, ITurtleAccess turtle, TurtlePlayer player, BlockPos position,
|
||||
Direction side, boolean allowReplaceable, ErrorMessage outErrorMessage
|
||||
)
|
||||
{
|
||||
World world = turtle.getWorld();
|
||||
if( !World.isInWorldBounds( position ) || world.isEmptyBlock( position ) ||
|
||||
@@ -309,7 +203,7 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
: TurtlePermissions.isBlockEditable( world, position.relative( side ), player );
|
||||
if( !editable )
|
||||
{
|
||||
if( outErrorMessage != null ) outErrorMessage[0] = "Cannot place in protected area";
|
||||
if( outErrorMessage != null ) outErrorMessage.message = "Cannot place in protected area";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -317,131 +211,123 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ItemStack deployOnBlock( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side, Object[] extraArguments, boolean allowReplace, String[] outErrorMessage )
|
||||
private static boolean deployOnBlock(
|
||||
@Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side,
|
||||
Object[] extraArguments, boolean allowReplace, ErrorMessage outErrorMessage
|
||||
)
|
||||
{
|
||||
// Re-orient the fake player
|
||||
Direction playerDir = side.getOpposite();
|
||||
BlockPos playerPosition = position.relative( side );
|
||||
orientPlayer( turtle, turtlePlayer, playerPosition, playerDir );
|
||||
|
||||
ItemStack stackCopy = stack.copy();
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
turtlePlayer.setPosition( turtle, playerPosition, playerDir );
|
||||
|
||||
// Calculate where the turtle would hit the block
|
||||
float hitX = 0.5f + side.getStepX() * 0.5f;
|
||||
float hitY = 0.5f + side.getStepY() * 0.5f;
|
||||
float hitZ = 0.5f + side.getStepZ() * 0.5f;
|
||||
if( Math.abs( hitY - 0.5f ) < 0.01f )
|
||||
{
|
||||
hitY = 0.45f;
|
||||
}
|
||||
if( Math.abs( hitY - 0.5f ) < 0.01f ) hitY = 0.45f;
|
||||
|
||||
// Check if there's something suitable to place onto
|
||||
BlockRayTraceResult hit = new BlockRayTraceResult( new Vec3d( hitX, hitY, hitZ ), side, position, false );
|
||||
ItemUseContext context = new ItemUseContext( turtlePlayer, Hand.MAIN_HAND, hit );
|
||||
if( !canDeployOnBlock( new BlockItemUseContext( context ), turtle, turtlePlayer, position, side, allowReplace, outErrorMessage ) )
|
||||
{
|
||||
return stack;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load up the turtle's inventory
|
||||
Item item = stack.getItem();
|
||||
|
||||
// Do the deploying (put everything in the players inventory)
|
||||
boolean placed = false;
|
||||
TileEntity existingTile = turtle.getWorld().getBlockEntity( position );
|
||||
|
||||
// See PlayerInteractionManager.processRightClickBlock
|
||||
// TODO: ^ Check we're still consistent.
|
||||
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, Hand.MAIN_HAND, position, side );
|
||||
if( !event.isCanceled() )
|
||||
{
|
||||
if( item.onItemUseFirst( stack, context ) == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
}
|
||||
else if( event.getUseItem() != Event.Result.DENY &&
|
||||
stackCopy.useOn( context ) == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
}
|
||||
}
|
||||
|
||||
if( !placed && (item instanceof BucketItem || item instanceof BoatItem || item instanceof LilyPadItem || item instanceof GlassBottleItem) )
|
||||
{
|
||||
ActionResultType actionResult = ForgeHooks.onItemRightClick( turtlePlayer, Hand.MAIN_HAND );
|
||||
if( actionResult == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else if( actionResult == null )
|
||||
{
|
||||
ActionResult<ItemStack> result = stackCopy.use( turtle.getWorld(), turtlePlayer, Hand.MAIN_HAND );
|
||||
if( result.getResult() == ActionResultType.SUCCESS && !ItemStack.matches( stack, result.getObject() ) )
|
||||
{
|
||||
placed = true;
|
||||
turtlePlayer.loadInventory( result.getObject() );
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean placed = doDeployOnBlock( stack, turtlePlayer, position, side, context ).consumesAction();
|
||||
|
||||
// Set text on signs
|
||||
if( placed && item instanceof SignItem )
|
||||
if( placed && item instanceof SignItem && extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String )
|
||||
{
|
||||
if( extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String )
|
||||
World world = turtle.getWorld();
|
||||
TileEntity tile = world.getBlockEntity( position );
|
||||
if( tile == null || tile == existingTile )
|
||||
{
|
||||
World world = turtle.getWorld();
|
||||
TileEntity tile = world.getBlockEntity( position );
|
||||
if( tile == null || tile == existingTile )
|
||||
{
|
||||
tile = world.getBlockEntity( position.relative( side ) );
|
||||
}
|
||||
if( tile instanceof SignTileEntity )
|
||||
{
|
||||
SignTileEntity signTile = (SignTileEntity) tile;
|
||||
String s = (String) extraArguments[0];
|
||||
String[] split = s.split( "\n" );
|
||||
int firstLine = split.length <= 2 ? 1 : 0;
|
||||
for( int i = 0; i < signTile.messages.length; i++ )
|
||||
{
|
||||
if( i >= firstLine && i < firstLine + split.length )
|
||||
{
|
||||
if( split[i - firstLine].length() > 15 )
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( split[i - firstLine].substring( 0, 15 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( split[i - firstLine] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( "" );
|
||||
}
|
||||
}
|
||||
signTile.setChanged();
|
||||
world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), 3 );
|
||||
}
|
||||
tile = world.getBlockEntity( position.relative( side ) );
|
||||
}
|
||||
|
||||
if( tile instanceof SignTileEntity ) setSignText( world, tile, (String) extraArguments[0] );
|
||||
}
|
||||
|
||||
return placed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to place an item into the world. Returns true/false if an item was placed.
|
||||
*
|
||||
* @param stack The stack the player is using.
|
||||
* @param turtlePlayer The player which represents the turtle
|
||||
* @param position The block we're deploying against's position.
|
||||
* @param side The side of the block we're deploying against.
|
||||
* @param context The context of this place action.
|
||||
* @return If this item was deployed.
|
||||
* @see net.minecraft.server.management.PlayerInteractionManager#useItemOn For the original implementation.
|
||||
*/
|
||||
private static ActionResultType doDeployOnBlock(
|
||||
@Nonnull ItemStack stack, TurtlePlayer turtlePlayer, BlockPos position, Direction side, ItemUseContext context
|
||||
)
|
||||
{
|
||||
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, Hand.MAIN_HAND, position, side );
|
||||
if( event.isCanceled() ) return event.getCancellationResult();
|
||||
|
||||
if( event.getUseItem() != Result.DENY )
|
||||
{
|
||||
ActionResultType result = stack.onItemUseFirst( context );
|
||||
if( result != ActionResultType.PASS ) return result;
|
||||
}
|
||||
|
||||
if( event.getUseItem() != Result.DENY )
|
||||
{
|
||||
ActionResultType result = stack.useOn( context );
|
||||
if( result != ActionResultType.PASS ) return result;
|
||||
}
|
||||
|
||||
Item item = stack.getItem();
|
||||
if( item instanceof BucketItem || item instanceof BoatItem || item instanceof LilyPadItem || item instanceof GlassBottleItem )
|
||||
{
|
||||
ActionResultType actionResult = ForgeHooks.onItemRightClick( turtlePlayer, Hand.MAIN_HAND );
|
||||
if( actionResult != null && actionResult != ActionResultType.PASS ) return actionResult;
|
||||
|
||||
ActionResult<ItemStack> result = stack.use( context.getLevel(), turtlePlayer, Hand.MAIN_HAND );
|
||||
if( result.getResult().consumesAction() && !ItemStack.matches( stack, result.getObject() ) )
|
||||
{
|
||||
turtlePlayer.setItemInHand( Hand.MAIN_HAND, result.getObject() );
|
||||
return result.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
||||
if( !placed && ItemStack.matches( stack, remainder ) )
|
||||
return ActionResultType.PASS;
|
||||
}
|
||||
|
||||
private static void setSignText( World world, TileEntity tile, String message )
|
||||
{
|
||||
SignTileEntity signTile = (SignTileEntity) tile;
|
||||
String[] split = message.split( "\n" );
|
||||
int firstLine = split.length <= 2 ? 1 : 0;
|
||||
for( int i = 0; i < signTile.messages.length; i++ )
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
else if( !remainder.isEmpty() )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
if( i >= firstLine && i < firstLine + split.length )
|
||||
{
|
||||
String line = split[i - firstLine];
|
||||
signTile.messages[i] = line.length() > 15
|
||||
? new StringTextComponent( line.substring( 0, 15 ) )
|
||||
: new StringTextComponent( line );
|
||||
}
|
||||
else
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( "" );
|
||||
}
|
||||
}
|
||||
signTile.setChanged();
|
||||
world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), 3 );
|
||||
}
|
||||
|
||||
private static class ErrorMessage
|
||||
{
|
||||
String message;
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import dan200.computercraft.shared.util.FakeNetHandler;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
@@ -73,23 +74,6 @@ public final class TurtlePlayer extends FakePlayer
|
||||
return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE;
|
||||
}
|
||||
|
||||
private void setState( ITurtleAccess turtle )
|
||||
{
|
||||
if( containerMenu != inventoryMenu )
|
||||
{
|
||||
ComputerCraft.log.warn( "Turtle has open container ({})", containerMenu );
|
||||
doCloseContainer();
|
||||
}
|
||||
|
||||
BlockPos position = turtle.getPosition();
|
||||
setPosRaw( position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5 );
|
||||
|
||||
yRot = turtle.getDirection().toYRot();
|
||||
xRot = 0.0f;
|
||||
|
||||
inventory.clearContent();
|
||||
}
|
||||
|
||||
public static TurtlePlayer get( ITurtleAccess access )
|
||||
{
|
||||
if( !(access instanceof TurtleBrain) ) return create( access );
|
||||
@@ -109,37 +93,114 @@ public final class TurtlePlayer extends FakePlayer
|
||||
return player;
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ItemStack currentStack )
|
||||
public static TurtlePlayer getWithPosition( ITurtleAccess turtle, BlockPos position, Direction direction )
|
||||
{
|
||||
// Load up the fake inventory
|
||||
inventory.selected = 0;
|
||||
inventory.setItem( 0, currentStack );
|
||||
TurtlePlayer turtlePlayer = get( turtle );
|
||||
turtlePlayer.setPosition( turtle, position, direction );
|
||||
return turtlePlayer;
|
||||
}
|
||||
|
||||
public ItemStack unloadInventory( ITurtleAccess turtle )
|
||||
private void setState( ITurtleAccess turtle )
|
||||
{
|
||||
// Get the item we placed with
|
||||
ItemStack results = inventory.getItem( 0 );
|
||||
inventory.setItem( 0, ItemStack.EMPTY );
|
||||
if( containerMenu != inventoryMenu )
|
||||
{
|
||||
ComputerCraft.log.warn( "Turtle has open container ({})", containerMenu );
|
||||
doCloseContainer();
|
||||
}
|
||||
|
||||
BlockPos position = turtle.getPosition();
|
||||
setPosRaw( position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5 );
|
||||
|
||||
yRot = turtle.getDirection().toYRot();
|
||||
xRot = 0.0f;
|
||||
|
||||
inventory.clearContent();
|
||||
}
|
||||
|
||||
public void setPosition( ITurtleAccess turtle, BlockPos position, Direction direction )
|
||||
{
|
||||
double posX = position.getX() + 0.5;
|
||||
double posY = position.getY() + 0.5;
|
||||
double posZ = position.getZ() + 0.5;
|
||||
|
||||
// Stop intersection with the turtle itself
|
||||
if( turtle.getPosition().equals( position ) )
|
||||
{
|
||||
posX += 0.48 * direction.getStepX();
|
||||
posY += 0.48 * direction.getStepY();
|
||||
posZ += 0.48 * direction.getStepZ();
|
||||
}
|
||||
|
||||
if( direction.getAxis() != Direction.Axis.Y )
|
||||
{
|
||||
yRot = direction.toYRot();
|
||||
xRot = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
yRot = turtle.getDirection().toYRot();
|
||||
xRot = DirectionUtil.toPitchAngle( direction );
|
||||
}
|
||||
|
||||
setPosRaw( posX, posY, posZ );
|
||||
xo = posX;
|
||||
yo = posY;
|
||||
zo = posZ;
|
||||
xRotO = xRot;
|
||||
yRotO = yRot;
|
||||
|
||||
yHeadRot = yRot;
|
||||
yHeadRotO = yHeadRot;
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ItemStack stack )
|
||||
{
|
||||
inventory.clearContent();
|
||||
inventory.selected = 0;
|
||||
inventory.setItem( 0, stack );
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ITurtleAccess turtle )
|
||||
{
|
||||
inventory.clearContent();
|
||||
|
||||
int currentSlot = turtle.getSelectedSlot();
|
||||
int slots = turtle.getItemHandler().getSlots();
|
||||
|
||||
// Load up the fake inventory
|
||||
inventory.selected = 0;
|
||||
for( int i = 0; i < slots; i++ )
|
||||
{
|
||||
inventory.setItem( i, turtle.getItemHandler().getStackInSlot( (currentSlot + i) % slots ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void unloadInventory( ITurtleAccess turtle )
|
||||
{
|
||||
int currentSlot = turtle.getSelectedSlot();
|
||||
int slots = turtle.getItemHandler().getSlots();
|
||||
|
||||
// Load up the fake inventory
|
||||
inventory.selected = 0;
|
||||
for( int i = 0; i < slots; i++ )
|
||||
{
|
||||
turtle.getItemHandler().setStackInSlot( (currentSlot + i) % slots, inventory.getItem( i ) );
|
||||
}
|
||||
|
||||
// Store (or drop) anything else we found
|
||||
BlockPos dropPosition = turtle.getPosition();
|
||||
Direction dropDirection = turtle.getDirection().getOpposite();
|
||||
for( int i = 0; i < inventory.getContainerSize(); i++ )
|
||||
int totalSize = inventory.getContainerSize();
|
||||
for( int i = slots; i < totalSize; i++ )
|
||||
{
|
||||
ItemStack stack = inventory.getItem( i );
|
||||
if( !stack.isEmpty() )
|
||||
ItemStack remainder = InventoryUtil.storeItems( inventory.getItem( i ), turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), dropPosition, dropDirection );
|
||||
}
|
||||
inventory.setItem( i, ItemStack.EMPTY );
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), dropPosition, dropDirection );
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setChanged();
|
||||
return results;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@@ -58,7 +58,7 @@ public class TurtleSuckCommand implements ITurtleCommand
|
||||
IItemHandler inventory = InventoryUtil.getInventory( world, blockPosition, side );
|
||||
|
||||
// Fire the event, exiting if it is cancelled.
|
||||
TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, turtlePosition, direction );
|
||||
TurtlePlayer player = TurtlePlayer.getWithPosition( turtle, turtlePosition, direction );
|
||||
TurtleInventoryEvent.Suck event = new TurtleInventoryEvent.Suck( turtle, player, world, blockPosition, inventory );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) )
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user