Compare commits
	
		
			30 Commits
		
	
	
		
			v1.80pr1.1
			...
			v1.12.2-1.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a4cd1fe77d | ||
|   | 4145914024 | ||
|   | 6bd11a5e4a | ||
|   | 46fa798797 | ||
|   | 70a226207e | ||
|   | 257a35f3ed | ||
|   | af01b9514b | ||
|   | 070fd1f2ff | ||
|   | fb59da2b06 | ||
|   | 11e4d0de82 | ||
|   | e46ab1e267 | ||
|   | d6e0f368df | ||
|   | 9f2884bc0f | ||
|   | 18d468e887 | ||
|   | 63f6735bb8 | ||
|   | ab6f0ccd16 | ||
|   | ae0f093e73 | ||
|   | e5f988e3fe | ||
|   | 12e82afad2 | ||
|   | 6c2db93cbd | ||
|   | d5edbe700b | ||
|   | 86ad43c3ab | ||
|   | f450c0156b | ||
|   | 8abcfcb4ac | ||
|   | f3cace1d03 | ||
|   | e1e5e898ab | ||
|   | 3aa3852ff6 | ||
|   | 709a6329c7 | ||
|   | c9f05a2939 | ||
|   | e41377f862 | 
							
								
								
									
										5
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -6,11 +6,10 @@ about: Report some misbehaviour in the mod | ||||
|  | ||||
| <!-- | ||||
| ## Before reporting | ||||
|  - Search for the bug both here and [on the ComputerCraft issues page](https://github.com/dan200/ComputerCraft/issues?utf8=%E2%9C%93&q=is%3Aissue+) | ||||
|  - If possible, try to reproduce on vanilla ComputerCraft. If it still occurs, [report on the ComputerCraft repo](https://github.com/dan200/ComputerCraft/issues/new) instead. | ||||
|  - 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 | ||||
|  - Detailed reproduction steps!** Sometimes I can spot a bug pretty easily, but often it's much more obscure. Anything you can give which will help reproduce it means it'll get fixed quicker. | ||||
|  - 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. | ||||
|   | ||||
							
								
								
									
										7
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -6,10 +6,9 @@ about: Suggest an idea or improvement | ||||
|  | ||||
| <!-- | ||||
| ## Before reporting | ||||
|  - Search for the suggestion both here and [on the ComputerCraft issues page](https://github.com/dan200/ComputerCraft/issues?utf8=%E2%9C%93&q=is%3Aissue+). It's possible someone's suggested it before! | ||||
|  - Unless something is specific to CC:Tweaked, try to [suggest them on the ComputerCraft repo](https://github.com/dan200/ComputerCraft/issues/new). There's a lot more people watching it, so it allows the wider community to contribute. | ||||
|  - Search for the suggestion here. It's possible someone's suggested it before! | ||||
| --> | ||||
|  | ||||
| ## Useful information to include: | ||||
|  - Explanation of how the feature/change chould work. | ||||
|  - Some rationale/use case for a feature. I'd like to keep CC:T as minimal | ||||
|  - Explanation of how the feature/change should work. | ||||
|  - Some rationale/use case for a feature. I'd like to keep CC:T as minimal as possible, so I like have a solid justification for each feature. | ||||
|   | ||||
							
								
								
									
										9
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,9 +0,0 @@ | ||||
| <!-- | ||||
| Unless this feature is specific to CC:Tweaked, try to [target the original ComputerCraft repo](https://github.com/dan200/ComputerCraft/) instead. There's a lot more people watching it, so it allows the wider community to contribute. | ||||
| --> | ||||
|  | ||||
| ## Useful information to include: | ||||
|  - Brief explanation of the changes you've made. | ||||
|  - Rationale of why this change has been made/reasoning behind it. | ||||
|  | ||||
| The more information you can provide, the easier it is to review something now _and_ to see why a change was made, when the code needs updating in the future. | ||||
							
								
								
									
										19
									
								
								LICENSE-luaj
									
									
									
									
									
								
							
							
						
						| @@ -1,19 +0,0 @@ | ||||
| Copyright (c) 2007 LuaJ. All rights reserved. | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
							
								
								
									
										50
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,35 +1,35 @@ | ||||
| #  | ||||
| [](https://travis-ci.org/SquidDev-CC/CC-Tweaked) | ||||
|  | ||||
| CC: Tweaked is a fork of ComputerCraft which aims to provide earlier access to the more experimental and in-development | ||||
| features of the mod. For a more stable experience, I recommend checking out the | ||||
| [original mod](https://github.com/dan200/ComputerCraft). | ||||
| CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers, | ||||
| turtles and more to Minecraft. | ||||
|  | ||||
| ## What? | ||||
| CC: Tweaked (or CC:T for short) does not aim to create a competing fork of ComputerCraft, nor am I planning to take it | ||||
| in in a vastly different direction to the original mod. In fact, CC:T aims to be a nurturing ground for various | ||||
| features, with a pull request against the original mod being the end goal. | ||||
| ComputerCraft has always held a fond place in my heart: it's the mod which really got me into Minecraft, and it's the | ||||
| mod which has kept me playing it for many years. However, development of the original mod has slowed, as the original  | ||||
| developers have had less time to work on the mod, and moved onto other projects and commitments.  | ||||
|  | ||||
| CC:T also includes many pull requests from the community which have not yet been merged, offering a large number | ||||
| of additional bug fixes and features over the original mod. | ||||
| CC:Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor | ||||
| to CC, nor do I want to take it in a vastly different direction to the original mod. Instead, CC:T focuses on making the | ||||
| ComputerCraft experience as _solid_ as possible, ironing out any wrinkles that may have developed over time. | ||||
|  | ||||
| ## Features | ||||
| CC: Tweaked contains all the features of the latest alpha, as well as numerous fixes, performance improvements and | ||||
| several additional features. I'd recommend checking out [the releases page](https://github.com/SquidDev-CC/CC-Tweaked/releases) | ||||
| to see the full changes, but here's a couple of the more interesting changes: | ||||
| CC: Tweaked contains all the features of the latest version of ComputerCraft, as well as numerous fixes, performance  | ||||
| improvements and several nifty additions. I'd recommend checking out [the releases page](https://github.com/SquidDev-CC/CC-Tweaked/releases) | ||||
| to see the full set of changes, but here's a couple of the more interesting additions: | ||||
|  | ||||
|  - Replace LuaJ with Cobalt. | ||||
|  - Allow running multiple computers at the same time. | ||||
|  - Websocket support in the HTTP library. | ||||
|  - Wired modems and cables act more like multiparts. | ||||
|  - Add map-like rendering for pocket computers and printed pages/books. | ||||
|  - Adds the `/computercraft` command, offering various diagnostic tools for server owners. This allows operators to | ||||
|    track which computers are hogging resources, turn on and shutdown multiple computers at once and interact with | ||||
|  - Improvements to the `http` library, including websockets, support for other HTTP methods (`PUT`, `DELETE`, etc...)  | ||||
|    and configurable limits on HTTP usage. | ||||
|  - Full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is | ||||
|    installed).    | ||||
|  - Pocket computers can be held like maps, allowing you to view the screen without entering a GUI. | ||||
|  - Printed pages and books can be placed in item frames and held like maps. | ||||
|  - Several profiling and administration tools for server owners, via the `/computercraft` command. This allows operators  | ||||
|    to track which computers are hogging resources, turn on and shutdown multiple computers at once and interact with | ||||
|    computers remotely. | ||||
|  - Add full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is | ||||
|    installed). | ||||
|  - Extended binary file handles. They support file seeking, and reading new lines, allowing full (and accurate) | ||||
|    emulation of the standard Lua `io` library. | ||||
|  - Closer emulation of standard Lua, adding the `debug` and `io` libraries. This also enables seeking within binary | ||||
|    files, meaning you don't need to read large files into memory. | ||||
|  - Allow running multiple computers on multiple threads, reducing latency on worlds with many computers. | ||||
|  | ||||
| ## Relation to CCTweaks? | ||||
| This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said, | ||||
| @@ -37,10 +37,8 @@ several features have been included, such as full block modems, the Cobalt runti | ||||
| computers. | ||||
|  | ||||
| ## Contributing | ||||
| Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you do wish to contribute | ||||
| code, do consider submitting it to the ComputerCraft repository first. | ||||
|  | ||||
| That being said, in order to start helping develop CC:T, you'll need to follow these steps: | ||||
| Any contribution is welcome, be that using the mod, reporting bugs or contributing code. In order to start helping | ||||
| develop CC:T, you'll need to follow these steps: | ||||
|  | ||||
|  - **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked` | ||||
|  - **Setup Forge:** `./gradlew setupDecompWorkspace` | ||||
|   | ||||
							
								
								
									
										143
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						| @@ -9,8 +9,10 @@ buildscript { | ||||
|         } | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.google.code.gson:gson:2.8.1' | ||||
|         classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' | ||||
|         classpath 'org.ajoberstar:gradle-git:1.6.0' | ||||
|         classpath 'net.sf.proguard:proguard-gradle:6.1.0beta1' | ||||
|         classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0' | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -23,12 +25,14 @@ apply plugin: 'org.ajoberstar.grgit' | ||||
| apply plugin: 'maven-publish' | ||||
| apply plugin: 'maven' | ||||
|  | ||||
| version = "1.80pr1.14" | ||||
| def mc_version = "1.12.2" | ||||
| version = "1.81.0" | ||||
|  | ||||
| group = "org.squiddev" | ||||
| archivesBaseName = "cc-tweaked" | ||||
| archivesBaseName = "cc-tweaked-${mc_version}" | ||||
|  | ||||
| minecraft { | ||||
|     version = "1.12.2-14.23.4.2749" | ||||
|     version = "${mc_version}-14.23.4.2749" | ||||
|     runDir = "run" | ||||
|     replace '${version}', project.version | ||||
|  | ||||
| @@ -48,7 +52,7 @@ repositories { | ||||
|     } | ||||
|     maven { | ||||
|         name = "squiddev" | ||||
|         url = "https://dl.bintray.com/squiddev/maven" | ||||
|         url = "https://squiddev.cc/maven" | ||||
|     } | ||||
|  | ||||
|     ivy { artifactPattern "https://asie.pl/files/mods/Charset/LibOnly/[module]-[revision](-[classifier]).[ext]" } | ||||
| @@ -66,7 +70,7 @@ dependencies { | ||||
|  | ||||
|     runtime "mezz.jei:jei_1.12.2:4.8.5.159" | ||||
|  | ||||
|     shade 'org.squiddev:Cobalt:0.4.0' | ||||
|     shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT' | ||||
|  | ||||
|     testCompile 'junit:junit:4.11' | ||||
|  | ||||
| @@ -84,39 +88,91 @@ jar { | ||||
|         attributes('FMLAT': 'computercraft_at.cfg') | ||||
|     } | ||||
|  | ||||
|     into("docs", { from (javadoc.destinationDir) }) | ||||
|  | ||||
|     into("api", { from (sourceSets.main.allSource) { | ||||
|     from (sourceSets.main.allSource) { | ||||
|         include "dan200/computercraft/api/**/*.java" | ||||
|     }}) | ||||
|     } | ||||
|  | ||||
|     from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) } | ||||
| } | ||||
|  | ||||
| import java.nio.charset.StandardCharsets | ||||
| import java.nio.file.* | ||||
| import java.util.zip.* | ||||
|  | ||||
| import com.google.gson.GsonBuilder | ||||
| import com.google.gson.JsonElement | ||||
| import org.ajoberstar.grgit.Grgit | ||||
| import proguard.gradle.ProGuardTask | ||||
|  | ||||
| task proguard(type: ProGuardTask, dependsOn: jar) { | ||||
|     description "Removes unused shadowed classes from the jar" | ||||
|     group "compact" | ||||
|  | ||||
|     injars jar.archivePath | ||||
|     outjars "${jar.archivePath.absolutePath.replace(".jar", "")}-min.jar" | ||||
|  | ||||
|     // Add the main runtime jar and all non-shadowed dependencies | ||||
|     libraryjars "${System.getProperty('java.home')}/lib/rt.jar" | ||||
|     doFirst { | ||||
|         sourceSets.main.compileClasspath | ||||
|             .filter { !it.name.contains("Cobalt") } | ||||
|             .each { libraryjars it } | ||||
|     } | ||||
|  | ||||
|     // We want to avoid as much obfuscation as possible. We're only doing this to shrink code size. | ||||
|     dontobfuscate; dontoptimize; keepattributes; keepparameternames | ||||
|  | ||||
|     // Proguard will remove directories by default, but that breaks JarMount. | ||||
|     keepdirectories 'assets/computercraft/lua**' | ||||
|  | ||||
|     // Preserve ComputerCraft classes - we only want to strip shadowed files. | ||||
|     keep 'class dan200.computercraft.** { *; }' | ||||
|  | ||||
|     // Preserve the constructors in Cobalt library class, as we init them via reflection | ||||
|     keepclassmembers 'class org.squiddev.cobalt.lib.** { <init>(...); }' | ||||
| } | ||||
|  | ||||
| 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 | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| reobfJar.dependsOn proguardMove | ||||
|  | ||||
| processResources { | ||||
|     inputs.property "version", project.version | ||||
|     inputs.property "mcversion", project.minecraft.version | ||||
|     inputs.property "mcversion", mc_version | ||||
|  | ||||
|     def grgit = Grgit.open(dir: '.') | ||||
|     inputs.property "commithash", grgit.head().id | ||||
|  | ||||
|     def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe'] | ||||
|     def hash = 'none' | ||||
|     Set<String> contributors = [] | ||||
|     try { | ||||
|         def grgit = Grgit.open(dir: '.') | ||||
|         hash = grgit.head().id | ||||
|  | ||||
|     grgit.log().each { | ||||
|         if (!blacklist.contains(it.author.name)) contributors.add(it.author.name) | ||||
|         if (!blacklist.contains(it.committer.name)) contributors.add(it.committer.name) | ||||
|     } | ||||
|         def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe'] | ||||
|         grgit.log().each { | ||||
|             if (!blacklist.contains(it.author.name)) contributors.add(it.author.name) | ||||
|             if (!blacklist.contains(it.committer.name)) contributors.add(it.committer.name) | ||||
|         } | ||||
|     } catch(Exception ignored) { } | ||||
|  | ||||
|     inputs.property "commithash", hash | ||||
|  | ||||
|     from(sourceSets.main.resources.srcDirs) { | ||||
|         include 'mcmod.info' | ||||
|         include 'assets/computercraft/lua/rom/help/credits.txt' | ||||
|  | ||||
|         expand 'version':project.version, | ||||
|                'mcversion':project.minecraft.version, | ||||
|                'gitcontributors':contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n') | ||||
|         expand 'version': project.version, | ||||
|                'mcversion': mc_version, | ||||
|                'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n') | ||||
|     } | ||||
|  | ||||
|     from(sourceSets.main.resources.srcDirs) { | ||||
| @@ -125,12 +181,53 @@ processResources { | ||||
|     } | ||||
| } | ||||
|  | ||||
| task compressJson(dependsOn: extractAnnotationsJar) { | ||||
|     group "compact" | ||||
|     description "Minifies all JSON files, stripping whitespace" | ||||
|  | ||||
|     def jarPath = file(jar.archivePath) | ||||
|  | ||||
|     def tempPath = File.createTempFile("input", ".jar", temporaryDir) | ||||
|     tempPath.deleteOnExit() | ||||
|  | ||||
|     def gson = new GsonBuilder().create() | ||||
|  | ||||
|     doLast { | ||||
|         // Copy over all files in the current jar to the new one, running json files from GSON. As pretty printing | ||||
|         // is turned off, they should be minified. | ||||
|         new ZipFile(jarPath).withCloseable { inJar -> | ||||
|             new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempPath))).withCloseable { outJar -> | ||||
|                 inJar.entries().each { entry -> | ||||
|                     if(entry.directory) { | ||||
|                         outJar.putNextEntry(entry) | ||||
|                     } else if(!entry.name.endsWith(".json")) { | ||||
|                         outJar.putNextEntry(entry) | ||||
|                         inJar.getInputStream(entry).withCloseable { outJar << it } | ||||
|                     } else { | ||||
|                         ZipEntry newEntry = new ZipEntry(entry.name) | ||||
|                         newEntry.setTime(entry.time) | ||||
|                         outJar.putNextEntry(newEntry) | ||||
|  | ||||
|                         def element = inJar.getInputStream(entry).withCloseable { gson.fromJson(it.newReader("UTF8"), JsonElement.class) } | ||||
|                         outJar.write(gson.toJson(element).getBytes(StandardCharsets.UTF_8)) | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // And replace the original jar again | ||||
|         Files.move(tempPath.toPath(), jarPath.toPath(), StandardCopyOption.REPLACE_EXISTING) | ||||
|     } | ||||
| } | ||||
|  | ||||
| assemble.dependsOn compressJson | ||||
|  | ||||
| curseforge { | ||||
|     apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : '' | ||||
|     project { | ||||
|         id = '282001' | ||||
|         releaseType = 'beta' | ||||
|         releaseType = 'release' | ||||
|         changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${project.version})." | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -235,7 +235,8 @@ public class ComputerCraft | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|     public static class Upgrades { | ||||
|     public static class Upgrades | ||||
|     { | ||||
|         public static TurtleModem advancedModem; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,10 @@ | ||||
| package dan200.computercraft.client; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.render.TurtleModelLoader; | ||||
| import dan200.computercraft.shared.media.items.ItemDiskLegacy; | ||||
| import dan200.computercraft.shared.turtle.items.ItemTurtleBase; | ||||
| import dan200.computercraft.shared.util.Colour; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.renderer.ItemMeshDefinition; | ||||
| import net.minecraft.client.renderer.block.model.IBakedModel; | ||||
| @@ -17,6 +21,7 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; | ||||
| import net.minecraft.item.Item; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.util.ResourceLocation; | ||||
| import net.minecraftforge.client.event.ColorHandlerEvent; | ||||
| import net.minecraftforge.client.event.ModelBakeEvent; | ||||
| import net.minecraftforge.client.event.ModelRegistryEvent; | ||||
| import net.minecraftforge.client.event.TextureStitchEvent; | ||||
| @@ -35,7 +40,7 @@ import javax.annotation.Nonnull; | ||||
| @Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Side.CLIENT ) | ||||
| public class ClientRegistry | ||||
| { | ||||
|     private static final String[] TURTLE_UPGRADES = { | ||||
|     private static final String[] EXTRA_MODELS = { | ||||
|         "turtle_modem_off_left", | ||||
|         "turtle_modem_on_left", | ||||
|         "turtle_modem_off_right", | ||||
| @@ -48,11 +53,16 @@ public class ClientRegistry | ||||
|         "advanced_turtle_modem_on_right", | ||||
|         "turtle_speaker_upgrade_left", | ||||
|         "turtle_speaker_upgrade_right", | ||||
|  | ||||
|         "turtle_white", | ||||
|         "turtle_elf_overlay", | ||||
|     }; | ||||
|  | ||||
|     @SubscribeEvent | ||||
|     public static void registerModels( ModelRegistryEvent event ) | ||||
|     { | ||||
|         ModelLoaderRegistry.registerLoader( TurtleModelLoader.INSTANCE ); | ||||
|  | ||||
|         // Register item models | ||||
|         registerUniversalItemModel( ComputerCraft.Items.computer, "computer" ); | ||||
|         registerItemModel( ComputerCraft.Items.commandComputer, 0, "command_computer" ); | ||||
| @@ -79,35 +89,70 @@ public class ClientRegistry | ||||
|         registerItemModel( ComputerCraft.Items.printout, 1, "pages" ); | ||||
|         registerItemModel( ComputerCraft.Items.printout, 2, "book" ); | ||||
|  | ||||
|         String[] extraTurtleModels = new String[] { "turtle", "turtle_advanced", "turtle_white", "turtle_elf_overlay" }; | ||||
|         registerUniversalItemModel( ComputerCraft.Items.turtle, "turtle_dynamic", extraTurtleModels ); | ||||
|         registerUniversalItemModel( ComputerCraft.Items.turtleExpanded, "turtle_dynamic", extraTurtleModels ); | ||||
|         registerUniversalItemModel( ComputerCraft.Items.turtleAdvanced, "turtle_dynamic", extraTurtleModels ); | ||||
|         registerUniversalItemModel( ComputerCraft.Items.turtle, "turtle" ); | ||||
|         registerUniversalItemModel( ComputerCraft.Items.turtleExpanded, "turtle" ); | ||||
|         registerUniversalItemModel( ComputerCraft.Items.turtleAdvanced, "turtle_advanced" ); | ||||
|     } | ||||
|  | ||||
|     @SubscribeEvent | ||||
|     public static void onTextureStitchEvent( TextureStitchEvent.Pre event ) | ||||
|     { | ||||
|         // Load all textures for upgrades | ||||
|         // Load all textures for the extra models | ||||
|         TextureMap map = event.getMap(); | ||||
|         for( String upgrade : TURTLE_UPGRADES ) | ||||
|         for( String upgrade : EXTRA_MODELS ) | ||||
|         { | ||||
|             IModel model = ModelLoaderRegistry.getModelOrMissing( new ResourceLocation( "computercraft", "block/" + upgrade ) ); | ||||
|             for( ResourceLocation texture : model.getTextures() ) | ||||
|             { | ||||
|                 map.registerSprite( texture ); | ||||
|             } | ||||
|             for( ResourceLocation texture : model.getTextures() ) map.registerSprite( texture ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SubscribeEvent | ||||
|     public static void onModelBakeEvent( ModelBakeEvent event ) | ||||
|     { | ||||
|         // Load all upgrade models | ||||
|         for( String upgrade : TURTLE_UPGRADES ) | ||||
|         { | ||||
|             loadBlockModel( event, upgrade ); | ||||
|         } | ||||
|         // Load all extra models | ||||
|         for( String model : EXTRA_MODELS ) loadBlockModel( event, model ); | ||||
|     } | ||||
|  | ||||
|     @SubscribeEvent | ||||
|     public static void onItemColours( ColorHandlerEvent.Item event ) | ||||
|     { | ||||
|         event.getItemColors().registerItemColorHandler( | ||||
|             ( stack, layer ) -> layer == 0 ? 0xFFFFFF : ((ItemDiskLegacy) stack.getItem()).getColour( stack ), | ||||
|             ComputerCraft.Items.disk, ComputerCraft.Items.diskExpanded | ||||
|         ); | ||||
|  | ||||
|         event.getItemColors().registerItemColorHandler( ( stack, layer ) -> { | ||||
|             switch( layer ) | ||||
|             { | ||||
|                 case 0: | ||||
|                 default: | ||||
|                     return 0xFFFFFF; | ||||
|                 case 1: | ||||
|                 { | ||||
|                     // Frame colour | ||||
|                     int colour = ComputerCraft.Items.pocketComputer.getColour( stack ); | ||||
|                     return colour == -1 ? 0xFFFFFF : colour; | ||||
|                 } | ||||
|                 case 2: | ||||
|                 { | ||||
|                     // Light colour | ||||
|                     int colour = ComputerCraft.Items.pocketComputer.getLightState( stack ); | ||||
|                     return colour == -1 ? Colour.Black.getHex() : colour; | ||||
|                 } | ||||
|             } | ||||
|         }, ComputerCraft.Items.pocketComputer ); | ||||
|  | ||||
|         // Setup turtle colours | ||||
|         event.getItemColors().registerItemColorHandler( ( stack, tintIndex ) -> { | ||||
|             if( tintIndex == 0 ) | ||||
|             { | ||||
|                 ItemTurtleBase turtle = (ItemTurtleBase) stack.getItem(); | ||||
|                 int colour = turtle.getColour( stack ); | ||||
|                 if( colour != -1 ) return colour; | ||||
|             } | ||||
|  | ||||
|             return 0xFFFFFF; | ||||
|         }, ComputerCraft.Blocks.turtle, ComputerCraft.Blocks.turtleExpanded, ComputerCraft.Blocks.turtleAdvanced ); | ||||
|     } | ||||
|  | ||||
|     private static void registerItemModel( Item item, int damage, String name ) | ||||
| @@ -118,18 +163,10 @@ public class ClientRegistry | ||||
|         ModelLoader.setCustomModelResourceLocation( item, damage, res ); | ||||
|     } | ||||
|  | ||||
|     private static void registerUniversalItemModel( Item item, String mainModel, String... extraModels ) | ||||
|     private static void registerUniversalItemModel( Item item, String mainModel ) | ||||
|     { | ||||
|         ResourceLocation mainLocation = new ResourceLocation( ComputerCraft.MOD_ID, mainModel ); | ||||
|  | ||||
|         ResourceLocation[] modelLocations = new ResourceLocation[extraModels.length + 1]; | ||||
|         modelLocations[0] = mainLocation; | ||||
|         for( int i = 0; i < extraModels.length; i++ ) | ||||
|         { | ||||
|             modelLocations[i + 1] = new ResourceLocation( ComputerCraft.MOD_ID, extraModels[i] ); | ||||
|         } | ||||
|  | ||||
|         ModelBakery.registerItemVariants( item, modelLocations ); | ||||
|         ModelBakery.registerItemVariants( item, mainLocation ); | ||||
|  | ||||
|         final ModelResourceLocation mainModelLocation = new ModelResourceLocation( mainLocation, "inventory" ); | ||||
|         ModelLoader.setCustomMeshDefinition( item, new ItemMeshDefinition() | ||||
|   | ||||
| @@ -13,12 +13,14 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.gui.FontRenderer; | ||||
| import net.minecraft.client.gui.GuiNewChat; | ||||
| import net.minecraft.client.gui.GuiUtilRenderComponents; | ||||
| import net.minecraft.util.math.MathHelper; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.TextFormatting; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
| import java.util.List; | ||||
|  | ||||
| public class ClientTableFormatter implements TableFormatter | ||||
| { | ||||
| @@ -62,7 +64,13 @@ public class ClientTableFormatter implements TableFormatter | ||||
|     @Override | ||||
|     public void writeLine( int id, ITextComponent component ) | ||||
|     { | ||||
|         Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessageWithOptionalDeletion( component, id ); | ||||
|         Minecraft mc = Minecraft.getMinecraft(); | ||||
|         GuiNewChat chat = mc.ingameGUI.getChatGUI(); | ||||
|  | ||||
|         // Trim the text if it goes over the allowed length | ||||
|         int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getChatScale() ); | ||||
|         List<ITextComponent> list = GuiUtilRenderComponents.splitText( component, maxWidth, mc.fontRenderer, false, false ); | ||||
|         if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -6,69 +6,19 @@ | ||||
|  | ||||
| package dan200.computercraft.client.proxy; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.render.TileEntityTurtleRenderer; | ||||
| import dan200.computercraft.client.render.TurtleSmartItemModel; | ||||
| import dan200.computercraft.shared.proxy.CCTurtleProxyCommon; | ||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| import dan200.computercraft.shared.turtle.items.ItemTurtleBase; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.renderer.block.model.ModelResourceLocation; | ||||
| import net.minecraft.client.resources.IReloadableResourceManager; | ||||
| import net.minecraft.client.resources.IResourceManager; | ||||
| import net.minecraftforge.client.event.ModelBakeEvent; | ||||
| import net.minecraftforge.common.MinecraftForge; | ||||
| import net.minecraftforge.fml.client.registry.ClientRegistry; | ||||
| import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; | ||||
|  | ||||
| public class CCTurtleProxyClient extends CCTurtleProxyCommon | ||||
| { | ||||
|     @Override | ||||
|     public void preInit() | ||||
|     { | ||||
|         super.preInit(); | ||||
|         MinecraftForge.EVENT_BUS.register( new ForgeHandlers() ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void init() | ||||
|     { | ||||
|         super.init(); | ||||
|  | ||||
|         // Setup turtle colours | ||||
|         Minecraft.getMinecraft().getItemColors().registerItemColorHandler( ( stack, tintIndex ) -> { | ||||
|             if( tintIndex == 0 ) | ||||
|             { | ||||
|                 ItemTurtleBase turtle = (ItemTurtleBase) stack.getItem(); | ||||
|                 int colour = turtle.getColour( stack ); | ||||
|                 if( colour != -1 ) return colour; | ||||
|             } | ||||
|  | ||||
|             return 0xFFFFFF; | ||||
|         }, ComputerCraft.Blocks.turtle, ComputerCraft.Blocks.turtleExpanded, ComputerCraft.Blocks.turtleAdvanced ); | ||||
|  | ||||
|         // Setup renderers | ||||
|         ClientRegistry.bindTileEntitySpecialRenderer( TileTurtle.class, new TileEntityTurtleRenderer() ); | ||||
|     } | ||||
|  | ||||
|     public static class ForgeHandlers | ||||
|     { | ||||
|         private final TurtleSmartItemModel m_turtleSmartItemModel = new TurtleSmartItemModel(); | ||||
|  | ||||
|         ForgeHandlers() | ||||
|         { | ||||
|             IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager(); | ||||
|             if( resourceManager instanceof IReloadableResourceManager ) | ||||
|             { | ||||
|                 ((IReloadableResourceManager) resourceManager).registerReloadListener( m_turtleSmartItemModel ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @SubscribeEvent | ||||
|         public void onModelBakeEvent( ModelBakeEvent event ) | ||||
|         { | ||||
|             event.getModelRegistry().putObject( new ModelResourceLocation( "computercraft:turtle_dynamic", "inventory" ), m_turtleSmartItemModel ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -10,24 +10,16 @@ import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.render.TileEntityCableRenderer; | ||||
| import dan200.computercraft.client.render.TileEntityMonitorRenderer; | ||||
| import dan200.computercraft.shared.command.CommandCopy; | ||||
| import dan200.computercraft.shared.media.items.ItemDiskLegacy; | ||||
| import dan200.computercraft.shared.peripheral.modem.wired.TileCable; | ||||
| import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; | ||||
| import dan200.computercraft.shared.peripheral.monitor.TileMonitor; | ||||
| import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon; | ||||
| import dan200.computercraft.shared.util.Colour; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.renderer.color.IItemColor; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraftforge.client.ClientCommandHandler; | ||||
| import net.minecraftforge.event.world.WorldEvent; | ||||
| import net.minecraftforge.fml.client.registry.ClientRegistry; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; | ||||
| import net.minecraftforge.fml.relauncher.Side; | ||||
| import net.minecraftforge.fml.relauncher.SideOnly; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| public class ComputerCraftProxyClient extends ComputerCraftProxyCommon | ||||
| { | ||||
| @@ -45,33 +37,6 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon | ||||
|     { | ||||
|         super.init(); | ||||
|  | ||||
|         Minecraft mc = Minecraft.getMinecraft(); | ||||
|  | ||||
|         // Setup | ||||
|         mc.getItemColors().registerItemColorHandler( new DiskColorHandler( ComputerCraft.Items.disk ), ComputerCraft.Items.disk ); | ||||
|         mc.getItemColors().registerItemColorHandler( new DiskColorHandler( ComputerCraft.Items.diskExpanded ), ComputerCraft.Items.diskExpanded ); | ||||
|  | ||||
|         mc.getItemColors().registerItemColorHandler( ( stack, layer ) -> { | ||||
|             switch( layer ) | ||||
|             { | ||||
|                 case 0: | ||||
|                 default: | ||||
|                     return 0xFFFFFF; | ||||
|                 case 1: | ||||
|                 { | ||||
|                     // Frame colour | ||||
|                     int colour = ComputerCraft.Items.pocketComputer.getColour( stack ); | ||||
|                     return colour == -1 ? 0xFFFFFF : colour; | ||||
|                 } | ||||
|                 case 2: | ||||
|                 { | ||||
|                     // Light colour | ||||
|                     int colour = ComputerCraft.Items.pocketComputer.getLightState( stack ); | ||||
|                     return colour == -1 ? Colour.Black.getHex() : colour; | ||||
|                 } | ||||
|             } | ||||
|         }, ComputerCraft.Items.pocketComputer ); | ||||
|  | ||||
|         // Setup renderers | ||||
|         ClientRegistry.bindTileEntitySpecialRenderer( TileMonitor.class, new TileEntityMonitorRenderer() ); | ||||
|         ClientRegistry.bindTileEntitySpecialRenderer( TileCable.class, new TileEntityCableRenderer() ); | ||||
| @@ -90,20 +55,5 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SideOnly( Side.CLIENT ) | ||||
|     private static class DiskColorHandler implements IItemColor | ||||
|     { | ||||
|         private final ItemDiskLegacy disk; | ||||
|  | ||||
|         private DiskColorHandler( ItemDiskLegacy disk ) | ||||
|         { | ||||
|             this.disk = disk; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int colorMultiplier( @Nonnull ItemStack stack, int layer ) | ||||
|         { | ||||
|             return layer == 0 ? 0xFFFFFF : disk.getColour( stack ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,23 +14,20 @@ import dan200.computercraft.shared.util.Holiday; | ||||
| import dan200.computercraft.shared.util.HolidayUtil; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.gui.FontRenderer; | ||||
| import net.minecraft.client.renderer.BufferBuilder; | ||||
| import net.minecraft.client.renderer.EntityRenderer; | ||||
| import net.minecraft.client.renderer.GlStateManager; | ||||
| import net.minecraft.client.renderer.Tessellator; | ||||
| import net.minecraft.client.renderer.block.model.BakedQuad; | ||||
| import net.minecraft.client.renderer.block.model.IBakedModel; | ||||
| import net.minecraft.client.renderer.block.model.ModelManager; | ||||
| import net.minecraft.client.renderer.block.model.ModelResourceLocation; | ||||
| import net.minecraft.client.renderer.entity.RenderManager; | ||||
| import net.minecraft.client.renderer.texture.TextureMap; | ||||
| import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; | ||||
| import net.minecraft.client.renderer.vertex.DefaultVertexFormats; | ||||
| import net.minecraft.client.renderer.vertex.VertexFormat; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.ResourceLocation; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.RayTraceResult; | ||||
| import net.minecraft.util.math.Vec3d; | ||||
| import net.minecraftforge.client.ForgeHooksClient; | ||||
| import net.minecraftforge.client.model.pipeline.LightUtil; | ||||
| @@ -65,7 +62,7 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static ModelResourceLocation getTurtleOverlayModel( ComputerFamily family, ResourceLocation overlay, boolean christmas ) | ||||
|     public static ModelResourceLocation getTurtleOverlayModel( ResourceLocation overlay, boolean christmas ) | ||||
|     { | ||||
|         if( overlay != null ) | ||||
|         { | ||||
| @@ -83,6 +80,19 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt | ||||
|  | ||||
|     private void renderTurtleAt( TileTurtle turtle, double posX, double posY, double posZ, float f, int i ) | ||||
|     { | ||||
|         // Render the label | ||||
|         String label = turtle.createProxy().getLabel(); | ||||
|         if( label != null && rendererDispatcher.cameraHitResult != null && turtle.getPos().equals( rendererDispatcher.cameraHitResult.getBlockPos() ) ) | ||||
|         { | ||||
|             setLightmapDisabled( true ); | ||||
|             EntityRenderer.drawNameplate( | ||||
|                 getFontRenderer(), label, | ||||
|                 (float) posX + 0.5F, (float) posY + 1.2F, (float) posZ + 0.5F, 0, | ||||
|                 rendererDispatcher.entityYaw, rendererDispatcher.entityPitch, false, false | ||||
|             ); | ||||
|             setLightmapDisabled( false ); | ||||
|         } | ||||
|  | ||||
|         IBlockState state = turtle.getWorld().getBlockState( turtle.getPos() ); | ||||
|         GlStateManager.pushMatrix(); | ||||
|         try | ||||
| @@ -94,13 +104,6 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt | ||||
|             yaw = turtle.getRenderYaw( f ); | ||||
|             GlStateManager.translate( posX + offset.x, posY + offset.y, posZ + offset.z ); | ||||
|  | ||||
|             // Render the label | ||||
|             String label = turtle.createProxy().getLabel(); | ||||
|             if( label != null ) | ||||
|             { | ||||
|                 renderLabel( turtle.getAccess().getPosition(), label ); | ||||
|             } | ||||
|  | ||||
|             // Render the turtle | ||||
|             GlStateManager.translate( 0.5f, 0.5f, 0.5f ); | ||||
|             GlStateManager.rotate( 180.0f - yaw, 0.0f, 1.0f, 0.0f ); | ||||
| @@ -123,7 +126,6 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt | ||||
|  | ||||
|             // Render the overlay | ||||
|             ModelResourceLocation overlayModel = getTurtleOverlayModel( | ||||
|                 family, | ||||
|                 overlay, | ||||
|                 HolidayUtil.getCurrentHoliday() == Holiday.Christmas | ||||
|             ); | ||||
| @@ -232,72 +234,4 @@ public class TileEntityTurtleRenderer extends TileEntitySpecialRenderer<TileTurt | ||||
|         } | ||||
|         tessellator.draw(); | ||||
|     } | ||||
|  | ||||
|     private void renderLabel( BlockPos position, String label ) | ||||
|     { | ||||
|         Minecraft mc = Minecraft.getMinecraft(); | ||||
|         RayTraceResult mop = mc.objectMouseOver; | ||||
|         if( mop != null && mop.typeOfHit == RayTraceResult.Type.BLOCK && mop.getBlockPos().equals( position ) ) | ||||
|         { | ||||
|             RenderManager renderManager = mc.getRenderManager(); | ||||
|             FontRenderer fontrenderer = renderManager.getFontRenderer(); | ||||
|             float scale = 0.016666668F * 1.6f; | ||||
|  | ||||
|             GlStateManager.pushMatrix(); | ||||
|             GlStateManager.disableLighting(); | ||||
|             GlStateManager.enableBlend(); | ||||
|             GlStateManager.blendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA ); | ||||
|             try | ||||
|             { | ||||
|                 GlStateManager.translate( 0.5f, 1.25f, 0.5f ); | ||||
|                 GlStateManager.rotate( -renderManager.playerViewY, 0.0F, 1.0F, 0.0F ); | ||||
|                 GlStateManager.rotate( renderManager.playerViewX, 1.0F, 0.0F, 0.0F ); | ||||
|                 GlStateManager.scale( -scale, -scale, scale ); | ||||
|  | ||||
|                 int yOffset = 0; | ||||
|                 int xOffset = fontrenderer.getStringWidth( label ) / 2; | ||||
|  | ||||
|                 // Draw background | ||||
|                 GlStateManager.depthMask( false ); | ||||
|                 GlStateManager.disableDepth(); | ||||
|                 try | ||||
|                 { | ||||
|                     // Quad | ||||
|                     GlStateManager.disableTexture2D(); | ||||
|                     try | ||||
|                     { | ||||
|                         Tessellator tessellator = Tessellator.getInstance(); | ||||
|                         BufferBuilder renderer = tessellator.getBuffer(); | ||||
|                         renderer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR ); | ||||
|                         renderer.pos( -xOffset - 1, -1 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex(); | ||||
|                         renderer.pos( -xOffset - 1, 8 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex(); | ||||
|                         renderer.pos( xOffset + 1, 8 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex(); | ||||
|                         renderer.pos( xOffset + 1, -1 + yOffset, 0.0D ).color( 0.0F, 0.0F, 0.0F, 0.25F ).endVertex(); | ||||
|                         tessellator.draw(); | ||||
|                     } | ||||
|                     finally | ||||
|                     { | ||||
|                         GlStateManager.enableTexture2D(); | ||||
|                     } | ||||
|  | ||||
|                     // Text | ||||
|                     fontrenderer.drawString( label, -fontrenderer.getStringWidth( label ) / 2, yOffset, 0x20ffffff ); | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     GlStateManager.enableDepth(); | ||||
|                     GlStateManager.depthMask( true ); | ||||
|                 } | ||||
|  | ||||
|                 // Draw foreground text | ||||
|                 fontrenderer.drawString( label, -fontrenderer.getStringWidth( label ) / 2, yOffset, -1 ); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 GlStateManager.disableBlend(); | ||||
|                 GlStateManager.enableLighting(); | ||||
|                 GlStateManager.popMatrix(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,121 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.client.render; | ||||
|  | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import net.minecraft.client.renderer.block.model.IBakedModel; | ||||
| import net.minecraft.client.renderer.texture.TextureAtlasSprite; | ||||
| import net.minecraft.client.renderer.vertex.VertexFormat; | ||||
| import net.minecraft.client.resources.IResourceManager; | ||||
| import net.minecraft.util.ResourceLocation; | ||||
| import net.minecraftforge.client.model.ICustomModelLoader; | ||||
| import net.minecraftforge.client.model.IModel; | ||||
| import net.minecraftforge.client.model.ModelLoaderRegistry; | ||||
| import net.minecraftforge.common.model.IModelState; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.function.Function; | ||||
|  | ||||
| public class TurtleModelLoader implements ICustomModelLoader | ||||
| { | ||||
|     private static final ResourceLocation NORMAL_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle" ); | ||||
|     private static final ResourceLocation ADVANCED_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/advanced_turtle" ); | ||||
|     private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_white" ); | ||||
|  | ||||
|     public static final TurtleModelLoader INSTANCE = new TurtleModelLoader(); | ||||
|  | ||||
|     private TurtleModelLoader() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResourceManagerReload( @Nonnull IResourceManager manager ) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean accepts( @Nonnull ResourceLocation name ) | ||||
|     { | ||||
|         return name.getNamespace().equals( ComputerCraft.MOD_ID ) | ||||
|             && (name.getPath().equals( "turtle" ) || name.getPath().equals( "turtle_advanced" )); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public IModel loadModel( @Nonnull ResourceLocation name ) throws Exception | ||||
|     { | ||||
|         if( name.getNamespace().equals( ComputerCraft.MOD_ID ) ) | ||||
|         { | ||||
|             IModel colourModel = ModelLoaderRegistry.getModel( COLOUR_TURTLE_MODEL ); | ||||
|             switch( name.getPath() ) | ||||
|             { | ||||
|                 case "turtle": | ||||
|                     return new TurtleModel( ModelLoaderRegistry.getModel( NORMAL_TURTLE_MODEL ), colourModel ); | ||||
|                 case "turtle_advanced": | ||||
|                     return new TurtleModel( ModelLoaderRegistry.getModel( ADVANCED_TURTLE_MODEL ), colourModel ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         throw new IllegalStateException( "Loader does not accept " + name ); | ||||
|     } | ||||
|  | ||||
|     private static class TurtleModel implements IModel | ||||
|     { | ||||
|         private final IModel family; | ||||
|         private final IModel colour; | ||||
|  | ||||
|         private TurtleModel( IModel family, IModel colour ) | ||||
|         { | ||||
|             this.family = family; | ||||
|             this.colour = colour; | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public IBakedModel bake( @Nonnull IModelState state, @Nonnull VertexFormat format, @Nonnull Function<ResourceLocation, TextureAtlasSprite> function ) | ||||
|         { | ||||
|             return new TurtleSmartItemModel( | ||||
|                 family.bake( state, format, function ), | ||||
|                 colour.bake( state, format, function ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         private TurtleModel copy( IModel family, IModel colour ) | ||||
|         { | ||||
|             return this.family == family && this.colour == colour ? this : new TurtleModel( family, colour ); | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public IModel smoothLighting( boolean value ) | ||||
|         { | ||||
|             return copy( family.smoothLighting( value ), colour.smoothLighting( value ) ); | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public IModel gui3d( boolean value ) | ||||
|         { | ||||
|             return copy( family.gui3d( value ), colour.gui3d( value ) ); | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public IModel uvlock( boolean value ) | ||||
|         { | ||||
|             return copy( family.uvlock( value ), colour.uvlock( value ) ); | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public IModel retexture( ImmutableMap<String, String> textures ) | ||||
|         { | ||||
|             return copy( family.retexture( textures ), colour.retexture( textures ) ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -9,7 +9,6 @@ package dan200.computercraft.client.render; | ||||
| import com.google.common.base.Objects; | ||||
| import dan200.computercraft.api.turtle.ITurtleUpgrade; | ||||
| import dan200.computercraft.api.turtle.TurtleSide; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.turtle.items.ItemTurtleBase; | ||||
| import dan200.computercraft.shared.util.Holiday; | ||||
| import dan200.computercraft.shared.util.HolidayUtil; | ||||
| @@ -17,15 +16,11 @@ import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.renderer.block.model.*; | ||||
| import net.minecraft.client.renderer.texture.TextureAtlasSprite; | ||||
| import net.minecraft.client.resources.IResourceManager; | ||||
| import net.minecraft.entity.EntityLivingBase; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.ResourceLocation; | ||||
| import net.minecraft.world.World; | ||||
| import net.minecraftforge.client.resource.IResourceType; | ||||
| import net.minecraftforge.client.resource.ISelectiveResourceReloadListener; | ||||
| import net.minecraftforge.client.resource.VanillaResourceType; | ||||
| import org.apache.commons.lang3.tuple.Pair; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| @@ -34,9 +29,8 @@ import javax.vecmath.Matrix4f; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.function.Predicate; | ||||
|  | ||||
| public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceReloadListener | ||||
| public class TurtleSmartItemModel implements IBakedModel | ||||
| { | ||||
|     private static final Matrix4f s_identity, s_flip; | ||||
|  | ||||
| @@ -53,17 +47,15 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|  | ||||
|     private static class TurtleModelCombination | ||||
|     { | ||||
|         public final ComputerFamily m_family; | ||||
|         public final boolean m_colour; | ||||
|         public final ITurtleUpgrade m_leftUpgrade; | ||||
|         public final ITurtleUpgrade m_rightUpgrade; | ||||
|         public final ResourceLocation m_overlay; | ||||
|         public final boolean m_christmas; | ||||
|         public final boolean m_flip; | ||||
|         final boolean m_colour; | ||||
|         final ITurtleUpgrade m_leftUpgrade; | ||||
|         final ITurtleUpgrade m_rightUpgrade; | ||||
|         final ResourceLocation m_overlay; | ||||
|         final boolean m_christmas; | ||||
|         final boolean m_flip; | ||||
|  | ||||
|         public TurtleModelCombination( ComputerFamily family, boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas, boolean flip ) | ||||
|         TurtleModelCombination( boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation overlay, boolean christmas, boolean flip ) | ||||
|         { | ||||
|             m_family = family; | ||||
|             m_colour = colour; | ||||
|             m_leftUpgrade = leftUpgrade; | ||||
|             m_rightUpgrade = rightUpgrade; | ||||
| @@ -79,8 +71,7 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|             if( !(other instanceof TurtleModelCombination) ) return false; | ||||
|  | ||||
|             TurtleModelCombination otherCombo = (TurtleModelCombination) other; | ||||
|             return otherCombo.m_family == m_family && | ||||
|                 otherCombo.m_colour == m_colour && | ||||
|             return otherCombo.m_colour == m_colour && | ||||
|                 otherCombo.m_leftUpgrade == m_leftUpgrade && | ||||
|                 otherCombo.m_rightUpgrade == m_rightUpgrade && | ||||
|                 Objects.equal( otherCombo.m_overlay, m_overlay ) && | ||||
| @@ -92,8 +83,7 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|         public int hashCode() | ||||
|         { | ||||
|             final int prime = 31; | ||||
|             int result = 1; | ||||
|             result = prime * result + m_family.hashCode(); | ||||
|             int result = 0; | ||||
|             result = prime * result + (m_colour ? 1 : 0); | ||||
|             result = prime * result + (m_leftUpgrade != null ? m_leftUpgrade.hashCode() : 0); | ||||
|             result = prime * result + (m_rightUpgrade != null ? m_rightUpgrade.hashCode() : 0); | ||||
| @@ -104,14 +94,18 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private final IBakedModel familyModel; | ||||
|     private final IBakedModel colourModel; | ||||
|  | ||||
|     private HashMap<TurtleModelCombination, IBakedModel> m_cachedModels; | ||||
|     private ItemOverrideList m_overrides; | ||||
|     private final TurtleModelCombination m_defaultCombination; | ||||
|  | ||||
|     public TurtleSmartItemModel() | ||||
|     public TurtleSmartItemModel( IBakedModel familyModel, IBakedModel colourModel ) | ||||
|     { | ||||
|         this.familyModel = familyModel; | ||||
|         this.colourModel = colourModel; | ||||
|  | ||||
|         m_cachedModels = new HashMap<>(); | ||||
|         m_defaultCombination = new TurtleModelCombination( ComputerFamily.Normal, false, null, null, null, false, false ); | ||||
|         m_overrides = new ItemOverrideList( new ArrayList<>() ) | ||||
|         { | ||||
|             @Nonnull | ||||
| @@ -119,7 +113,6 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|             public IBakedModel handleItemState( @Nonnull IBakedModel originalModel, @Nonnull ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity ) | ||||
|             { | ||||
|                 ItemTurtleBase turtle = (ItemTurtleBase) stack.getItem(); | ||||
|                 ComputerFamily family = turtle.getFamily( stack ); | ||||
|                 int colour = turtle.getColour( stack ); | ||||
|                 ITurtleUpgrade leftUpgrade = turtle.getUpgrade( stack, TurtleSide.Left ); | ||||
|                 ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.Right ); | ||||
| @@ -127,17 +120,11 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|                 boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.Christmas; | ||||
|                 String label = turtle.getLabel( stack ); | ||||
|                 boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )); | ||||
|                 TurtleModelCombination combo = new TurtleModelCombination( family, colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip ); | ||||
|                 if( m_cachedModels.containsKey( combo ) ) | ||||
|                 { | ||||
|                     return m_cachedModels.get( combo ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     IBakedModel model = buildModel( combo ); | ||||
|                     m_cachedModels.put( combo, model ); | ||||
|                     return model; | ||||
|                 } | ||||
|                 TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip ); | ||||
|  | ||||
|                 IBakedModel model = m_cachedModels.get( combo ); | ||||
|                 if( model == null ) m_cachedModels.put( combo, model = buildModel( combo ) ); | ||||
|                 return model; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| @@ -149,19 +136,13 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|         return m_overrides; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResourceManagerReload( @Nonnull IResourceManager resourceManager, @Nonnull Predicate<IResourceType> resourcePredicate ) | ||||
|     { | ||||
|         if( resourcePredicate.test( VanillaResourceType.MODELS ) ) m_cachedModels.clear(); | ||||
|     } | ||||
|  | ||||
|     private IBakedModel buildModel( TurtleModelCombination combo ) | ||||
|     { | ||||
|         Minecraft mc = Minecraft.getMinecraft(); | ||||
|         ModelManager modelManager = mc.getRenderItem().getItemModelMesher().getModelManager(); | ||||
|         ModelResourceLocation baseModelLocation = TileEntityTurtleRenderer.getTurtleModel( combo.m_family, combo.m_colour ); | ||||
|         ModelResourceLocation overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.m_family, combo.m_overlay, combo.m_christmas ); | ||||
|         IBakedModel baseModel = modelManager.getModel( baseModelLocation ); | ||||
|         ModelResourceLocation overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.m_overlay, combo.m_christmas ); | ||||
|  | ||||
|         IBakedModel baseModel = combo.m_colour ? colourModel : familyModel; | ||||
|         IBakedModel overlayModel = (overlayModelLocation != null) ? modelManager.getModel( overlayModelLocation ) : null; | ||||
|         Matrix4f transform = combo.m_flip ? s_flip : s_identity; | ||||
|         Pair<IBakedModel, Matrix4f> leftModel = (combo.m_leftUpgrade != null) ? combo.m_leftUpgrade.getModel( null, TurtleSide.Left ) : null; | ||||
| @@ -184,38 +165,36 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // These should not be called: | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public List<BakedQuad> getQuads( IBlockState state, EnumFacing facing, long rand ) | ||||
|     { | ||||
|         return getDefaultModel().getQuads( state, facing, rand ); | ||||
|         return familyModel.getQuads( state, facing, rand ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isAmbientOcclusion() | ||||
|     { | ||||
|         return getDefaultModel().isAmbientOcclusion(); | ||||
|         return familyModel.isAmbientOcclusion(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isGui3d() | ||||
|     { | ||||
|         return getDefaultModel().isGui3d(); | ||||
|         return familyModel.isGui3d(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isBuiltInRenderer() | ||||
|     { | ||||
|         return getDefaultModel().isBuiltInRenderer(); | ||||
|         return familyModel.isBuiltInRenderer(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public TextureAtlasSprite getParticleTexture() | ||||
|     { | ||||
|         return getDefaultModel().getParticleTexture(); | ||||
|         return familyModel.getParticleTexture(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
| @@ -223,18 +202,7 @@ public class TurtleSmartItemModel implements IBakedModel, ISelectiveResourceRelo | ||||
|     @Deprecated | ||||
|     public ItemCameraTransforms getItemCameraTransforms() | ||||
|     { | ||||
|         return getDefaultModel().getItemCameraTransforms(); | ||||
|         return familyModel.getItemCameraTransforms(); | ||||
|     } | ||||
|  | ||||
|     private IBakedModel getDefaultModel() | ||||
|     { | ||||
|         IBakedModel model = m_cachedModels.get( m_defaultCombination ); | ||||
|         if( model == null ) | ||||
|         { | ||||
|             model = buildModel( m_defaultCombination ); | ||||
|             m_cachedModels.put( m_defaultCombination, model ); | ||||
|         } | ||||
|  | ||||
|         return model; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,20 +14,32 @@ import dan200.computercraft.core.filesystem.FileSystem; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.core.tracking.TrackingField; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
|  | ||||
| public interface IAPIEnvironment extends IComputerOwned | ||||
| { | ||||
|     String[] SIDE_NAMES = new String[] { | ||||
|         "bottom", "top", "back", "front", "right", "left", | ||||
|     }; | ||||
|  | ||||
|     int SIDE_COUNT = 6; | ||||
|  | ||||
|     interface IPeripheralChangeListener | ||||
|     { | ||||
|         void onPeripheralChanged( int side, IPeripheral newPeripheral ); | ||||
|         void onPeripheralChanged( int side, @Nullable IPeripheral newPeripheral ); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     Computer getComputer(); | ||||
|  | ||||
|     int getComputerID(); | ||||
|  | ||||
|     @Nonnull | ||||
|     IComputerEnvironment getComputerEnvironment(); | ||||
|  | ||||
|     @Nonnull | ||||
|     Terminal getTerminal(); | ||||
|  | ||||
|     FileSystem getFileSystem(); | ||||
| @@ -50,17 +62,18 @@ public interface IAPIEnvironment extends IComputerOwned | ||||
|  | ||||
|     int getBundledInput( int side ); | ||||
|  | ||||
|     void setPeripheralChangeListener( IPeripheralChangeListener listener ); | ||||
|     void setPeripheralChangeListener( @Nullable IPeripheralChangeListener listener ); | ||||
|  | ||||
|     @Nullable | ||||
|     IPeripheral getPeripheral( int side ); | ||||
|  | ||||
|     String getLabel(); | ||||
|  | ||||
|     void setLabel( String label ); | ||||
|     void setLabel( @Nullable String label ); | ||||
|  | ||||
|     void addTrackingChange( TrackingField field, long change ); | ||||
|     void addTrackingChange( @Nonnull TrackingField field, long change ); | ||||
|  | ||||
|     default void addTrackingChange( TrackingField field ) | ||||
|     default void addTrackingChange( @Nonnull TrackingField field ) | ||||
|     { | ||||
|         addTrackingChange( field, 1 ); | ||||
|     } | ||||
|   | ||||
| @@ -278,13 +278,13 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange | ||||
|                 }, null ); | ||||
|  | ||||
|                 // Queue a detachment event | ||||
|                 m_environment.queueEvent( "peripheral_detach", new Object[] { Computer.s_sideNames[side] } ); | ||||
|                 m_environment.queueEvent( "peripheral_detach", new Object[] { IAPIEnvironment.SIDE_NAMES[side] } ); | ||||
|             } | ||||
|  | ||||
|             // Assign the new peripheral | ||||
|             if( newPeripheral != null ) | ||||
|             { | ||||
|                 m_peripherals[side] = new PeripheralWrapper( newPeripheral, Computer.s_sideNames[side] ); | ||||
|                 m_peripherals[side] = new PeripheralWrapper( newPeripheral, IAPIEnvironment.SIDE_NAMES[side] ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
| @@ -317,7 +317,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange | ||||
|                 }, null ); | ||||
|  | ||||
|                 // Queue an attachment event | ||||
|                 m_environment.queueEvent( "peripheral", new Object[] { Computer.s_sideNames[side] } ); | ||||
|                 m_environment.queueEvent( "peripheral", new Object[] { IAPIEnvironment.SIDE_NAMES[side] } ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -483,9 +483,9 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange | ||||
|     private int parseSide( Object[] args ) throws LuaException | ||||
|     { | ||||
|         String side = getString( args, 0 ); | ||||
|         for( int n = 0; n < Computer.s_sideNames.length; n++ ) | ||||
|         for( int n = 0; n < IAPIEnvironment.SIDE_NAMES.length; n++ ) | ||||
|         { | ||||
|             if( side.equals( Computer.s_sideNames[n] ) ) | ||||
|             if( side.equals( IAPIEnvironment.SIDE_NAMES[n] ) ) | ||||
|             { | ||||
|                 return n; | ||||
|             } | ||||
|   | ||||
| @@ -9,7 +9,6 @@ package dan200.computercraft.core.apis; | ||||
| import dan200.computercraft.api.lua.ILuaAPI; | ||||
| import dan200.computercraft.api.lua.ILuaContext; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.core.computer.Computer; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.HashMap; | ||||
| @@ -65,9 +64,9 @@ public class RedstoneAPI implements ILuaAPI | ||||
|             { | ||||
|                 // getSides | ||||
|                 Map<Object, Object> table = new HashMap<>(); | ||||
|                 for( int i = 0; i < Computer.s_sideNames.length; i++ ) | ||||
|                 for( int i = 0; i < IAPIEnvironment.SIDE_NAMES.length; i++ ) | ||||
|                 { | ||||
|                     table.put( i + 1, Computer.s_sideNames[i] ); | ||||
|                     table.put( i + 1, IAPIEnvironment.SIDE_NAMES[i] ); | ||||
|                 } | ||||
|                 return new Object[] { table }; | ||||
|             } | ||||
| @@ -156,9 +155,9 @@ public class RedstoneAPI implements ILuaAPI | ||||
|     private int parseSide( Object[] args ) throws LuaException | ||||
|     { | ||||
|         String side = getString( args, 0 ); | ||||
|         for( int n = 0; n < Computer.s_sideNames.length; n++ ) | ||||
|         for( int n = 0; n < IAPIEnvironment.SIDE_NAMES.length; n++ ) | ||||
|         { | ||||
|             if( side.equals( Computer.s_sideNames[n] ) ) | ||||
|             if( side.equals( IAPIEnvironment.SIDE_NAMES[n] ) ) | ||||
|             { | ||||
|                 return n; | ||||
|             } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import dan200.computercraft.api.lua.ILuaContext; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.core.computer.IComputerEnvironment; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.shared.util.Colour; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
| import org.apache.commons.lang3.ArrayUtils; | ||||
|  | ||||
| @@ -65,6 +66,8 @@ public class TermAPI implements ILuaAPI | ||||
|             "setPaletteColor", | ||||
|             "getPaletteColour", | ||||
|             "getPaletteColor", | ||||
|             "nativePaletteColour", | ||||
|             "nativePaletteColor", | ||||
|             "getCursorBlink", | ||||
|         }; | ||||
|     } | ||||
| @@ -289,6 +292,19 @@ public class TermAPI implements ILuaAPI | ||||
|                 return null; | ||||
|             } | ||||
|             case 23: | ||||
|             case 24: | ||||
|             { | ||||
|                 // nativePaletteColour/nativePaletteColor | ||||
|                 int colour = 15 - parseColour( args ); | ||||
|                 Colour c = Colour.fromInt( colour ); | ||||
|  | ||||
|                 float[] rgb = c.getRGB(); | ||||
|  | ||||
|                 Object[] rgbObj = new Object[rgb.length]; | ||||
|                 for( int i = 0; i < rgbObj.length; ++i ) rgbObj[i] = rgb[i]; | ||||
|                 return rgbObj; | ||||
|             } | ||||
|             case 25: | ||||
|                 // getCursorBlink | ||||
|                 return new Object[] { m_terminal.getCursorBlink() }; | ||||
|             default: | ||||
|   | ||||
| @@ -169,27 +169,42 @@ public class BinaryReadableHandle extends HandleGeneric | ||||
|                 { | ||||
|                     ByteArrayOutputStream stream = new ByteArrayOutputStream(); | ||||
|  | ||||
|                     boolean readAnything = false; | ||||
|                     boolean readAnything = false, readRc = false; | ||||
|                     while( true ) | ||||
|                     { | ||||
|                         single.clear(); | ||||
|                         int r = m_reader.read( single ); | ||||
|                         if( r == -1 ) break; | ||||
|                         int read = m_reader.read( single ); | ||||
|                         if( read <= 0 ) | ||||
|                         { | ||||
|                             // Nothing else to read, and we saw no \n. Return the array. If we saw a \r, then add it | ||||
|                             // back. | ||||
|                             if( readRc ) stream.write( '\r' ); | ||||
|                             return readAnything ? new Object[] { stream.toByteArray() } : null; | ||||
|                         } | ||||
|  | ||||
|                         readAnything = true; | ||||
|                         byte b = single.get( 0 ); | ||||
|                         if( b == '\n' ) | ||||
|  | ||||
|                         byte chr = single.get( 0 ); | ||||
|                         if( chr == '\n' ) | ||||
|                         { | ||||
|                             if( withTrailing ) stream.write( b ); | ||||
|                             break; | ||||
|                             if( withTrailing ) | ||||
|                             { | ||||
|                                 if( readRc ) stream.write( '\r' ); | ||||
|                                 stream.write( chr ); | ||||
|                             } | ||||
|                             return new Object[] { stream.toByteArray() }; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             stream.write( b ); | ||||
|                             // We want to skip \r\n, but obviously need to include cases where \r is not followed by \n. | ||||
|                             // Note, this behaviour is non-standard compliant (strictly speaking we should have no | ||||
|                             // special logic for \r), but we preserve compatibility with EncodedReadableHandle and | ||||
|                             // previous behaviour of the io library. | ||||
|                             if( readRc ) stream.write( '\r' ); | ||||
|                             readRc = chr == '\r'; | ||||
|                             if( !readRc ) stream.write( chr ); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     return readAnything ? new Object[] { stream.toByteArray() } : null; | ||||
|                 } | ||||
|                 catch( IOException e ) | ||||
|                 { | ||||
|   | ||||
| @@ -161,7 +161,10 @@ public class Websocket extends Resource<Websocket> | ||||
|                     } | ||||
|                 } ) | ||||
|                 .remoteAddress( socketAddress ) | ||||
|                 .connect(); | ||||
|                 .connect() | ||||
|                 .addListener( c -> { | ||||
|                     if( !c.isSuccess() ) failure( c.cause().getMessage() ); | ||||
|                 } ); | ||||
|  | ||||
|             // Do an additional check for cancellation | ||||
|             checkClosed(); | ||||
|   | ||||
| @@ -83,6 +83,11 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object> | ||||
|             CloseWebSocketFrame closeFrame = (CloseWebSocketFrame) frame; | ||||
|             websocket.close( closeFrame.statusCode(), closeFrame.reasonText() ); | ||||
|         } | ||||
|         else if( frame instanceof PingWebSocketFrame ) | ||||
|         { | ||||
|             frame.content().retain(); | ||||
|             ctx.channel().writeAndFlush( new PongWebSocketFrame( frame.content() ) ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -108,6 +113,13 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object> | ||||
|             message = "Could not connect"; | ||||
|         } | ||||
|  | ||||
|         websocket.failure( message ); | ||||
|         if( handshaker.isHandshakeComplete() ) | ||||
|         { | ||||
|             websocket.close( -1, message ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             websocket.failure( message ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.core.computer; | ||||
|  | ||||
| import dan200.computercraft.api.lua.ILuaAPI; | ||||
| import dan200.computercraft.api.lua.ILuaContext; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
|  | ||||
| /** | ||||
|  * A wrapper for {@link ILuaAPI}s which cleans up after a {@link ComputerSystem} when the computer is shutdown. | ||||
|  */ | ||||
| public class ApiWrapper implements ILuaAPI | ||||
| { | ||||
|     private final ILuaAPI delegate; | ||||
|     private final ComputerSystem system; | ||||
|  | ||||
|     ApiWrapper( ILuaAPI delegate, ComputerSystem system ) | ||||
|     { | ||||
|         this.delegate = delegate; | ||||
|         this.system = system; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String[] getNames() | ||||
|     { | ||||
|         return delegate.getNames(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void startup() | ||||
|     { | ||||
|         delegate.startup(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void update() | ||||
|     { | ||||
|         delegate.update(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void shutdown() | ||||
|     { | ||||
|         delegate.shutdown(); | ||||
|         system.unmountAll(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public String[] getMethodNames() | ||||
|     { | ||||
|         return delegate.getMethodNames(); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException | ||||
|     { | ||||
|         return delegate.callMethod( context, method, arguments ); | ||||
|     } | ||||
| } | ||||
| @@ -8,11 +8,10 @@ package dan200.computercraft.core.computer; | ||||
|  | ||||
| import com.google.common.base.Objects; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.filesystem.IFileSystem; | ||||
| import dan200.computercraft.api.filesystem.IMount; | ||||
| import dan200.computercraft.api.filesystem.IWritableMount; | ||||
| import dan200.computercraft.api.lua.ILuaAPI; | ||||
| import dan200.computercraft.api.lua.*; | ||||
| import dan200.computercraft.api.lua.ILuaAPIFactory; | ||||
| import dan200.computercraft.api.peripheral.IPeripheral; | ||||
| import dan200.computercraft.core.apis.*; | ||||
| import dan200.computercraft.core.filesystem.FileSystem; | ||||
| @@ -20,11 +19,7 @@ import dan200.computercraft.core.filesystem.FileSystemException; | ||||
| import dan200.computercraft.core.lua.CobaltLuaMachine; | ||||
| import dan200.computercraft.core.lua.ILuaMachine; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.core.tracking.Tracking; | ||||
| import dan200.computercraft.core.tracking.TrackingField; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.util.ArrayList; | ||||
| @@ -32,10 +27,6 @@ import java.util.List; | ||||
|  | ||||
| public class Computer | ||||
| { | ||||
|     public static final String[] s_sideNames = new String[] { | ||||
|         "bottom", "top", "back", "front", "right", "left", | ||||
|     }; | ||||
|  | ||||
|     private enum State | ||||
|     { | ||||
|         Off, | ||||
| @@ -44,319 +35,81 @@ public class Computer | ||||
|         Stopping, | ||||
|     } | ||||
|  | ||||
|     private static class APIEnvironment implements IAPIEnvironment | ||||
|     { | ||||
|         private Computer m_computer; | ||||
|         private IAPIEnvironment.IPeripheralChangeListener m_peripheralListener; | ||||
|  | ||||
|         public APIEnvironment( Computer computer ) | ||||
|         { | ||||
|             m_computer = computer; | ||||
|             m_peripheralListener = null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public Computer getComputer() | ||||
|         { | ||||
|             return m_computer; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getComputerID() | ||||
|         { | ||||
|             return m_computer.assignID(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public IComputerEnvironment getComputerEnvironment() | ||||
|         { | ||||
|             return m_computer.m_environment; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public Terminal getTerminal() | ||||
|         { | ||||
|             return m_computer.m_terminal; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public FileSystem getFileSystem() | ||||
|         { | ||||
|             return m_computer.m_fileSystem; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void shutdown() | ||||
|         { | ||||
|             m_computer.shutdown(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void reboot() | ||||
|         { | ||||
|             m_computer.reboot(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void queueEvent( String event, Object[] args ) | ||||
|         { | ||||
|             m_computer.queueEvent( event, args ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void setOutput( int side, int output ) | ||||
|         { | ||||
|             m_computer.setRedstoneOutput( side, output ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getOutput( int side ) | ||||
|         { | ||||
|             return m_computer.getInternalRedstoneOutput( side ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getInput( int side ) | ||||
|         { | ||||
|             return m_computer.getRedstoneInput( side ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void setBundledOutput( int side, int output ) | ||||
|         { | ||||
|             m_computer.setBundledRedstoneOutput( side, output ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getBundledOutput( int side ) | ||||
|         { | ||||
|             return m_computer.getInternalBundledRedstoneOutput( side ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getBundledInput( int side ) | ||||
|         { | ||||
|             return m_computer.getBundledRedstoneInput( side ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public IPeripheral getPeripheral( int side ) | ||||
|         { | ||||
|             synchronized( m_computer.m_peripherals ) | ||||
|             { | ||||
|                 return m_computer.m_peripherals[side]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void setPeripheralChangeListener( IPeripheralChangeListener listener ) | ||||
|         { | ||||
|             synchronized( m_computer.m_peripherals ) | ||||
|             { | ||||
|                 m_peripheralListener = listener; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public String getLabel() | ||||
|         { | ||||
|             return m_computer.getLabel(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void setLabel( String label ) | ||||
|         { | ||||
|             m_computer.setLabel( label ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void addTrackingChange( TrackingField field, long change ) | ||||
|         { | ||||
|             Tracking.addValue( m_computer, field, change ); | ||||
|         } | ||||
|  | ||||
|         public void onPeripheralChanged( int side, IPeripheral peripheral ) | ||||
|         { | ||||
|             synchronized( m_computer.m_peripherals ) | ||||
|             { | ||||
|                 if( m_peripheralListener != null ) | ||||
|                 { | ||||
|                     m_peripheralListener.onPeripheralChanged( side, peripheral ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class ComputerSystem extends ComputerAccess implements IComputerSystem | ||||
|     { | ||||
|         private final IAPIEnvironment m_environment; | ||||
|  | ||||
|         private ComputerSystem( IAPIEnvironment m_environment ) | ||||
|         { | ||||
|             super( m_environment ); | ||||
|             this.m_environment = m_environment; | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public String getAttachmentName() | ||||
|         { | ||||
|             return "computer"; | ||||
|         } | ||||
|  | ||||
|         @Nullable | ||||
|         @Override | ||||
|         public IFileSystem getFileSystem() | ||||
|         { | ||||
|             FileSystem fs = m_environment.getFileSystem(); | ||||
|             return fs == null ? null : fs.getMountWrapper(); | ||||
|         } | ||||
|  | ||||
|         @Nullable | ||||
|         @Override | ||||
|         public String getLabel() | ||||
|         { | ||||
|             return m_environment.getLabel(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class APIWrapper implements ILuaAPI | ||||
|     { | ||||
|         private final ILuaAPI delegate; | ||||
|         private final ComputerSystem system; | ||||
|  | ||||
|         private APIWrapper( ILuaAPI delegate, ComputerSystem system ) | ||||
|         { | ||||
|             this.delegate = delegate; | ||||
|             this.system = system; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public String[] getNames() | ||||
|         { | ||||
|             return delegate.getNames(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void startup() | ||||
|         { | ||||
|             delegate.startup(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void update() | ||||
|         { | ||||
|             delegate.update(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void shutdown() | ||||
|         { | ||||
|             delegate.shutdown(); | ||||
|             system.unmountAll(); | ||||
|         } | ||||
|  | ||||
|         @Nonnull | ||||
|         @Override | ||||
|         public String[] getMethodNames() | ||||
|         { | ||||
|             return delegate.getMethodNames(); | ||||
|         } | ||||
|  | ||||
|         @Nullable | ||||
|         @Override | ||||
|         public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException | ||||
|         { | ||||
|             return delegate.callMethod( context, method, arguments ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static IMount s_romMount = null; | ||||
|  | ||||
|     private int m_id; | ||||
|     private String m_label; | ||||
|     private String m_label = null; | ||||
|     private final IComputerEnvironment m_environment; | ||||
|  | ||||
|     private int m_ticksSinceStart; | ||||
|     private boolean m_startRequested; | ||||
|     private State m_state; | ||||
|     private boolean m_blinking; | ||||
|     private int m_ticksSinceStart = -1; | ||||
|     private boolean m_startRequested = false; | ||||
|     private State m_state = State.Off; | ||||
|     private boolean m_blinking = false; | ||||
|  | ||||
|     private ILuaMachine m_machine; | ||||
|     private final List<ILuaAPI> m_apis; | ||||
|     private final APIEnvironment m_apiEnvironment; | ||||
|     private ILuaMachine m_machine = null; | ||||
|     private final List<ILuaAPI> m_apis = new ArrayList<>(); | ||||
|     private final Environment m_internalEnvironment = new Environment( this ); | ||||
|  | ||||
|     private final Terminal m_terminal; | ||||
|     private FileSystem m_fileSystem; | ||||
|     private IWritableMount m_rootMount; | ||||
|     private FileSystem m_fileSystem = null; | ||||
|     private IWritableMount m_rootMount = null; | ||||
|  | ||||
|     private final int[] m_internalOutput; | ||||
|     private final int[] m_internalBundledOutput; | ||||
|     private boolean m_internalOutputChanged; | ||||
|  | ||||
|     private final int[] m_externalOutput; | ||||
|     private final int[] m_externalBundledOutput; | ||||
|     private boolean m_externalOutputChanged; | ||||
|  | ||||
|     private final int[] m_input; | ||||
|     private final int[] m_bundledInput; | ||||
|     private boolean m_inputChanged; | ||||
|  | ||||
|     private final IPeripheral[] m_peripherals; | ||||
|  | ||||
|     public Computer( IComputerEnvironment environment, Terminal terminal, int id ) | ||||
|     { | ||||
|         m_id = id; | ||||
|         m_environment = environment; | ||||
|         m_terminal = terminal; | ||||
|  | ||||
|         // Ensure the computer thread is running as required. | ||||
|         ComputerThread.start(); | ||||
|  | ||||
|         m_id = id; | ||||
|         m_label = null; | ||||
|         m_environment = environment; | ||||
|         // Add all default APIs to the loaded list. | ||||
|         m_apis.add( new TermAPI( m_internalEnvironment ) ); | ||||
|         m_apis.add( new RedstoneAPI( m_internalEnvironment ) ); | ||||
|         m_apis.add( new FSAPI( m_internalEnvironment ) ); | ||||
|         m_apis.add( new PeripheralAPI( m_internalEnvironment ) ); | ||||
|         m_apis.add( new OSAPI( m_internalEnvironment ) ); | ||||
|         if( ComputerCraft.http_enable ) m_apis.add( new HTTPAPI( m_internalEnvironment ) ); | ||||
|  | ||||
|         m_ticksSinceStart = -1; | ||||
|         m_startRequested = false; | ||||
|         m_state = State.Off; | ||||
|         m_blinking = false; | ||||
|  | ||||
|         m_terminal = terminal; | ||||
|         m_fileSystem = null; | ||||
|  | ||||
|         m_machine = null; | ||||
|         m_apis = new ArrayList<>(); | ||||
|         m_apiEnvironment = new APIEnvironment( this ); | ||||
|  | ||||
|         m_internalOutput = new int[6]; | ||||
|         m_internalBundledOutput = new int[6]; | ||||
|         m_internalOutputChanged = true; | ||||
|  | ||||
|         m_externalOutput = new int[6]; | ||||
|         m_externalBundledOutput = new int[6]; | ||||
|         m_externalOutputChanged = true; | ||||
|  | ||||
|         m_input = new int[6]; | ||||
|         m_bundledInput = new int[6]; | ||||
|         m_inputChanged = false; | ||||
|  | ||||
|         m_peripherals = new IPeripheral[6]; | ||||
|         for( int i = 0; i < 6; i++ ) | ||||
|         // Load in the API registered APIs. | ||||
|         for( ILuaAPIFactory factory : ApiFactories.getAll() ) | ||||
|         { | ||||
|             m_peripherals[i] = null; | ||||
|             ComputerSystem system = new ComputerSystem( m_internalEnvironment ); | ||||
|             ILuaAPI api = factory.create( system ); | ||||
|             if( api != null ) m_apis.add( new ApiWrapper( api, system ) ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         m_rootMount = null; | ||||
|         createAPIs(); | ||||
|     IComputerEnvironment getComputerEnvironment() | ||||
|     { | ||||
|         return m_environment; | ||||
|     } | ||||
|  | ||||
|     FileSystem getFileSystem() | ||||
|     { | ||||
|         return m_fileSystem; | ||||
|     } | ||||
|  | ||||
|     Terminal getTerminal() | ||||
|     { | ||||
|         return m_terminal; | ||||
|     } | ||||
|  | ||||
|     public Environment getEnvironment() | ||||
|     { | ||||
|         return m_internalEnvironment; | ||||
|     } | ||||
|  | ||||
|     public IAPIEnvironment getAPIEnvironment() | ||||
|     { | ||||
|         return m_apiEnvironment; | ||||
|         return m_internalEnvironment; | ||||
|     } | ||||
|  | ||||
|     public void turnOn() | ||||
|     { | ||||
|         if( m_state == State.Off ) | ||||
|         { | ||||
|             m_startRequested = true; | ||||
|         } | ||||
|         if( m_state == State.Off ) m_startRequested = true; | ||||
|     } | ||||
|  | ||||
|     public void shutdown() | ||||
| @@ -397,10 +150,7 @@ public class Computer | ||||
|  | ||||
|     public void unload() | ||||
|     { | ||||
|         synchronized( this ) | ||||
|         { | ||||
|             stopComputer( false ); | ||||
|         } | ||||
|         stopComputer( false ); | ||||
|     } | ||||
|  | ||||
|     public int getID() | ||||
| @@ -436,7 +186,7 @@ public class Computer | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void advance( double _dt ) | ||||
|     public void advance() | ||||
|     { | ||||
|         synchronized( this ) | ||||
|         { | ||||
| @@ -453,67 +203,27 @@ public class Computer | ||||
|  | ||||
|             if( m_state == State.Running ) | ||||
|             { | ||||
|                 // Fire the redstone event if our redstone input has changed | ||||
|                 synchronized( m_input ) | ||||
|                 { | ||||
|                     if( m_inputChanged ) | ||||
|                     { | ||||
|                         queueEvent( "redstone", null ); | ||||
|                         m_inputChanged = false; | ||||
|                     } | ||||
|                 } | ||||
|                 // Update the environment's internal state. | ||||
|                 m_internalEnvironment.update(); | ||||
|  | ||||
|                 // Advance our APIs | ||||
|                 synchronized( m_apis ) | ||||
|                 { | ||||
|                     for( ILuaAPI api : m_apis ) | ||||
|                     { | ||||
|                         api.update(); | ||||
|                     } | ||||
|                 } | ||||
|                 for( ILuaAPI api : m_apis ) api.update(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Set outputchanged if the internal redstone has changed | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             if( m_internalOutputChanged ) | ||||
|             { | ||||
|                 boolean changed = false; | ||||
|                 for( int i = 0; i < 6; i++ ) | ||||
|                 { | ||||
|                     if( m_externalOutput[i] != m_internalOutput[i] ) | ||||
|                     { | ||||
|                         m_externalOutput[i] = m_internalOutput[i]; | ||||
|                         changed = true; | ||||
|                     } | ||||
|                     if( m_externalBundledOutput[i] != m_internalBundledOutput[i] ) | ||||
|                     { | ||||
|                         m_externalBundledOutput[i] = m_internalBundledOutput[i]; | ||||
|                         changed = true; | ||||
|                     } | ||||
|                 } | ||||
|                 m_internalOutputChanged = false; | ||||
|                 if( changed ) | ||||
|                 { | ||||
|                     m_externalOutputChanged = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // Prepare to propagate the environment's output to the world. | ||||
|         if( m_internalEnvironment.updateOutput() ) m_externalOutputChanged = true; | ||||
|  | ||||
|         // Set outputchanged if the terminal has changed from blinking to not | ||||
|         synchronized( m_terminal ) | ||||
|         { | ||||
|             boolean blinking = | ||||
|                 m_terminal.getCursorBlink() && | ||||
|                     m_terminal.getCursorX() >= 0 && m_terminal.getCursorX() < m_terminal.getWidth() && | ||||
|                     m_terminal.getCursorY() >= 0 && m_terminal.getCursorY() < m_terminal.getHeight(); | ||||
|         // Set output changed if the terminal has changed from blinking to not | ||||
|         boolean blinking = | ||||
|             m_terminal.getCursorBlink() && | ||||
|                 m_terminal.getCursorX() >= 0 && m_terminal.getCursorX() < m_terminal.getWidth() && | ||||
|                 m_terminal.getCursorY() >= 0 && m_terminal.getCursorY() < m_terminal.getHeight(); | ||||
|  | ||||
|             if( blinking != m_blinking ) | ||||
|             { | ||||
|                 m_blinking = blinking; | ||||
|                 m_externalOutputChanged = true; | ||||
|             } | ||||
|         if( blinking != m_blinking ) | ||||
|         { | ||||
|             m_blinking = blinking; | ||||
|             m_externalOutputChanged = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -529,10 +239,7 @@ public class Computer | ||||
|  | ||||
|     public boolean isBlinking() | ||||
|     { | ||||
|         synchronized( m_terminal ) | ||||
|         { | ||||
|             return isOn() && m_blinking; | ||||
|         } | ||||
|         return isOn() && m_blinking; | ||||
|     } | ||||
|  | ||||
|     public IWritableMount getRootMount() | ||||
| @@ -553,10 +260,7 @@ public class Computer | ||||
|         try | ||||
|         { | ||||
|             m_fileSystem = new FileSystem( "hdd", getRootMount() ); | ||||
|             if( s_romMount == null ) | ||||
|             { | ||||
|                 s_romMount = m_environment.createResourceMount( "computercraft", "lua/rom" ); | ||||
|             } | ||||
|             if( s_romMount == null ) s_romMount = m_environment.createResourceMount( "computercraft", "lua/rom" ); | ||||
|             if( s_romMount != null ) | ||||
|             { | ||||
|                 m_fileSystem.mount( "rom", "rom", s_romMount ); | ||||
| @@ -571,104 +275,6 @@ public class Computer | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Redstone | ||||
|  | ||||
|     public int getRedstoneOutput( int side ) | ||||
|     { | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             return isOn() ? m_externalOutput[side] : 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int getInternalRedstoneOutput( int side ) | ||||
|     { | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             return isOn() ? m_internalOutput[side] : 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void setRedstoneOutput( int side, int level ) | ||||
|     { | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             if( m_internalOutput[side] != level ) | ||||
|             { | ||||
|                 m_internalOutput[side] = level; | ||||
|                 m_internalOutputChanged = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setRedstoneInput( int side, int level ) | ||||
|     { | ||||
|         synchronized( m_input ) | ||||
|         { | ||||
|             if( m_input[side] != level ) | ||||
|             { | ||||
|                 m_input[side] = level; | ||||
|                 m_inputChanged = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int getRedstoneInput( int side ) | ||||
|     { | ||||
|         synchronized( m_input ) | ||||
|         { | ||||
|             return m_input[side]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public int getBundledRedstoneOutput( int side ) | ||||
|     { | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             return isOn() ? m_externalBundledOutput[side] : 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int getInternalBundledRedstoneOutput( int side ) | ||||
|     { | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             return isOn() ? m_internalBundledOutput[side] : 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void setBundledRedstoneOutput( int side, int combination ) | ||||
|     { | ||||
|         synchronized( m_internalOutput ) | ||||
|         { | ||||
|             if( m_internalBundledOutput[side] != combination ) | ||||
|             { | ||||
|                 m_internalBundledOutput[side] = combination; | ||||
|                 m_internalOutputChanged = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setBundledRedstoneInput( int side, int combination ) | ||||
|     { | ||||
|         synchronized( m_input ) | ||||
|         { | ||||
|             if( m_bundledInput[side] != combination ) | ||||
|             { | ||||
|                 m_bundledInput[side] = combination; | ||||
|                 m_inputChanged = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int getBundledRedstoneInput( int side ) | ||||
|     { | ||||
|         synchronized( m_input ) | ||||
|         { | ||||
|             return m_bundledInput[side]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Peripherals | ||||
|  | ||||
|     public void addAPI( ILuaAPI api ) | ||||
| @@ -676,57 +282,8 @@ public class Computer | ||||
|         m_apis.add( api ); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings( "deprecation" ) | ||||
|     public void addAPI( dan200.computercraft.core.apis.ILuaAPI api ) | ||||
|     { | ||||
|         addAPI( (ILuaAPI) api ); | ||||
|     } | ||||
|  | ||||
|     public void setPeripheral( int side, IPeripheral peripheral ) | ||||
|     { | ||||
|         synchronized( m_peripherals ) | ||||
|         { | ||||
|             IPeripheral existing = m_peripherals[side]; | ||||
|             if( (existing == null && peripheral != null) || | ||||
|                 (existing != null && peripheral == null) || | ||||
|                 (existing != null && !existing.equals( peripheral )) ) | ||||
|             { | ||||
|                 m_peripherals[side] = peripheral; | ||||
|                 m_apiEnvironment.onPeripheralChanged( side, peripheral ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public IPeripheral getPeripheral( int side ) | ||||
|     { | ||||
|         synchronized( m_peripherals ) | ||||
|         { | ||||
|             return m_peripherals[side]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Lua | ||||
|  | ||||
|     private void createAPIs() | ||||
|     { | ||||
|         m_apis.add( new TermAPI( m_apiEnvironment ) ); | ||||
|         m_apis.add( new RedstoneAPI( m_apiEnvironment ) ); | ||||
|         m_apis.add( new FSAPI( m_apiEnvironment ) ); | ||||
|         m_apis.add( new PeripheralAPI( m_apiEnvironment ) ); | ||||
|         m_apis.add( new OSAPI( m_apiEnvironment ) ); | ||||
|         if( ComputerCraft.http_enable ) | ||||
|         { | ||||
|             m_apis.add( new HTTPAPI( m_apiEnvironment ) ); | ||||
|         } | ||||
|  | ||||
|         for( ILuaAPIFactory factory : ApiFactories.getAll() ) | ||||
|         { | ||||
|             ComputerSystem system = new ComputerSystem( m_apiEnvironment ); | ||||
|             ILuaAPI api = factory.create( system ); | ||||
|             if( api != null ) m_apis.add( api ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void initLua() | ||||
|     { | ||||
|         // Create the lua machine | ||||
| @@ -824,10 +381,7 @@ public class Computer | ||||
|                     } | ||||
|  | ||||
|                     // Init terminal | ||||
|                     synchronized( m_terminal ) | ||||
|                     { | ||||
|                         m_terminal.reset(); | ||||
|                     } | ||||
|                     m_terminal.reset(); | ||||
|  | ||||
|                     // Init filesystem | ||||
|                     if( !initFileSystem() ) | ||||
| @@ -920,10 +474,7 @@ public class Computer | ||||
|  | ||||
|                     if( m_machine != null ) | ||||
|                     { | ||||
|                         synchronized( m_terminal ) | ||||
|                         { | ||||
|                             m_terminal.reset(); | ||||
|                         } | ||||
|                         m_terminal.reset(); | ||||
|  | ||||
|                         synchronized( m_machine ) | ||||
|                         { | ||||
| @@ -933,15 +484,7 @@ public class Computer | ||||
|                     } | ||||
|  | ||||
|                     // Reset redstone output | ||||
|                     synchronized( m_internalOutput ) | ||||
|                     { | ||||
|                         for( int i = 0; i < 6; i++ ) | ||||
|                         { | ||||
|                             m_internalOutput[i] = 0; | ||||
|                             m_internalBundledOutput[i] = 0; | ||||
|                         } | ||||
|                         m_internalOutputChanged = true; | ||||
|                     } | ||||
|                     m_internalEnvironment.resetOutput(); | ||||
|  | ||||
|                     m_state = State.Off; | ||||
|                     m_externalOutputChanged = true; | ||||
| @@ -1002,4 +545,23 @@ public class Computer | ||||
|  | ||||
|         ComputerThread.queueTask( task, computer ); | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|     public void setPeripheral( int side, IPeripheral peripheral ) | ||||
|     { | ||||
|         m_internalEnvironment.setPeripheral( side, peripheral ); | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|     public void addAPI( dan200.computercraft.core.apis.ILuaAPI api ) | ||||
|     { | ||||
|         addAPI( (ILuaAPI) api ); | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|     @SuppressWarnings( "unused" ) | ||||
|     public void advance( double dt ) | ||||
|     { | ||||
|         advance(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.core.computer; | ||||
|  | ||||
| import dan200.computercraft.api.filesystem.IFileSystem; | ||||
| import dan200.computercraft.api.lua.IComputerSystem; | ||||
| import dan200.computercraft.api.lua.ILuaAPIFactory; | ||||
| import dan200.computercraft.api.peripheral.IComputerAccess; | ||||
| import dan200.computercraft.core.apis.ComputerAccess; | ||||
| import dan200.computercraft.core.apis.IAPIEnvironment; | ||||
| import dan200.computercraft.core.filesystem.FileSystem; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
|  | ||||
| /** | ||||
|  * Implementation of {@link IComputerAccess}/{@link IComputerSystem} for external APIs. | ||||
|  * | ||||
|  * @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory) | ||||
|  * @see ILuaAPIFactory | ||||
|  * @see ApiWrapper | ||||
|  */ | ||||
| public class ComputerSystem extends ComputerAccess implements IComputerSystem | ||||
| { | ||||
|     private final IAPIEnvironment m_environment; | ||||
|  | ||||
|     ComputerSystem( IAPIEnvironment m_environment ) | ||||
|     { | ||||
|         super( m_environment ); | ||||
|         this.m_environment = m_environment; | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public String getAttachmentName() | ||||
|     { | ||||
|         return "computer"; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public IFileSystem getFileSystem() | ||||
|     { | ||||
|         FileSystem fs = m_environment.getFileSystem(); | ||||
|         return fs == null ? null : fs.getMountWrapper(); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public String getLabel() | ||||
|     { | ||||
|         return m_environment.getLabel(); | ||||
|     } | ||||
| } | ||||
| @@ -16,6 +16,7 @@ import java.util.WeakHashMap; | ||||
| import java.util.concurrent.BlockingQueue; | ||||
| import java.util.concurrent.LinkedBlockingQueue; | ||||
| import java.util.concurrent.ThreadFactory; | ||||
| import java.util.concurrent.locks.LockSupport; | ||||
|  | ||||
| public class ComputerThread | ||||
| { | ||||
| @@ -216,22 +217,37 @@ public class ComputerThread | ||||
|                     // Interrupt the thread | ||||
|                     if( !done ) | ||||
|                     { | ||||
|                         StringBuilder builder = new StringBuilder( "Terminating " ); | ||||
|                         if( computer != null ) | ||||
|                         if( ComputerCraft.logPeripheralErrors ) | ||||
|                         { | ||||
|                             builder.append( "computer " ).append( computer.getID() ); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             builder.append( "unknown computer" ); | ||||
|                         } | ||||
|                             long time = System.nanoTime() - start; | ||||
|                             StringBuilder builder = new StringBuilder( "Terminating " ); | ||||
|                             if( computer != null ) | ||||
|                             { | ||||
|                                 builder.append( "computer #" ).append( computer.getID() ); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 builder.append( "unknown computer" ); | ||||
|                             } | ||||
|  | ||||
|                         builder.append( ". Thread is currently running" ); | ||||
|                         for( StackTraceElement element : thread.getStackTrace() ) | ||||
|                         { | ||||
|                             builder.append( "\n  at " ).append( element ); | ||||
|                             { | ||||
|                                 builder.append( " due to timeout (running for " ) | ||||
|                                     .append( time / 1e9 ) | ||||
|                                     .append( " seconds). This is NOT a bug, but may mean a computer is misbehaving. " ) | ||||
|                                     .append( thread.getName() ) | ||||
|                                     .append( " is currently " ) | ||||
|                                     .append( thread.getState() ); | ||||
|                                 Object blocking = LockSupport.getBlocker( thread ); | ||||
|                                 if( blocking != null ) builder.append( "\n  on " ).append( blocking ); | ||||
|  | ||||
|                                 for( StackTraceElement element : thread.getStackTrace() ) | ||||
|                                 { | ||||
|                                     builder.append( "\n  at " ).append( element ); | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
|                             ComputerCraft.log.warn( builder.toString() ); | ||||
|                         } | ||||
|                         ComputerCraft.log.error( builder.toString() ); | ||||
|  | ||||
|                         thread.interrupt(); | ||||
|                         thread = null; | ||||
|   | ||||
| @@ -0,0 +1,306 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.core.computer; | ||||
|  | ||||
| import dan200.computercraft.api.peripheral.IPeripheral; | ||||
| import dan200.computercraft.core.apis.IAPIEnvironment; | ||||
| import dan200.computercraft.core.filesystem.FileSystem; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.core.tracking.Tracking; | ||||
| import dan200.computercraft.core.tracking.TrackingField; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.Arrays; | ||||
|  | ||||
| /** | ||||
|  * Represents the "environment" that a {@link Computer} exists in. | ||||
|  * | ||||
|  * This handles storing and updating of peripherals and redstone. | ||||
|  * | ||||
|  * <h2>Redstone</h2> | ||||
|  * We holds three kinds of arrays for redstone, in normal and bundled versions: | ||||
|  * <ul> | ||||
|  * <li>{@link #internalOutput} is the redstone output which the computer has currently set. This is read on both | ||||
|  * threads, and written on the computer thread.</li> | ||||
|  * <li>{@link #externalOutput} is the redstone output currently propagated to the world. This is only read and written | ||||
|  * on the main thread.</li> | ||||
|  * <li>{@link #input} is the redstone input from external sources. This is read on both threads, and written on the main | ||||
|  * thread.</li> | ||||
|  * </ul> | ||||
|  * | ||||
|  * <h2>Peripheral</h2> | ||||
|  * We also keep track of peripherals. These are read on both threads, and only written on the main thread. | ||||
|  */ | ||||
| public final class Environment implements IAPIEnvironment | ||||
| { | ||||
|     private final Computer computer; | ||||
|  | ||||
|     private boolean internalOutputChanged = false; | ||||
|     private final int[] internalOutput = new int[SIDE_COUNT]; | ||||
|     private final int[] internalBundledOutput = new int[SIDE_COUNT]; | ||||
|  | ||||
|     private final int[] externalOutput = new int[SIDE_COUNT]; | ||||
|     private final int[] externalBundledOutput = new int[SIDE_COUNT]; | ||||
|  | ||||
|     private boolean inputChanged = false; | ||||
|     private final int[] input = new int[SIDE_COUNT]; | ||||
|     private final int[] bundledInput = new int[SIDE_COUNT]; | ||||
|  | ||||
|     private final IPeripheral[] peripherals = new IPeripheral[SIDE_COUNT]; | ||||
|     private IPeripheralChangeListener peripheralListener = null; | ||||
|  | ||||
|     Environment( Computer computer ) | ||||
|     { | ||||
|         this.computer = computer; | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public Computer getComputer() | ||||
|     { | ||||
|         return computer; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getComputerID() | ||||
|     { | ||||
|         return computer.assignID(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public IComputerEnvironment getComputerEnvironment() | ||||
|     { | ||||
|         return computer.getComputerEnvironment(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public Terminal getTerminal() | ||||
|     { | ||||
|         return computer.getTerminal(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public FileSystem getFileSystem() | ||||
|     { | ||||
|         return computer.getFileSystem(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void shutdown() | ||||
|     { | ||||
|         computer.shutdown(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void reboot() | ||||
|     { | ||||
|         computer.reboot(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void queueEvent( String event, Object[] args ) | ||||
|     { | ||||
|         computer.queueEvent( event, args ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getInput( int side ) | ||||
|     { | ||||
|         return input[side]; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getBundledInput( int side ) | ||||
|     { | ||||
|         return bundledInput[side]; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setOutput( int side, int output ) | ||||
|     { | ||||
|         synchronized( internalOutput ) | ||||
|         { | ||||
|             if( internalOutput[side] != output ) | ||||
|             { | ||||
|                 internalOutput[side] = output; | ||||
|                 internalOutputChanged = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getOutput( int side ) | ||||
|     { | ||||
|         synchronized( internalOutput ) | ||||
|         { | ||||
|             return computer.isOn() ? internalOutput[side] : 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setBundledOutput( int side, int output ) | ||||
|     { | ||||
|         synchronized( internalOutput ) | ||||
|         { | ||||
|             if( internalBundledOutput[side] != output ) | ||||
|             { | ||||
|                 internalBundledOutput[side] = output; | ||||
|                 internalOutputChanged = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getBundledOutput( int side ) | ||||
|     { | ||||
|         synchronized( internalOutput ) | ||||
|         { | ||||
|             return computer.isOn() ? internalBundledOutput[side] : 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public int getExternalRedstoneOutput( int side ) | ||||
|     { | ||||
|         return computer.isOn() ? externalOutput[side] : 0; | ||||
|     } | ||||
|  | ||||
|     public int getExternalBundledRedstoneOutput( int side ) | ||||
|     { | ||||
|         return computer.isOn() ? externalBundledOutput[side] : 0; | ||||
|     } | ||||
|  | ||||
|     public void setRedstoneInput( int side, int level ) | ||||
|     { | ||||
|         if( input[side] != level ) | ||||
|         { | ||||
|             input[side] = level; | ||||
|             inputChanged = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setBundledRedstoneInput( int side, int combination ) | ||||
|     { | ||||
|         if( bundledInput[side] != combination ) | ||||
|         { | ||||
|             bundledInput[side] = combination; | ||||
|             inputChanged = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Called on the main thread to update the internal state of the computer. | ||||
|      * | ||||
|      * This just queues a {@code redstone} event if the input has changed. | ||||
|      */ | ||||
|     void update() | ||||
|     { | ||||
|         if( inputChanged ) | ||||
|         { | ||||
|             inputChanged = false; | ||||
|             queueEvent( "redstone", null ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Called on the main thread to propagate the internal outputs to the external ones. | ||||
|      * | ||||
|      * @return If the outputs have changed. | ||||
|      */ | ||||
|     boolean updateOutput() | ||||
|     { | ||||
|         // Set outputchanged if the internal redstone has changed | ||||
|         synchronized( internalOutput ) | ||||
|         { | ||||
|             if( !internalOutputChanged ) return false; | ||||
|  | ||||
|             boolean changed = false; | ||||
|  | ||||
|             for( int i = 0; i < SIDE_COUNT; i++ ) | ||||
|             { | ||||
|                 if( externalOutput[i] != internalOutput[i] ) | ||||
|                 { | ||||
|                     externalOutput[i] = internalOutput[i]; | ||||
|                     changed = true; | ||||
|                 } | ||||
|  | ||||
|                 if( externalBundledOutput[i] != internalBundledOutput[i] ) | ||||
|                 { | ||||
|                     externalBundledOutput[i] = internalBundledOutput[i]; | ||||
|                     changed = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             internalOutputChanged = false; | ||||
|  | ||||
|             return changed; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void resetOutput() | ||||
|     { | ||||
|         // Reset redstone output | ||||
|         synchronized( internalOutput ) | ||||
|         { | ||||
|             Arrays.fill( internalOutput, 0 ); | ||||
|             Arrays.fill( internalBundledOutput, 0 ); | ||||
|             internalOutputChanged = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public IPeripheral getPeripheral( int side ) | ||||
|     { | ||||
|         synchronized( peripherals ) | ||||
|         { | ||||
|             return peripherals[side]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setPeripheral( int side, IPeripheral peripheral ) | ||||
|     { | ||||
|         synchronized( peripherals ) | ||||
|         { | ||||
|             IPeripheral existing = peripherals[side]; | ||||
|             if( (existing == null && peripheral != null) || | ||||
|                 (existing != null && peripheral == null) || | ||||
|                 (existing != null && !existing.equals( peripheral )) ) | ||||
|             { | ||||
|                 peripherals[side] = peripheral; | ||||
|                 if( peripheralListener != null ) peripheralListener.onPeripheralChanged( side, peripheral ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setPeripheralChangeListener( IPeripheralChangeListener listener ) | ||||
|     { | ||||
|         synchronized( peripherals ) | ||||
|         { | ||||
|             peripheralListener = listener; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getLabel() | ||||
|     { | ||||
|         return computer.getLabel(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setLabel( String label ) | ||||
|     { | ||||
|         computer.setLabel( label ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void addTrackingChange( @Nonnull TrackingField field, long change ) | ||||
|     { | ||||
|         Tracking.addValue( computer, field, change ); | ||||
|     } | ||||
| } | ||||
| @@ -46,7 +46,7 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
| { | ||||
|     private static final ThreadPoolExecutor coroutines = new ThreadPoolExecutor( | ||||
|         0, Integer.MAX_VALUE, | ||||
|         60L, TimeUnit.SECONDS, | ||||
|         5L, TimeUnit.MINUTES, | ||||
|         new SynchronousQueue<>(), | ||||
|         ThreadUtils.factory( "Coroutine" ) | ||||
|     ); | ||||
| @@ -74,12 +74,12 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                 private boolean hasSoftAbort; | ||||
|  | ||||
|                 @Override | ||||
|                 public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError | ||||
|                 public void onInstruction( DebugState ds, DebugFrame di, int pc ) throws LuaError, UnwindThrowable | ||||
|                 { | ||||
|                     int count = ++this.count; | ||||
|                     if( count > 100000 ) | ||||
|                     { | ||||
|                         if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE ); | ||||
|                         if( m_hardAbortMessage != null ) throw HardAbortError.INSTANCE; | ||||
|                         this.count = 0; | ||||
|                     } | ||||
|                     else | ||||
| @@ -87,13 +87,13 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                         handleSoftAbort(); | ||||
|                     } | ||||
|  | ||||
|                     super.onInstruction( ds, di, pc, extras, top ); | ||||
|                     super.onInstruction( ds, di, pc ); | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void poll() throws LuaError | ||||
|                 { | ||||
|                     if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE ); | ||||
|                     if( m_hardAbortMessage != null ) throw HardAbortError.INSTANCE; | ||||
|                     handleSoftAbort(); | ||||
|                 } | ||||
|  | ||||
| @@ -117,7 +117,7 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                     throw new LuaError( message ); | ||||
|                 } | ||||
|             } ) | ||||
|             .coroutineFactory( command -> { | ||||
|             .yieldThreader( command -> { | ||||
|                 Tracking.addValue( m_computer, TrackingField.COROUTINES_CREATED, 1 ); | ||||
|                 coroutines.execute( () -> { | ||||
|                     try | ||||
| @@ -145,10 +145,10 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|         if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() ); | ||||
|  | ||||
|         // Register custom load/loadstring provider which automatically adds prefixes. | ||||
|         LibFunction.bind( state, m_globals, PrefixLoader.class, new String[] { "load", "loadstring" } ); | ||||
|         LibFunction.bind( m_globals, PrefixLoader.class, new String[] { "load", "loadstring" } ); | ||||
|  | ||||
|         // Remove globals we don't want to expose | ||||
|         // m_globals.rawset( "collectgarbage", Constants.NIL ); | ||||
|         m_globals.rawset( "collectgarbage", Constants.NIL ); | ||||
|         m_globals.rawset( "dofile", Constants.NIL ); | ||||
|         m_globals.rawset( "loadfile", Constants.NIL ); | ||||
|         m_globals.rawset( "print", Constants.NIL ); | ||||
| @@ -222,26 +222,18 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                 resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) ); | ||||
|             } | ||||
|  | ||||
|             Varargs results = m_mainRoutine.resume( resumeArgs ); | ||||
|             if( m_hardAbortMessage != null ) | ||||
|             { | ||||
|                 throw new LuaError( m_hardAbortMessage ); | ||||
|             } | ||||
|             else if( !results.first().checkBoolean() ) | ||||
|             { | ||||
|                 throw new LuaError( results.arg( 2 ).checkString() ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 LuaValue filter = results.arg( 2 ); | ||||
|                 m_eventFilter = filter.isString() ? filter.toString() : null; | ||||
|             } | ||||
|             Varargs results = LuaThread.run( m_mainRoutine, resumeArgs ); | ||||
|             if( m_hardAbortMessage != null ) throw new LuaError( m_hardAbortMessage ); | ||||
|  | ||||
|             LuaValue filter = results.first(); | ||||
|             m_eventFilter = filter.isString() ? filter.toString() : null; | ||||
|  | ||||
|             if( m_mainRoutine.getStatus().equals( "dead" ) ) unload(); | ||||
|         } | ||||
|         catch( LuaError e ) | ||||
|         catch( LuaError | HardAbortError e ) | ||||
|         { | ||||
|             unload(); | ||||
|             ComputerCraft.log.warn( "Top level coroutine errored", e ); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
| @@ -339,16 +331,12 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                                 { | ||||
|                                     try | ||||
|                                     { | ||||
|                                         Varargs results = LuaThread.yield( state, toValues( yieldArgs ) ); | ||||
|                                         Varargs results = LuaThread.yieldBlocking( state, toValues( yieldArgs ) ); | ||||
|                                         return toObjects( results, 1 ); | ||||
|                                     } | ||||
|                                     catch( OrphanedThread e ) | ||||
|                                     catch( LuaError e ) | ||||
|                                     { | ||||
|                                         throw new InterruptedException(); | ||||
|                                     } | ||||
|                                     catch( Throwable e ) | ||||
|                                     { | ||||
|                                         throw new RuntimeException( e ); | ||||
|                                         throw new IllegalStateException( e ); | ||||
|                                     } | ||||
|                                 } | ||||
|  | ||||
| @@ -699,7 +687,7 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                 LuaValue s; | ||||
|                 try | ||||
|                 { | ||||
|                     s = OperationHelper.call( state, func ); | ||||
|                     s = OperationHelper.noYield( state, () -> OperationHelper.call( state, func ) ); | ||||
|                 } | ||||
|                 catch( LuaError e ) | ||||
|                 { | ||||
| @@ -731,4 +719,16 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|             return bytes[offset++]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class HardAbortError extends Error | ||||
|     { | ||||
|         private static final long serialVersionUID = 7954092008586367501L; | ||||
|  | ||||
|         public static final HardAbortError INSTANCE = new HardAbortError(); | ||||
|  | ||||
|         private HardAbortError() | ||||
|         { | ||||
|             super( "Hard Abort", null, true, false ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ package dan200.computercraft.shared.command; | ||||
| import com.google.common.collect.Sets; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.peripheral.IPeripheral; | ||||
| import dan200.computercraft.core.apis.IAPIEnvironment; | ||||
| import dan200.computercraft.core.computer.Computer; | ||||
| import dan200.computercraft.core.tracking.ComputerTracker; | ||||
| import dan200.computercraft.core.tracking.Tracking; | ||||
| @@ -126,7 +127,7 @@ public final class CommandComputerCraft extends CommandDelegate | ||||
|                         IPeripheral peripheral = computer.getPeripheral( i ); | ||||
|                         if( peripheral != null ) | ||||
|                         { | ||||
|                             table.row( header( "Peripheral " + Computer.s_sideNames[i] ), text( peripheral.getType() ) ); | ||||
|                             table.row( header( "Peripheral " + IAPIEnvironment.SIDE_NAMES[i] ), text( peripheral.getType() ) ); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|   | ||||
| @@ -101,6 +101,11 @@ public class TableBuilder | ||||
|         return additional; | ||||
|     } | ||||
|  | ||||
|     public void setAdditional( int additional ) | ||||
|     { | ||||
|         this.additional = additional; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Trim this table to a given height | ||||
|      * | ||||
| @@ -110,8 +115,8 @@ public class TableBuilder | ||||
|     { | ||||
|         if( rows.size() > height ) | ||||
|         { | ||||
|             additional += rows.size() - height; | ||||
|             rows.subList( height, rows.size() ).clear(); | ||||
|             additional += rows.size() - height - 1; | ||||
|             rows.subList( height - 1, rows.size() ).clear(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -74,8 +74,6 @@ public interface TableFormatter | ||||
|         int totalWidth = (columns - 1) * getWidth( SEPARATOR ); | ||||
|         for( int x : maxWidths ) totalWidth += x; | ||||
|  | ||||
|         // TODO: Limit the widths of some entries if totalWidth > maxWidth | ||||
|  | ||||
|         if( headers != null ) | ||||
|         { | ||||
|             TextComponentString line = new TextComponentString( "" ); | ||||
|   | ||||
| @@ -11,11 +11,9 @@ import net.minecraft.block.ITileEntityProvider; | ||||
| import net.minecraft.block.material.Material; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.IBlockAccess; | ||||
| import net.minecraft.world.World; | ||||
| @@ -35,108 +33,36 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider | ||||
|  | ||||
|     protected abstract TileGeneric createTile( int damage ); | ||||
|  | ||||
|     @Override | ||||
|     public final void dropBlockAsItemWithChance( World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, float chance, int fortune ) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final void getDrops( @Nonnull NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, @Nonnull IBlockState state, int fortune ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             generic.getDroppedItems( drops, false ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest ) | ||||
|     { | ||||
|         if( !world.isRemote ) | ||||
|         { | ||||
|             // Drop items | ||||
|             boolean creative = player.capabilities.isCreativeMode; | ||||
|             dropAllItems( world, pos, creative ); | ||||
|         } | ||||
|  | ||||
|         // Remove block | ||||
|         return super.removedByPlayer( state, world, pos, player, willHarvest ); | ||||
|     } | ||||
|  | ||||
|     public final void dropAllItems( World world, BlockPos pos, boolean creative ) | ||||
|     { | ||||
|         // Get items to drop | ||||
|         NonNullList<ItemStack> drops = NonNullList.create(); | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             generic.getDroppedItems( drops, creative ); | ||||
|         } | ||||
|  | ||||
|         // Drop items | ||||
|         if( drops.size() > 0 ) | ||||
|         { | ||||
|             for( ItemStack item : drops ) | ||||
|             { | ||||
|                 dropItem( world, pos, item ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public final void dropItem( World world, BlockPos pos, @Nonnull ItemStack stack ) | ||||
|     { | ||||
|         Block.spawnAsEntity( world, pos, stack ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final void breakBlock( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState newState ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         super.breakBlock( world, pos, newState ); | ||||
|         world.removeTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             generic.destroy(); | ||||
|         } | ||||
|         if( tile instanceof TileGeneric ) ((TileGeneric) tile).destroy(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final boolean onBlockActivated( World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             return generic.onActivate( player, hand, side, hitX, hitY, hitZ ); | ||||
|         } | ||||
|         return false; | ||||
|         return tile instanceof TileGeneric && ((TileGeneric) tile).onActivate( player, hand, side, hitX, hitY, hitZ ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Deprecated | ||||
|     public final void neighborChanged( IBlockState state, World world, BlockPos pos, Block block, BlockPos neighorPos ) | ||||
|     @SuppressWarnings( "deprecation" ) | ||||
|     public final void neighborChanged( IBlockState state, World world, BlockPos pos, Block block, BlockPos neighbour ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             generic.onNeighbourChange(); | ||||
|         } | ||||
|         if( tile instanceof TileGeneric ) ((TileGeneric) tile).onNeighbourChange( neighbour ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final void onNeighborChange( IBlockAccess world, BlockPos pos, BlockPos neighbour ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             generic.onNeighbourTileEntityChange( neighbour ); | ||||
|         } | ||||
|         if( tile instanceof TileGeneric ) ((TileGeneric) tile).onNeighbourTileEntityChange( neighbour ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -157,12 +83,7 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider | ||||
|     public final boolean canConnectRedstone( IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing side ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             return generic.getRedstoneConnectivity( side ); | ||||
|         } | ||||
|         return false; | ||||
|         return tile instanceof TileGeneric && ((TileGeneric) tile).getRedstoneConnectivity( side ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -172,8 +93,7 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric && tile.hasWorld() ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             return generic.getRedstoneOutput( oppositeSide.getOpposite() ); | ||||
|             return ((TileGeneric) tile).getRedstoneOutput( oppositeSide.getOpposite() ); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| @@ -190,8 +110,7 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             return generic.getBundledRedstoneConnectivity( side ); | ||||
|             return ((TileGeneric) tile).getBundledRedstoneConnectivity( side ); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| @@ -201,8 +120,7 @@ public abstract class BlockGeneric extends Block implements ITileEntityProvider | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileGeneric && tile.hasWorld() ) | ||||
|         { | ||||
|             TileGeneric generic = (TileGeneric) tile; | ||||
|             return generic.getBundledRedstoneOutput( side ); | ||||
|             return ((TileGeneric) tile).getBundledRedstoneOutput( side ); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|   | ||||
| @@ -9,14 +9,12 @@ package dan200.computercraft.shared.common; | ||||
| import net.minecraft.block.Block; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.network.NetworkManager; | ||||
| import net.minecraft.network.play.server.SPacketUpdateTileEntity; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
|  | ||||
| @@ -55,19 +53,22 @@ public abstract class TileGeneric extends TileEntity | ||||
|         getWorld().setBlockState( getPos(), newState, 3 ); | ||||
|     } | ||||
|  | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     public boolean onActivate( EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ ) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|     public void onNeighbourChange() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings( "deprecation" ) | ||||
|     public void onNeighbourChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         onNeighbourChange(); | ||||
|     } | ||||
|  | ||||
|     public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|     } | ||||
|   | ||||
| @@ -8,11 +8,13 @@ package dan200.computercraft.shared.computer.blocks; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.items.ComputerItemFactory; | ||||
| import net.minecraft.block.material.Material; | ||||
| import net.minecraft.block.properties.PropertyDirection; | ||||
| import net.minecraft.block.properties.PropertyEnum; | ||||
| import net.minecraft.block.state.BlockStateContainer; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| @@ -103,4 +105,11 @@ public class BlockCommandComputer extends BlockComputerBase | ||||
|     { | ||||
|         return new TileCommandComputer(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     protected ItemStack getItem( TileComputerBase tile ) | ||||
|     { | ||||
|         return tile instanceof TileCommandComputer ? ComputerItemFactory.create( (TileComputer) tile ) : ItemStack.EMPTY; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,14 +15,11 @@ import net.minecraft.block.properties.PropertyDirection; | ||||
| import net.minecraft.block.properties.PropertyEnum; | ||||
| import net.minecraft.block.state.BlockStateContainer; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.RayTraceResult; | ||||
| import net.minecraft.world.IBlockAccess; | ||||
| import net.minecraft.world.World; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| @@ -136,9 +133,8 @@ public class BlockComputer extends BlockComputerBase | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player ) | ||||
|     protected ItemStack getItem( TileComputerBase tile ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         return tile instanceof TileComputer ? ComputerItemFactory.create( (TileComputer) tile ) : super.getPickBlock( state, target, world, pos, player ); | ||||
|         return tile instanceof TileComputer ? ComputerItemFactory.create( (TileComputer) tile ) : ItemStack.EMPTY; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,9 +12,13 @@ import dan200.computercraft.shared.util.DirectionUtil; | ||||
| import net.minecraft.block.material.Material; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.EntityLivingBase; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.RayTraceResult; | ||||
| import net.minecraft.world.IBlockAccess; | ||||
| import net.minecraft.world.World; | ||||
|  | ||||
| @@ -49,6 +53,9 @@ public abstract class BlockComputerBase extends BlockDirectional | ||||
|  | ||||
|     protected abstract TileComputerBase createTile( ComputerFamily family ); | ||||
|  | ||||
|     @Nonnull | ||||
|     protected abstract ItemStack getItem( TileComputerBase tile ); | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     @Deprecated | ||||
| @@ -83,4 +90,55 @@ public abstract class BlockComputerBase extends BlockDirectional | ||||
|             computer.updateInput(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Nonnull | ||||
|     public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileComputerBase ) | ||||
|         { | ||||
|             ItemStack result = getItem( (TileComputerBase) tile ); | ||||
|             if( !result.isEmpty() ) return result; | ||||
|         } | ||||
|  | ||||
|         return super.getPickBlock( state, target, world, pos, player ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final void dropBlockAsItemWithChance( World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, float chance, int fortune ) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final void getDrops( @Nonnull NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, @Nonnull IBlockState state, int fortune ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile instanceof TileComputerBase ) | ||||
|         { | ||||
|             ItemStack stack = getItem( (TileComputerBase) tile ); | ||||
|             if( !stack.isEmpty() ) drops.add( stack ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnull BlockPos pos, @Nonnull EntityPlayer player, boolean willHarvest ) | ||||
|     { | ||||
|         if( !world.isRemote ) | ||||
|         { | ||||
|             // We drop the item here instead of doing it in the harvest method, as we | ||||
|             // need to drop it for creative players too. | ||||
|             TileEntity tile = world.getTileEntity( pos ); | ||||
|             if( tile instanceof TileComputerBase ) | ||||
|             { | ||||
|                 TileComputerBase computer = (TileComputerBase) tile; | ||||
|                 if( !player.capabilities.isCreativeMode || computer.getLabel() != null ) | ||||
|                 { | ||||
|                     spawnAsEntity( world, pos, getItem( computer ) ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return super.removedByPlayer( state, world, pos, player, willHarvest ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,13 +9,10 @@ package dan200.computercraft.shared.computer.blocks; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.ServerComputer; | ||||
| import dan200.computercraft.shared.computer.items.ComputerItemFactory; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.NonNullList; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| @@ -60,12 +57,6 @@ public class TileComputer extends TileComputerBase | ||||
|         return m_proxy; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative || getLabel() != null ) drops.add( ComputerItemFactory.create( this ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void openGUI( EntityPlayer player ) | ||||
|     { | ||||
|   | ||||
| @@ -173,9 +173,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNeighbourChange() | ||||
|     public void onNeighbourChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         updateInput(); | ||||
|         updateInput( neighbour ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -296,7 +296,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT | ||||
|         int localDir = remapLocalSide( DirectionUtil.toLocal( this, dir ) ); | ||||
|         if( !isRedstoneBlockedOnSide( localDir ) ) | ||||
|         { | ||||
|             computer.setRedstoneInput( localDir, getWorld().getRedstonePower( offset, offsetSide ) ); | ||||
|             computer.setRedstoneInput( localDir, getWorld().getRedstonePower( offset, dir ) ); | ||||
|             computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getWorld(), offset, offsetSide ) ); | ||||
|         } | ||||
|         if( !isPeripheralBlockedOnSide( localDir ) ) | ||||
|   | ||||
| @@ -105,7 +105,7 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput | ||||
|     public void update() | ||||
|     { | ||||
|         super.update(); | ||||
|         m_computer.advance( 0.05 ); | ||||
|         m_computer.advance(); | ||||
|  | ||||
|         m_changedLastFrame = m_computer.pollAndResetChanged() || m_changed; | ||||
|         m_changed = false; | ||||
| @@ -283,22 +283,22 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput | ||||
|  | ||||
|     public int getRedstoneOutput( int side ) | ||||
|     { | ||||
|         return m_computer.getRedstoneOutput( side ); | ||||
|         return m_computer.getEnvironment().getExternalRedstoneOutput( side ); | ||||
|     } | ||||
|  | ||||
|     public void setRedstoneInput( int side, int level ) | ||||
|     { | ||||
|         m_computer.setRedstoneInput( side, level ); | ||||
|         m_computer.getEnvironment().setRedstoneInput( side, level ); | ||||
|     } | ||||
|  | ||||
|     public int getBundledRedstoneOutput( int side ) | ||||
|     { | ||||
|         return m_computer.getBundledRedstoneOutput( side ); | ||||
|         return m_computer.getEnvironment().getExternalBundledRedstoneOutput( side ); | ||||
|     } | ||||
|  | ||||
|     public void setBundledRedstoneInput( int side, int combination ) | ||||
|     { | ||||
|         m_computer.setBundledRedstoneInput( side, combination ); | ||||
|         m_computer.getEnvironment().setBundledRedstoneInput( side, combination ); | ||||
|     } | ||||
|  | ||||
|     public void addAPI( ILuaAPI api ) | ||||
| @@ -306,7 +306,7 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput | ||||
|         m_computer.addAPI( api ); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings( "deprecation" ) | ||||
|     @Deprecated | ||||
|     public void addAPI( dan200.computercraft.core.apis.ILuaAPI api ) | ||||
|     { | ||||
|         m_computer.addAPI( api ); | ||||
| @@ -314,12 +314,12 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput | ||||
|  | ||||
|     public void setPeripheral( int side, IPeripheral peripheral ) | ||||
|     { | ||||
|         m_computer.setPeripheral( side, peripheral ); | ||||
|         m_computer.getEnvironment().setPeripheral( side, peripheral ); | ||||
|     } | ||||
|  | ||||
|     public IPeripheral getPeripheral( int side ) | ||||
|     { | ||||
|         return m_computer.getPeripheral( side ); | ||||
|         return m_computer.getEnvironment().getPeripheral( side ); | ||||
|     } | ||||
|  | ||||
|     public void setLabel( String label ) | ||||
|   | ||||
| @@ -43,7 +43,10 @@ final class BundledCapabilityProvider implements ICapabilityProvider | ||||
|         if( capability == CAPABILITY_RECEIVER ) | ||||
|         { | ||||
|             IBundledReceiver receiver = this.receiver; | ||||
|             if( receiver == null ) receiver = this.receiver = tile::onNeighbourChange; | ||||
|             if( receiver == null ) | ||||
|             { | ||||
|                 receiver = this.receiver = () -> tile.onNeighbourChange( tile.getPos().offset( side ) ); | ||||
|             } | ||||
|  | ||||
|             return CAPABILITY_RECEIVER.cast( receiver ); | ||||
|         } | ||||
|   | ||||
| @@ -48,6 +48,8 @@ public class ChatTableClientMessage implements NetworkMessage | ||||
|         { | ||||
|             for( ITextComponent column : row ) buf.writeTextComponent( column ); | ||||
|         } | ||||
|  | ||||
|         buf.writeVarInt( table.getAdditional() ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -74,6 +76,8 @@ public class ChatTableClientMessage implements NetworkMessage | ||||
|             for( int j = 0; j < columns; j++ ) row[j] = NBTUtil.readTextComponent( buf ); | ||||
|             table.row( row ); | ||||
|         } | ||||
|  | ||||
|         table.setAdditional( buf.readVarInt() ); | ||||
|         this.table = table; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,7 @@ import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.BlockRenderLayer; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.AxisAlignedBB; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.RayTraceResult; | ||||
| @@ -375,17 +376,9 @@ public class BlockPeripheral extends BlockGeneric | ||||
|         { | ||||
|             case DiskDrive: | ||||
|             default: | ||||
|             { | ||||
|                 IBlockState state = getDefaultState().withProperty( Properties.VARIANT, BlockPeripheralVariant.DiskDriveEmpty ); | ||||
|                 if( placedSide.getAxis() != EnumFacing.Axis.Y ) | ||||
|                 { | ||||
|                     return state.withProperty( Properties.FACING, placedSide ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return state.withProperty( Properties.FACING, EnumFacing.NORTH ); | ||||
|                 } | ||||
|             } | ||||
|                 return getDefaultState() | ||||
|                     .withProperty( Properties.VARIANT, BlockPeripheralVariant.DiskDriveEmpty ) | ||||
|                     .withProperty( Properties.FACING, placedSide.getAxis() == EnumFacing.Axis.Y ? EnumFacing.NORTH : placedSide ); | ||||
|             case WirelessModem: | ||||
|             { | ||||
|                 EnumFacing dir = placedSide.getOpposite(); | ||||
| @@ -409,21 +402,13 @@ public class BlockPeripheral extends BlockGeneric | ||||
|                 } | ||||
|             } | ||||
|             case Monitor: | ||||
|             { | ||||
|                 return getDefaultState().withProperty( Properties.VARIANT, BlockPeripheralVariant.Monitor ); | ||||
|             } | ||||
|             case Printer: | ||||
|             { | ||||
|                 return getDefaultState().withProperty( Properties.VARIANT, BlockPeripheralVariant.PrinterEmpty ); | ||||
|             } | ||||
|             case AdvancedMonitor: | ||||
|             { | ||||
|                 return getDefaultState().withProperty( Properties.VARIANT, BlockPeripheralVariant.AdvancedMonitor ); | ||||
|             } | ||||
|             case Speaker: | ||||
|             { | ||||
|                 return getDefaultState().withProperty( Properties.VARIANT, BlockPeripheralVariant.Speaker ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -465,19 +450,15 @@ public class BlockPeripheral extends BlockGeneric | ||||
|             case Speaker: | ||||
|             case DiskDrive: | ||||
|             case Printer: | ||||
|             { | ||||
|                 EnumFacing dir = DirectionUtil.fromEntityRot( player ); | ||||
|                 if( stack.hasDisplayName() && tile instanceof TilePeripheralBase ) | ||||
|                 if( tile instanceof TilePeripheralBase ) | ||||
|                 { | ||||
|                     TilePeripheralBase peripheral = (TilePeripheralBase) tile; | ||||
|                     peripheral.setLabel( stack.getDisplayName() ); | ||||
|                     peripheral.setDirection( dir ); | ||||
|                     peripheral.setDirection( DirectionUtil.fromEntityRot( player ) ); | ||||
|                     if( stack.hasDisplayName() ) peripheral.setLabel( stack.getDisplayName() ); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case Monitor: | ||||
|             case AdvancedMonitor: | ||||
|             { | ||||
|                 if( tile instanceof TileMonitor ) | ||||
|                 { | ||||
|                     int direction = DirectionUtil.fromEntityRot( player ).getIndex(); | ||||
| @@ -504,7 +485,6 @@ public class BlockPeripheral extends BlockGeneric | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -600,4 +580,10 @@ public class BlockPeripheral extends BlockGeneric | ||||
|             ? PeripheralItemFactory.create( (ITilePeripheral) tile ) | ||||
|             : super.getPickBlock( state, target, world, pos, player ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDrops( @Nonnull NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, @Nonnull IBlockState state, int fortune ) | ||||
|     { | ||||
|         drops.add( PeripheralItemFactory.create( getPeripheralType( state ), null, 1 ) ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -10,11 +10,9 @@ import dan200.computercraft.api.peripheral.IPeripheral; | ||||
| import dan200.computercraft.shared.common.IDirectionalTile; | ||||
| import dan200.computercraft.shared.common.TileGeneric; | ||||
| import dan200.computercraft.shared.peripheral.PeripheralType; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.ITickable; | ||||
| import net.minecraft.util.NonNullList; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| @@ -42,15 +40,6 @@ public abstract class TilePeripheralBase extends TileGeneric implements IPeriphe | ||||
|         return (BlockPeripheral) super.getBlock(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative ) | ||||
|         { | ||||
|             drops.add( PeripheralItemFactory.create( this ) ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final PeripheralType getPeripheralType() | ||||
|     { | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import dan200.computercraft.shared.common.TileGeneric; | ||||
| import dan200.computercraft.shared.peripheral.PeripheralType; | ||||
| import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; | ||||
| import dan200.computercraft.shared.util.WorldUtil; | ||||
| import net.minecraft.block.Block; | ||||
| import net.minecraft.block.material.Material; | ||||
| import net.minecraft.block.properties.PropertyBool; | ||||
| import net.minecraft.block.properties.PropertyEnum; | ||||
| @@ -26,6 +27,7 @@ import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.AxisAlignedBB; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.RayTraceResult; | ||||
| @@ -322,7 +324,10 @@ public class BlockCable extends BlockGeneric | ||||
|  | ||||
|                     cable.modemChanged(); | ||||
|                     cable.connectionsChanged(); | ||||
|                     if( !world.isRemote && !player.capabilities.isCreativeMode ) dropItem( world, pos, item ); | ||||
|                     if( !world.isRemote && !player.capabilities.isCreativeMode ) | ||||
|                     { | ||||
|                         Block.spawnAsEntity( world, pos, item ); | ||||
|                     } | ||||
|  | ||||
|                     return false; | ||||
|                 } | ||||
| @@ -332,6 +337,23 @@ public class BlockCable extends BlockGeneric | ||||
|         return super.removedByPlayer( state, world, pos, player, willHarvest ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDrops( @Nonnull NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, @Nonnull IBlockState state, int fortune ) | ||||
|     { | ||||
|         PeripheralType type = getPeripheralType( state ); | ||||
|         switch( type ) | ||||
|         { | ||||
|             case Cable: | ||||
|             case WiredModem: | ||||
|                 drops.add( PeripheralItemFactory.create( type, null, 1 ) ); | ||||
|                 break; | ||||
|             case WiredModemWithCable: | ||||
|                 drops.add( PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 ) ); | ||||
|                 drops.add( PeripheralItemFactory.create( PeripheralType.Cable, null, 1 ) ); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult hit, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player ) | ||||
|   | ||||
| @@ -20,13 +20,12 @@ import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; | ||||
| import dan200.computercraft.shared.peripheral.modem.ModemState; | ||||
| import dan200.computercraft.shared.util.TickScheduler; | ||||
| import dan200.computercraft.shared.wired.CapabilityWiredElement; | ||||
| import net.minecraft.block.Block; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.Vec3d; | ||||
| import net.minecraft.util.text.TextComponentTranslation; | ||||
| @@ -177,44 +176,17 @@ public class TileCable extends TileGeneric implements IPeripheralTile | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative ) | ||||
|         { | ||||
|             PeripheralType type = getPeripheralType(); | ||||
|             switch( type ) | ||||
|             { | ||||
|                 case Cable: | ||||
|                 case WiredModem: | ||||
|                 { | ||||
|                     drops.add( PeripheralItemFactory.create( type, null, 1 ) ); | ||||
|                     break; | ||||
|                 } | ||||
|                 case WiredModemWithCable: | ||||
|                 { | ||||
|                     drops.add( PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 ) ); | ||||
|                     drops.add( PeripheralItemFactory.create( PeripheralType.Cable, null, 1 ) ); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNeighbourChange() | ||||
|     public void onNeighbourChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         EnumFacing dir = getDirection(); | ||||
|         if( !getWorld().isSideSolid( | ||||
|             getPos().offset( dir ), | ||||
|             dir.getOpposite() | ||||
|         ) ) | ||||
|         if( neighbour.equals( getPos().offset( dir ) ) && !getWorld().isSideSolid( neighbour, dir.getOpposite() ) ) | ||||
|         { | ||||
|             switch( getPeripheralType() ) | ||||
|             { | ||||
|                 case WiredModem: | ||||
|                 { | ||||
|                     // Drop everything and remove block | ||||
|                     getBlock().dropAllItems( getWorld(), getPos(), false ); | ||||
|                     getBlock().dropBlockAsItem( getWorld(), getPos(), getBlockState(), 0 ); | ||||
|                     getWorld().setBlockToAir( getPos() ); | ||||
|  | ||||
|                     // This'll call #destroy(), so we don't need to reset the network here. | ||||
| @@ -223,7 +195,7 @@ public class TileCable extends TileGeneric implements IPeripheralTile | ||||
|                 case WiredModemWithCable: | ||||
|                 { | ||||
|                     // Drop the modem and convert to cable | ||||
|                     getBlock().dropItem( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 ) ); | ||||
|                     Block.spawnAsEntity( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 ) ); | ||||
|                     setBlockState( getBlockState().withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None ) ); | ||||
|                     modemChanged(); | ||||
|                     connectionsChanged(); | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
| package dan200.computercraft.shared.peripheral.modem.wired; | ||||
|  | ||||
| import com.google.common.base.Objects; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.ComputerCraftAPI; | ||||
| import dan200.computercraft.api.network.wired.IWiredElement; | ||||
| import dan200.computercraft.api.network.wired.IWiredNode; | ||||
| @@ -19,11 +18,9 @@ import dan200.computercraft.shared.peripheral.modem.ModemState; | ||||
| import dan200.computercraft.shared.util.TickScheduler; | ||||
| import dan200.computercraft.shared.wired.CapabilityWiredElement; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.Vec3d; | ||||
| import net.minecraft.util.text.TextComponentString; | ||||
| @@ -136,24 +133,9 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     public void onNeighbourChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         drops.add( new ItemStack( ComputerCraft.Items.wiredModemFull ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNeighbourChange() | ||||
|     { | ||||
|         if( !world.isRemote && m_peripheralAccessAllowed ) | ||||
|         { | ||||
|             boolean hasChanged = false; | ||||
|             for( EnumFacing facing : EnumFacing.VALUES ) | ||||
|             { | ||||
|                 hasChanged |= m_peripherals[facing.ordinal()].attach( world, getPos(), facing ); | ||||
|             } | ||||
|  | ||||
|             if( hasChanged ) updateConnectedPeripherals(); | ||||
|         } | ||||
|         onNeighbourTileEntityChange( neighbour ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -6,24 +6,18 @@ | ||||
|  | ||||
| package dan200.computercraft.shared.peripheral.modem.wireless; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.NonNullList; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| public class TileAdvancedModem extends TileWirelessModemBase | ||||
| { | ||||
|     public TileAdvancedModem() | ||||
|     { | ||||
|         super( true ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected EnumFacing getDirection() | ||||
|     { | ||||
|         return getBlockState().getValue( BlockAdvancedModem.Properties.FACING ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative ) drops.add( new ItemStack( ComputerCraft.Items.advancedModem ) ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,11 +12,8 @@ import dan200.computercraft.shared.peripheral.PeripheralType; | ||||
| import dan200.computercraft.shared.peripheral.common.BlockPeripheral; | ||||
| import dan200.computercraft.shared.peripheral.common.BlockPeripheralVariant; | ||||
| import dan200.computercraft.shared.peripheral.common.ITilePeripheral; | ||||
| import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
|  | ||||
| @@ -24,6 +21,11 @@ import javax.annotation.Nonnull; | ||||
|  | ||||
| public class TileWirelessModem extends TileWirelessModemBase implements IDirectionalTile, ITilePeripheral | ||||
| { | ||||
|     public TileWirelessModem() | ||||
|     { | ||||
|         super( false ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public EnumFacing getDirection() | ||||
|     { | ||||
| @@ -75,12 +77,6 @@ public class TileWirelessModem extends TileWirelessModemBase implements IDirecti | ||||
|         return super.shouldRefresh( world, pos, oldState, newState ) || ComputerCraft.Blocks.peripheral.getPeripheralType( newState ) != PeripheralType.WirelessModem; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative ) drops.add( PeripheralItemFactory.create( PeripheralType.WirelessModem, null, 1 ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public PeripheralType getPeripheralType() | ||||
|     { | ||||
|   | ||||
| @@ -22,13 +22,19 @@ import javax.annotation.Nonnull; | ||||
|  | ||||
| public abstract class TileWirelessModemBase extends TileGeneric implements IPeripheralTile | ||||
| { | ||||
|     protected TileWirelessModemBase( boolean advanced ) | ||||
|     { | ||||
|         this.advanced = advanced; | ||||
|         this.modem = new Peripheral( this ); // Needs to be initialised after advanced | ||||
|     } | ||||
|  | ||||
|     private static class Peripheral extends WirelessModemPeripheral | ||||
|     { | ||||
|         private final TileWirelessModemBase entity; | ||||
|  | ||||
|         Peripheral( TileWirelessModemBase entity ) | ||||
|         { | ||||
|             super( new ModemState( () -> TickScheduler.schedule( entity ) ), true ); | ||||
|             super( new ModemState( () -> TickScheduler.schedule( entity ) ), entity.advanced ); | ||||
|             this.entity = entity; | ||||
|         } | ||||
|  | ||||
| @@ -54,9 +60,10 @@ public abstract class TileWirelessModemBase extends TileGeneric implements IPeri | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private final boolean advanced; | ||||
|     private boolean hasModemDirection = false; | ||||
|     private EnumFacing modemDirection = EnumFacing.DOWN; | ||||
|     private final ModemPeripheral modem = new Peripheral( this ); | ||||
|     private final ModemPeripheral modem; | ||||
|     private boolean destroyed = false; | ||||
|  | ||||
|     private boolean on = false; | ||||
| @@ -115,13 +122,13 @@ public abstract class TileWirelessModemBase extends TileGeneric implements IPeri | ||||
|     protected abstract EnumFacing getDirection(); | ||||
|  | ||||
|     @Override | ||||
|     public void onNeighbourChange() | ||||
|     public void onNeighbourChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         EnumFacing dir = getDirection(); | ||||
|         if( !getWorld().isSideSolid( getPos().offset( dir ), dir.getOpposite() ) ) | ||||
|         if( neighbour.equals( getPos().offset( dir ) ) && !getWorld().isSideSolid( neighbour, dir.getOpposite() ) ) | ||||
|         { | ||||
|             // Drop everything and remove block | ||||
|             getBlock().dropAllItems( getWorld(), getPos(), false ); | ||||
|             getBlock().dropBlockAsItem( getWorld(), getPos(), getBlockState(), 0 ); | ||||
|             getWorld().setBlockToAir( getPos() ); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -28,8 +28,6 @@ public class MonitorPeripheral implements IPeripheral | ||||
|         m_monitor = monitor; | ||||
|     } | ||||
|  | ||||
|     // IPeripheral implementation | ||||
|  | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public String getType() | ||||
|   | ||||
| @@ -16,27 +16,23 @@ import dan200.computercraft.shared.peripheral.PeripheralType; | ||||
| import dan200.computercraft.shared.peripheral.common.BlockPeripheral; | ||||
| import dan200.computercraft.shared.peripheral.common.IPeripheralTile; | ||||
| import dan200.computercraft.shared.peripheral.common.ITilePeripheral; | ||||
| import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.math.AxisAlignedBB; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.HashSet; | ||||
| import java.util.Collections; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
|  | ||||
| public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeripheralTile | ||||
| { | ||||
|     // Statics | ||||
|  | ||||
|     public static final double RENDER_BORDER = (2.0 / 16.0); | ||||
|     public static final double RENDER_MARGIN = (0.5 / 16.0); | ||||
|     public static final double RENDER_PIXEL_SCALE = (1.0 / 64.0); | ||||
| @@ -44,39 +40,23 @@ public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeriph | ||||
|     private static final int MAX_WIDTH = 8; | ||||
|     private static final int MAX_HEIGHT = 6; | ||||
|  | ||||
|     // Members | ||||
|     private ServerMonitor m_serverMonitor; | ||||
|     private ClientMonitor m_clientMonitor; | ||||
|     private MonitorPeripheral m_peripheral; | ||||
|     private final Set<IComputerAccess> m_computers; | ||||
|     private final Set<IComputerAccess> m_computers = Collections.newSetFromMap( new ConcurrentHashMap<>() ); | ||||
|  | ||||
|     private boolean m_destroyed; | ||||
|     private boolean m_ignoreMe; | ||||
|     private boolean m_destroyed = false; | ||||
|     private boolean m_ignoreMe = false; | ||||
|  | ||||
|     private int m_width; | ||||
|     private int m_height; | ||||
|     private int m_xIndex; | ||||
|     private int m_yIndex; | ||||
|     private int m_width = 1; | ||||
|     private int m_height = 1; | ||||
|     private int m_xIndex = 0; | ||||
|     private int m_yIndex = 0; | ||||
|  | ||||
|     private int m_dir; | ||||
|     private int m_dir = 2; | ||||
|  | ||||
|     private boolean m_advanced; | ||||
|  | ||||
|     public TileMonitor() | ||||
|     { | ||||
|         m_computers = new HashSet<>(); | ||||
|  | ||||
|         m_destroyed = false; | ||||
|         m_ignoreMe = false; | ||||
|  | ||||
|         m_width = 1; | ||||
|         m_height = 1; | ||||
|         m_xIndex = 0; | ||||
|         m_yIndex = 0; | ||||
|  | ||||
|         m_dir = 2; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onLoad() | ||||
|     { | ||||
| @@ -697,20 +677,14 @@ public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeriph | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void addComputer( IComputerAccess computer ) | ||||
|     void addComputer( IComputerAccess computer ) | ||||
|     { | ||||
|         synchronized( this ) | ||||
|         { | ||||
|             m_computers.add( computer ); | ||||
|         } | ||||
|         m_computers.add( computer ); | ||||
|     } | ||||
|  | ||||
|     public void removeComputer( IComputerAccess computer ) | ||||
|     void removeComputer( IComputerAccess computer ) | ||||
|     { | ||||
|         synchronized( this ) | ||||
|         { | ||||
|             m_computers.remove( computer ); | ||||
|         } | ||||
|         m_computers.remove( computer ); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
| @@ -757,10 +731,4 @@ public class TileMonitor extends TileGeneric implements ITilePeripheral, IPeriph | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative ) drops.add( PeripheralItemFactory.create( this ) ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,6 @@ import dan200.computercraft.shared.media.items.ItemPrintout; | ||||
| import dan200.computercraft.shared.peripheral.PeripheralType; | ||||
| import dan200.computercraft.shared.peripheral.common.TilePeripheralBase; | ||||
| import dan200.computercraft.shared.util.DefaultSidedInventory; | ||||
| import dan200.computercraft.shared.util.InventoryUtil; | ||||
| import dan200.computercraft.shared.util.WorldUtil; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| @@ -262,6 +261,23 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isItemValidForSlot( int slot, @Nonnull ItemStack stack ) | ||||
|     { | ||||
|         if( slot == 0 ) | ||||
|         { | ||||
|             return isInk( stack ); | ||||
|         } | ||||
|         else if( slot >= topSlots[0] && slot <= topSlots[topSlots.length - 1] ) | ||||
|         { | ||||
|             return isPaper( stack ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean hasCustomName() | ||||
|     { | ||||
| @@ -393,15 +409,15 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean isInk( @Nonnull ItemStack stack ) | ||||
|     private static boolean isInk( @Nonnull ItemStack stack ) | ||||
|     { | ||||
|         return (stack.getItem() == Items.DYE); | ||||
|         return stack.getItem() == Items.DYE; | ||||
|     } | ||||
|  | ||||
|     private boolean isPaper( @Nonnull ItemStack stack ) | ||||
|     private static boolean isPaper( @Nonnull ItemStack stack ) | ||||
|     { | ||||
|         Item item = stack.getItem(); | ||||
|         return (item == Items.PAPER || (item instanceof ItemPrintout && ItemPrintout.getType( stack ) == ItemPrintout.Type.Single)); | ||||
|         return item == Items.PAPER || (item instanceof ItemPrintout && ItemPrintout.getType( stack ) == ItemPrintout.Type.Single); | ||||
|     } | ||||
|  | ||||
|     private boolean canInputPage() | ||||
| @@ -493,11 +509,14 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven | ||||
|             ItemStack stack = ItemPrintout.createSingleFromTitleAndText( m_pageTitle, lines, colours ); | ||||
|             synchronized( m_inventory ) | ||||
|             { | ||||
|                 ItemStack remainder = InventoryUtil.storeItems( stack, m_itemHandlerAll, 7, 6, 7 ); | ||||
|                 if( remainder.isEmpty() ) | ||||
|                 for( int slot : bottomSlots ) | ||||
|                 { | ||||
|                     m_printing = false; | ||||
|                     return true; | ||||
|                     if( m_inventory.get( slot ).isEmpty() ) | ||||
|                     { | ||||
|                         m_inventory.set( slot, stack ); | ||||
|                         m_printing = false; | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|   | ||||
| @@ -27,7 +27,6 @@ import net.minecraft.util.EnumBlockRenderType; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| import net.minecraft.util.math.AxisAlignedBB; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.math.RayTraceResult; | ||||
| import net.minecraft.util.math.Vec3d; | ||||
| import net.minecraft.world.IBlockAccess; | ||||
| import net.minecraft.world.World; | ||||
| @@ -200,11 +199,10 @@ public class BlockTurtle extends BlockComputerBase | ||||
|         return super.getExplosionResistance( exploder ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Nonnull | ||||
|     public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult target, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player ) | ||||
|     @Override | ||||
|     protected ItemStack getItem( TileComputerBase tile ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         return tile instanceof TileTurtle ? TurtleItemFactory.create( (TileTurtle) tile ) : super.getPickBlock( state, target, world, pos, player ); | ||||
|         return tile instanceof TileTurtle ? TurtleItemFactory.create( (TileTurtle) tile ) : ItemStack.EMPTY; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,6 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.ServerComputer; | ||||
| import dan200.computercraft.shared.turtle.apis.TurtleAPI; | ||||
| import dan200.computercraft.shared.turtle.core.TurtleBrain; | ||||
| import dan200.computercraft.shared.turtle.items.TurtleItemFactory; | ||||
| import dan200.computercraft.shared.util.DefaultInventory; | ||||
| import dan200.computercraft.shared.util.InventoryUtil; | ||||
| import dan200.computercraft.shared.util.RedstoneUtil; | ||||
| @@ -81,7 +80,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default | ||||
|         m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY ); | ||||
|         m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY ); | ||||
|         m_inventoryChanged = false; | ||||
|         m_brain = createBrain(); | ||||
|         m_brain = new TurtleBrain( this ); | ||||
|         m_moveState = MoveState.NOT_MOVED; | ||||
|         m_family = family; | ||||
|     } | ||||
| @@ -91,21 +90,12 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default | ||||
|         return m_moveState == MoveState.MOVED; | ||||
|     } | ||||
|  | ||||
|     protected TurtleBrain createBrain() | ||||
|     { | ||||
|         return new TurtleBrain( this ); | ||||
|     } | ||||
|  | ||||
|     protected final ServerComputer createComputer( int instanceID, int id, int termWidth, int termHeight ) | ||||
|     @Override | ||||
|     protected ServerComputer createComputer( int instanceID, int id ) | ||||
|     { | ||||
|         ServerComputer computer = new ServerComputer( | ||||
|             getWorld(), | ||||
|             id, | ||||
|             m_label, | ||||
|             instanceID, | ||||
|             getFamily(), | ||||
|             termWidth, | ||||
|             termHeight | ||||
|             getWorld(), id, m_label, instanceID, getFamily(), | ||||
|             ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle | ||||
|         ); | ||||
|         computer.setPosition( getPos() ); | ||||
|         computer.addAPI( new TurtleAPI( computer.getAPIEnvironment(), getAccess() ) ); | ||||
| @@ -113,12 +103,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default | ||||
|         return computer; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected ServerComputer createComputer( int instanceID, int id ) | ||||
|     { | ||||
|         return createComputer( instanceID, id, ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ComputerProxy createProxy() | ||||
|     { | ||||
| @@ -166,12 +150,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void getDroppedItems( @Nonnull NonNullList<ItemStack> drops, boolean creative ) | ||||
|     { | ||||
|         if( !creative || getLabel() != null ) drops.add( TurtleItemFactory.create( this ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onActivate( EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ ) | ||||
|     { | ||||
| @@ -262,38 +240,26 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNeighbourChange() | ||||
|     public void onNeighbourChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         if( m_moveState == MoveState.NOT_MOVED ) | ||||
|         { | ||||
|             super.onNeighbourChange(); | ||||
|         } | ||||
|         if( m_moveState == MoveState.NOT_MOVED ) super.onNeighbourChange( neighbour ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) | ||||
|     { | ||||
|         if( m_moveState == MoveState.NOT_MOVED ) | ||||
|         { | ||||
|             super.onNeighbourTileEntityChange( neighbour ); | ||||
|         } | ||||
|         if( m_moveState == MoveState.NOT_MOVED ) super.onNeighbourTileEntityChange( neighbour ); | ||||
|     } | ||||
|  | ||||
|     public void notifyMoveStart() | ||||
|     { | ||||
|         if( m_moveState == MoveState.NOT_MOVED ) | ||||
|         { | ||||
|             m_moveState = MoveState.IN_PROGRESS; | ||||
|         } | ||||
|         if( m_moveState == MoveState.NOT_MOVED ) m_moveState = MoveState.IN_PROGRESS; | ||||
|     } | ||||
|  | ||||
|     public void notifyMoveEnd() | ||||
|     { | ||||
|         // MoveState.MOVED is final | ||||
|         if( m_moveState == MoveState.IN_PROGRESS ) | ||||
|         { | ||||
|             m_moveState = MoveState.NOT_MOVED; | ||||
|         } | ||||
|         if( m_moveState == MoveState.IN_PROGRESS ) m_moveState = MoveState.NOT_MOVED; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import dan200.computercraft.api.lua.ILuaContext; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.api.peripheral.IPeripheral; | ||||
| import dan200.computercraft.api.turtle.*; | ||||
| import dan200.computercraft.core.tracking.Tracking; | ||||
| import dan200.computercraft.shared.computer.blocks.ComputerProxy; | ||||
| import dan200.computercraft.shared.computer.blocks.TileComputerBase; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| @@ -53,7 +54,7 @@ public class TurtleBrain implements ITurtleAccess | ||||
|     private int m_commandsIssued = 0; | ||||
|  | ||||
|     private Map<TurtleSide, ITurtleUpgrade> m_upgrades = new EnumMap<>( TurtleSide.class ); | ||||
|     private Map<TurtleSide, IPeripheral> m_peripherals = new EnumMap<>( TurtleSide.class ); | ||||
|     private Map<TurtleSide, IPeripheral> peripherals = new EnumMap<>( TurtleSide.class ); | ||||
|     private Map<TurtleSide, NBTTagCompound> m_upgradeNBTData = new EnumMap<>( TurtleSide.class ); | ||||
|  | ||||
|     private int m_selectedSlot = 0; | ||||
| @@ -823,9 +824,9 @@ public class TurtleBrain implements ITurtleAccess | ||||
|     @Override | ||||
|     public IPeripheral getPeripheral( @Nonnull TurtleSide side ) | ||||
|     { | ||||
|         if( m_peripherals.containsKey( side ) ) | ||||
|         if( peripherals.containsKey( side ) ) | ||||
|         { | ||||
|             return m_peripherals.get( side ); | ||||
|             return peripherals.get( side ); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| @@ -923,13 +924,9 @@ public class TurtleBrain implements ITurtleAccess | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void updatePeripherals( ServerComputer serverComputer ) | ||||
|     private void updatePeripherals( ServerComputer serverComputer ) | ||||
|     { | ||||
|         if( serverComputer == null ) | ||||
|         { | ||||
|             // Nothing to do | ||||
|             return; | ||||
|         } | ||||
|         if( serverComputer == null ) return; | ||||
|  | ||||
|         // Update peripherals | ||||
|         for( TurtleSide side : TurtleSide.values() ) | ||||
| @@ -941,26 +938,20 @@ public class TurtleBrain implements ITurtleAccess | ||||
|                 peripheral = upgrade.createPeripheral( this, side ); | ||||
|             } | ||||
|  | ||||
|             int dir = toDirection( side ); | ||||
|             if( peripheral != null ) | ||||
|             IPeripheral existing = peripherals.get( side ); | ||||
|             if( existing == peripheral || (existing != null && peripheral != null && existing.equals( peripheral )) ) | ||||
|             { | ||||
|                 if( !m_peripherals.containsKey( side ) ) | ||||
|                 { | ||||
|                     serverComputer.setPeripheral( dir, peripheral ); | ||||
|                     m_peripherals.put( side, peripheral ); | ||||
|                 } | ||||
|                 else if( !m_peripherals.get( side ).equals( peripheral ) ) | ||||
|                 { | ||||
|                     serverComputer.setPeripheral( dir, peripheral ); | ||||
|                     m_peripherals.remove( side ); | ||||
|                     m_peripherals.put( side, peripheral ); | ||||
|                 } | ||||
|                 // If the peripheral is the same, just use that. | ||||
|                 peripheral = existing; | ||||
|             } | ||||
|             else if( m_peripherals.containsKey( side ) ) | ||||
|             else | ||||
|             { | ||||
|                 serverComputer.setPeripheral( dir, null ); | ||||
|                 m_peripherals.remove( side ); | ||||
|                 // Otherwise update our map | ||||
|                 peripherals.put( side, peripheral ); | ||||
|             } | ||||
|  | ||||
|             // Always update the computer: it may not be the same computer as before! | ||||
|             serverComputer.setPeripheral( toDirection( side ), peripheral ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -972,17 +963,21 @@ public class TurtleBrain implements ITurtleAccess | ||||
|             TurtleCommandQueueEntry nextCommand = m_commandQueue.poll(); | ||||
|             if( nextCommand != null ) | ||||
|             { | ||||
|                 ServerComputer computer = m_owner.getServerComputer(); | ||||
|  | ||||
|                 // Execute the command | ||||
|                 long start = System.nanoTime(); | ||||
|                 TurtleCommandResult result = nextCommand.command.execute( this ); | ||||
|                 long end = System.nanoTime(); | ||||
|  | ||||
|                 // Dispatch the callback | ||||
|                 int callbackID = nextCommand.callbackID; | ||||
|                 if( callbackID >= 0 ) | ||||
|                 if( computer != null ) | ||||
|                 { | ||||
|                     if( result != null && result.isSuccess() ) | ||||
|                     Tracking.addServerTiming( computer.getComputer(), end - start ); | ||||
|                     int callbackID = nextCommand.callbackID; | ||||
|                     if( callbackID >= 0 ) | ||||
|                     { | ||||
|                         ServerComputer computer = m_owner.getServerComputer(); | ||||
|                         if( computer != null ) | ||||
|                         if( result != null && result.isSuccess() ) | ||||
|                         { | ||||
|                             Object[] results = result.getResults(); | ||||
|                             if( results != null ) | ||||
| @@ -1000,11 +995,7 @@ public class TurtleBrain implements ITurtleAccess | ||||
|                                 } ); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         ServerComputer computer = m_owner.getServerComputer(); | ||||
|                         if( computer != null ) | ||||
|                         else | ||||
|                         { | ||||
|                             computer.queueEvent( "turtle_response", new Object[] { | ||||
|                                 callbackID, false, result != null ? result.getErrorMessage() : null | ||||
|   | ||||
| @@ -18,7 +18,7 @@ public class RedstoneUtil | ||||
|     @Deprecated | ||||
|     public static int getRedstoneOutput( World world, BlockPos pos, EnumFacing side ) | ||||
|     { | ||||
|         return world.getRedstonePower( pos, side ); | ||||
|         return world.getRedstonePower( pos, side.getOpposite() ); | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|   | ||||
| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.util; | ||||
|  | ||||
| import net.minecraft.inventory.IInventory; | ||||
| import net.minecraft.inventory.Slot; | ||||
| import net.minecraft.item.ItemStack; | ||||
|  | ||||
| public class ValidatingSlot extends Slot | ||||
| { | ||||
|     public ValidatingSlot( IInventory inventoryIn, int index, int xPosition, int yPosition ) | ||||
|     { | ||||
|         super( inventoryIn, index, xPosition, yPosition ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isItemValid( ItemStack stack ) | ||||
|     { | ||||
|         return true; // inventory.isItemValidForSlot( slotNumber, stack ); | ||||
|     } | ||||
| } | ||||
| @@ -48,22 +48,36 @@ function test( colors, color ) | ||||
|     if type( color ) ~= "number" then | ||||
|         error( "bad argument #2 (expected number, got " .. type( color ) .. ")", 2 ) | ||||
|     end | ||||
|     return ((bit32.band(colors, color)) == color) | ||||
|     return bit32.band(colors, color) == color | ||||
| end | ||||
|  | ||||
| function packRGB( r, g, b ) | ||||
|     if type( r ) ~= "number" then | ||||
|         error( "bad argument #1 (expected number, got " .. type( r ) .. ")", 2 ) | ||||
|     end | ||||
|     if type( g ) ~= "number" then | ||||
|         error( "bad argument #2 (expected number, got " .. type( g ) .. ")", 2 ) | ||||
|     end | ||||
|     if type( b ) ~= "number" then | ||||
|         error( "bad argument #3 (expected number, got " .. type( b ) .. ")", 2 ) | ||||
|     end | ||||
|     return | ||||
|         bit32.band( r, 0xFF ) * 2^16 + | ||||
|         bit32.band( g, 0xFF ) * 2^8 + | ||||
|         bit32.band( b, 0xFF ) | ||||
| end | ||||
|  | ||||
| function unpackRGB( rgb ) | ||||
|     if type( rgb ) ~= "number" then | ||||
|         error( "bad argument #1 (expected number, got " .. type( rgb ) .. ")", 2 ) | ||||
|     end | ||||
|     return bit32.band( bit32.rshift( rgb, 16 ), 0xFF ), bit32.band( bit32.rshift( rgb, 8 ), 0xFF ), bit32.band( rgb, 0xFF ) | ||||
| end | ||||
|  | ||||
| function rgb8( r, g, b ) | ||||
|     if type( r ) ~= "number" then | ||||
|         error( "bad argument #1 (expected number, got " .. type( r ) .. ")", 2 ) | ||||
|     elseif type(r) == "number" and g == nil and b == nil then | ||||
|         return bit32.band( bit32.rshift( r, 16 ), 0xFF ) / 255, bit32.band( bit32.rshift( r, 8 ), 0xFF ) / 255, bit32.band( r, 0xFF ) / 255 | ||||
|     elseif type(r) == "number" and type(g) == "number" and type(b) == "number" then | ||||
|         return | ||||
|             bit32.lshift( bit32.band(r * 255, 0xFF), 16 ) + | ||||
|             bit32.lshift( bit32.band(g * 255, 0xFF), 8 ) + | ||||
|             bit32.band(b * 255, 0xFF) | ||||
|     elseif type( g ) ~= "number" then | ||||
|         error( "bad argument #2 (expected number, got " .. type( g ) .. ")", 2 ) | ||||
|     elseif type( b ) ~= "number" then | ||||
|         error( "bad argument #3 (expected number, got " .. type( b ) .. ")", 2 ) | ||||
|     if g == nil and b == nil then | ||||
|         return unpackRGB( r ) | ||||
|     else | ||||
|         return packRGB( r, g, b ) | ||||
|     end | ||||
| end | ||||
|   | ||||
| @@ -42,11 +42,16 @@ term.native = function() | ||||
|     return native | ||||
| end | ||||
|  | ||||
| -- Some methods shouldn't go through redirects, so we move them to the main | ||||
| -- term API. | ||||
| for _, method in ipairs { "nativePaletteColor", "nativePaletteColour"} do | ||||
|     term[method] = native[method] | ||||
|     native[method] = nil | ||||
| end | ||||
|  | ||||
| for k,v in pairs( native ) do | ||||
|     if type( k ) == "string" and type( v ) == "function" then | ||||
|         if term[k] == nil then | ||||
|             term[k] = wrap( k ) | ||||
|         end | ||||
|     if type( k ) == "string" and type( v ) == "function" and term[k] == nil then | ||||
|         term[k] = wrap( k ) | ||||
|     end | ||||
| end | ||||
|  | ||||
|   | ||||
| @@ -298,7 +298,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) | ||||
|  | ||||
|         local tCol | ||||
|         if type(r) == "number" and g == nil and b == nil then | ||||
|             tCol = { colours.rgb8( r ) } | ||||
|             tCol = { colours.unpackRGB( r ) } | ||||
|             tPalette[ colour ] = tCol | ||||
|         else | ||||
|             if type( r ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( r ) .. ")", 2 ) end | ||||
|   | ||||
| @@ -8,6 +8,8 @@ local nCurrentProcess = nil | ||||
| local nRunningProcess = nil | ||||
| local bShowMenu = false | ||||
| local bWindowsResized = false | ||||
| local nScrollPos = 1 | ||||
| local bScrollRight = false | ||||
|  | ||||
| local function selectProcess( n ) | ||||
|     if nCurrentProcess ~= n then | ||||
| @@ -86,6 +88,9 @@ local function cullProcess( nProcess ) | ||||
|                 selectProcess( 1 ) | ||||
|             end | ||||
|         end | ||||
|         if nScrollPos ~= 1 then | ||||
|             nScrollPos = nScrollPos - 1 | ||||
|         end | ||||
|         return true | ||||
|     end | ||||
|     return false | ||||
| @@ -115,7 +120,15 @@ local function redrawMenu() | ||||
|         parentTerm.setCursorPos( 1, 1 ) | ||||
|         parentTerm.setBackgroundColor( menuOtherBgColor ) | ||||
|         parentTerm.clearLine() | ||||
|         for n=1,#tProcesses do | ||||
|         local nCharCount = 0 | ||||
|         local nSize = parentTerm.getSize() | ||||
|         if nScrollPos ~= 1 then | ||||
|             parentTerm.setTextColor( menuOtherTextColor ) | ||||
|             parentTerm.setBackgroundColor( menuOtherBgColor ) | ||||
|             parentTerm.write( "<" ) | ||||
|             nCharCount = 1 | ||||
|         end | ||||
|         for n=nScrollPos,#tProcesses do | ||||
|             if n == nCurrentProcess then | ||||
|                 parentTerm.setTextColor( menuMainTextColor ) | ||||
|                 parentTerm.setBackgroundColor( menuMainBgColor ) | ||||
| @@ -124,6 +137,16 @@ local function redrawMenu() | ||||
|                 parentTerm.setBackgroundColor( menuOtherBgColor ) | ||||
|             end | ||||
|             parentTerm.write( " " .. tProcesses[n].sTitle .. " " ) | ||||
|             nCharCount = nCharCount + #tProcesses[n].sTitle + 2 | ||||
|         end | ||||
|         if nCharCount > nSize then | ||||
|             parentTerm.setTextColor( menuOtherTextColor ) | ||||
|             parentTerm.setBackgroundColor( menuOtherBgColor ) | ||||
|             parentTerm.setCursorPos( nSize, 1 ) | ||||
|             parentTerm.write( ">" ) | ||||
|             bScrollRight = true | ||||
|         else | ||||
|             bScrollRight = false | ||||
|         end | ||||
|  | ||||
|         -- Put the cursor back where it should be | ||||
| @@ -262,15 +285,26 @@ while #tProcesses > 0 do | ||||
|         local button, x, y = tEventData[2], tEventData[3], tEventData[4] | ||||
|         if bShowMenu and y == 1 then | ||||
|             -- Switch process | ||||
|             local tabStart = 1 | ||||
|             for n=1,#tProcesses do | ||||
|                 local tabEnd = tabStart + string.len( tProcesses[n].sTitle ) + 1 | ||||
|                 if x >= tabStart and x <= tabEnd then | ||||
|                     selectProcess( n ) | ||||
|                     redrawMenu() | ||||
|                     break | ||||
|             if x == 1 and nScrollPos ~= 1 then | ||||
|                 nScrollPos = nScrollPos - 1 | ||||
|                 redrawMenu() | ||||
|             elseif bScrollRight and x == term.getSize() then | ||||
|                 nScrollPos = nScrollPos + 1 | ||||
|                 redrawMenu() | ||||
|             else | ||||
|                 local tabStart = 1 | ||||
|                 if nScrollPos ~= 1 then | ||||
|                     tabStart = 2 | ||||
|                 end | ||||
|                 for n=nScrollPos,#tProcesses do | ||||
|                     local tabEnd = tabStart + string.len( tProcesses[n].sTitle ) + 1 | ||||
|                     if x >= tabStart and x <= tabEnd then | ||||
|                         selectProcess( n ) | ||||
|                         redrawMenu() | ||||
|                         break | ||||
|                     end | ||||
|                     tabStart = tabEnd + 1 | ||||
|                 end | ||||
|                 tabStart = tabEnd + 1 | ||||
|             end | ||||
|         else | ||||
|             -- Passthrough to current process | ||||
| @@ -284,7 +318,15 @@ while #tProcesses > 0 do | ||||
|     elseif sEvent == "mouse_drag" or sEvent == "mouse_up" or sEvent == "mouse_scroll" then | ||||
|         -- Other mouse event | ||||
|         local p1, x, y = tEventData[2], tEventData[3], tEventData[4] | ||||
|         if not (bShowMenu and y == 1) then | ||||
|         if bShowMenu and sEvent == "mouse_scroll" and y == 1 then | ||||
|             if p1 == -1 and nScrollPos ~= 1 then | ||||
|                 nScrollPos = nScrollPos - 1 | ||||
|                 redrawMenu() | ||||
|             elseif bScrollRight and p1 == 1 then | ||||
|                 nScrollPos = nScrollPos + 1 | ||||
|                 redrawMenu() | ||||
|             end | ||||
|         elseif not (bShowMenu and y == 1) then | ||||
|             -- Passthrough to current process | ||||
|             resumeProcess( nCurrentProcess, sEvent, p1, x, (bShowMenu and y-1) or y ) | ||||
|             if cullProcess( nCurrentProcess ) then | ||||
|   | ||||
| @@ -63,7 +63,7 @@ local function createShellEnv( sDir ) | ||||
|                     end | ||||
|                 else | ||||
|                     if #sError > 0 then | ||||
|                         sError = sError .. "\n" | ||||
|                         sError = sError .. "\n  " | ||||
|                     end | ||||
|                     sError = sError .. "no file '" .. sPath .. "'" | ||||
|                 end | ||||
| @@ -78,27 +78,24 @@ local function createShellEnv( sDir ) | ||||
|             error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 2 ) | ||||
|         end | ||||
|         if package.loaded[name] == sentinel then | ||||
|             error("Loop detected requiring '" .. name .. "'", 0) | ||||
|             error("loop or previous error loading module '" .. name .. "'", 0) | ||||
|         end | ||||
|         if package.loaded[name] then | ||||
|             return package.loaded[name] | ||||
|         end | ||||
|  | ||||
|         local sError = "Error loading module '" .. name .. "':" | ||||
|         for n,searcher in ipairs(package.loaders) do | ||||
|             local loader, err = searcher(name) | ||||
|             if loader then | ||||
|         local sError = "module '" .. name .. "' not found:" | ||||
|         for _, searcher in ipairs(package.loaders) do | ||||
|             local loader = table.pack(searcher(name)) | ||||
|             if loader[1] then | ||||
|                 package.loaded[name] = sentinel | ||||
|                 local result = loader( err ) | ||||
|                 if result ~= nil then | ||||
|                     package.loaded[name] = result | ||||
|                     return result | ||||
|                 else | ||||
|                     package.loaded[name] = true | ||||
|                     return true | ||||
|                 end | ||||
|                 local result = loader[1](name, table.unpack(loader, 2, loader.n)) | ||||
|                 if result == nil then result = true end | ||||
|  | ||||
|                 package.loaded[name] = result | ||||
|                 return result | ||||
|             else | ||||
|                 sError = sError .. "\n" .. err | ||||
|                 sError = sError .. "\n  " .. loader[2] | ||||
|             end | ||||
|         end | ||||
|         error(sError, 2) | ||||
|   | ||||
| Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 190 B | 
| Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 181 B | 
| Before Width: | Height: | Size: 643 B After Width: | Height: | Size: 641 B | 
| Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 205 B | 
| Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 296 B | 
| Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 294 B | 
| Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 179 B | 
| Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 237 B | 
| Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 267 B | 
| Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 224 B | 
| Before Width: | Height: | Size: 250 B After Width: | Height: | Size: 248 B | 
| Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 234 B | 
| Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 235 B | 
| Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 179 B | 
| Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 221 B | 
| Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 224 B | 
| Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 302 B | 
| Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 619 B After Width: | Height: | Size: 294 B | 
| Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB | 
| Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 241 B | 
| Before Width: | Height: | Size: 316 B After Width: | Height: | Size: 282 B | 
| Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 239 B | 
| Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 235 B | 
| Before Width: | Height: | Size: 974 B After Width: | Height: | Size: 426 B | 
| Before Width: | Height: | Size: 989 B After Width: | Height: | Size: 436 B | 
| Before Width: | Height: | Size: 992 B After Width: | Height: | Size: 430 B | 
| Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 490 B | 
| Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 223 B | 
| Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 200 B | 
| Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 260 B | 
| Before Width: | Height: | Size: 212 B After Width: | Height: | Size: 200 B |