├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── .gitignore ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── litematica │ ├── InitHandler.java │ ├── Litematica.java │ ├── Reference.java │ ├── compat │ └── modmenu │ │ └── ModMenuImpl.java │ ├── config │ ├── Configs.java │ ├── HotkeyCallbackMisc.java │ ├── HotkeyCallbackOpenGui.java │ ├── HotkeyCallbackToolActions.java │ ├── HotkeyCallbacks.java │ └── Hotkeys.java │ ├── data │ ├── DataManager.java │ ├── FileMigrationUtils.java │ └── SchematicHolder.java │ ├── event │ ├── ClientWorldChangeHandler.java │ └── RenderHandler.java │ ├── gui │ ├── AreaSelectionBrowserScreen.java │ ├── AreaSubRegionEditScreen.java │ ├── BaseAreaSubRegionEditScreen.java │ ├── BaseSaveSchematicScreen.java │ ├── BaseSavedSchematicPlacementsBrowserScreen.java │ ├── BaseSchematicBrowserScreen.java │ ├── ConfigScreen.java │ ├── LoadedSchematicsListScreen.java │ ├── MainMenuScreen.java │ ├── MaterialListScreen.java │ ├── MultiRegionModeAreaEditorScreen.java │ ├── PlacementGridSettingsScreen.java │ ├── RenderLayerEditScreen.java │ ├── SaveConvertSchematicScreen.java │ ├── SaveSchematicFromAreaScreen.java │ ├── SaveSchematicPlacementScreen.java │ ├── SavedSchematicPlacementsBrowserScreen.java │ ├── SchematicBrowserScreen.java │ ├── SchematicInfoConfigScreen.java │ ├── SchematicManagerScreen.java │ ├── SchematicPlacementSettingsScreen.java │ ├── SchematicPlacementSubRegionSettingsScreen.java │ ├── SchematicPlacementsListScreen.java │ ├── SchematicSelectorScreen.java │ ├── SchematicVcsProjectBrowserScreen.java │ ├── SchematicVcsProjectManagerScreen.java │ ├── SchematicVerifierScreen.java │ ├── SelectLoadedSchematicScreen.java │ ├── SimpleModeAreaEditorScreen.java │ ├── TaskManagerScreen.java │ ├── util │ │ ├── AbstractSchematicInfoCache.java │ │ ├── LitematicaIcons.java │ │ ├── SchematicBrowserIconProvider.java │ │ ├── SchematicInfoCacheByPath.java │ │ ├── SchematicInfoCacheBySchematic.java │ │ └── SchematicPlacementInfoCache.java │ └── widget │ │ ├── AbstractSchematicInfoWidget.java │ │ ├── MaterialListEntryHoverInfoWidget.java │ │ ├── SavedSchematicPlacementInfoWidget.java │ │ ├── SchematicInfoWidgetByPath.java │ │ ├── SchematicInfoWidgetBySchematic.java │ │ ├── SchematicVcsProjectInfoWidget.java │ │ ├── SchematicVerifierBlockInfoWidget.java │ │ └── list │ │ └── entry │ │ ├── AreaSelectionEntryWidget.java │ │ ├── AreaSubRegionEntryWidget.java │ │ ├── BaseSchematicEntryWidget.java │ │ ├── MaterialListEntryWidget.java │ │ ├── SchematicEntryWidget.java │ │ ├── SchematicPlacementBrowserEntryWidget.java │ │ ├── SchematicPlacementEntryWidget.java │ │ ├── SchematicPlacementSubRegionEntryWidget.java │ │ ├── SchematicVcsVersionEntryWidget.java │ │ ├── SchematicVerifierCategoryEntryWidget.java │ │ ├── SchematicVerifierResultEntryWidget.java │ │ └── TaskEntryWidget.java │ ├── input │ ├── LitematicaHotkeyProvider.java │ └── MouseScrollHandlerImpl.java │ ├── interfaces │ ├── IMixinChunkProviderClient.java │ └── IWorldUpdateSuppressor.java │ ├── materials │ ├── IMaterialList.java │ ├── MaterialCache.java │ ├── MaterialListAreaAnalyzer.java │ ├── MaterialListBase.java │ ├── MaterialListEntry.java │ ├── MaterialListHudRenderer.java │ ├── MaterialListPlacement.java │ ├── MaterialListSchematic.java │ ├── MaterialListSorter.java │ └── MaterialListUtils.java │ ├── mixin │ ├── IMixinBlockRendererDispatcher.java │ ├── IMixinCompiledChunk.java │ ├── IMixinDataFixer.java │ ├── IMixinItemBlockSpecial.java │ ├── IMixinViewFrustum.java │ ├── IMixinWorldClient.java │ ├── MixinBlock.java │ ├── MixinBlockRail.java │ ├── MixinChunk.java │ ├── MixinChunkProviderClient.java │ ├── MixinEntityRenderer.java │ ├── MixinGuiContainer.java │ ├── MixinGuiEditSign.java │ ├── MixinIntegratedServer.java │ ├── MixinMinecraft.java │ ├── MixinNetHandlerPlayClient.java │ ├── MixinPlayerControllerMP.java │ ├── MixinRenderGlobal.java │ ├── MixinWorld.java │ └── debug │ │ └── GuiOverlayDebugMixin.java │ ├── network │ └── SchematicSavePacketHandler.java │ ├── render │ ├── BlockInfo.java │ ├── DebugScreenMessages.java │ ├── LitematicaRenderer.java │ ├── OverlayRenderer.java │ ├── RenderUtils.java │ ├── infohud │ │ ├── IInfoHudRenderer.java │ │ ├── InfoHud.java │ │ ├── RenderPhase.java │ │ ├── StatusInfoRenderer.java │ │ └── ToolHud.java │ └── schematic │ │ ├── BlockModelRendererSchematic.java │ │ ├── ChunkCacheSchematic.java │ │ ├── ChunkCompileTaskGeneratorSchematic.java │ │ ├── ChunkRenderContainerSchematic.java │ │ ├── ChunkRenderDispatcherLitematica.java │ │ ├── ChunkRenderWorkerLitematica.java │ │ ├── CompiledChunkSchematic.java │ │ ├── RenderChunkFactoryList.java │ │ ├── RenderChunkFactoryVbo.java │ │ ├── RenderChunkSchematicList.java │ │ ├── RenderChunkSchematicVbo.java │ │ ├── RenderGlobalSchematic.java │ │ ├── RenderListSchematic.java │ │ ├── VboRenderListSchematic.java │ │ └── VertexBuilderCache.java │ ├── scheduler │ ├── ClientTickHandler.java │ ├── ITask.java │ ├── TaskScheduler.java │ ├── TaskTimer.java │ └── tasks │ │ ├── SetSchematicPreviewTask.java │ │ ├── TaskBase.java │ │ ├── TaskCountBlocksArea.java │ │ ├── TaskCountBlocksBase.java │ │ ├── TaskCountBlocksMaterialList.java │ │ ├── TaskCountBlocksPlacement.java │ │ ├── TaskDelay.java │ │ ├── TaskDeleteArea.java │ │ ├── TaskFillArea.java │ │ ├── TaskPasteSchematicDirect.java │ │ ├── TaskPasteSchematicPerChunkBase.java │ │ ├── TaskPasteSchematicPerChunkCommand.java │ │ ├── TaskPasteSchematicPerChunkDirect.java │ │ ├── TaskProcessChunkBase.java │ │ └── TaskUpdateBlocks.java │ ├── schematic │ ├── EntityInfo.java │ ├── ISchematic.java │ ├── ISchematicRegion.java │ ├── LitematicaSchematic.java │ ├── SchematicBase.java │ ├── SchematicMetadata.java │ ├── SchematicType.java │ ├── SchematicaSchematic.java │ ├── SingleRegionSchematic.java │ ├── SpongeSchematic.java │ ├── SubRegion.java │ ├── VanillaStructure.java │ ├── container │ │ ├── ILitematicaBlockStateContainer.java │ │ ├── ILitematicaBlockStatePalette.java │ │ ├── IPaletteResizeHandler.java │ │ ├── LitematicaBitArray.java │ │ ├── LitematicaBlockStateContainerBase.java │ │ ├── LitematicaBlockStateContainerFull.java │ │ ├── LitematicaBlockStateContainerSparse.java │ │ ├── LitematicaBlockStatePaletteHashMap.java │ │ ├── LitematicaBlockStatePaletteLinear.java │ │ └── VanillaStructurePalette.java │ ├── placement │ │ ├── BasePlacement.java │ │ ├── GridPlacementManager.java │ │ ├── GridSettings.java │ │ ├── SchematicPlacement.java │ │ ├── SchematicPlacementManager.java │ │ └── SubRegionPlacement.java │ ├── projects │ │ ├── SchematicProject.java │ │ ├── SchematicProjectsManager.java │ │ └── SchematicVersion.java │ ├── util │ │ ├── SchematicCreationUtils.java │ │ ├── SchematicEditUtils.java │ │ ├── SchematicPlacingUtils.java │ │ ├── SchematicSaveSettings.java │ │ └── SchematicUtils.java │ └── verifier │ │ ├── BlockPairTypePosition.java │ │ ├── BlockPairTypePositionComparator.java │ │ ├── BlockStatePair.java │ │ ├── BlockStatePairCount.java │ │ ├── SchematicVerifier.java │ │ ├── SchematicVerifierManager.java │ │ ├── VerifierResultType.java │ │ └── VerifierStatus.java │ ├── selection │ ├── AreaSelection.java │ ├── AreaSelectionManager.java │ ├── AreaSelectionSimple.java │ ├── AreaSelectionType.java │ ├── BoxCorner.java │ ├── CornerDefinedBox.java │ ├── SelectionBox.java │ ├── SlicedBox.java │ └── ToolSelectionMode.java │ ├── task │ ├── CreateSchematicTask.java │ ├── MultiplayerCreateSchematicTask.java │ └── SchematicVerifierTask.java │ ├── tool │ ├── ToolMode.java │ └── ToolModeData.java │ ├── util │ ├── EasyPlaceUtils.java │ ├── EntityUtils.java │ ├── ItemUtils.java │ ├── LitematicaDirectories.java │ ├── Nags.java │ ├── PickBlockUtils.java │ ├── PositionUtils.java │ ├── RayTraceUtils.java │ ├── ToolUtils.java │ ├── WorldUtils.java │ └── value │ │ ├── BlockInfoAlignment.java │ │ ├── BlockInfoListType.java │ │ ├── OverlayType.java │ │ └── ReplaceBehavior.java │ └── world │ ├── ChunkProviderSchematic.java │ ├── ChunkSchematic.java │ ├── SchematicWorldHandler.java │ ├── SchematicWorldRenderingNotifier.java │ └── WorldSchematic.java └── resources ├── assets └── litematica │ ├── icon.png │ ├── lang │ └── en_us.lang │ ├── shaders │ └── alpha.frag │ └── textures │ ├── gui │ └── gui_widgets.png │ └── xcf │ └── gui_widgets.xcf ├── fabric.mod.json ├── mixins.litematica.json └── pack.mcmeta /.gitattributes: -------------------------------------------------------------------------------- 1 | # text stuff 2 | * text=auto 3 | *.bat text eol=crlf 4 | *.info text eol=lf 5 | *.java text eol=crlf 6 | *.json text eol=lf 7 | *.mcmeta text eol=lf 8 | *.md text eol=lf 9 | *.properties text eol=lf 10 | *.sh text eol=lf 11 | .gitattributes text eol=lf 12 | .gitignore text eol=lf 13 | build.gradle text eol=lf 14 | build.properties text eol=lf 15 | gradlew text eol=lf 16 | gradle/wrapper/gradle-wrapper.properties text eol=lf 17 | LICENSE.txt text eol=lf 18 | README.md text eol=lf 19 | 20 | #binary 21 | *.dat binary 22 | *.bin binary 23 | *.png binary 24 | *.exe binary 25 | *.dll binary 26 | *.zip binary 27 | *.jar binary 28 | *.7z binary 29 | *.db binary 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Describe the bug or unwanted behavior you have encountered 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Please don't post simple questions on this issue tracker! 11 | Simple questions like "How do I do X?" should instead be handled either on the Discussions area (see on the top of the page) or on Discord: 12 | * My Discord server: https://masa.dy.fi/discord -> `#litematica` 13 | * or the SciCraft Discord: https://discord.gg/scicraft -> `#masas-mods` 14 | Every message on the issue tracker here sends an email to me, and a message on two Discord servers. So having simple support conversations here gets really spammy and annoying. Please use one of the above mentioned alternatives instead. 15 | 16 | ### Other rules 17 | * **Before reporting an issue, please make sure it has not already been fixed in a later version!** Also make sure you have the latest version of **both Litematica and malilib!** There are occasionally changes that **will require** both mods to be updated at the same time, or something will break! 18 | * There are occasionally development versions here: https://masa.dy.fi/mcmods/client_mods/ 19 | * Also check on CurseForge: https://www.curseforge.com/minecraft/mc-mods/litematica/files 20 | * See which page has the latest version. Starting with Minecraft 1.17, I will not keep all the mods for the current Minecraft version on my server anymore. There are now only mod builds on my server leading up to a new version on CurseForge. So in other words, if the mod is listed on my server, then it should be the latest version. If it's not there at all, then the latest version is on CurseForge. But check both just in case, to see what the latest version is. 21 | * **No snapshot issues** - Don't post issues for mod versions made for Minecraft snapshots on this tracker. The snapshot versions aren't "properly" supported or tested. Snapshot versions (when or if they are available) are only provided as quick ports (when I have the time and motivation to make them) in a "might work, mostly" basis. Any issues with snapshot versions should rather be handled on Discord, see above. 22 | 23 | ### Provide at least the following information 24 | * **Minecraft version** - Which Minecraft version are you playing on? 25 | * **Mod version and malilib version** - Which **exact** mod version are you using? 26 | * Include the **full** mod file name which includes the build date and time for the development versions, or you can see the mod version in the title of the Litematica main menu. 27 | * **"Latest" is not a version!!** It's ambiguous, it's a moving target, and very often the version is not *actually* even the latest available version at that time. 28 | * **A clear description of the issue** - I'm not a psychic, so you have to describe the issue thoroughly enough for me to be able to reproduce it **without knowing anything at all that is not described in the issue report**. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: SciCraft Discord (`#masas-mods` channel) 4 | url: https://discord.gg/scicraft 5 | about: For simple questions and discussions 6 | - name: masa's Discord 7 | url: https://masa.dy.fi/discord 8 | about: Alternative Discord, for simple questions and discussions -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea/improvement/feature for this mod 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .settings 3 | bin/ 4 | build/ 5 | eclipse/ 6 | .classpath 7 | .project 8 | build.number 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Curseforge](http://cf.way2muchnoise.eu/full_litematica_downloads.svg)](https://minecraft.curseforge.com/projects/litematica) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_litematica_all.svg)](https://minecraft.curseforge.com/projects/litematica) 2 | 3 | ## Litematica 4 | Litematica is a client-side schematic mod for Minecraft, with also lots of extra functionality 5 | especially for creative mode (such as schematic pasting, area cloning, moving, filling, deletion). 6 | 7 | It's primarily developed on MC 1.12.2 for LiteLoader. It has also been ported to Rift on MC 1.13.2, 8 | and for Fabric on MC 1.14 and later. There are also Forge versions for 1.12.2, and Forge ports for 1.14.4+ 9 | are also planned, but Forge will need to start shipping the Mixin library before those can happen. 10 | 11 | Litematica was started as an alternative for [Schematica](https://minecraft.curseforge.com/projects/schematica), 12 | for players who don't want to have Forge installed on their client, and that's why it was developed for Liteloader. 13 | 14 | For compiled builds (= downloads), see: 15 | * CurseForge: http://minecraft.curseforge.com/projects/litematica 16 | * For more up-to-date development builds: https://masa.dy.fi/mcmods/client_mods/ 17 | * **Note:** Litematica also requires the malilib library mod! But on the other hand Fabric API is not needed. 18 | 19 | ## Compiling 20 | * Clone the repository 21 | * Open a command prompt/terminal to the repository directory 22 | * On 1.12.x you will first need to run `gradlew setupDecompWorkspace` 23 | (unless you have already done it once for another project on the same 1.12.x MC version 24 | and mappings and the same mod loader, Forge or LiteLoader) 25 | * Run `gradlew build` to build the mod 26 | * The built jar file will be inside `build/libs/` 27 | 28 | ## YourKit 29 | ![](https://www.yourkit.com/images/yklogo.png) 30 | 31 | We appreciate YourKit for providing the project developers licenses of its profiler to help us improve performance! 32 | 33 | YourKit supports open source projects with innovative and intelligent tools 34 | for monitoring and profiling Java and .NET applications. 35 | YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), 36 | [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/) and 37 | [YourKit YouMonitor](https://www.yourkit.com/youmonitor), tools for profiling Java and .NET applications. -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx3G 2 | org.gradle.daemon = false 3 | org.gradle.cache.cleanup = false 4 | 5 | mod_id = litematica 6 | mod_name = Litematica 7 | mod_file_name = litematica-ornithe 8 | 9 | # Current mod version 10 | mod_version = 0.40.0-alpha.1 11 | 12 | # Required malilib version 13 | malilib_version = 0.60.0-alpha.1 14 | 15 | # Minecraft, Fabric Loader, mappings and dependencies versions 16 | minecraft_version_out = 1.12.2 17 | minecraft_version = 1.12.2 18 | fabric_loader_version = 0.15.3 19 | osl_version = 0.11.3 20 | mod_menu_version = 0.1.1+mc1.12.2 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maruohon/litematica/dd3dd561e248fa57e1477aed922acb2f6b441848/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | maven { 8 | name = 'Ornithe Releases' 9 | url = 'https://maven.ornithemc.net/releases' 10 | } 11 | maven { 12 | name = 'Ornithe Snapshots' 13 | url = 'https://maven.ornithemc.net/snapshots' 14 | } 15 | gradlePluginPortal() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/litematica/InitHandler.java: -------------------------------------------------------------------------------- 1 | package litematica; 2 | 3 | import malilib.config.JsonModConfig; 4 | import malilib.config.JsonModConfig.ConfigDataUpdater; 5 | import malilib.config.util.ConfigUpdateUtils.KeyBindSettingsResetter; 6 | import malilib.event.InitializationHandler; 7 | import malilib.registry.Registry; 8 | import litematica.config.Configs; 9 | import litematica.config.HotkeyCallbacks; 10 | import litematica.data.FileMigrationUtils; 11 | import litematica.event.ClientWorldChangeHandler; 12 | import litematica.event.RenderHandler; 13 | import litematica.gui.ConfigScreen; 14 | import litematica.input.LitematicaHotkeyProvider; 15 | import litematica.input.MouseScrollHandlerImpl; 16 | import litematica.network.SchematicSavePacketHandler; 17 | import litematica.render.infohud.StatusInfoRenderer; 18 | import litematica.scheduler.ClientTickHandler; 19 | import litematica.util.LitematicaDirectories; 20 | 21 | public class InitHandler implements InitializationHandler 22 | { 23 | @Override 24 | public void registerModHandlers() 25 | { 26 | // Reset all KeyBindSettings when updating to the first post-malilib-refactor version 27 | ConfigDataUpdater updater = new KeyBindSettingsResetter(LitematicaHotkeyProvider.INSTANCE::getAllHotkeys, 0); 28 | Registry.CONFIG_MANAGER.registerConfigHandler(JsonModConfig.createJsonModConfig(Reference.MOD_INFO, Configs.CURRENT_VERSION, Configs.CATEGORIES, updater)); 29 | 30 | Registry.CONFIG_SCREEN.registerConfigScreenFactory(Reference.MOD_INFO, ConfigScreen::create); 31 | Registry.CONFIG_TAB.registerConfigTabSupplier(Reference.MOD_INFO, ConfigScreen::getConfigTabs); 32 | 33 | Registry.HOTKEY_MANAGER.registerHotkeyProvider(new LitematicaHotkeyProvider()); 34 | Registry.INPUT_DISPATCHER.registerMouseScrollHandler(new MouseScrollHandlerImpl()); 35 | 36 | RenderHandler renderer = new RenderHandler(); 37 | Registry.RENDER_EVENT_DISPATCHER.registerGameOverlayRenderer(renderer); 38 | Registry.RENDER_EVENT_DISPATCHER.registerWorldPostRenderer(renderer); 39 | 40 | Registry.TICK_EVENT_DISPATCHER.registerClientTickHandler(new ClientTickHandler()); 41 | 42 | Registry.CLIENT_WORLD_CHANGE_EVENT_DISPATCHER.registerClientWorldChangeHandler(new ClientWorldChangeHandler()); 43 | 44 | Configs.init(); 45 | FileMigrationUtils.tryMigrateOldPerWorldData(); 46 | FileMigrationUtils.tryMigrateOldAreaSelections(); 47 | 48 | HotkeyCallbacks.init(); 49 | StatusInfoRenderer.init(); 50 | 51 | // This creates the directories if they don't exist yet 52 | LitematicaDirectories.getAreaSelectionsBaseDirectory(); 53 | LitematicaDirectories.getSchematicsBaseDirectory(); 54 | 55 | Registry.CLIENT_PACKET_CHANNEL_HANDLER.registerClientChannelHandler(SchematicSavePacketHandler.INSTANCE); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/litematica/Litematica.java: -------------------------------------------------------------------------------- 1 | package litematica; 2 | 3 | import net.ornithemc.osl.entrypoints.api.client.ClientModInitializer; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import malilib.registry.Registry; 8 | import litematica.config.Configs; 9 | 10 | public class Litematica implements ClientModInitializer 11 | { 12 | public static final Logger LOGGER = LogManager.getLogger(Reference.MOD_ID); 13 | 14 | public static void printDebug(String key, Object... args) 15 | { 16 | if (Configs.Generic.DEBUG_MESSAGES.getBooleanValue()) 17 | { 18 | LOGGER.info(String.format(key, args)); 19 | } 20 | } 21 | 22 | @Override 23 | public void initClient() 24 | { 25 | Registry.INITIALIZATION_DISPATCHER.registerInitializationHandler(new InitHandler()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/litematica/Reference.java: -------------------------------------------------------------------------------- 1 | package litematica; 2 | 3 | import malilib.util.data.ModInfo; 4 | 5 | public class Reference 6 | { 7 | public static final String MOD_ID = "litematica"; 8 | public static final String MOD_NAME = "Litematica"; 9 | public static final String MOD_VERSION = "@MOD_VERSION@"; 10 | 11 | public static final ModInfo MOD_INFO = new ModInfo(MOD_ID, MOD_NAME); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/litematica/compat/modmenu/ModMenuImpl.java: -------------------------------------------------------------------------------- 1 | package litematica.compat.modmenu; 2 | 3 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 4 | import com.terraformersmc.modmenu.api.ModMenuApi; 5 | 6 | import malilib.gui.BaseScreen; 7 | import litematica.gui.ConfigScreen; 8 | 9 | public class ModMenuImpl implements ModMenuApi 10 | { 11 | @Override 12 | public ConfigScreenFactory getModConfigScreenFactory() 13 | { 14 | return (currentScreen) -> { 15 | BaseScreen screen = ConfigScreen.create(); 16 | screen.setParent(currentScreen); 17 | return screen; 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/litematica/data/SchematicHolder.java: -------------------------------------------------------------------------------- 1 | package litematica.data; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import javax.annotation.Nullable; 8 | 9 | import litematica.schematic.ISchematic; 10 | import litematica.schematic.SchematicType; 11 | 12 | public class SchematicHolder 13 | { 14 | private static final SchematicHolder INSTANCE = new SchematicHolder(); 15 | private final List schematics = new ArrayList<>(); 16 | 17 | public static SchematicHolder getInstance() 18 | { 19 | return INSTANCE; 20 | } 21 | 22 | public void clearLoadedSchematics() 23 | { 24 | this.schematics.clear(); 25 | } 26 | 27 | public List getAllOf(Path file) 28 | { 29 | List list = new ArrayList<>(); 30 | 31 | for (ISchematic schematic : this.schematics) 32 | { 33 | if (file.equals(schematic.getFile())) 34 | { 35 | list.add(schematic); 36 | } 37 | } 38 | 39 | return list; 40 | } 41 | 42 | @Nullable 43 | public ISchematic getOrLoad(Path file) 44 | { 45 | if (Files.isRegularFile(file) == false || Files.isReadable(file) == false) 46 | { 47 | return null; 48 | } 49 | 50 | for (ISchematic schematic : this.schematics) 51 | { 52 | if (file.equals(schematic.getFile())) 53 | { 54 | return schematic; 55 | } 56 | } 57 | 58 | ISchematic schematic = SchematicType.tryCreateSchematicFrom(file); 59 | 60 | if (schematic != null) 61 | { 62 | this.schematics.add(schematic); 63 | } 64 | 65 | return schematic; 66 | } 67 | 68 | public void addSchematic(ISchematic schematic, boolean allowDuplicates) 69 | { 70 | if (allowDuplicates || this.schematics.contains(schematic) == false) 71 | { 72 | if (allowDuplicates == false && schematic.getFile() != null) 73 | { 74 | for (ISchematic tmp : this.schematics) 75 | { 76 | if (schematic.getFile().equals(tmp.getFile())) 77 | { 78 | return; 79 | } 80 | } 81 | } 82 | 83 | this.schematics.add(schematic); 84 | } 85 | } 86 | 87 | public boolean removeSchematic(ISchematic schematic) 88 | { 89 | if (this.schematics.remove(schematic)) 90 | { 91 | DataManager.getSchematicPlacementManager().removeAllPlacementsOfSchematic(schematic); 92 | return true; 93 | } 94 | 95 | return false; 96 | } 97 | 98 | public List getAllSchematics() 99 | { 100 | return this.schematics; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/litematica/event/ClientWorldChangeHandler.java: -------------------------------------------------------------------------------- 1 | package litematica.event; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import net.minecraft.client.multiplayer.WorldClient; 6 | 7 | import litematica.data.DataManager; 8 | import litematica.world.SchematicWorldHandler; 9 | 10 | public class ClientWorldChangeHandler implements malilib.event.ClientWorldChangeHandler 11 | { 12 | @Override 13 | public void onPreClientWorldChange(@Nullable WorldClient worldBefore, @Nullable WorldClient worldAfter) 14 | { 15 | // Save the settings before the integrated server gets shut down 16 | if (worldBefore != null) 17 | { 18 | boolean isDimensionChange = worldAfter != null; 19 | DataManager.save(isDimensionChange); 20 | } 21 | } 22 | 23 | @Override 24 | public void onPostClientWorldChange(@Nullable WorldClient worldBefore, @Nullable WorldClient worldAfter) 25 | { 26 | SchematicWorldHandler.recreateSchematicWorld(worldAfter == null); 27 | 28 | if (worldAfter != null) 29 | { 30 | boolean isDimensionChange = worldBefore != null; 31 | DataManager.load(isDimensionChange); 32 | } 33 | else 34 | { 35 | DataManager.clear(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/litematica/event/RenderHandler.java: -------------------------------------------------------------------------------- 1 | package litematica.event; 2 | 3 | import malilib.event.PostGameOverlayRenderer; 4 | import malilib.event.PostWorldRenderer; 5 | import malilib.gui.util.GuiUtils; 6 | import malilib.render.RenderContext; 7 | import litematica.config.Configs; 8 | import litematica.config.Hotkeys; 9 | import litematica.data.DataManager; 10 | import litematica.render.LitematicaRenderer; 11 | import litematica.render.OverlayRenderer; 12 | import litematica.render.infohud.InfoHud; 13 | import litematica.render.infohud.ToolHud; 14 | import litematica.scheduler.TaskScheduler; 15 | import litematica.scheduler.tasks.SetSchematicPreviewTask; 16 | import litematica.tool.ToolMode; 17 | 18 | public class RenderHandler implements PostGameOverlayRenderer, PostWorldRenderer 19 | { 20 | @Override 21 | public void onPostWorldRender(RenderContext ctx, float tickDelta) 22 | { 23 | if (Configs.Visuals.MAIN_RENDERING_TOGGLE.getBooleanValue()) 24 | { 25 | boolean invert = Hotkeys.INVERT_SCHEMATIC_RENDER_STATE.isHeld(); 26 | 27 | if (Configs.Visuals.SCHEMATIC_RENDERING.getBooleanValue() != invert && 28 | Configs.Generic.BETTER_RENDER_ORDER.getBooleanValue() == false) 29 | { 30 | LitematicaRenderer.getInstance().renderSchematicWorld(ctx, tickDelta); 31 | } 32 | 33 | OverlayRenderer.getInstance().renderBoxes(ctx, tickDelta); 34 | 35 | if (Configs.InfoOverlays.VERIFIER_OVERLAY_RENDERING.getBooleanValue()) 36 | { 37 | OverlayRenderer.getInstance().renderSchematicVerifierMismatches(ctx, tickDelta); 38 | } 39 | 40 | if (Configs.Visuals.RENDER_COLLIDING_BLOCK_AT_CURSOR.getBooleanValue()) 41 | { 42 | boolean render = Configs.Visuals.SCHEMATIC_BLOCKS_RENDERING.getBooleanValue() && 43 | Configs.Visuals.SCHEMATIC_RENDERING.getBooleanValue() != invert; 44 | 45 | if (render) 46 | { 47 | OverlayRenderer.getInstance().renderHoveredSchematicBlock(ctx, tickDelta); 48 | } 49 | } 50 | 51 | if (DataManager.getToolMode() == ToolMode.SCHEMATIC_EDIT) 52 | { 53 | OverlayRenderer.getInstance().renderSchematicRebuildTargetingOverlay(tickDelta, ctx); 54 | } 55 | } 56 | } 57 | 58 | @Override 59 | public void onPostGameOverlayRender(RenderContext ctx) 60 | { 61 | if (Configs.Visuals.MAIN_RENDERING_TOGGLE.getBooleanValue()) 62 | { 63 | // The Info HUD renderers can decide if they want to be rendered in GUIs 64 | InfoHud.getInstance().renderHud(ctx); 65 | 66 | if (GuiUtils.noScreenOpen()) 67 | { 68 | ToolHud.getInstance().renderHud(ctx); 69 | OverlayRenderer.getInstance().renderHoverInfo(ctx); 70 | 71 | SetSchematicPreviewTask task = TaskScheduler.getInstanceClient().getFirstTaskOfType(SetSchematicPreviewTask.class); 72 | 73 | if (task != null) 74 | { 75 | OverlayRenderer.getInstance().renderPreviewFrame(ctx); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/AreaSubRegionEditScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import litematica.selection.AreaSelection; 4 | import litematica.selection.SelectionBox; 5 | 6 | public class AreaSubRegionEditScreen extends BaseAreaSubRegionEditScreen 7 | { 8 | public AreaSubRegionEditScreen(AreaSelection selection, SelectionBox selectionBox) 9 | { 10 | super(selection, selectionBox); 11 | 12 | this.setTitle("litematica.title.screen.area_editor.selection_box", selectionBox.getName()); 13 | } 14 | 15 | @Override 16 | protected void updateWidgetPositions() 17 | { 18 | super.updateWidgetPositions(); 19 | 20 | int x = this.x + 10; 21 | int y = this.y + 24; 22 | 23 | this.selectionBoxNameLabel.setPosition(x, y); 24 | this.selectionBoxNameTextField.setPosition(x, this.selectionBoxNameLabel.getBottom()); 25 | this.setSelectionBoxNameButton.setPosition(this.selectionBoxNameTextField.getRight() + 2, this.selectionBoxNameTextField.getY() - 1); 26 | 27 | y = this.selectionBoxNameTextField.getBottom() + 4; 28 | this.corner1EditWidget.setPosition(x, y + 14); 29 | this.corner2EditWidget.setPosition(this.corner1EditWidget.getRight() + 8, y + 14); 30 | this.corner1Checkbox.setPosition(this.corner1EditWidget.getTextFieldStartX(), y); 31 | this.corner2Checkbox.setPosition(this.corner2EditWidget.getTextFieldStartX(), y); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/RenderLayerEditScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import net.minecraft.client.gui.GuiScreen; 6 | 7 | import malilib.gui.edit.BaseLayerRangeEditScreen; 8 | import litematica.Reference; 9 | import litematica.data.DataManager; 10 | 11 | public class RenderLayerEditScreen extends BaseLayerRangeEditScreen 12 | { 13 | public RenderLayerEditScreen() 14 | { 15 | super("litematica", ConfigScreen.ALL_TABS, ConfigScreen.RENDER_LAYERS, DataManager.getRenderLayerRange()); 16 | 17 | this.shouldCreateTabButtons = true; 18 | this.editWidget.setAddLayerRangeHotkeyCheckboxes(true); 19 | this.editWidget.setAddPlayerFollowingOptions(true); 20 | 21 | this.setTitle("litematica.title.screen.render_layers", Reference.MOD_VERSION); 22 | this.createSwitchModConfigScreenDropDown(Reference.MOD_INFO); 23 | } 24 | 25 | @Override 26 | protected void updateWidgetPositions() 27 | { 28 | super.updateWidgetPositions(); 29 | 30 | this.editWidget.setPosition(this.x + 10, this.y + 60); 31 | } 32 | 33 | public static boolean screenValidator(@Nullable GuiScreen currentScreen) 34 | { 35 | return currentScreen instanceof RenderLayerEditScreen; 36 | } 37 | 38 | public static RenderLayerEditScreen openRenderLayerEditScreen() 39 | { 40 | RenderLayerEditScreen screen = new RenderLayerEditScreen(); 41 | screen.setCurrentTab(ConfigScreen.RENDER_LAYERS); 42 | return screen; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/SavedSchematicPlacementsBrowserScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import malilib.gui.icon.DefaultFileBrowserIconProvider; 4 | import malilib.gui.icon.FileBrowserIconProvider; 5 | import malilib.gui.widget.button.GenericButton; 6 | import malilib.gui.widget.list.BaseFileBrowserWidget; 7 | import litematica.Reference; 8 | import litematica.gui.util.LitematicaIcons; 9 | import litematica.gui.widget.list.entry.SchematicPlacementBrowserEntryWidget; 10 | 11 | public class SavedSchematicPlacementsBrowserScreen extends BaseSavedSchematicPlacementsBrowserScreen 12 | { 13 | protected final GenericButton openMainMenuButton; 14 | protected final GenericButton openPlacementListScreenButton; 15 | 16 | public SavedSchematicPlacementsBrowserScreen() 17 | { 18 | super(10, 30, 20 + 2 + 170, 102); 19 | 20 | this.openMainMenuButton = GenericButton.create("litematica.button.change_menu.main_menu", MainMenuScreen::openMainMenuScreen); 21 | this.openPlacementListScreenButton = GenericButton.create("litematica.button.change_menu.schematic_placements", LitematicaIcons.SCHEMATIC_PLACEMENTS); 22 | this.openPlacementListScreenButton.setActionListener(() -> openScreen(new SchematicPlacementsListScreen())); 23 | 24 | this.setTitle("litematica.title.screen.saved_placements", Reference.MOD_VERSION); 25 | } 26 | 27 | @Override 28 | protected void reAddActiveWidgets() 29 | { 30 | super.reAddActiveWidgets(); 31 | 32 | this.addWidget(this.openMainMenuButton); 33 | this.addWidget(this.openPlacementListScreenButton); 34 | } 35 | 36 | @Override 37 | protected void updateWidgetPositions() 38 | { 39 | super.updateWidgetPositions(); 40 | 41 | int y = this.getBottom() - 24; 42 | this.openPlacementListScreenButton.setPosition(this.x + 10, y); 43 | 44 | this.openMainMenuButton.setRight(this.getRight() - 10); 45 | this.openMainMenuButton.setY(y); 46 | } 47 | 48 | @Override 49 | protected BaseFileBrowserWidget createListWidget() 50 | { 51 | BaseFileBrowserWidget listWidget = super.createListWidget(); 52 | FileBrowserIconProvider iconProvider = new DefaultFileBrowserIconProvider(); 53 | listWidget.setAreEntriesFixedHeight(false); 54 | listWidget.setListEntryWidgetFixedHeight(20); 55 | listWidget.setDataListEntryWidgetFactory((d, cd) -> new SchematicPlacementBrowserEntryWidget(d, cd, listWidget, iconProvider)); 56 | return listWidget; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/SchematicInfoConfigScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import malilib.config.option.BooleanConfig; 8 | import malilib.config.option.ConfigOption; 9 | import malilib.gui.BaseScreen; 10 | import malilib.gui.widget.LabelWidget; 11 | import malilib.gui.widget.button.BooleanConfigButton; 12 | import malilib.util.StringUtils; 13 | import litematica.config.Configs; 14 | 15 | public class SchematicInfoConfigScreen extends BaseScreen 16 | { 17 | protected final List labels = new ArrayList<>(); 18 | protected final List buttons = new ArrayList<>(); 19 | protected int labelWidth; 20 | 21 | public SchematicInfoConfigScreen() 22 | { 23 | this.backgroundColor = 0xFF000000; 24 | this.renderBorder = true; 25 | this.useTitleHierarchy = false; 26 | 27 | this.setTitle("litematica.title.screen.schematic_info_options"); 28 | 29 | List> configList = Configs.Internal.OPTIONS.stream().filter(c -> c.getName().startsWith("schematicInfo")).collect(Collectors.toList()); 30 | this.labelWidth = StringUtils.getMaxStringRenderWidthOfObjects(configList, ConfigOption::getPrettyName); 31 | 32 | int totalWidth = this.labelWidth + 70; 33 | totalWidth = Math.max(totalWidth, this.titleText.renderWidth + 20); // title needs to have been set before! 34 | int totalHeight = configList.size() * 18 + 30; 35 | 36 | for (ConfigOption cfg : configList) 37 | { 38 | if (cfg instanceof BooleanConfig) 39 | { 40 | this.createLabelAndConfigWidgets(this.labelWidth, (BooleanConfig) cfg); 41 | } 42 | } 43 | 44 | this.setScreenWidthAndHeight(totalWidth, totalHeight); 45 | this.centerOnScreen(); 46 | } 47 | 48 | @Override 49 | protected void reAddActiveWidgets() 50 | { 51 | super.reAddActiveWidgets(); 52 | 53 | this.labels.forEach(this::addWidget); 54 | this.buttons.forEach(this::addWidget); 55 | } 56 | 57 | @Override 58 | protected void updateWidgetPositions() 59 | { 60 | super.updateWidgetPositions(); 61 | 62 | int x = this.x + 10; 63 | int y = this.y + 24; 64 | int max = this.buttons.size(); 65 | 66 | for (int i = 0; i < max; ++i) 67 | { 68 | this.labels.get(i).setPosition(x, y); 69 | this.buttons.get(i).setPosition(x + this.labelWidth + 10, y); 70 | y += 18; 71 | } 72 | } 73 | 74 | protected void createLabelAndConfigWidgets(int labelWidth, BooleanConfig config) 75 | { 76 | int color = config.isModified() ? 0xFFFFFF55 : 0xFFAAAAAA; 77 | LabelWidget label = new LabelWidget(color).setLines(config.getPrettyName()); 78 | label.setSize(labelWidth + 4, 16); 79 | label.getPadding().setTop(3); 80 | config.getComment().ifPresent(label::addHoverStrings); 81 | 82 | this.labels.add(label); 83 | this.buttons.add(new BooleanConfigButton(-1, 16, config)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/SchematicSelectorScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import java.nio.file.Path; 4 | import java.util.function.Consumer; 5 | 6 | import malilib.gui.widget.button.GenericButton; 7 | import malilib.gui.widget.list.BaseFileBrowserWidget.DirectoryEntry; 8 | import malilib.gui.widget.list.BaseFileBrowserWidget.DirectoryEntryType; 9 | import malilib.overlay.message.MessageDispatcher; 10 | import litematica.Reference; 11 | import litematica.data.SchematicHolder; 12 | import litematica.schematic.ISchematic; 13 | 14 | public class SchematicSelectorScreen extends BaseSchematicBrowserScreen 15 | { 16 | protected final GenericButton cancelButton; 17 | protected final GenericButton loadButton; 18 | protected final Consumer schematicConsumer; 19 | 20 | public SchematicSelectorScreen(Consumer schematicConsumer) 21 | { 22 | super(10, 24, 20 + 170 + 2, 70, "schematic_browser"); 23 | 24 | this.schematicConsumer = schematicConsumer; 25 | this.cancelButton = GenericButton.create("litematica.button.misc.cancel", this::openParentScreen); 26 | this.loadButton = GenericButton.create("litematica.button.misc.select", this::selectSchematic); 27 | 28 | this.setTitle("litematica.title.screen.schematic_browser", Reference.MOD_VERSION); 29 | } 30 | 31 | @Override 32 | protected void reAddActiveWidgets() 33 | { 34 | super.reAddActiveWidgets(); 35 | 36 | this.addWidget(this.cancelButton); 37 | this.addWidget(this.loadButton); 38 | } 39 | 40 | @Override 41 | protected void updateWidgetPositions() 42 | { 43 | super.updateWidgetPositions(); 44 | 45 | this.loadButton.setX(this.x + 10); 46 | this.loadButton.setBottom(this.getBottom() - 6); 47 | this.cancelButton.setPosition(this.loadButton.getRight() + 2, this.loadButton.getY()); 48 | } 49 | 50 | protected void selectSchematic() 51 | { 52 | DirectoryEntry entry = this.getListWidget().getEntrySelectionHandler().getLastSelectedEntry(); 53 | Path file = entry != null && entry.getType() == DirectoryEntryType.FILE ? entry.getFullPath() : null; 54 | 55 | if (file != null) 56 | { 57 | ISchematic schematic = SchematicHolder.getInstance().getOrLoad(file); 58 | 59 | if (schematic != null) 60 | { 61 | this.schematicConsumer.accept(schematic); 62 | } 63 | } 64 | else 65 | { 66 | MessageDispatcher.error("litematica.message.error.schematic_load.no_schematic_selected"); 67 | } 68 | 69 | this.openParentScreen(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/SelectLoadedSchematicScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import java.util.List; 4 | import java.util.function.Consumer; 5 | import java.util.function.Supplier; 6 | import javax.annotation.Nullable; 7 | 8 | import malilib.gui.BaseListScreen; 9 | import malilib.gui.widget.button.GenericButton; 10 | import malilib.gui.widget.list.DataListWidget; 11 | import litematica.data.SchematicHolder; 12 | import litematica.gui.widget.SchematicInfoWidgetBySchematic; 13 | import litematica.gui.widget.list.entry.BaseSchematicEntryWidget; 14 | import litematica.schematic.ISchematic; 15 | 16 | public class SelectLoadedSchematicScreen extends BaseListScreen> 17 | { 18 | protected final GenericButton selectSchematicButton; 19 | protected final SchematicInfoWidgetBySchematic schematicInfoWidget; 20 | protected final Consumer schematicConsumer; 21 | 22 | public SelectLoadedSchematicScreen(Consumer schematicConsumer) 23 | { 24 | super(10, 30, 192, 56); 25 | 26 | this.schematicConsumer = schematicConsumer; 27 | this.schematicInfoWidget = new SchematicInfoWidgetBySchematic(170, 290); 28 | this.selectSchematicButton = GenericButton.create("litematica.button.select_schematic.confirm", this::onSelectButtonClicked); 29 | 30 | this.setTitle("litematica.title.screen.select_loaded_schematic"); 31 | this.addPreScreenCloseListener(this::clearSchematicInfoCache); 32 | } 33 | 34 | @Override 35 | protected void reAddActiveWidgets() 36 | { 37 | super.reAddActiveWidgets(); 38 | 39 | this.addWidget(this.schematicInfoWidget); 40 | this.addWidget(this.selectSchematicButton); 41 | } 42 | 43 | @Override 44 | protected void updateWidgetPositions() 45 | { 46 | super.updateWidgetPositions(); 47 | 48 | int y = this.getBottom() - 24; 49 | this.selectSchematicButton.setPosition(this.x + 10, y); 50 | 51 | this.schematicInfoWidget.setHeight(this.getListHeight()); 52 | this.schematicInfoWidget.setRight(this.getRight() - 10); 53 | this.schematicInfoWidget.setY(this.getListY()); 54 | } 55 | 56 | @Override 57 | protected DataListWidget createListWidget() 58 | { 59 | Supplier> supplier = SchematicHolder.getInstance()::getAllSchematics; 60 | DataListWidget listWidget = new DataListWidget<>(supplier, true); 61 | listWidget.addDefaultSearchBar(); 62 | listWidget.setEntryFilter(BaseSchematicEntryWidget::schematicSearchFilter); 63 | listWidget.setDataListEntryWidgetFactory(BaseSchematicEntryWidget::new); 64 | listWidget.setAllowSelection(true); 65 | listWidget.getEntrySelectionHandler().setSelectionListener(this::onSelectionChange); 66 | 67 | return listWidget; 68 | } 69 | 70 | protected void onSelectButtonClicked() 71 | { 72 | ISchematic schematic = this.getListWidget().getLastSelectedEntry(); 73 | 74 | if (schematic != null) 75 | { 76 | this.openParentScreen(); 77 | this.schematicConsumer.accept(schematic); 78 | } 79 | } 80 | 81 | public void onSelectionChange(@Nullable ISchematic entry) 82 | { 83 | this.schematicInfoWidget.onSelectionChange(entry); 84 | } 85 | 86 | protected void clearSchematicInfoCache() 87 | { 88 | this.schematicInfoWidget.clearCache(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/TaskManagerScreen.java: -------------------------------------------------------------------------------- 1 | package litematica.gui; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import malilib.gui.BaseListScreen; 7 | import malilib.gui.widget.button.GenericButton; 8 | import malilib.gui.widget.list.DataListWidget; 9 | import litematica.Reference; 10 | import litematica.gui.widget.list.entry.TaskEntryWidget; 11 | import litematica.scheduler.ITask; 12 | import litematica.scheduler.TaskScheduler; 13 | 14 | public class TaskManagerScreen extends BaseListScreen> 15 | { 16 | protected final GenericButton mainMenuButton; 17 | 18 | public TaskManagerScreen() 19 | { 20 | super(12, 30, 20, 60); 21 | 22 | this.mainMenuButton = GenericButton.create("litematica.button.change_menu.main_menu", MainMenuScreen::openMainMenuScreen); 23 | this.setTitle("litematica.title.screen.task_manager", Reference.MOD_VERSION); 24 | } 25 | 26 | @Override 27 | protected void reAddActiveWidgets() 28 | { 29 | super.reAddActiveWidgets(); 30 | this.addWidget(this.mainMenuButton); 31 | } 32 | 33 | @Override 34 | protected void updateWidgetPositions() 35 | { 36 | super.updateWidgetPositions(); 37 | 38 | this.mainMenuButton.setRight(this.getRight() - 10); 39 | this.mainMenuButton.setBottom(this.getBottom() - 6); 40 | } 41 | 42 | protected List getAllTasks() 43 | { 44 | ArrayList list = new ArrayList<>(); 45 | list.addAll(TaskScheduler.getInstanceClient().getAllTasks()); 46 | list.addAll(TaskScheduler.getInstanceServer().getAllTasks()); 47 | return list; 48 | } 49 | 50 | @Override 51 | protected DataListWidget createListWidget() 52 | { 53 | DataListWidget listWidget = new DataListWidget<>(this::getAllTasks, true); 54 | listWidget.setDataListEntryWidgetFactory(TaskEntryWidget::new); 55 | return listWidget; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/util/AbstractSchematicInfoCache.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.util; 2 | 3 | import java.util.HashMap; 4 | import javax.annotation.Nullable; 5 | 6 | import net.minecraft.client.renderer.texture.DynamicTexture; 7 | 8 | import malilib.util.data.Identifier; 9 | import malilib.util.game.wrap.GameWrap; 10 | import litematica.schematic.SchematicMetadata; 11 | 12 | public abstract class AbstractSchematicInfoCache 13 | { 14 | protected final HashMap cachedData = new HashMap<>(); 15 | 16 | @Nullable 17 | protected abstract SchematicInfo createSchematicInfo(T key); 18 | 19 | @Nullable 20 | public SchematicInfo getSchematicInfo(T key) 21 | { 22 | return this.cachedData.get(key); 23 | } 24 | 25 | @Nullable 26 | public SchematicInfo getOrCacheSchematicInfo(T key) 27 | { 28 | SchematicInfo info = this.cachedData.get(key); 29 | 30 | if (info != null) 31 | { 32 | return info; 33 | } 34 | 35 | SchematicInfo data = this.createSchematicInfo(key); 36 | this.cachedData.put(key, data); 37 | 38 | return data; 39 | } 40 | 41 | public void clearCache() 42 | { 43 | for (SchematicInfo info : this.cachedData.values()) 44 | { 45 | if (info != null && info.texture != null) 46 | { 47 | GameWrap.getClient().getTextureManager().deleteTexture(info.iconName); 48 | } 49 | } 50 | 51 | this.cachedData.clear(); 52 | } 53 | 54 | @Nullable 55 | protected DynamicTexture createPreviewImage(Identifier iconName, SchematicMetadata meta) 56 | { 57 | int[] previewImageData = meta.getPreviewImagePixelData(); 58 | 59 | if (previewImageData != null && previewImageData.length > 0) 60 | { 61 | try 62 | { 63 | int size = (int) Math.sqrt(previewImageData.length); 64 | 65 | if (size * size == previewImageData.length) 66 | { 67 | //BufferedImage buf = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB); 68 | //buf.setRGB(0, 0, size, size, previewImageData, 0, size); 69 | 70 | DynamicTexture tex = new DynamicTexture(size, size); 71 | GameWrap.getClient().getTextureManager().loadTexture(iconName, tex); 72 | 73 | System.arraycopy(previewImageData, 0, tex.getTextureData(), 0, previewImageData.length); 74 | tex.updateDynamicTexture(); 75 | 76 | return tex; 77 | } 78 | } 79 | catch (Exception ignore) {} 80 | } 81 | 82 | return null; 83 | } 84 | 85 | public static class SchematicInfo 86 | { 87 | public final SchematicMetadata schematicMetadata; 88 | public final Identifier iconName; 89 | @Nullable public final DynamicTexture texture; 90 | 91 | protected SchematicInfo(SchematicMetadata schematicMetadata, 92 | Identifier iconName, 93 | @Nullable DynamicTexture texture) 94 | { 95 | this.schematicMetadata = schematicMetadata; 96 | this.iconName = iconName; 97 | this.texture = texture; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/util/LitematicaIcons.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.util; 2 | 3 | import malilib.gui.icon.BaseIcon; 4 | import malilib.registry.Registry; 5 | import malilib.util.data.Identifier; 6 | import litematica.Reference; 7 | 8 | public class LitematicaIcons 9 | { 10 | public static final Identifier LITEMATICA_GUI_TEXTURES = new Identifier(Reference.MOD_ID, "textures/gui/gui_widgets.png"); 11 | 12 | public static final BaseIcon DUMMY = register( 0, 0, 0, 0, 0, 0); 13 | public static final BaseIcon ENCLOSING_BOX_ENABLED = register( 0, 144, 16, 16); 14 | public static final BaseIcon ENCLOSING_BOX_DISABLED = register( 0, 160, 16, 16); 15 | 16 | // Non-hover-variant icons 17 | public static final BaseIcon SCHEMATIC_LITEMATIC = register(144, 0, 12, 12, 0, 0); 18 | public static final BaseIcon SCHEMATIC_SCHEMATICA = register(144, 12, 12, 12, 0, 0); 19 | public static final BaseIcon SCHEMATIC_SPONGE = register(144, 24, 12, 12, 0, 0); 20 | public static final BaseIcon SCHEMATIC_VANILLA = register(144, 36, 12, 12, 0, 0); 21 | public static final BaseIcon SCHEMATIC_MCEDIT = register(144, 48, 12, 12, 0, 0); 22 | public static final BaseIcon FILE_ICON_JSON = register(144, 60, 12, 12, 0, 0); 23 | 24 | public static final BaseIcon SCHEMATIC_TYPE_MEMORY = register(144, 72, 12, 12, 0, 0); 25 | public static final BaseIcon IN_MEMORY_OVERLAY = register(144, 84, 12, 12, 0, 0); 26 | 27 | public static final BaseIcon SCHEMATIC_IN_MEMORY_LITEMATIC = register(156, 0, 12, 12, 0, 0); 28 | public static final BaseIcon SCHEMATIC_IN_MEMORY_SCHEMATICA = register(156, 12, 12, 12, 0, 0); 29 | public static final BaseIcon SCHEMATIC_IN_MEMORY_SPONGE = register(156, 24, 12, 12, 0, 0); 30 | public static final BaseIcon SCHEMATIC_IN_MEMORY_VANILLA = register(156, 36, 12, 12, 0, 0); 31 | public static final BaseIcon SCHEMATIC_IN_MEMORY_MCEDIT = register(156, 48, 12, 12, 0, 0); 32 | 33 | // Hover-variant icons 34 | public static final BaseIcon AREA_EDITOR = register(102, 70, 14, 14); 35 | public static final BaseIcon AREA_SELECTION = register(102, 0, 14, 14); 36 | public static final BaseIcon CONFIGURATION = register(102, 84, 14, 14); 37 | public static final BaseIcon DUPLICATE = register(102, 168, 14, 14); 38 | public static final BaseIcon LOADED_SCHEMATICS = register(102, 14, 14, 14); 39 | public static final BaseIcon PLACEMENT = register(102, 196, 14, 14); 40 | public static final BaseIcon RELOAD = register(102, 182, 14, 14); 41 | public static final BaseIcon SAVE_TO_DISK = register(102, 140, 14, 14); 42 | public static final BaseIcon SCHEMATIC_BROWSER = register(102, 28, 14, 14); 43 | public static final BaseIcon SCHEMATIC_MANAGER = register(102, 56, 14, 14); 44 | public static final BaseIcon SCHEMATIC_PLACEMENTS = register(102, 42, 14, 14); 45 | public static final BaseIcon SCHEMATIC_VCS = register(102, 98, 14, 14); 46 | public static final BaseIcon TASK_MANAGER = register(102, 112, 14, 14); 47 | public static final BaseIcon TRASH_CAN = register(102, 154, 14, 14); 48 | 49 | private static BaseIcon register(int u, int v, int w, int h) 50 | { 51 | return register(u, v, w, h, w, 0); 52 | } 53 | 54 | private static BaseIcon register(int u, int v, int w, int h, int variantOffU, int variantOffV) 55 | { 56 | BaseIcon icon = new BaseIcon(u, v, w, h, variantOffU, variantOffV, LITEMATICA_GUI_TEXTURES); 57 | return Registry.ICON.registerModIcon(icon); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/util/SchematicBrowserIconProvider.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.util; 2 | 3 | import java.nio.file.Path; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import javax.annotation.Nullable; 7 | 8 | import malilib.gui.icon.FileBrowserIconProvider; 9 | import malilib.gui.icon.Icon; 10 | import litematica.schematic.SchematicType; 11 | 12 | public class SchematicBrowserIconProvider implements FileBrowserIconProvider 13 | { 14 | protected final HashMap cachedIcons = new HashMap<>(); 15 | 16 | @Override 17 | @Nullable 18 | public Icon getIconForFile(Path file) 19 | { 20 | Icon icon = this.cachedIcons.get(file); 21 | 22 | if (icon == null && this.cachedIcons.containsKey(file) == false) 23 | { 24 | List> possibleTypes = SchematicType.getPossibleTypesFromFileName(file); 25 | 26 | if (possibleTypes.isEmpty() == false) 27 | { 28 | icon = possibleTypes.get(0).getIcon(); 29 | } 30 | 31 | this.cachedIcons.put(file, icon); 32 | } 33 | 34 | return icon; 35 | } 36 | 37 | public void setIconForFile(Path file, @Nullable Icon icon) 38 | { 39 | this.cachedIcons.put(file, icon); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/util/SchematicInfoCacheByPath.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.util; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Locale; 5 | import javax.annotation.Nullable; 6 | 7 | import net.minecraft.client.renderer.texture.DynamicTexture; 8 | import net.minecraft.nbt.NBTTagCompound; 9 | 10 | import malilib.util.FileNameUtils; 11 | import malilib.util.data.Identifier; 12 | import malilib.util.nbt.NbtUtils; 13 | import litematica.Reference; 14 | import litematica.schematic.ISchematic; 15 | import litematica.schematic.SchematicMetadata; 16 | import litematica.schematic.SchematicType; 17 | 18 | public class SchematicInfoCacheByPath extends AbstractSchematicInfoCache 19 | { 20 | @Override 21 | @Nullable 22 | protected SchematicInfo createSchematicInfo(Path file) 23 | { 24 | // TODO Use a partial NBT read method to only read the metadata tag 25 | // TODO (that's only beneficial if it's stored before the bulk schematic data in the stream) 26 | NBTTagCompound tag = NbtUtils.readNbtFromFile(file); 27 | 28 | if (tag != null) 29 | { 30 | ISchematic schematic = SchematicType.tryCreateSchematicFrom(file, tag); 31 | 32 | if (schematic != null) 33 | { 34 | SchematicMetadata metadata = schematic.getMetadata(); 35 | String filePath = FileNameUtils.generateSimpleSafeFileName(file.toAbsolutePath().toString().toLowerCase(Locale.ROOT)); 36 | Identifier iconName = new Identifier(Reference.MOD_ID, filePath); 37 | DynamicTexture texture = this.createPreviewImage(iconName, metadata); 38 | return new SchematicInfo(metadata, iconName, texture); 39 | } 40 | } 41 | 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/util/SchematicInfoCacheBySchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.util; 2 | 3 | import java.util.Locale; 4 | import javax.annotation.Nullable; 5 | 6 | import net.minecraft.client.renderer.texture.DynamicTexture; 7 | 8 | import malilib.util.FileNameUtils; 9 | import malilib.util.data.Identifier; 10 | import litematica.Reference; 11 | import litematica.schematic.ISchematic; 12 | import litematica.schematic.SchematicMetadata; 13 | 14 | public class SchematicInfoCacheBySchematic extends AbstractSchematicInfoCache 15 | { 16 | @Override 17 | @Nullable 18 | protected SchematicInfo createSchematicInfo(ISchematic schematic) 19 | { 20 | SchematicMetadata metadata = schematic.getMetadata(); 21 | String name; 22 | 23 | if (schematic.getFile() != null) 24 | { 25 | name = FileNameUtils.generateSimpleSafeFileName(schematic.getFile().toAbsolutePath().toString().toLowerCase(Locale.ROOT)); 26 | } 27 | else 28 | { 29 | name = FileNameUtils.generateSimpleSafeFileName(metadata.getName() + "_" + metadata.getAuthor() + "_" + metadata.getTimeCreated()); 30 | } 31 | 32 | Identifier iconName = new Identifier(Reference.MOD_ID, name); 33 | DynamicTexture texture = this.createPreviewImage(iconName, metadata); 34 | return new SchematicInfo(metadata, iconName, texture); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/util/SchematicPlacementInfoCache.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.util; 2 | 3 | import java.nio.file.Path; 4 | import java.util.HashMap; 5 | import javax.annotation.Nullable; 6 | 7 | import litematica.schematic.placement.SchematicPlacement; 8 | 9 | public class SchematicPlacementInfoCache 10 | { 11 | protected final HashMap cachedData = new HashMap<>(); 12 | 13 | @Nullable 14 | public SchematicPlacement getPlacementInfo(Path file) 15 | { 16 | return this.cachedData.get(file); 17 | } 18 | 19 | @Nullable 20 | public SchematicPlacement cacheAndGetPlacementInfo(Path file) 21 | { 22 | if (this.cachedData.containsKey(file) == false) 23 | { 24 | this.cachedData.put(file, SchematicPlacement.createFromFile(file)); 25 | } 26 | 27 | return this.cachedData.get(file); 28 | } 29 | 30 | public void clearCache() 31 | { 32 | this.cachedData.clear(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/widget/SchematicInfoWidgetByPath.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.widget; 2 | 3 | import java.nio.file.Path; 4 | 5 | import litematica.gui.util.SchematicInfoCacheByPath; 6 | 7 | public class SchematicInfoWidgetByPath extends AbstractSchematicInfoWidget 8 | { 9 | public SchematicInfoWidgetByPath(int width, int height) 10 | { 11 | super(width, height, new SchematicInfoCacheByPath()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/widget/SchematicInfoWidgetBySchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.widget; 2 | 3 | import litematica.gui.util.SchematicInfoCacheBySchematic; 4 | import litematica.schematic.ISchematic; 5 | 6 | public class SchematicInfoWidgetBySchematic extends AbstractSchematicInfoWidget 7 | { 8 | public SchematicInfoWidgetBySchematic(int width, int height) 9 | { 10 | super(width, height, new SchematicInfoCacheBySchematic()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/widget/list/entry/SchematicPlacementBrowserEntryWidget.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.widget.list.entry; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Locale; 7 | import javax.annotation.Nullable; 8 | 9 | import malilib.gui.icon.FileBrowserIconProvider; 10 | import malilib.gui.widget.button.GenericButton; 11 | import malilib.gui.widget.list.BaseFileBrowserWidget; 12 | import malilib.gui.widget.list.BaseFileBrowserWidget.DirectoryEntry; 13 | import malilib.gui.widget.list.BaseFileBrowserWidget.DirectoryEntryType; 14 | import malilib.gui.widget.list.entry.DataListEntryWidgetData; 15 | import malilib.gui.widget.list.entry.DirectoryEntryWidget; 16 | import malilib.overlay.message.MessageDispatcher; 17 | import malilib.util.FileNameUtils; 18 | import malilib.util.FileUtils; 19 | import litematica.data.DataManager; 20 | 21 | public class SchematicPlacementBrowserEntryWidget extends DirectoryEntryWidget 22 | { 23 | protected final GenericButton loadButton; 24 | protected final GenericButton removeButton; 25 | protected final boolean isSelectionEntry; 26 | 27 | public SchematicPlacementBrowserEntryWidget(DirectoryEntry entry, 28 | DataListEntryWidgetData constructData, 29 | BaseFileBrowserWidget fileBrowserWidget, 30 | @Nullable FileBrowserIconProvider iconProvider) 31 | { 32 | super(entry, constructData, fileBrowserWidget, iconProvider); 33 | 34 | this.isSelectionEntry = entry.getType() == DirectoryEntryType.FILE && entry.getName().endsWith(".json"); 35 | this.loadButton = GenericButton.create(18, "litematica.button.misc.load", this::loadSavedPlacement); 36 | this.removeButton = GenericButton.create(18, "litematica.button.misc.remove", this::removeSavedPlacement); 37 | } 38 | 39 | @Override 40 | public void reAddSubWidgets() 41 | { 42 | super.reAddSubWidgets(); 43 | 44 | if (this.isSelectionEntry) 45 | { 46 | this.addWidget(this.loadButton); 47 | this.addWidget(this.removeButton); 48 | } 49 | } 50 | 51 | @Override 52 | public void updateSubWidgetPositions() 53 | { 54 | super.updateSubWidgetPositions(); 55 | 56 | if (this.isSelectionEntry) 57 | { 58 | this.loadButton.centerVerticallyInside(this); 59 | this.removeButton.centerVerticallyInside(this); 60 | this.removeButton.setRight(this.getRight() - 2); 61 | this.loadButton.setRight(this.removeButton.getX() - 1); 62 | } 63 | } 64 | 65 | protected void loadSavedPlacement() 66 | { 67 | Path file = this.data.getFullPath(); 68 | DataManager.getSchematicPlacementManager().loadPlacementFromFile(file); 69 | } 70 | 71 | protected void removeSavedPlacement() 72 | { 73 | this.scheduleTask(() -> { 74 | Path file = this.data.getFullPath(); 75 | FileUtils.deleteFiles(Collections.singletonList(file), MessageDispatcher::error); 76 | this.listWidget.clearSelection(); 77 | this.listWidget.refreshEntries(); 78 | }); 79 | } 80 | 81 | public static boolean placementSearchFilter(DirectoryEntry entry, List searchTerms) 82 | { 83 | String fileName = FileNameUtils.getFileNameWithoutExtension(entry.getName()).toLowerCase(Locale.ROOT); 84 | 85 | for (String searchTerm : searchTerms) 86 | { 87 | if (fileName.contains(searchTerm)) 88 | { 89 | return true; 90 | } 91 | } 92 | 93 | return false; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/widget/list/entry/SchematicVcsVersionEntryWidget.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.widget.list.entry; 2 | 3 | import malilib.gui.widget.list.entry.BaseDataListEntryWidget; 4 | import malilib.gui.widget.list.entry.DataListEntryWidgetData; 5 | import malilib.render.text.StyledTextLine; 6 | import litematica.schematic.projects.SchematicVersion; 7 | 8 | public class SchematicVcsVersionEntryWidget extends BaseDataListEntryWidget 9 | { 10 | public SchematicVcsVersionEntryWidget(SchematicVersion data, DataListEntryWidgetData constructData) 11 | { 12 | super(data, constructData); 13 | 14 | String key = "litematica.label.widget.schematic_vcs.version_entry"; 15 | this.setText(StyledTextLine.translateFirstLine(key, data.getVersion(), data.getName())); 16 | } 17 | 18 | @Override 19 | protected boolean isSelected() 20 | { 21 | return this.data.getProject().getCurrentVersion() == this.data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/widget/list/entry/SchematicVerifierCategoryEntryWidget.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.widget.list.entry; 2 | 3 | import malilib.gui.widget.list.entry.BaseListEntryWidget; 4 | import malilib.gui.widget.list.entry.DataListEntryWidgetData; 5 | import malilib.render.text.StyledTextLine; 6 | import litematica.schematic.verifier.SchematicVerifier; 7 | import litematica.schematic.verifier.VerifierResultType; 8 | 9 | public class SchematicVerifierCategoryEntryWidget extends BaseListEntryWidget 10 | { 11 | protected final SchematicVerifier verifier; 12 | protected final VerifierResultType type; 13 | protected boolean selected; 14 | 15 | public SchematicVerifierCategoryEntryWidget(VerifierResultType type, 16 | DataListEntryWidgetData constructData, 17 | SchematicVerifier verifier, 18 | int pairCount, 19 | int positionCount) 20 | { 21 | super(constructData); 22 | 23 | this.type = type; 24 | this.verifier = verifier; 25 | this.canReceiveMouseClicks = true; 26 | 27 | this.selected = this.verifier.isTypeSelected(type); 28 | this.textSettings.setTextColor(type.getTextColor()); 29 | this.setText(StyledTextLine.translateFirstLine(type.getCategoryWidgetTranslationKey(), pairCount, positionCount)); 30 | } 31 | 32 | @Override 33 | protected boolean onMouseClicked(int mouseX, int mouseY, int mouseButton) 34 | { 35 | if (super.onMouseClicked(mouseX, mouseY, mouseButton) == false) 36 | { 37 | this.verifier.toggleTypeSelected(this.type); 38 | this.selected = this.verifier.isTypeSelected(this.type); 39 | } 40 | 41 | return true; 42 | } 43 | 44 | @Override 45 | protected boolean isSelected() 46 | { 47 | return this.selected; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/litematica/gui/widget/list/entry/TaskEntryWidget.java: -------------------------------------------------------------------------------- 1 | package litematica.gui.widget.list.entry; 2 | 3 | import malilib.gui.widget.button.GenericButton; 4 | import malilib.gui.widget.list.entry.BaseDataListEntryWidget; 5 | import malilib.gui.widget.list.entry.DataListEntryWidgetData; 6 | import malilib.render.text.StyledTextLine; 7 | import litematica.scheduler.ITask; 8 | import litematica.scheduler.TaskScheduler; 9 | 10 | public class TaskEntryWidget extends BaseDataListEntryWidget 11 | { 12 | protected final GenericButton removeButton; 13 | 14 | public TaskEntryWidget(ITask data, DataListEntryWidgetData constructData) 15 | { 16 | super(data, constructData); 17 | 18 | this.removeButton = GenericButton.create("litematica.gui.button.remove"); 19 | this.removeButton.setActionListener(() -> { 20 | if (TaskScheduler.getInstanceClient().removeTask(this.getData()) == false) 21 | { 22 | TaskScheduler.getInstanceServer().removeTask(this.getData()); 23 | } 24 | 25 | this.listWidget.refreshEntries(); 26 | }); 27 | 28 | this.setText(StyledTextLine.parseFirstLine(data.getDisplayName())); 29 | } 30 | 31 | @Override 32 | public void reAddSubWidgets() 33 | { 34 | super.reAddSubWidgets(); 35 | 36 | this.addWidget(this.removeButton); 37 | } 38 | 39 | @Override 40 | public void updateSubWidgetPositions() 41 | { 42 | super.updateSubWidgetPositions(); 43 | 44 | this.removeButton.setRight(this.getRight() - 2); 45 | this.removeButton.setY(this.getY() + 1); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/litematica/input/LitematicaHotkeyProvider.java: -------------------------------------------------------------------------------- 1 | package litematica.input; 2 | 3 | import java.util.List; 4 | import com.google.common.collect.ImmutableList; 5 | 6 | import malilib.input.Hotkey; 7 | import malilib.input.HotkeyCategory; 8 | import malilib.input.HotkeyProvider; 9 | import litematica.Reference; 10 | import litematica.config.Configs; 11 | import litematica.config.Hotkeys; 12 | 13 | public class LitematicaHotkeyProvider implements HotkeyProvider 14 | { 15 | public static final ImmutableList ALL_HOTKEYS = buildFullHotkeyList(); 16 | public static final LitematicaHotkeyProvider INSTANCE = new LitematicaHotkeyProvider(); 17 | 18 | @Override 19 | public List getAllHotkeys() 20 | { 21 | return ALL_HOTKEYS; 22 | } 23 | 24 | @Override 25 | public List getHotkeysByCategories() 26 | { 27 | return ImmutableList.of(new HotkeyCategory(Reference.MOD_INFO, "litematica.hotkeys.category.hotkeys", Hotkeys.HOTKEY_LIST), 28 | new HotkeyCategory(Reference.MOD_INFO, "litematica.hotkeys.category.generic", Configs.Generic.HOTKEYS), 29 | new HotkeyCategory(Reference.MOD_INFO, "litematica.hotkeys.category.visuals", Configs.Visuals.HOTKEYS), 30 | new HotkeyCategory(Reference.MOD_INFO, "litematica.hotkeys.category.overlays", Configs.InfoOverlays.HOTKEYS)); 31 | } 32 | 33 | private static ImmutableList buildFullHotkeyList() 34 | { 35 | ImmutableList.Builder builder = ImmutableList.builder(); 36 | 37 | builder.addAll(Hotkeys.HOTKEY_LIST); 38 | builder.addAll(Configs.Generic.HOTKEYS); 39 | builder.addAll(Configs.Visuals.HOTKEYS); 40 | builder.addAll(Configs.InfoOverlays.HOTKEYS); 41 | 42 | return builder.build(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/litematica/interfaces/IMixinChunkProviderClient.java: -------------------------------------------------------------------------------- 1 | package litematica.interfaces; 2 | 3 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap; 4 | 5 | import net.minecraft.world.chunk.Chunk; 6 | 7 | public interface IMixinChunkProviderClient 8 | { 9 | Long2ObjectMap getLoadedChunks(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/litematica/interfaces/IWorldUpdateSuppressor.java: -------------------------------------------------------------------------------- 1 | package litematica.interfaces; 2 | 3 | public interface IWorldUpdateSuppressor 4 | { 5 | boolean getShouldPreventUpdates(); 6 | 7 | void setShouldPreventUpdates(boolean preventUpdates); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/litematica/materials/IMaterialList.java: -------------------------------------------------------------------------------- 1 | package litematica.materials; 2 | 3 | import java.util.List; 4 | 5 | import litematica.util.value.BlockInfoListType; 6 | 7 | public interface IMaterialList 8 | { 9 | BlockInfoListType getMaterialListType(); 10 | 11 | void setMaterialListEntries(List list); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/litematica/materials/MaterialListAreaAnalyzer.java: -------------------------------------------------------------------------------- 1 | package litematica.materials; 2 | 3 | import malilib.overlay.message.MessageDispatcher; 4 | import malilib.util.StringUtils; 5 | import litematica.scheduler.TaskScheduler; 6 | import litematica.scheduler.tasks.TaskCountBlocksArea; 7 | import litematica.selection.AreaSelection; 8 | 9 | public class MaterialListAreaAnalyzer extends MaterialListBase 10 | { 11 | private final AreaSelection selection; 12 | 13 | public MaterialListAreaAnalyzer(AreaSelection selection) 14 | { 15 | super(); 16 | 17 | this.selection = selection; 18 | } 19 | 20 | @Override 21 | public String getName() 22 | { 23 | return this.selection.getName(); 24 | } 25 | 26 | @Override 27 | public String getTitle() 28 | { 29 | return StringUtils.translate("litematica.title.screen.material_list.area_analyzer", this.getName()); 30 | } 31 | 32 | @Override 33 | public void reCreateMaterialList() 34 | { 35 | TaskCountBlocksArea task = new TaskCountBlocksArea(this.selection, this); 36 | TaskScheduler.getInstanceClient().scheduleTask(task, 20); 37 | MessageDispatcher.generic(1000).translate("litematica.message.scheduled_task_added"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/litematica/materials/MaterialListEntry.java: -------------------------------------------------------------------------------- 1 | package litematica.materials; 2 | 3 | import net.minecraft.item.ItemStack; 4 | 5 | import malilib.util.data.ItemType; 6 | 7 | public class MaterialListEntry 8 | { 9 | private final ItemType item; 10 | private final long countTotal; 11 | private final long countMissing; 12 | private final long countMismatched; 13 | private long countAvailable; 14 | 15 | public MaterialListEntry(ItemStack stack, long countTotal, long countMissing, long countMismatched, long countAvailable) 16 | { 17 | this.item = new ItemType(stack, false, false); 18 | this.countTotal = countTotal; 19 | this.countMissing = countMissing; 20 | this.countMismatched = countMismatched; 21 | this.countAvailable = countAvailable; 22 | } 23 | 24 | public ItemType getItemType() 25 | { 26 | return this.item; 27 | } 28 | 29 | public ItemStack getStack() 30 | { 31 | return this.item.getStack(); 32 | } 33 | 34 | /** 35 | * Returns the total number of required items of this type in the counted area. 36 | * @return 37 | */ 38 | public long getTotalCount() 39 | { 40 | return this.countTotal; 41 | } 42 | 43 | /** 44 | * Returns the number of items still missing (or having the wrong block state) 45 | * in the counted area for this item type. 46 | * @return 47 | */ 48 | public long getMissingCount() 49 | { 50 | return this.countMissing; 51 | } 52 | 53 | public long getCountMismatched() 54 | { 55 | return this.countMismatched; 56 | } 57 | 58 | public long getAvailableCount() 59 | { 60 | return this.countAvailable; 61 | } 62 | 63 | public void setCountAvailable(long countAvailable) 64 | { 65 | this.countAvailable = countAvailable; 66 | } 67 | 68 | @Override 69 | public int hashCode() 70 | { 71 | final int prime = 31; 72 | int result = 1; 73 | result = prime * result + ((this.item == null) ? 0 : this.item.hashCode()); 74 | return result; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object obj) 79 | { 80 | if (this == obj) 81 | return true; 82 | if (obj == null) 83 | return false; 84 | if (getClass() != obj.getClass()) 85 | return false; 86 | MaterialListEntry other = (MaterialListEntry) obj; 87 | if (this.item == null) 88 | { 89 | if (other.item != null) 90 | return false; 91 | } 92 | else if (! this.item.equals(other.item)) 93 | return false; 94 | return true; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/litematica/materials/MaterialListPlacement.java: -------------------------------------------------------------------------------- 1 | package litematica.materials; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | import malilib.overlay.message.MessageDispatcher; 6 | import malilib.util.StringUtils; 7 | import litematica.scheduler.TaskScheduler; 8 | import litematica.scheduler.tasks.TaskCountBlocksPlacement; 9 | import litematica.schematic.placement.SchematicPlacement; 10 | 11 | public class MaterialListPlacement extends MaterialListBase 12 | { 13 | private final SchematicPlacement placement; 14 | 15 | public MaterialListPlacement(SchematicPlacement placement, boolean reCreate) 16 | { 17 | super(); 18 | 19 | this.placement = placement; 20 | 21 | if (reCreate) 22 | { 23 | this.reCreateMaterialList(); 24 | } 25 | } 26 | 27 | @Override 28 | public boolean isForPlacement() 29 | { 30 | return true; 31 | } 32 | 33 | @Override 34 | public boolean supportsRenderLayers() 35 | { 36 | return true; 37 | } 38 | 39 | @Override 40 | public String getName() 41 | { 42 | return this.placement.getName(); 43 | } 44 | 45 | @Override 46 | public String getTitle() 47 | { 48 | return StringUtils.translate("litematica.title.screen.material_list.placement", this.getName()); 49 | } 50 | 51 | @Override 52 | public void reCreateMaterialList() 53 | { 54 | TaskCountBlocksPlacement task = new TaskCountBlocksPlacement(this.placement, this); 55 | TaskScheduler.getInstanceClient().scheduleTask(task, 20); 56 | MessageDispatcher.generic(1000).translate("litematica.message.scheduled_task_added"); 57 | } 58 | 59 | public static MaterialListPlacement createFromJson(JsonObject obj, SchematicPlacement schematicPlacement) 60 | { 61 | MaterialListPlacement materialList = new MaterialListPlacement(schematicPlacement, false); 62 | materialList.fromJson(obj); 63 | return materialList; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/litematica/materials/MaterialListSchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.materials; 2 | 3 | import java.util.Collection; 4 | import com.google.common.collect.ImmutableList; 5 | 6 | import malilib.util.StringUtils; 7 | import litematica.schematic.ISchematic; 8 | 9 | public class MaterialListSchematic extends MaterialListBase 10 | { 11 | private final ISchematic schematic; 12 | private final ImmutableList regions; 13 | 14 | public MaterialListSchematic(ISchematic schematic, boolean reCreate) 15 | { 16 | this(schematic, schematic.getRegionNames(), reCreate); 17 | } 18 | 19 | public MaterialListSchematic(ISchematic schematic, Collection subRegions, boolean reCreate) 20 | { 21 | super(); 22 | 23 | this.schematic = schematic; 24 | this.regions = ImmutableList.copyOf(subRegions); 25 | 26 | if (reCreate) 27 | { 28 | this.reCreateMaterialList(); 29 | } 30 | } 31 | 32 | @Override 33 | public void reCreateMaterialList() 34 | { 35 | this.materialListAll = ImmutableList.copyOf(MaterialListUtils.createMaterialListFor(this.schematic, this.regions)); 36 | this.refreshPreFilteredList(); 37 | this.updateCounts(); 38 | } 39 | 40 | @Override 41 | public String getName() 42 | { 43 | return this.schematic.getMetadata().getName(); 44 | } 45 | 46 | @Override 47 | public String getTitle() 48 | { 49 | return StringUtils.translate("litematica.title.screen.material_list.schematic", 50 | this.getName(), this.regions.size(), this.schematic.getRegionNames().size()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/litematica/materials/MaterialListSorter.java: -------------------------------------------------------------------------------- 1 | package litematica.materials; 2 | 3 | import java.util.Comparator; 4 | 5 | import litematica.materials.MaterialListBase.SortCriteria; 6 | 7 | public class MaterialListSorter implements Comparator 8 | { 9 | private final MaterialListBase materialList; 10 | 11 | public MaterialListSorter(MaterialListBase materialList) 12 | { 13 | this.materialList = materialList; 14 | } 15 | 16 | @Override 17 | public int compare(MaterialListEntry entry1, MaterialListEntry entry2) 18 | { 19 | boolean reverse = this.materialList.getSortInReverse(); 20 | SortCriteria sortCriteria = this.materialList.getSortCriteria(); 21 | int nameCompare = entry1.getStack().getDisplayName().compareTo(entry2.getStack().getDisplayName()); 22 | 23 | if (sortCriteria == SortCriteria.COUNT_TOTAL) 24 | { 25 | return entry1.getTotalCount() == entry2.getTotalCount() ? nameCompare : ((entry1.getTotalCount() > entry2.getTotalCount()) != reverse ? -1 : 1); 26 | } 27 | else if (sortCriteria == SortCriteria.COUNT_MISSING) 28 | { 29 | return entry1.getMissingCount() == entry2.getMissingCount() ? nameCompare : ((entry1.getMissingCount() > entry2.getMissingCount()) != reverse ? -1 : 1); 30 | } 31 | else if (sortCriteria == SortCriteria.COUNT_AVAILABLE) 32 | { 33 | return entry1.getAvailableCount() == entry2.getAvailableCount() ? nameCompare : ((entry1.getAvailableCount() > entry2.getAvailableCount()) != reverse ? -1 : 1); 34 | } 35 | 36 | return reverse == false ? nameCompare * -1 : nameCompare; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/IMixinBlockRendererDispatcher.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | 6 | import net.minecraft.client.renderer.BlockFluidRenderer; 7 | import net.minecraft.client.renderer.BlockRendererDispatcher; 8 | 9 | @Mixin(BlockRendererDispatcher.class) 10 | public interface IMixinBlockRendererDispatcher 11 | { 12 | @Accessor 13 | BlockFluidRenderer getFluidRenderer(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/IMixinCompiledChunk.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Invoker; 5 | 6 | import net.minecraft.client.renderer.chunk.CompiledChunk; 7 | import net.minecraft.util.BlockRenderLayer; 8 | 9 | @Mixin(CompiledChunk.class) 10 | public interface IMixinCompiledChunk 11 | { 12 | @Invoker 13 | void invokeSetLayerUsed(BlockRenderLayer layer); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/IMixinDataFixer.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | 6 | import net.minecraft.util.datafix.DataFixer; 7 | 8 | @Mixin(DataFixer.class) 9 | public interface IMixinDataFixer 10 | { 11 | @Accessor("version") 12 | int getVersion(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/IMixinItemBlockSpecial.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | 6 | import net.minecraft.block.Block; 7 | import net.minecraft.item.ItemBlockSpecial; 8 | 9 | @Mixin(ItemBlockSpecial.class) 10 | public interface IMixinItemBlockSpecial 11 | { 12 | @Accessor("block") 13 | Block getBlock(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/IMixinViewFrustum.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Invoker; 5 | 6 | import net.minecraft.client.renderer.ViewFrustum; 7 | import net.minecraft.client.renderer.chunk.RenderChunk; 8 | import net.minecraft.util.math.BlockPos; 9 | 10 | @Mixin(ViewFrustum.class) 11 | public interface IMixinViewFrustum 12 | { 13 | @Invoker 14 | RenderChunk invokeGetRenderChunk(BlockPos pos); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/IMixinWorldClient.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | 6 | import net.minecraft.client.multiplayer.ChunkProviderClient; 7 | import net.minecraft.client.multiplayer.WorldClient; 8 | 9 | @Mixin(WorldClient.class) 10 | public interface IMixinWorldClient 11 | { 12 | @Accessor 13 | void setClientChunkProvider(ChunkProviderClient provider); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinBlock.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import litematica.util.WorldUtils; 9 | 10 | @Mixin(net.minecraft.block.Block.class) 11 | public abstract class MixinBlock 12 | { 13 | @Inject(method = "spawnAsEntity", at = @At("HEAD"), cancellable = true) 14 | private static void preventItemDrops( 15 | net.minecraft.world.World worldIn, 16 | net.minecraft.util.math.BlockPos pos, 17 | net.minecraft.item.ItemStack stack, 18 | CallbackInfo ci) 19 | { 20 | if (WorldUtils.shouldPreventBlockUpdates(worldIn)) 21 | { 22 | ci.cancel(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinBlockRail.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import net.minecraft.block.BlockRail; 9 | import net.minecraft.block.BlockRailBase; 10 | import net.minecraft.block.BlockRailDetector; 11 | import net.minecraft.block.BlockRailPowered; 12 | import net.minecraft.block.state.IBlockState; 13 | import net.minecraft.util.Rotation; 14 | 15 | import litematica.config.Configs; 16 | 17 | @Mixin({BlockRail.class, BlockRailDetector.class, BlockRailPowered.class}) 18 | public abstract class MixinBlockRail extends BlockRailBase 19 | { 20 | protected MixinBlockRail(boolean isPowered) 21 | { 22 | super(isPowered); 23 | } 24 | 25 | @Inject(method = "withRotation", at = @At("HEAD"), cancellable = true) 26 | private void fixRailRotation(IBlockState state, Rotation rot, CallbackInfoReturnable cir) 27 | { 28 | if (Configs.Generic.FIX_RAIL_ROTATION.getBooleanValue() && rot == Rotation.CLOCKWISE_180) 29 | { 30 | BlockRailBase.EnumRailDirection dir = null; 31 | 32 | if (((Object) this) instanceof BlockRail) 33 | { 34 | dir = state.getValue(BlockRail.SHAPE); 35 | } 36 | else if (((Object) this) instanceof BlockRailDetector) 37 | { 38 | dir = state.getValue(BlockRailDetector.SHAPE); 39 | } 40 | else if (((Object) this) instanceof BlockRailPowered) 41 | { 42 | dir = state.getValue(BlockRailPowered.SHAPE); 43 | } 44 | 45 | // Fix the incomplete switch statement causing the ccw_90 rotation being used instead 46 | // for the 180 degree rotation of the straight rails. 47 | if (dir == BlockRailBase.EnumRailDirection.EAST_WEST || dir == BlockRailBase.EnumRailDirection.NORTH_SOUTH) 48 | { 49 | cir.setReturnValue(state); 50 | cir.cancel(); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinChunk.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Redirect; 6 | 7 | import net.minecraft.world.World; 8 | import net.minecraft.world.chunk.Chunk; 9 | 10 | import litematica.util.WorldUtils; 11 | 12 | @Mixin(Chunk.class) 13 | public abstract class MixinChunk 14 | { 15 | @Redirect(method = "setBlockState", 16 | at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isRemote:Z")) 17 | private boolean redirectIsRemote(World world) 18 | { 19 | return WorldUtils.shouldPreventBlockUpdates(world) ? true : world.isRemote; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinChunkProviderClient.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap; 4 | import org.spongepowered.asm.mixin.Final; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | import net.minecraft.client.multiplayer.ChunkProviderClient; 12 | import net.minecraft.world.chunk.Chunk; 13 | 14 | import litematica.config.Configs; 15 | import litematica.data.DataManager; 16 | import litematica.interfaces.IMixinChunkProviderClient; 17 | import litematica.world.ChunkProviderSchematic; 18 | 19 | @Mixin(ChunkProviderClient.class) 20 | public abstract class MixinChunkProviderClient implements IMixinChunkProviderClient 21 | { 22 | @Shadow 23 | @Final 24 | private Long2ObjectMap loadedChunks; 25 | 26 | @Override 27 | public Long2ObjectMap getLoadedChunks() 28 | { 29 | return this.loadedChunks; 30 | } 31 | 32 | @Inject(method = "unloadChunk", at = @At("RETURN")) 33 | private void onChunkUnload(int x, int z, CallbackInfo ci) 34 | { 35 | if (Configs.Generic.LOAD_ENTIRE_SCHEMATICS.getBooleanValue() == false && 36 | ((Object) this instanceof ChunkProviderSchematic) == false) 37 | { 38 | DataManager.getSchematicPlacementManager().onClientChunkUnload(x, z); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.At.Shift; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import net.minecraft.client.renderer.EntityRenderer; 10 | 11 | import malilib.render.RenderContext; 12 | import litematica.config.Configs; 13 | import litematica.render.LitematicaRenderer; 14 | 15 | @Mixin(EntityRenderer.class) 16 | public abstract class MixinEntityRenderer 17 | { 18 | private boolean renderCollidingSchematicBlocks; 19 | 20 | @Inject(method = "renderWorldPass", at = @At(value = "INVOKE", 21 | target = "Lnet/minecraft/client/renderer/RenderGlobal;updateChunks(J)V", shift = Shift.AFTER)) 22 | private void setupAndUpdate(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) 23 | { 24 | this.renderCollidingSchematicBlocks = Configs.Visuals.RENDER_COLLIDING_SCHEMATIC_BLOCKS.getBooleanValue(); 25 | LitematicaRenderer.getInstance().piecewisePrepareAndUpdate(partialTicks); 26 | } 27 | 28 | @Inject(method = "renderWorldPass", at = @At(value = "INVOKE", 29 | target = "Lnet/minecraft/client/renderer/RenderGlobal;renderBlockLayer(" + 30 | "Lnet/minecraft/util/BlockRenderLayer;DILnet/minecraft/entity/Entity;)I", ordinal = 0, shift = Shift.AFTER)) 31 | private void renderSolid(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) 32 | { 33 | LitematicaRenderer.getInstance().piecewiseRenderSolid(this.renderCollidingSchematicBlocks, partialTicks); 34 | } 35 | 36 | @Inject(method = "renderWorldPass", at = @At(value = "INVOKE", 37 | target = "Lnet/minecraft/client/renderer/RenderGlobal;renderBlockLayer(" + 38 | "Lnet/minecraft/util/BlockRenderLayer;DILnet/minecraft/entity/Entity;)I", ordinal = 1, shift = Shift.AFTER)) 39 | private void renderCutoutMipped(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) 40 | { 41 | LitematicaRenderer.getInstance().piecewiseRenderCutoutMipped(this.renderCollidingSchematicBlocks, partialTicks); 42 | } 43 | 44 | @Inject(method = "renderWorldPass", at = @At(value = "INVOKE", 45 | target = "Lnet/minecraft/client/renderer/RenderGlobal;renderBlockLayer(" + 46 | "Lnet/minecraft/util/BlockRenderLayer;DILnet/minecraft/entity/Entity;)I", ordinal = 2, shift = Shift.AFTER)) 47 | private void renderCutout(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) 48 | { 49 | LitematicaRenderer.getInstance().piecewiseRenderCutout(this.renderCollidingSchematicBlocks, partialTicks); 50 | } 51 | 52 | @Inject(method = "renderWorldPass", at = @At(value = "INVOKE", 53 | target = "Lnet/minecraft/client/renderer/RenderGlobal;renderBlockLayer(" + 54 | "Lnet/minecraft/util/BlockRenderLayer;DILnet/minecraft/entity/Entity;)I", ordinal = 3, shift = Shift.AFTER)) 55 | private void renderTranslucent(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) 56 | { 57 | LitematicaRenderer.getInstance().piecewiseRenderTranslucent(this.renderCollidingSchematicBlocks, partialTicks, RenderContext.DUMMY); 58 | } 59 | 60 | @Inject(method = "renderWorldPass", at = @At(value = "INVOKE", 61 | target = "Lnet/minecraft/client/renderer/RenderGlobal;renderEntities(" + 62 | "Lnet/minecraft/entity/Entity;Lnet/minecraft/client/renderer/culling/ICamera;F)V")) 63 | private void renderEntities(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) 64 | { 65 | LitematicaRenderer.getInstance().piecewiseRenderEntities(partialTicks); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinGuiContainer.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import litematica.config.Configs; 9 | import litematica.materials.MaterialListHudRenderer; 10 | 11 | @Mixin(net.minecraft.client.gui.inventory.GuiContainer.class) 12 | public abstract class MixinGuiContainer extends net.minecraft.client.gui.GuiScreen 13 | { 14 | @Inject(method = "drawScreen", at = @At(value = "INVOKE", shift = At.Shift.AFTER, 15 | target = "Lnet/minecraft/client/gui/inventory/GuiContainer;drawGuiContainerBackgroundLayer(FII)V")) 16 | private void hilightSlots(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) 17 | { 18 | if (Configs.InfoOverlays.MATERIAL_LIST_SLOT_HIGHLIGHT.getBooleanValue()) 19 | { 20 | MaterialListHudRenderer.renderSlotHighlights((net.minecraft.client.gui.inventory.GuiContainer) (Object) this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinGuiEditSign.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Final; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | import litematica.config.Configs; 11 | import litematica.util.WorldUtils; 12 | 13 | @Mixin(value = net.minecraft.client.gui.inventory.GuiEditSign.class, priority = 999) 14 | public abstract class MixinGuiEditSign 15 | { 16 | @Shadow @Final private net.minecraft.tileentity.TileEntitySign tileSign; 17 | 18 | @Inject(method = "initGui", at = @At("HEAD")) 19 | private void insertSignText(CallbackInfo ci) 20 | { 21 | if (Configs.Generic.SIGN_TEXT_PASTE.getBooleanValue()) 22 | { 23 | WorldUtils.insertSignTextFromSchematic(this.tileSign); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinIntegratedServer.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import java.io.File; 4 | import java.net.Proxy; 5 | import com.mojang.authlib.GameProfileRepository; 6 | import com.mojang.authlib.minecraft.MinecraftSessionService; 7 | import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.At.Shift; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | import net.minecraft.server.MinecraftServer; 15 | import net.minecraft.server.integrated.IntegratedServer; 16 | import net.minecraft.server.management.PlayerProfileCache; 17 | import net.minecraft.util.datafix.DataFixer; 18 | 19 | import litematica.scheduler.TaskScheduler; 20 | 21 | @Mixin(IntegratedServer.class) 22 | public abstract class MixinIntegratedServer extends MinecraftServer 23 | { 24 | private MixinIntegratedServer(File anvilFileIn, Proxy proxyIn, DataFixer dataFixerIn, 25 | YggdrasilAuthenticationService authServiceIn, MinecraftSessionService sessionServiceIn, 26 | GameProfileRepository profileRepoIn, PlayerProfileCache profileCacheIn) 27 | { 28 | super(anvilFileIn, proxyIn, dataFixerIn, authServiceIn, sessionServiceIn, profileRepoIn, profileCacheIn); 29 | } 30 | 31 | @Inject(method = "tick", at = @At(value = "INVOKE", 32 | target = "Lnet/minecraft/server/MinecraftServer;tick()V", shift = Shift.AFTER)) 33 | private void onPostTick(CallbackInfo ci) 34 | { 35 | TaskScheduler.getInstanceServer().runTasks(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinMinecraft.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import litematica.data.DataManager; 9 | import litematica.schematic.util.SchematicEditUtils; 10 | import litematica.util.EasyPlaceUtils; 11 | 12 | @Mixin(net.minecraft.client.Minecraft.class) 13 | public abstract class MixinMinecraft 14 | { 15 | @Inject(method = "processKeyBinds", at = @At(value = "INVOKE", ordinal = 0, 16 | target = "Lnet/minecraft/client/Minecraft;rightClickMouse()V")) 17 | private void onRightClickMouse(CallbackInfo ci) 18 | { 19 | EasyPlaceUtils.setIsFirstClick(); 20 | } 21 | 22 | @Inject(method = "rightClickMouse", cancellable = true, at = @At(value = "FIELD", ordinal = 0, 23 | target = "Lnet/minecraft/client/Minecraft;objectMouseOver:Lnet/minecraft/util/math/RayTraceResult;")) 24 | private void onHandleRightClickPre(CallbackInfo ci) 25 | { 26 | if (SchematicEditUtils.rebuildHandleBlockPlace()) 27 | { 28 | ci.cancel(); 29 | } 30 | } 31 | 32 | @Inject(method = "rightClickMouse", at = @At("TAIL")) 33 | private void onRightClickMouseTail(CallbackInfo ci) 34 | { 35 | if (EasyPlaceUtils.shouldDoEasyPlaceActions()) 36 | { 37 | EasyPlaceUtils.onRightClickTail(); 38 | } 39 | } 40 | 41 | @Inject(method = "clickMouse", cancellable = true, at = @At(value = "FIELD", ordinal = 0, 42 | target = "Lnet/minecraft/util/math/RayTraceResult;typeOfHit:Lnet/minecraft/util/math/RayTraceResult$Type;")) 43 | private void onLeftClickMouseStart(CallbackInfo ci) 44 | { 45 | if (SchematicEditUtils.rebuildHandleBlockBreak()) 46 | { 47 | ci.cancel(); 48 | } 49 | } 50 | 51 | @Inject(method = "runTick()V", at = @At("HEAD")) 52 | private void onRunTickStart(CallbackInfo ci) 53 | { 54 | DataManager.onClientTickStart(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinNetHandlerPlayClient.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import net.minecraft.client.network.NetHandlerPlayClient; 9 | import net.minecraft.network.play.server.SPacketChunkData; 10 | 11 | import litematica.config.Configs; 12 | import litematica.schematic.verifier.SchematicVerifierManager; 13 | import litematica.world.SchematicWorldRenderingNotifier; 14 | 15 | @Mixin(NetHandlerPlayClient.class) 16 | public abstract class MixinNetHandlerPlayClient 17 | { 18 | @Inject(method = "handleChunkData", at = @At("RETURN")) 19 | private void onChunkData(SPacketChunkData packetIn, CallbackInfo ci) 20 | { 21 | if (Configs.Visuals.MAIN_RENDERING_TOGGLE.getBooleanValue() && 22 | Configs.Visuals.SCHEMATIC_RENDERING.getBooleanValue()) 23 | { 24 | SchematicWorldRenderingNotifier.markSchematicChunksForRenderUpdate(packetIn.getChunkX(), packetIn.getChunkZ()); 25 | } 26 | 27 | SchematicVerifierManager.INSTANCE.onChunkChanged(packetIn.getChunkX(), packetIn.getChunkZ()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinPlayerControllerMP.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import litematica.config.Configs; 9 | import litematica.util.EasyPlaceUtils; 10 | import litematica.util.PickBlockUtils; 11 | 12 | @Mixin(net.minecraft.client.multiplayer.PlayerControllerMP.class) 13 | public abstract class MixinPlayerControllerMP 14 | { 15 | @Inject(method = "processRightClickBlock", at = @At("HEAD"), cancellable = true) 16 | private void onProcessRightlickBlock( 17 | net.minecraft.client.entity.EntityPlayerSP player, 18 | net.minecraft.client.multiplayer.WorldClient worldIn, 19 | net.minecraft.util.math.BlockPos pos, 20 | net.minecraft.util.EnumFacing direction, 21 | net.minecraft.util.math.Vec3d vec, 22 | net.minecraft.util.EnumHand hand, 23 | CallbackInfoReturnable cir) 24 | { 25 | // Prevent recursion, since the Easy Place mode can call this code again 26 | if (EasyPlaceUtils.isHandling() == false) 27 | { 28 | if (EasyPlaceUtils.shouldDoEasyPlaceActions()) 29 | { 30 | if (EasyPlaceUtils.handleEasyPlaceWithMessage()) 31 | { 32 | cir.setReturnValue(net.minecraft.util.EnumActionResult.FAIL); 33 | } 34 | } 35 | else 36 | { 37 | if (Configs.Generic.PICK_BLOCK_AUTO.getBooleanValue() && 38 | PickBlockUtils.shouldPickBlock()) 39 | { 40 | PickBlockUtils.pickBlockLast(); 41 | } 42 | 43 | if (Configs.Generic.PLACEMENT_RESTRICTION.getBooleanValue()) 44 | { 45 | if (EasyPlaceUtils.handlePlacementRestriction()) 46 | { 47 | cir.setReturnValue(net.minecraft.util.EnumActionResult.FAIL); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | // Handle right clicks on air, which needs to happen for Easy Place mode 55 | @Inject(method = "processRightClick", cancellable = true, at = @At(value = "INVOKE", 56 | target = "Lnet/minecraft/client/multiplayer/PlayerControllerMP;syncCurrentPlayItem()V")) 57 | private void onProcessRightClickPre( 58 | net.minecraft.entity.player.EntityPlayer player, 59 | net.minecraft.world.World world, 60 | net.minecraft.util.EnumHand hand, 61 | CallbackInfoReturnable cir) 62 | { 63 | // Prevent recursion, since the Easy Place mode can call this code again 64 | if (EasyPlaceUtils.isHandling() == false) 65 | { 66 | if (EasyPlaceUtils.shouldDoEasyPlaceActions() && 67 | EasyPlaceUtils.handleEasyPlaceWithMessage()) 68 | { 69 | cir.setReturnValue(net.minecraft.util.EnumActionResult.FAIL); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinRenderGlobal.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Shadow; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import malilib.util.position.BlockPos; 10 | import litematica.config.Configs; 11 | import litematica.render.LitematicaRenderer; 12 | import litematica.schematic.verifier.SchematicVerifierManager; 13 | import litematica.world.SchematicWorldRenderingNotifier; 14 | 15 | @Mixin(net.minecraft.client.renderer.RenderGlobal.class) 16 | public abstract class MixinRenderGlobal 17 | { 18 | @Shadow private net.minecraft.client.multiplayer.WorldClient world; 19 | 20 | @Inject(method = "loadRenderers()V", at = @At("RETURN")) 21 | private void onLoadRenderers(CallbackInfo ci) 22 | { 23 | // Also (re-)load our renderer when the vanilla renderer gets reloaded 24 | if (this.world != null && this.world == net.minecraft.client.Minecraft.getMinecraft().world) 25 | { 26 | LitematicaRenderer.getInstance().loadRenderers(); 27 | } 28 | } 29 | 30 | @Inject(method = "notifyBlockUpdate", at = @At("RETURN")) 31 | private void onNotifyBlockUpdate( 32 | net.minecraft.world.World worldIn, 33 | net.minecraft.util.math.BlockPos pos, 34 | net.minecraft.block.state.IBlockState oldState, 35 | net.minecraft.block.state.IBlockState newState, int flags, CallbackInfo ci) 36 | { 37 | if (oldState != newState) 38 | { 39 | BlockPos bp = BlockPos.of(pos); 40 | SchematicVerifierManager.INSTANCE.onBlockChanged(bp); 41 | 42 | if (Configs.Visuals.MAIN_RENDERING_TOGGLE.getBooleanValue() && 43 | Configs.Visuals.SCHEMATIC_RENDERING.getBooleanValue()) 44 | { 45 | SchematicWorldRenderingNotifier.markSchematicChunkForRenderUpdate(bp); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/MixinWorld.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | 5 | import net.minecraft.world.World; 6 | 7 | import litematica.interfaces.IWorldUpdateSuppressor; 8 | 9 | @Mixin(World.class) 10 | public class MixinWorld implements IWorldUpdateSuppressor 11 | { 12 | private boolean preventUpdates; 13 | 14 | @Override 15 | public boolean getShouldPreventUpdates() 16 | { 17 | return this.preventUpdates; 18 | } 19 | 20 | @Override 21 | public void setShouldPreventUpdates(boolean preventUpdates) 22 | { 23 | this.preventUpdates = preventUpdates; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/litematica/mixin/debug/GuiOverlayDebugMixin.java: -------------------------------------------------------------------------------- 1 | package litematica.mixin.debug; 2 | 3 | import java.util.List; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 8 | 9 | import net.minecraft.client.gui.GuiOverlayDebug; 10 | 11 | import litematica.render.DebugScreenMessages; 12 | 13 | @Mixin(GuiOverlayDebug.class) 14 | public abstract class GuiOverlayDebugMixin 15 | { 16 | @Inject(method = "call", at = @At("RETURN")) 17 | private void litematica_addDebugLines(CallbackInfoReturnable> cir) 18 | { 19 | DebugScreenMessages.addDebugScreenMessages(cir.getReturnValue()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/DebugScreenMessages.java: -------------------------------------------------------------------------------- 1 | package litematica.render; 2 | 3 | import java.util.List; 4 | 5 | import net.minecraft.client.renderer.RenderGlobal; 6 | 7 | import litematica.world.SchematicWorldHandler; 8 | import litematica.world.WorldSchematic; 9 | 10 | public class DebugScreenMessages 11 | { 12 | public static void addDebugScreenMessages(List list) 13 | { 14 | WorldSchematic world = SchematicWorldHandler.getSchematicWorld(); 15 | 16 | if (world != null) 17 | { 18 | RenderGlobal render = LitematicaRenderer.getInstance().getWorldRenderer(); 19 | 20 | /* 21 | world.getRegularEntityCount(), 22 | world.getChunkProvider().getLoadedChunks().size(), 23 | DataManager.getSchematicPlacementManager().getAllTouchedSubChunks().size(), 24 | DataManager.getSchematicPlacementManager().getLastVisibleSubChunks().size()); 25 | */ 26 | list.add(String.format("§6[Litematica]§r %s", render.getDebugInfoRenders())); 27 | list.add(String.format("§6[Litematica]§r %s E: %s BE: %d", 28 | render.getDebugInfoEntities(), 29 | world.getDebugLoadedEntities(), 30 | world.loadedTileEntityList.size())); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/infohud/IInfoHudRenderer.java: -------------------------------------------------------------------------------- 1 | package litematica.render.infohud; 2 | 3 | import java.util.List; 4 | 5 | import malilib.config.value.HudAlignment; 6 | import malilib.render.RenderContext; 7 | 8 | public interface IInfoHudRenderer 9 | { 10 | /** 11 | * Return true if this renderer should render its text in the indicated phase 12 | * @return 13 | */ 14 | boolean getShouldRenderText(RenderPhase phase); 15 | 16 | /** 17 | * Return true if this renderer should render its custom content via render() 18 | * @return 19 | */ 20 | default boolean getShouldRenderCustom() 21 | { 22 | return false; 23 | } 24 | 25 | /** 26 | * Whether or not this renderer should also be rendered when GUIs are open 27 | * @return 28 | */ 29 | default boolean shouldRenderInGuis() 30 | { 31 | return false; 32 | } 33 | 34 | /** 35 | * Returns the text lines rendered by the InfoHud, if any 36 | * @return 37 | */ 38 | List getText(RenderPhase phase); 39 | 40 | /** 41 | * Render any custom content on the HUD. 42 | * @param yOffset The starting y offset from the edge of the screen indicated by alignment 43 | * @param alignment the screen position to render at 44 | * @return the required y height used up for the rendered content 45 | */ 46 | default int render(int xOffset, int yOffset, HudAlignment alignment, RenderContext ctx) 47 | { 48 | return 0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/infohud/RenderPhase.java: -------------------------------------------------------------------------------- 1 | package litematica.render.infohud; 2 | 3 | public enum RenderPhase 4 | { 5 | PRE, 6 | POST 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/ChunkRenderContainerSchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import net.minecraft.client.renderer.ChunkRenderContainer; 7 | 8 | import litematica.render.schematic.RenderChunkSchematicVbo.OverlayRenderType; 9 | 10 | public abstract class ChunkRenderContainerSchematic extends ChunkRenderContainer 11 | { 12 | protected List overlayRenderChunks = new ArrayList<>(128); 13 | 14 | @Override 15 | public void initialize(double viewEntityXIn, double viewEntityYIn, double viewEntityZIn) 16 | { 17 | super.initialize(viewEntityXIn, viewEntityYIn, viewEntityZIn); 18 | 19 | this.overlayRenderChunks.clear(); 20 | } 21 | 22 | public void addOverlayChunk(RenderChunkSchematicVbo renderChunk) 23 | { 24 | this.overlayRenderChunks.add(renderChunk); 25 | } 26 | 27 | public abstract void renderBlockOverlays(OverlayRenderType type); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/CompiledChunkSchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import net.minecraft.client.renderer.BufferBuilder; 4 | import net.minecraft.client.renderer.chunk.CompiledChunk; 5 | import net.minecraft.util.BlockRenderLayer; 6 | 7 | import litematica.render.schematic.RenderChunkSchematicVbo.OverlayRenderType; 8 | 9 | public class CompiledChunkSchematic extends CompiledChunk 10 | { 11 | private final boolean[] overlayLayersUsed = new boolean[OverlayRenderType.COUNT]; 12 | private final boolean[] overlayLayersStarted = new boolean[OverlayRenderType.COUNT]; 13 | private final BufferBuilder.State[] blockBufferStates = new BufferBuilder.State[BlockRenderLayer.values().length]; 14 | private final BufferBuilder.State[] overlayBufferStates = new BufferBuilder.State[OverlayRenderType.COUNT]; 15 | private boolean overlayEmpty = true; 16 | 17 | public boolean isOverlayEmpty() 18 | { 19 | return this.overlayEmpty; 20 | } 21 | 22 | protected void setOverlayTypeUsed(OverlayRenderType type) 23 | { 24 | this.overlayEmpty = false; 25 | this.overlayLayersUsed[type.ordinal()] = true; 26 | } 27 | 28 | public boolean isOverlayTypeEmpty(OverlayRenderType type) 29 | { 30 | return this.overlayLayersUsed[type.ordinal()] == false; 31 | } 32 | 33 | public void setOverlayTypeStarted(OverlayRenderType type) 34 | { 35 | this.overlayLayersStarted[type.ordinal()] = true; 36 | } 37 | 38 | public boolean isOverlayTypeStarted(OverlayRenderType type) 39 | { 40 | return this.overlayLayersStarted[type.ordinal()]; 41 | } 42 | 43 | public BufferBuilder.State getBlockBufferState(BlockRenderLayer layer) 44 | { 45 | return this.blockBufferStates[layer.ordinal()]; 46 | } 47 | 48 | public void setBlockBufferState(BlockRenderLayer layer, BufferBuilder.State state) 49 | { 50 | this.blockBufferStates[layer.ordinal()] = state; 51 | } 52 | 53 | public BufferBuilder.State getOverlayBufferState(OverlayRenderType type) 54 | { 55 | return this.overlayBufferStates[type.ordinal()]; 56 | } 57 | 58 | public void setOverlayBufferState(OverlayRenderType type, BufferBuilder.State state) 59 | { 60 | this.overlayBufferStates[type.ordinal()] = state; 61 | } 62 | 63 | public static final CompiledChunkSchematic EMPTY = new CompiledChunkSchematic() { 64 | @Override 65 | public void setLayerUsed(BlockRenderLayer layer) 66 | { 67 | throw new UnsupportedOperationException(); 68 | } 69 | 70 | @Override 71 | public void setLayerStarted(BlockRenderLayer layer) 72 | { 73 | throw new UnsupportedOperationException(); 74 | } 75 | 76 | @Override 77 | public void setOverlayTypeUsed(OverlayRenderType layer) 78 | { 79 | throw new UnsupportedOperationException(); 80 | } 81 | 82 | @Override 83 | public void setOverlayTypeStarted(OverlayRenderType layer) 84 | { 85 | throw new UnsupportedOperationException(); 86 | } 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/RenderChunkFactoryList.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import net.minecraft.client.renderer.RenderGlobal; 4 | import net.minecraft.client.renderer.chunk.IRenderChunkFactory; 5 | import net.minecraft.client.renderer.chunk.RenderChunk; 6 | import net.minecraft.world.World; 7 | 8 | public class RenderChunkFactoryList implements IRenderChunkFactory 9 | { 10 | @Override 11 | public RenderChunk create(World worldIn, RenderGlobal renderGlobalIn, int index) 12 | { 13 | return new RenderChunkSchematicList(worldIn, renderGlobalIn, index); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/RenderChunkFactoryVbo.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import net.minecraft.client.renderer.RenderGlobal; 4 | import net.minecraft.client.renderer.chunk.IRenderChunkFactory; 5 | import net.minecraft.client.renderer.chunk.RenderChunk; 6 | import net.minecraft.world.World; 7 | 8 | public class RenderChunkFactoryVbo implements IRenderChunkFactory 9 | { 10 | @Override 11 | public RenderChunk create(World worldIn, RenderGlobal renderGlobalIn, int index) 12 | { 13 | return new RenderChunkSchematicVbo(worldIn, renderGlobalIn, index); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/RenderChunkSchematicList.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import net.minecraft.client.renderer.GLAllocation; 4 | import net.minecraft.client.renderer.RenderGlobal; 5 | import net.minecraft.client.renderer.chunk.CompiledChunk; 6 | import net.minecraft.util.BlockRenderLayer; 7 | import net.minecraft.world.World; 8 | 9 | public class RenderChunkSchematicList extends RenderChunkSchematicVbo 10 | { 11 | private static final int BLOCK_LAYERS = BlockRenderLayer.values().length; 12 | private static final int LIST_SIZE = BLOCK_LAYERS + OverlayRenderType.values().length; 13 | 14 | private final int baseDisplayList; 15 | private final int baseOverlay; 16 | 17 | public RenderChunkSchematicList(World worldIn, RenderGlobal renderGlobalIn, int index) 18 | { 19 | super(worldIn, renderGlobalIn, index); 20 | 21 | this.baseDisplayList = GLAllocation.generateDisplayLists(LIST_SIZE); 22 | this.baseOverlay = this.baseDisplayList + BLOCK_LAYERS; 23 | } 24 | 25 | public int getDisplayList(BlockRenderLayer layer, CompiledChunk compiledChunk) 26 | { 27 | return compiledChunk.isLayerEmpty(layer) == false ? this.baseDisplayList + layer.ordinal() : -1; 28 | } 29 | 30 | public int getOverlayDisplayList(OverlayRenderType type, CompiledChunkSchematic compiledChunk) 31 | { 32 | return compiledChunk.isOverlayTypeEmpty(type) == false ? this.baseOverlay + type.ordinal() : -1; 33 | } 34 | 35 | public void deleteGlResources() 36 | { 37 | super.deleteGlResources(); 38 | 39 | GLAllocation.deleteDisplayLists(this.baseDisplayList, LIST_SIZE); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/RenderListSchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import net.minecraft.client.renderer.GlStateManager; 4 | import net.minecraft.client.renderer.chunk.RenderChunk; 5 | import net.minecraft.util.BlockRenderLayer; 6 | 7 | import malilib.render.RenderContext; 8 | import malilib.util.game.wrap.RenderWrap; 9 | import litematica.render.schematic.RenderChunkSchematicVbo.OverlayRenderType; 10 | 11 | public class RenderListSchematic extends ChunkRenderContainerSchematic 12 | { 13 | @Override 14 | public void renderChunkLayer(BlockRenderLayer layer) 15 | { 16 | if (this.initialized) 17 | { 18 | RenderContext ctx = RenderContext.DUMMY; 19 | 20 | for (RenderChunk renderChunk : this.renderChunks) 21 | { 22 | RenderChunkSchematicList listedrenderchunk = (RenderChunkSchematicList) renderChunk; 23 | RenderWrap.pushMatrix(ctx); 24 | this.preRenderChunk(renderChunk); 25 | GlStateManager.callList(listedrenderchunk.getDisplayList(layer, listedrenderchunk.getChunkRenderData())); 26 | RenderWrap.popMatrix(ctx); 27 | } 28 | 29 | RenderWrap.resetColor(); 30 | this.renderChunks.clear(); 31 | } 32 | } 33 | 34 | @Override 35 | public void renderBlockOverlays(OverlayRenderType type) 36 | { 37 | if (this.initialized) 38 | { 39 | RenderContext ctx = RenderContext.DUMMY; 40 | 41 | for (RenderChunkSchematicVbo renderChunk : this.overlayRenderChunks) 42 | { 43 | RenderChunkSchematicList listedRenderChunk = (RenderChunkSchematicList) renderChunk; 44 | 45 | RenderWrap.pushMatrix(ctx); 46 | this.preRenderChunk(renderChunk); 47 | GlStateManager.callList(listedRenderChunk.getOverlayDisplayList(type, listedRenderChunk.getChunkRenderData())); 48 | RenderWrap.popMatrix(ctx); 49 | } 50 | 51 | RenderWrap.resetColor(); 52 | this.overlayRenderChunks.clear(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/VboRenderListSchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import org.lwjgl.opengl.GL11; 4 | 5 | import net.minecraft.client.renderer.chunk.RenderChunk; 6 | import net.minecraft.client.renderer.vertex.VertexBuffer; 7 | import net.minecraft.util.BlockRenderLayer; 8 | 9 | import malilib.render.RenderContext; 10 | import malilib.util.game.wrap.RenderWrap; 11 | import litematica.render.schematic.RenderChunkSchematicVbo.OverlayRenderType; 12 | 13 | public class VboRenderListSchematic extends ChunkRenderContainerSchematic 14 | { 15 | @Override 16 | public void renderChunkLayer(BlockRenderLayer layer) 17 | { 18 | if (this.initialized) 19 | { 20 | for (RenderChunk renderChunk : this.renderChunks) 21 | { 22 | this.renderBlocks(renderChunk.getVertexBufferByLayer(layer.ordinal()), renderChunk, RenderContext.DUMMY); 23 | } 24 | 25 | RenderWrap.bindBuffer(RenderWrap.GL_ARRAY_BUFFER, 0); 26 | RenderWrap.resetColor(); 27 | 28 | this.renderChunks.clear(); 29 | } 30 | } 31 | 32 | @Override 33 | public void renderBlockOverlays(OverlayRenderType type) 34 | { 35 | if (this.initialized) 36 | { 37 | for (RenderChunkSchematicVbo renderChunk : this.overlayRenderChunks) 38 | { 39 | this.renderOverlay(renderChunk.getOverlayVertexBuffer(type), renderChunk, type.getGlMode(), RenderContext.DUMMY); 40 | } 41 | 42 | RenderWrap.bindBuffer(RenderWrap.GL_ARRAY_BUFFER, 0); 43 | RenderWrap.resetColor(); 44 | 45 | this.overlayRenderChunks.clear(); 46 | } 47 | } 48 | 49 | private void renderBlocks(VertexBuffer vertexBuffer, RenderChunk renderChunk, RenderContext ctx) 50 | { 51 | RenderWrap.pushMatrix(ctx); 52 | 53 | this.preRenderChunk(renderChunk); 54 | //renderChunk.multModelviewMatrix(); 55 | vertexBuffer.bindBuffer(); 56 | this.setupArrayPointersBlocks(); 57 | vertexBuffer.drawArrays(GL11.GL_QUADS); 58 | 59 | RenderWrap.popMatrix(ctx); 60 | } 61 | 62 | private void renderOverlay(VertexBuffer vertexBuffer, RenderChunk renderChunk, int glMode, RenderContext ctx) 63 | { 64 | RenderWrap.pushMatrix(ctx); 65 | 66 | this.preRenderChunk(renderChunk); 67 | //renderChunk.multModelviewMatrix(); 68 | vertexBuffer.bindBuffer(); 69 | this.setupArrayPointersOverlay(); 70 | vertexBuffer.drawArrays(glMode); 71 | 72 | RenderWrap.popMatrix(ctx); 73 | } 74 | 75 | private void setupArrayPointersBlocks() 76 | { 77 | RenderWrap.vertexPointer(3, GL11.GL_FLOAT, 28, 0); 78 | RenderWrap.colorPointer(4, GL11.GL_UNSIGNED_BYTE, 28, 12); 79 | RenderWrap.texCoordPointer(2, GL11.GL_FLOAT, 28, 16); 80 | RenderWrap.setClientActiveTexture(RenderWrap.LIGHTMAP_TEX_UNIT); 81 | RenderWrap.texCoordPointer(2, GL11.GL_SHORT, 28, 24); 82 | RenderWrap.setClientActiveTexture(RenderWrap.DEFAULT_TEX_UNIT); 83 | } 84 | 85 | private void setupArrayPointersOverlay() 86 | { 87 | RenderWrap.vertexPointer(3, GL11.GL_FLOAT, 16, 0); 88 | RenderWrap.colorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/litematica/render/schematic/VertexBuilderCache.java: -------------------------------------------------------------------------------- 1 | package litematica.render.schematic; 2 | 3 | import org.lwjgl.opengl.GL11; 4 | 5 | import net.minecraft.client.renderer.vertex.DefaultVertexFormats; 6 | import net.minecraft.util.BlockRenderLayer; 7 | 8 | import malilib.render.buffer.VanillaWrappingVertexBuilder; 9 | import malilib.render.buffer.VertexBuilder; 10 | import litematica.render.schematic.RenderChunkSchematicVbo.OverlayRenderType; 11 | 12 | public class VertexBuilderCache 13 | { 14 | private final VertexBuilder[] worldRenderers; 15 | private final VertexBuilder[] overlayBufferBuilders; 16 | 17 | public VertexBuilderCache() 18 | { 19 | this.worldRenderers = new VertexBuilder[BlockRenderLayer.values().length]; 20 | this.overlayBufferBuilders = new VertexBuilder[2]; 21 | 22 | this.worldRenderers[BlockRenderLayer.SOLID.ordinal()] = VanillaWrappingVertexBuilder.create(2097152, GL11.GL_QUADS, DefaultVertexFormats.BLOCK); 23 | this.worldRenderers[BlockRenderLayer.CUTOUT.ordinal()] = VanillaWrappingVertexBuilder.create(131072, GL11.GL_QUADS, DefaultVertexFormats.BLOCK); 24 | this.worldRenderers[BlockRenderLayer.CUTOUT_MIPPED.ordinal()] = VanillaWrappingVertexBuilder.create(131072, GL11.GL_QUADS, DefaultVertexFormats.BLOCK); 25 | this.worldRenderers[BlockRenderLayer.TRANSLUCENT.ordinal()] = VanillaWrappingVertexBuilder.create(262144, GL11.GL_QUADS, DefaultVertexFormats.BLOCK); 26 | 27 | this.overlayBufferBuilders[0] = VanillaWrappingVertexBuilder.create(262144, GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR); 28 | this.overlayBufferBuilders[1] = VanillaWrappingVertexBuilder.create(262144, GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR); 29 | } 30 | 31 | public VertexBuilder getWorldRendererByLayer(BlockRenderLayer layer) 32 | { 33 | return this.worldRenderers[layer.ordinal()]; 34 | } 35 | 36 | public VertexBuilder getWorldRendererByLayerId(int id) 37 | { 38 | return this.worldRenderers[id]; 39 | } 40 | 41 | public VertexBuilder getOverlayBuffer(OverlayRenderType type) 42 | { 43 | return this.overlayBufferBuilders[type.ordinal()]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/ClientTickHandler.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler; 2 | 3 | import malilib.gui.util.GuiUtils; 4 | import malilib.util.game.wrap.GameWrap; 5 | import litematica.data.DataManager; 6 | import litematica.schematic.verifier.SchematicVerifierManager; 7 | import litematica.util.EasyPlaceUtils; 8 | 9 | public class ClientTickHandler implements malilib.event.ClientTickHandler 10 | { 11 | protected int tickCounter; 12 | 13 | @Override 14 | public void onClientTick() 15 | { 16 | if (GameWrap.getClientPlayer() == null || GameWrap.getClientWorld() == null) 17 | { 18 | return; 19 | } 20 | 21 | DataManager.getRenderLayerRange().followPlayerIfEnabled(GameWrap.getClientPlayer()); 22 | DataManager.getSchematicPlacementManager().processQueuedChunks(); 23 | TaskScheduler.getInstanceClient().runTasks(); 24 | 25 | if ((this.tickCounter) % 10 == 0) 26 | { 27 | SchematicVerifierManager.INSTANCE.scheduleReChecks(); 28 | } 29 | 30 | if (GuiUtils.noScreenOpen()) 31 | { 32 | EasyPlaceUtils.easyPlaceOnUseTick(); 33 | } 34 | 35 | ++this.tickCounter; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/ITask.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler; 2 | 3 | public interface ITask 4 | { 5 | /** 6 | * Get the display name for this task, used in the Task Manager GUI 7 | * @return 8 | */ 9 | String getDisplayName(); 10 | 11 | /** 12 | * Initialize the task. Called when the task is added to the task list. 13 | */ 14 | void init(); 15 | 16 | /** 17 | * Return whether this task can be executed. 18 | * @return true if the task can be executed 19 | */ 20 | boolean canExecute(); 21 | 22 | /** 23 | * Execute the task. Return true to indicate that this task has finished. 24 | * @return true to indicate the task has finished and can be removed 25 | */ 26 | boolean execute(); 27 | 28 | /** 29 | * Returns true if this task should be removed 30 | * @return 31 | */ 32 | boolean shouldRemove(); 33 | 34 | /** 35 | * Stop the task. This is also called when the tasks are removed. 36 | */ 37 | void stop(); 38 | 39 | /** 40 | * Returns the task's timer 41 | * @return 42 | */ 43 | TaskTimer getTimer(); 44 | 45 | /** 46 | * Creates a new timer for the task, with the given execution interval in game ticks 47 | * @param interval 48 | */ 49 | void createTimer(int interval); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/TaskTimer.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler; 2 | 3 | public class TaskTimer 4 | { 5 | private final int interval; 6 | private int counter; 7 | 8 | public TaskTimer(int interval) 9 | { 10 | this.interval = interval; 11 | this.counter = interval; 12 | } 13 | 14 | /** 15 | * Ticks the timer. Returns true when it hits 0, and then resets itself. 16 | * @return 17 | */ 18 | public boolean tick() 19 | { 20 | if (--this.counter <= 0) 21 | { 22 | this.reset(); 23 | return true; 24 | } 25 | 26 | return false; 27 | } 28 | 29 | public void reset() 30 | { 31 | this.counter = this.interval; 32 | } 33 | 34 | public void setNextDelay(int delay) 35 | { 36 | this.counter = delay; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/SetSchematicPreviewTask.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.geom.AffineTransform; 5 | import java.awt.image.AffineTransformOp; 6 | import java.awt.image.BufferedImage; 7 | 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.util.ScreenShotHelper; 10 | 11 | import malilib.overlay.message.MessageDispatcher; 12 | import malilib.util.StringUtils; 13 | import litematica.render.infohud.InfoHud; 14 | import litematica.schematic.ISchematic; 15 | import litematica.schematic.SchematicMetadata; 16 | 17 | /** 18 | * This task doesn't actually do/run anything on its own. 19 | * It gets triggered by a hotkey, which takes the first matching 20 | * task from the task manager, and triggers it, which then marks 21 | * the task as completed, and it will get removed after that. 22 | * This is to both allow removing the tasks via the Task Manager menu, 23 | * and also to allow multiple preview tasks to get started at once, 24 | * which will then get triggered one by one in order. 25 | */ 26 | public class SetSchematicPreviewTask extends TaskBase 27 | { 28 | protected final ISchematic schematic; 29 | 30 | public SetSchematicPreviewTask(ISchematic schematic) 31 | { 32 | this.schematic = schematic; 33 | this.name = StringUtils.translate("litematica.label.task.set_schematic_preview", 34 | schematic.getMetadata().getName()); 35 | this.infoHudLines.add(this.name); 36 | InfoHud.getInstance().addInfoHudRenderer(this, true); 37 | } 38 | 39 | @Override 40 | public boolean execute() 41 | { 42 | return this.finished; 43 | } 44 | 45 | public void setPreview() 46 | { 47 | try 48 | { 49 | Minecraft mc = Minecraft.getMinecraft(); 50 | BufferedImage screenshot = ScreenShotHelper.createScreenshot(mc.displayWidth, mc.displayHeight, mc.getFramebuffer()); 51 | 52 | int x = screenshot.getWidth() >= screenshot.getHeight() ? (screenshot.getWidth() - screenshot.getHeight()) / 2 : 0; 53 | int y = screenshot.getHeight() >= screenshot.getWidth() ? (screenshot.getHeight() - screenshot.getWidth()) / 2 : 0; 54 | int longerSide = Math.min(screenshot.getWidth(), screenshot.getHeight()); 55 | //System.out.printf("w: %d, h: %d, x: %d, y: %d\n", screenshot.getWidth(), screenshot.getHeight(), x, y); 56 | 57 | int previewDimensions = 140; 58 | double scaleFactor = (double) previewDimensions / longerSide; 59 | BufferedImage scaled = new BufferedImage(previewDimensions, previewDimensions, BufferedImage.TYPE_INT_ARGB); 60 | AffineTransform at = new AffineTransform(); 61 | at.scale(scaleFactor, scaleFactor); 62 | AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC); 63 | 64 | Graphics2D graphics = scaled.createGraphics(); 65 | graphics.drawImage(screenshot.getSubimage(x, y, longerSide, longerSide), scaleOp, 0, 0); 66 | 67 | int[] pixels = scaled.getRGB(0, 0, previewDimensions, previewDimensions, null, 0, scaled.getWidth()); 68 | 69 | SchematicMetadata meta = this.schematic.getMetadata(); 70 | meta.setPreviewImagePixelData(pixels); 71 | meta.setTimeModifiedToNowIfNotRecentlyCreated(); 72 | 73 | this.schematic.writeToFile(this.schematic.getFile(), true); 74 | 75 | MessageDispatcher.success("litematica.message.info.schematic_manager.set_preview.success", meta.getName()); 76 | } 77 | catch (Exception e) 78 | { 79 | MessageDispatcher.error().console(e).translate("litematica.message.error.schematic_preview_set_failed", 80 | this.schematic.getMetadata().getName()); 81 | } 82 | 83 | this.finished = true; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskCountBlocksArea.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import net.minecraft.block.state.IBlockState; 4 | 5 | import malilib.util.position.BlockPos; 6 | import litematica.materials.IMaterialList; 7 | import litematica.selection.AreaSelection; 8 | 9 | public class TaskCountBlocksArea extends TaskCountBlocksMaterialList 10 | { 11 | protected final AreaSelection selection; 12 | 13 | public TaskCountBlocksArea(AreaSelection selection, IMaterialList materialList) 14 | { 15 | super(materialList, "litematica.gui.label.task_name.area_analyzer"); 16 | 17 | this.selection = selection; 18 | this.addPerChunkBoxes(selection.getAllSelectionBoxes()); 19 | this.updateInfoHudLinesMissingChunks(this.requiredChunks); 20 | } 21 | 22 | @Override 23 | protected void countAtPosition(BlockPos pos) 24 | { 25 | IBlockState stateClient = this.worldClient.getBlockState(pos).getActualState(this.worldClient, pos); 26 | this.countsTotal.addTo(stateClient, 1); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskCountBlocksBase.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; 4 | 5 | import net.minecraft.block.state.IBlockState; 6 | 7 | import malilib.util.position.BlockPos; 8 | import malilib.util.position.ChunkPos; 9 | import malilib.util.position.Direction; 10 | import malilib.util.position.IntBoundingBox; 11 | import malilib.util.position.LayerRange; 12 | 13 | public abstract class TaskCountBlocksBase extends TaskProcessChunkBase 14 | { 15 | protected final Object2LongOpenHashMap countsTotal = new Object2LongOpenHashMap<>(); 16 | 17 | protected TaskCountBlocksBase(String nameOnHud) 18 | { 19 | super(nameOnHud); 20 | } 21 | 22 | @Override 23 | protected boolean canProcessChunk(ChunkPos pos) 24 | { 25 | // All surrounding chunks are loaded on the client 26 | return this.areSurroundingChunksLoaded(pos, this.worldClient, 1); 27 | } 28 | 29 | protected void countBlocksInChunk(ChunkPos pos) 30 | { 31 | BlockPos.MutBlockPos posMutable = new BlockPos.MutBlockPos(); 32 | 33 | for (IntBoundingBox bb : this.getBoxesInChunk(pos)) 34 | { 35 | final int startX = bb.minX; 36 | final int startY = bb.minY; 37 | final int startZ = bb.minZ; 38 | final int endX = bb.maxX; 39 | final int endY = bb.maxY; 40 | final int endZ = bb.maxZ; 41 | 42 | for (int y = startY; y <= endY; ++y) 43 | { 44 | for (int z = startZ; z <= endZ; ++z) 45 | { 46 | for (int x = startX; x <= endX; ++x) 47 | { 48 | posMutable.set(x, y, z); 49 | this.countAtPosition(posMutable); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | protected void countBlocksInChunkRespectingLayerRange(ChunkPos pos, LayerRange range) 57 | { 58 | Direction.Axis axis = range.getAxis(); 59 | BlockPos.MutBlockPos posMutable = new BlockPos.MutBlockPos(); 60 | 61 | for (IntBoundingBox bb : this.getBoxesInChunk(pos)) 62 | { 63 | final int startX = axis == Direction.Axis.X ? Math.max(bb.minX, range.getMinLayerBoundary()) : bb.minX; 64 | final int startY = axis == Direction.Axis.Y ? Math.max(bb.minY, range.getMinLayerBoundary()) : bb.minY; 65 | final int startZ = axis == Direction.Axis.Z ? Math.max(bb.minZ, range.getMinLayerBoundary()) : bb.minZ; 66 | final int endX = axis == Direction.Axis.X ? Math.min(bb.maxX, range.getMaxLayerBoundary()) : bb.maxX; 67 | final int endY = axis == Direction.Axis.Y ? Math.min(bb.maxY, range.getMaxLayerBoundary()) : bb.maxY; 68 | final int endZ = axis == Direction.Axis.Z ? Math.min(bb.maxZ, range.getMaxLayerBoundary()) : bb.maxZ; 69 | 70 | for (int y = startY; y <= endY; ++y) 71 | { 72 | for (int z = startZ; z <= endZ; ++z) 73 | { 74 | for (int x = startX; x <= endX; ++x) 75 | { 76 | posMutable.set(x, y, z); 77 | this.countAtPosition(posMutable); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | protected abstract void countAtPosition(BlockPos pos); 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskCountBlocksMaterialList.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import java.util.List; 4 | import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; 5 | 6 | import net.minecraft.block.state.IBlockState; 7 | 8 | import malilib.util.game.wrap.GameWrap; 9 | import malilib.util.position.ChunkPos; 10 | import malilib.util.position.LayerRange; 11 | import litematica.data.DataManager; 12 | import litematica.materials.IMaterialList; 13 | import litematica.materials.MaterialListEntry; 14 | import litematica.materials.MaterialListUtils; 15 | import litematica.render.infohud.InfoHud; 16 | import litematica.util.value.BlockInfoListType; 17 | import litematica.world.SchematicWorldRenderingNotifier; 18 | 19 | public abstract class TaskCountBlocksMaterialList extends TaskCountBlocksBase 20 | { 21 | protected final Object2LongOpenHashMap countsMissing = new Object2LongOpenHashMap<>(); 22 | protected final Object2LongOpenHashMap countsMismatch = new Object2LongOpenHashMap<>(); 23 | protected final IMaterialList materialList; 24 | protected final LayerRange layerRange; 25 | 26 | protected TaskCountBlocksMaterialList(IMaterialList materialList, String nameOnHud) 27 | { 28 | super(nameOnHud); 29 | 30 | this.materialList = materialList; 31 | 32 | if (materialList.getMaterialListType() == BlockInfoListType.ALL) 33 | { 34 | this.layerRange = new LayerRange(SchematicWorldRenderingNotifier.INSTANCE); 35 | } 36 | else 37 | { 38 | this.layerRange = DataManager.getRenderLayerRange(); 39 | } 40 | } 41 | 42 | @Override 43 | protected boolean processChunk(ChunkPos pos) 44 | { 45 | this.countBlocksInChunkRespectingLayerRange(pos, this.layerRange); 46 | return true; 47 | } 48 | 49 | @Override 50 | protected void onStop() 51 | { 52 | if (this.finished && GameWrap.getClientPlayer() != null) 53 | { 54 | List list = MaterialListUtils.getMaterialList( 55 | this.countsTotal, this.countsMissing, this.countsMismatch); 56 | this.materialList.setMaterialListEntries(list); 57 | } 58 | 59 | InfoHud.getInstance().removeInfoHudRenderer(this, false); 60 | 61 | this.notifyListener(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskCountBlocksPlacement.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import java.util.Collection; 4 | 5 | import net.minecraft.block.state.IBlockState; 6 | import net.minecraft.init.Blocks; 7 | 8 | import malilib.util.data.EnabledCondition; 9 | import malilib.util.position.BlockPos; 10 | import litematica.config.Configs; 11 | import litematica.data.DataManager; 12 | import litematica.materials.IMaterialList; 13 | import litematica.schematic.placement.SchematicPlacement; 14 | import litematica.selection.SelectionBox; 15 | import litematica.util.value.BlockInfoListType; 16 | import litematica.world.SchematicWorldHandler; 17 | import litematica.world.WorldSchematic; 18 | 19 | public class TaskCountBlocksPlacement extends TaskCountBlocksMaterialList 20 | { 21 | protected final SchematicPlacement schematicPlacement; 22 | protected final WorldSchematic worldSchematic; 23 | protected final boolean ignoreState; 24 | 25 | public TaskCountBlocksPlacement(SchematicPlacement schematicPlacement, IMaterialList materialList) 26 | { 27 | super(materialList, "litematica.gui.label.task_name.material_list"); 28 | 29 | this.ignoreState = Configs.Generic.MATERIAL_LIST_IGNORE_BLOCK_STATE.getBooleanValue(); 30 | this.worldSchematic = SchematicWorldHandler.getSchematicWorld(); 31 | this.schematicPlacement = schematicPlacement; 32 | 33 | Collection boxes = schematicPlacement.getSubRegionBoxes(EnabledCondition.ENABLED).values(); 34 | 35 | // Filter/clamp the boxes to intersect with the render layer 36 | if (materialList.getMaterialListType() == BlockInfoListType.RENDER_LAYERS) 37 | { 38 | this.addPerChunkBoxes(boxes, DataManager.getRenderLayerRange()); 39 | } 40 | else 41 | { 42 | this.addPerChunkBoxes(boxes); 43 | } 44 | 45 | this.updateInfoHudLinesMissingChunks(this.requiredChunks); 46 | } 47 | 48 | @Override 49 | public boolean canExecute() 50 | { 51 | return super.canExecute() && this.worldSchematic != null; 52 | } 53 | 54 | @Override 55 | protected void countAtPosition(BlockPos pos) 56 | { 57 | IBlockState stateSchematic = this.worldSchematic.getBlockState(pos).getActualState(this.worldSchematic, pos); 58 | 59 | if (stateSchematic.getBlock() != Blocks.AIR) 60 | { 61 | IBlockState stateClient = this.worldClient.getBlockState(pos).getActualState(this.worldClient, pos); 62 | 63 | this.countsTotal.addTo(stateSchematic, 1); 64 | 65 | if (stateClient.getBlock() == Blocks.AIR) 66 | { 67 | this.countsMissing.addTo(stateSchematic, 1); 68 | } 69 | else if (this.ignoreState ? stateClient.getBlock() != stateSchematic.getBlock() : stateClient != stateSchematic) 70 | { 71 | this.countsMissing.addTo(stateSchematic, 1); 72 | this.countsMismatch.addTo(stateSchematic, 1); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskDelay.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import java.util.function.BooleanSupplier; 4 | 5 | import litematica.scheduler.TaskScheduler; 6 | 7 | public class TaskDelay extends TaskBase 8 | { 9 | protected final TaskScheduler scheduler; 10 | protected final TaskBase task; 11 | protected final BooleanSupplier startConditionChecker; 12 | protected final int interval; 13 | 14 | public TaskDelay(TaskBase task, int interval, TaskScheduler scheduler, BooleanSupplier startConditionChecker) 15 | { 16 | this.task = task; 17 | this.scheduler = scheduler; 18 | this.interval = interval; 19 | this.startConditionChecker = startConditionChecker; 20 | } 21 | 22 | @Override 23 | public boolean execute() 24 | { 25 | if (this.startConditionChecker.getAsBoolean()) 26 | { 27 | this.scheduler.scheduleTask(this.task, this.interval); 28 | this.finished = true; 29 | return true; 30 | } 31 | 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskDeleteArea.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import java.util.List; 4 | 5 | import net.minecraft.init.Blocks; 6 | 7 | import malilib.overlay.message.MessageDispatcher; 8 | import litematica.selection.SelectionBox; 9 | 10 | public class TaskDeleteArea extends TaskFillArea 11 | { 12 | public TaskDeleteArea(List boxes, boolean removeEntities) 13 | { 14 | super(boxes, Blocks.AIR.getDefaultState(), null, removeEntities, "litematica.gui.label.task_name.delete"); 15 | } 16 | 17 | @Override 18 | protected void printCompletionMessage() 19 | { 20 | if (this.finished) 21 | { 22 | if (this.printCompletionMessage) 23 | { 24 | MessageDispatcher.success().customHotbar().translate("litematica.message.area_cleared"); 25 | } 26 | } 27 | else 28 | { 29 | MessageDispatcher.error().customHotbar().translate("litematica.message.error.area_deletion_aborted"); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskPasteSchematicDirect.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import net.minecraft.world.WorldServer; 4 | 5 | import malilib.overlay.message.MessageDispatcher; 6 | import malilib.util.game.wrap.WorldWrap; 7 | import malilib.util.game.wrap.GameWrap; 8 | import malilib.util.position.LayerRange; 9 | import litematica.schematic.placement.SchematicPlacement; 10 | import litematica.schematic.util.SchematicPlacingUtils; 11 | 12 | public class TaskPasteSchematicDirect extends TaskBase 13 | { 14 | private final SchematicPlacement placement; 15 | private final LayerRange range; 16 | 17 | public TaskPasteSchematicDirect(SchematicPlacement placement, LayerRange range) 18 | { 19 | this.placement = placement; 20 | this.range = range; 21 | } 22 | 23 | @Override 24 | public boolean canExecute() 25 | { 26 | return this.placement.isValid() && this.placement.isSchematicLoaded() && 27 | GameWrap.getClientWorld() != null && 28 | GameWrap.getClientPlayer() != null && 29 | GameWrap.isSinglePlayer(); 30 | } 31 | 32 | @Override 33 | public boolean execute() 34 | { 35 | WorldServer world = WorldWrap.getServerWorldForClientWorld(); 36 | 37 | if (world != null && SchematicPlacingUtils.placeToWorld(this.placement, world, this.range, false)) 38 | { 39 | this.finished = true; 40 | } 41 | 42 | return true; 43 | } 44 | 45 | @Override 46 | public void stop() 47 | { 48 | if (this.finished) 49 | { 50 | if (this.printCompletionMessage) 51 | { 52 | MessageDispatcher.success().screenOrActionbar().translate("litematica.message.schematic_pasted"); 53 | } 54 | } 55 | else 56 | { 57 | MessageDispatcher.error().translate("litematica.message.error.schematic_paste_failed"); 58 | } 59 | 60 | super.stop(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/litematica/scheduler/tasks/TaskUpdateBlocks.java: -------------------------------------------------------------------------------- 1 | package litematica.scheduler.tasks; 2 | 3 | import java.util.List; 4 | 5 | import net.minecraft.block.Block; 6 | import net.minecraft.world.World; 7 | 8 | import malilib.overlay.message.MessageDispatcher; 9 | import malilib.util.position.BlockPos; 10 | import malilib.util.position.ChunkPos; 11 | import malilib.util.position.IntBoundingBox; 12 | import litematica.render.infohud.InfoHud; 13 | import litematica.selection.SelectionBox; 14 | 15 | public class TaskUpdateBlocks extends TaskProcessChunkBase 16 | { 17 | public TaskUpdateBlocks(List boxes) 18 | { 19 | super("litematica.gui.label.task_name.update_blocks"); 20 | 21 | this.addPerChunkBoxes(boxes); 22 | this.updateInfoHudLinesMissingChunks(this.requiredChunks); 23 | } 24 | 25 | @Override 26 | public boolean canExecute() 27 | { 28 | return super.canExecute() && this.mc.isSingleplayer(); 29 | } 30 | 31 | @Override 32 | protected boolean canProcessChunk(ChunkPos pos) 33 | { 34 | return this.mc.player != null && this.areSurroundingChunksLoaded(pos, this.worldClient, 1); 35 | } 36 | 37 | @Override 38 | protected boolean processChunk(ChunkPos pos) 39 | { 40 | for (IntBoundingBox box : this.getBoxesInChunk(pos)) 41 | { 42 | this.updateBlocks(box, this.world); 43 | } 44 | 45 | return true; 46 | } 47 | 48 | protected void updateBlocks(IntBoundingBox box, World world) 49 | { 50 | for (int y = box.minY; y <= box.maxY; ++y) 51 | { 52 | for (int z = box.minZ; z <= box.maxZ; ++z) 53 | { 54 | for (int x = box.minX; x <= box.maxX; ++x) 55 | { 56 | BlockPos pos = new BlockPos(x, y, z); 57 | Block block = world.getBlockState(pos).getBlock(); 58 | world.neighborChanged(pos, block, pos); 59 | } 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | protected void onStop() 66 | { 67 | this.printCompletionMessage(); 68 | InfoHud.getInstance().removeInfoHudRenderer(this, false); 69 | this.notifyListener(); 70 | } 71 | 72 | protected void printCompletionMessage() 73 | { 74 | if (this.finished) 75 | { 76 | if (this.printCompletionMessage) 77 | { 78 | MessageDispatcher.success().translate("litematica.message.blocks_updated"); 79 | } 80 | } 81 | else 82 | { 83 | MessageDispatcher.error().translate("litematica.message.error.update_blocks_aborted"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/EntityInfo.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic; 2 | 3 | import net.minecraft.nbt.NBTTagCompound; 4 | 5 | import malilib.util.game.wrap.NbtWrap; 6 | import malilib.util.position.Vec3d; 7 | 8 | public class EntityInfo 9 | { 10 | public final Vec3d pos; 11 | public final NBTTagCompound nbt; 12 | 13 | public EntityInfo(Vec3d posVec, NBTTagCompound nbt) 14 | { 15 | this.pos = posVec; 16 | this.nbt = nbt; 17 | } 18 | 19 | public EntityInfo copy() 20 | { 21 | return new EntityInfo(this.pos, NbtWrap.copy(this.nbt)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/ISchematicRegion.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import net.minecraft.nbt.NBTTagCompound; 7 | import net.minecraft.world.NextTickListEntry; 8 | 9 | import malilib.util.position.BlockPos; 10 | import malilib.util.position.Vec3i; 11 | import litematica.schematic.container.ILitematicaBlockStateContainer; 12 | 13 | public interface ISchematicRegion 14 | { 15 | /** 16 | * Returns the relative position of this region in relation to the origin of the entire schematic. 17 | * @return 18 | */ 19 | BlockPos getPosition(); 20 | 21 | /** 22 | * Returns the size of this region. 23 | * Note: The size can be negative, if the second corner is on the negative side 24 | * on any axis compared to the primary/origin corner. 25 | * @return 26 | */ 27 | Vec3i getSize(); 28 | 29 | /** 30 | * Returns the block state container used for storing the block states in this region 31 | * @return 32 | */ 33 | ILitematicaBlockStateContainer getBlockStateContainer(); 34 | 35 | /** 36 | * Returns the BlockEntity map used for this region 37 | * @return 38 | */ 39 | Map getBlockEntityMap(); 40 | 41 | /** 42 | * Returns the entity list for this region 43 | * @return 44 | */ 45 | List getEntityList(); 46 | 47 | /* 48 | * Returns the map for the scheduled Block ticks in this region 49 | */ 50 | Map getBlockTickMap(); 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/SubRegion.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic; 2 | 3 | import malilib.util.position.BlockPos; 4 | import malilib.util.position.Vec3i; 5 | 6 | public class SubRegion 7 | { 8 | public final BlockPos pos; 9 | public final Vec3i size; 10 | 11 | public SubRegion(BlockPos pos, Vec3i size) 12 | { 13 | this.pos = pos; 14 | this.size = size; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/ILitematicaBlockStateContainer.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import java.util.Map; 4 | 5 | import net.minecraft.block.state.IBlockState; 6 | 7 | import malilib.util.position.Vec3i; 8 | 9 | public interface ILitematicaBlockStateContainer 10 | { 11 | Vec3i getSize(); 12 | 13 | long getTotalBlockCount(); 14 | 15 | Map getBlockCountsMap(); 16 | 17 | ILitematicaBlockStatePalette getPalette(); 18 | 19 | IBlockState getBlockState(int x, int y, int z); 20 | 21 | void setBlockState(int x, int y, int z, IBlockState state); 22 | 23 | ILitematicaBlockStateContainer copy(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/ILitematicaBlockStatePalette.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import java.util.List; 4 | import javax.annotation.Nullable; 5 | 6 | import net.minecraft.block.state.IBlockState; 7 | 8 | public interface ILitematicaBlockStatePalette 9 | { 10 | /** 11 | * Returns the current number of entries in the palette 12 | * @return 13 | */ 14 | int getPaletteSize(); 15 | 16 | /** 17 | * Gets the palette id for the given block state and adds 18 | * the state to the palette if it doesn't exist there yet. 19 | */ 20 | int idFor(IBlockState state); 21 | 22 | /** 23 | * Gets the block state by the palette ID, if the provided ID exists. 24 | */ 25 | @Nullable 26 | IBlockState getBlockState(int id); 27 | 28 | /** 29 | * Returns the current full mappings of IDs to values. 30 | * The ID is the position in the returned list. 31 | * @return 32 | */ 33 | List getMapping(); 34 | 35 | /** 36 | * Sets the current mapping of the palette. 37 | * This is meant for reading the palette from file. 38 | * @param list 39 | * @return true if the mapping was set successfully, false if it failed 40 | */ 41 | boolean setMapping(List list); 42 | 43 | /** 44 | * Overrides the mapping for the given ID. 45 | * @param id 46 | * @param state 47 | * @return true if the ID was found in the palette and thus possible to override 48 | */ 49 | boolean overrideMapping(int id, IBlockState state); 50 | 51 | /** 52 | * Creates a copy of this palette, using the provided resize handler 53 | * @param resizeHandler 54 | * @return 55 | */ 56 | ILitematicaBlockStatePalette copy(IPaletteResizeHandler resizeHandler); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/IPaletteResizeHandler.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import net.minecraft.block.state.IBlockState; 4 | 5 | public interface IPaletteResizeHandler 6 | { 7 | /** 8 | * Called when a palette runs out of IDs in the current entry width, 9 | * and the underlying container needs to be resized for the new entry bit width. 10 | * @param newSizeBits 11 | * @param stateBeingAdded 12 | * @param oldPalette 13 | * @return the ID for the new state being added when the resize happens 14 | */ 15 | int onResize(int newSizeBits, IBlockState stateBeingAdded, ILitematicaBlockStatePalette oldPalette); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/LitematicaBitArray.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import javax.annotation.Nullable; 4 | import org.apache.commons.lang3.Validate; 5 | 6 | import malilib.util.MathUtils; 7 | 8 | public class LitematicaBitArray 9 | { 10 | /** The long array that is used to store the data for this BitArray. */ 11 | private final long[] longArray; 12 | /** Number of bits a single entry takes up */ 13 | private final int bitsPerEntry; 14 | /** 15 | * The maximum value for a single entry. This also works as a bitmask for a single entry. 16 | * For instance, if bitsPerEntry were 5, this value would be 31 (ie, {@code 0b00011111}). 17 | */ 18 | private final long maxEntryValue; 19 | /** Number of entries in this array (not the length of the long array that internally backs this array) */ 20 | private final long arraySize; 21 | 22 | public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn) 23 | { 24 | this(bitsPerEntryIn, arraySizeIn, null); 25 | } 26 | 27 | public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn) 28 | { 29 | Validate.inclusiveBetween(1L, 32L, (long) bitsPerEntryIn); 30 | this.arraySize = arraySizeIn; 31 | this.bitsPerEntry = bitsPerEntryIn; 32 | this.maxEntryValue = (1L << bitsPerEntryIn) - 1L; 33 | 34 | if (longArrayIn != null) 35 | { 36 | this.longArray = longArrayIn; 37 | } 38 | else 39 | { 40 | this.longArray = new long[(int) (MathUtils.roundUp((long) arraySizeIn * (long) bitsPerEntryIn, 64L) / 64L)]; 41 | } 42 | } 43 | 44 | public void setAt(long index, int value) 45 | { 46 | Validate.inclusiveBetween(0L, this.arraySize - 1L, (long) index); 47 | Validate.inclusiveBetween(0L, this.maxEntryValue, (long) value); 48 | long startOffset = index * (long) this.bitsPerEntry; 49 | int startArrIndex = (int) (startOffset >> 6); // startOffset / 64 50 | int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6); 51 | int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64 52 | this.longArray[startArrIndex] = this.longArray[startArrIndex] & ~(this.maxEntryValue << startBitOffset) | ((long) value & this.maxEntryValue) << startBitOffset; 53 | 54 | if (startArrIndex != endArrIndex) 55 | { 56 | int endOffset = 64 - startBitOffset; 57 | int j1 = this.bitsPerEntry - endOffset; 58 | this.longArray[endArrIndex] = this.longArray[endArrIndex] >>> j1 << j1 | ((long) value & this.maxEntryValue) >> endOffset; 59 | } 60 | } 61 | 62 | public int getAt(long index) 63 | { 64 | Validate.inclusiveBetween(0L, this.arraySize - 1L, (long) index); 65 | long startOffset = index * (long) this.bitsPerEntry; 66 | int startArrIndex = (int) (startOffset >> 6); // startOffset / 64 67 | int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6); 68 | int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64 69 | 70 | if (startArrIndex == endArrIndex) 71 | { 72 | return (int) (this.longArray[startArrIndex] >>> startBitOffset & this.maxEntryValue); 73 | } 74 | else 75 | { 76 | int endOffset = 64 - startBitOffset; 77 | return (int) ((this.longArray[startArrIndex] >>> startBitOffset | this.longArray[endArrIndex] << endOffset) & this.maxEntryValue); 78 | } 79 | } 80 | 81 | public long[] getValueCounts() 82 | { 83 | long[] counts = new long[(int) this.maxEntryValue + 1]; 84 | final long size = this.arraySize; 85 | 86 | for (long i = 0; i < size; ++i) 87 | { 88 | ++counts[this.getAt(i)]; 89 | } 90 | 91 | return counts; 92 | } 93 | 94 | public long[] getBackingLongArray() 95 | { 96 | return this.longArray; 97 | } 98 | 99 | public long size() 100 | { 101 | return this.arraySize; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/LitematicaBlockStateContainerSparse.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; 4 | 5 | import net.minecraft.block.state.IBlockState; 6 | 7 | import malilib.util.position.Vec3i; 8 | 9 | public class LitematicaBlockStateContainerSparse extends LitematicaBlockStateContainerBase 10 | { 11 | private final Long2ObjectOpenHashMap blocks = new Long2ObjectOpenHashMap<>(); 12 | 13 | public LitematicaBlockStateContainerSparse(Vec3i size) 14 | { 15 | super(size); 16 | 17 | this.palette = new VanillaStructurePalette(); 18 | this.blockCounts = new long[256]; 19 | } 20 | 21 | @Override 22 | public IBlockState getBlockState(int x, int y, int z) 23 | { 24 | long pos = (long) y << 32 | (long) (z & 0xFFFF) << 16 | (long) (x & 0xFFFF); 25 | IBlockState state = this.blocks.get(pos); 26 | return state != null ? state : AIR_BLOCK_STATE; 27 | } 28 | 29 | @Override 30 | public void setBlockState(int x, int y, int z, IBlockState state) 31 | { 32 | long pos = (long) y << 32 | (long) (z & 0xFFFF) << 16 | (long) (x & 0xFFFF); 33 | 34 | IBlockState oldState = this.blocks.put(pos, state); 35 | int id = this.palette.idFor(state); 36 | 37 | if (id >= this.blockCounts.length) 38 | { 39 | long[] oldArr = this.blockCounts; 40 | this.blockCounts = new long[oldArr.length * 2]; 41 | System.arraycopy(oldArr, 0, this.blockCounts, 0, oldArr.length); 42 | } 43 | 44 | if (oldState != state) 45 | { 46 | if (oldState != null) 47 | { 48 | int oldId = this.palette.idFor(oldState); 49 | --this.blockCounts[oldId]; 50 | } 51 | 52 | ++this.blockCounts[id]; 53 | } 54 | } 55 | 56 | @Override 57 | public LitematicaBlockStateContainerSparse copy() 58 | { 59 | LitematicaBlockStateContainerSparse copy = new LitematicaBlockStateContainerSparse(this.size); 60 | copy.blocks.putAll(this.blocks); 61 | copy.blockCounts = this.blockCounts.clone(); 62 | copy.palette = this.palette.copy(null); 63 | return copy; 64 | } 65 | 66 | @Override 67 | protected void calculateBlockCountsIfNeeded() 68 | { 69 | } 70 | 71 | public Long2ObjectOpenHashMap getBlockMap() 72 | { 73 | return this.blocks; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/LitematicaBlockStatePaletteHashMap.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import javax.annotation.Nullable; 6 | 7 | import net.minecraft.block.state.IBlockState; 8 | import net.minecraft.util.IntIdentityHashBiMap; 9 | 10 | public class LitematicaBlockStatePaletteHashMap implements ILitematicaBlockStatePalette 11 | { 12 | protected final IntIdentityHashBiMap statePaletteMap; 13 | protected final IPaletteResizeHandler paletteResizer; 14 | protected final int bits; 15 | 16 | public LitematicaBlockStatePaletteHashMap(int bitsIn, IPaletteResizeHandler paletteResizer) 17 | { 18 | this.bits = bitsIn; 19 | this.paletteResizer = paletteResizer; 20 | this.statePaletteMap = new IntIdentityHashBiMap<>(1 << bitsIn); 21 | } 22 | 23 | @Override 24 | public int idFor(IBlockState state) 25 | { 26 | int id = this.statePaletteMap.getId(state); 27 | 28 | if (id == -1) 29 | { 30 | id = this.statePaletteMap.add(state); 31 | 32 | if (id >= (1 << this.bits)) 33 | { 34 | id = this.paletteResizer.onResize(this.bits + 1, state, this); 35 | } 36 | } 37 | 38 | return id; 39 | } 40 | 41 | @Override 42 | @Nullable 43 | public IBlockState getBlockState(int indexKey) 44 | { 45 | return this.statePaletteMap.get(indexKey); 46 | } 47 | 48 | @Override 49 | public int getPaletteSize() 50 | { 51 | return this.statePaletteMap.size(); 52 | } 53 | 54 | @Override 55 | public List getMapping() 56 | { 57 | final int size = this.statePaletteMap.size(); 58 | List list = new ArrayList<>(size); 59 | 60 | for (int id = 0; id < size; ++id) 61 | { 62 | list.add(this.statePaletteMap.get(id)); 63 | } 64 | 65 | return list; 66 | } 67 | 68 | @Override 69 | public boolean setMapping(List list) 70 | { 71 | this.statePaletteMap.clear(); 72 | final int size = list.size(); 73 | 74 | for (int id = 0; id < size; ++id) 75 | { 76 | this.statePaletteMap.add(list.get(id)); 77 | } 78 | 79 | return true; 80 | } 81 | 82 | @Override 83 | public boolean overrideMapping(int id, IBlockState state) 84 | { 85 | List mapping = this.getMapping(); 86 | 87 | if (id >= 0 && id < mapping.size()) 88 | { 89 | // The put method of the map doesn't work for this, it increases the size etc. :/ 90 | mapping.set(id, state); 91 | this.setMapping(mapping); 92 | return true; 93 | } 94 | 95 | return false; 96 | } 97 | 98 | @Override 99 | public LitematicaBlockStatePaletteHashMap copy(IPaletteResizeHandler resizeHandler) 100 | { 101 | LitematicaBlockStatePaletteHashMap copy = new LitematicaBlockStatePaletteHashMap(this.bits, resizeHandler); 102 | 103 | for (int id = 0; id < this.statePaletteMap.size(); ++id) 104 | { 105 | copy.statePaletteMap.add(this.statePaletteMap.get(id)); 106 | } 107 | 108 | return copy; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/LitematicaBlockStatePaletteLinear.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import javax.annotation.Nullable; 6 | 7 | import net.minecraft.block.state.IBlockState; 8 | 9 | public class LitematicaBlockStatePaletteLinear implements ILitematicaBlockStatePalette 10 | { 11 | private final IBlockState[] states; 12 | private final IPaletteResizeHandler paletteResizer; 13 | private final int bits; 14 | private int currentSize; 15 | 16 | public LitematicaBlockStatePaletteLinear(int bitsIn, IPaletteResizeHandler paletteResizer) 17 | { 18 | this.states = new IBlockState[1 << bitsIn]; 19 | this.bits = bitsIn; 20 | this.paletteResizer = paletteResizer; 21 | } 22 | 23 | @Override 24 | public int getPaletteSize() 25 | { 26 | return this.currentSize; 27 | } 28 | 29 | @Override 30 | public int idFor(IBlockState state) 31 | { 32 | for (int i = 0; i < this.currentSize; ++i) 33 | { 34 | if (this.states[i] == state) 35 | { 36 | return i; 37 | } 38 | } 39 | 40 | final int size = this.currentSize; 41 | 42 | if (size < this.states.length) 43 | { 44 | this.states[size] = state; 45 | ++this.currentSize; 46 | return size; 47 | } 48 | else 49 | { 50 | return this.paletteResizer.onResize(this.bits + 1, state, this); 51 | } 52 | } 53 | 54 | @Override 55 | @Nullable 56 | public IBlockState getBlockState(int id) 57 | { 58 | return id >= 0 && id < this.currentSize ? this.states[id] : null; 59 | } 60 | 61 | @Override 62 | public List getMapping() 63 | { 64 | List list = new ArrayList<>(this.currentSize); 65 | 66 | for (int id = 0; id < this.currentSize; ++id) 67 | { 68 | list.add(this.states[id]); 69 | } 70 | 71 | return list; 72 | } 73 | 74 | @Override 75 | public boolean setMapping(List list) 76 | { 77 | final int size = list.size(); 78 | 79 | if (size <= this.states.length) 80 | { 81 | for (int id = 0; id < size; ++id) 82 | { 83 | this.states[id] = list.get(id); 84 | } 85 | 86 | this.currentSize = size; 87 | 88 | return true; 89 | } 90 | 91 | return false; 92 | } 93 | 94 | @Override 95 | public boolean overrideMapping(int id, IBlockState state) 96 | { 97 | if (id >= 0 && id < this.states.length) 98 | { 99 | this.states[id] = state; 100 | return true; 101 | } 102 | 103 | return false; 104 | } 105 | 106 | @Override 107 | public LitematicaBlockStatePaletteLinear copy(IPaletteResizeHandler resizeHandler) 108 | { 109 | LitematicaBlockStatePaletteLinear copy = new LitematicaBlockStatePaletteLinear(this.bits, resizeHandler); 110 | 111 | System.arraycopy(this.states, 0, copy.states, 0, this.states.length); 112 | copy.currentSize = this.currentSize; 113 | 114 | return copy; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/container/VanillaStructurePalette.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.container; 2 | 3 | import net.minecraft.block.state.IBlockState; 4 | 5 | public class VanillaStructurePalette extends LitematicaBlockStatePaletteHashMap 6 | { 7 | public VanillaStructurePalette() 8 | { 9 | super(32, null); 10 | } 11 | 12 | @Override 13 | public int idFor(IBlockState state) 14 | { 15 | int id = this.statePaletteMap.getId(state); 16 | 17 | if (id == -1) 18 | { 19 | id = this.statePaletteMap.add(state); 20 | } 21 | 22 | return id; 23 | } 24 | 25 | @Override 26 | public VanillaStructurePalette copy(IPaletteResizeHandler resizeHandler) 27 | { 28 | VanillaStructurePalette copy = new VanillaStructurePalette(); 29 | 30 | for (int id = 0; id < this.statePaletteMap.size(); ++id) 31 | { 32 | copy.statePaletteMap.add(this.statePaletteMap.get(id)); 33 | } 34 | 35 | return copy; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/projects/SchematicVersion.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.projects; 2 | 3 | import javax.annotation.Nullable; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonPrimitive; 6 | 7 | import malilib.util.data.json.JsonUtils; 8 | import malilib.util.position.BlockPos; 9 | import malilib.util.position.Vec3i; 10 | 11 | public class SchematicVersion 12 | { 13 | protected final SchematicProject project; 14 | protected final String name; 15 | protected final String fileName; 16 | protected final Vec3i areaOffset; 17 | protected final int version; 18 | protected final long timeStamp; 19 | 20 | SchematicVersion(SchematicProject project, String name, String fileName, 21 | Vec3i areaOffset, int version, long timeStamp) 22 | { 23 | this.project = project; 24 | this.name = name; 25 | this.fileName = fileName; 26 | this.areaOffset = areaOffset; 27 | this.version = version; 28 | this.timeStamp = timeStamp; 29 | } 30 | 31 | public SchematicProject getProject() 32 | { 33 | return this.project; 34 | } 35 | 36 | public String getName() 37 | { 38 | return this.name; 39 | } 40 | 41 | public String getFileName() 42 | { 43 | return this.fileName; 44 | } 45 | 46 | public Vec3i getAreaOffset() 47 | { 48 | return this.areaOffset; 49 | } 50 | 51 | public int getVersion() 52 | { 53 | return this.version; 54 | } 55 | 56 | public long getTimeStamp() 57 | { 58 | return this.timeStamp; 59 | } 60 | 61 | public JsonObject toJson() 62 | { 63 | JsonObject obj = new JsonObject(); 64 | 65 | obj.add("name", new JsonPrimitive(this.name)); 66 | obj.add("file_name", new JsonPrimitive(this.fileName)); 67 | obj.add("area_offset", JsonUtils.blockPosToJson(this.areaOffset)); 68 | obj.add("version", new JsonPrimitive(this.version)); 69 | obj.add("timestamp", new JsonPrimitive(this.timeStamp)); 70 | 71 | return obj; 72 | } 73 | 74 | @Nullable 75 | public static SchematicVersion fromJson(JsonObject obj, SchematicProject project) 76 | { 77 | BlockPos areaOffset = JsonUtils.getBlockPos(obj, "area_offset"); 78 | 79 | if (areaOffset != null && 80 | JsonUtils.hasString(obj, "name") && 81 | JsonUtils.hasString(obj, "file_name")) 82 | { 83 | String name = JsonUtils.getString(obj, "name"); 84 | String fileName = JsonUtils.getString(obj, "file_name"); 85 | int version = JsonUtils.getInteger(obj, "version"); 86 | long timeStamp = JsonUtils.getLong(obj, "timestamp"); 87 | 88 | return new SchematicVersion(project, name, fileName, areaOffset, version, timeStamp); 89 | } 90 | 91 | return null; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/util/SchematicSaveSettings.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.util; 2 | 3 | import malilib.util.data.BooleanStorageWithDefault; 4 | import malilib.util.data.SimpleBooleanStorageWithDefault; 5 | 6 | public class SchematicSaveSettings 7 | { 8 | public final BooleanStorageWithDefault saveBlocks = new SimpleBooleanStorageWithDefault(true); 9 | public final BooleanStorageWithDefault saveBlockEntities = new SimpleBooleanStorageWithDefault(true); 10 | public final BooleanStorageWithDefault saveEntities = new SimpleBooleanStorageWithDefault(true); 11 | public final BooleanStorageWithDefault saveBlockTicks = new SimpleBooleanStorageWithDefault(true); 12 | 13 | public final BooleanStorageWithDefault exposedBlocksOnly = new SimpleBooleanStorageWithDefault(false); 14 | public final BooleanStorageWithDefault saveFromNormalWorld = new SimpleBooleanStorageWithDefault(true); 15 | public final BooleanStorageWithDefault saveFromSchematicWorld = new SimpleBooleanStorageWithDefault(false); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/verifier/BlockPairTypePosition.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.verifier; 2 | 3 | import malilib.util.position.BlockPos; 4 | import malilib.util.position.PositionUtils; 5 | 6 | public class BlockPairTypePosition 7 | { 8 | public final VerifierResultType type; 9 | public final BlockStatePair pair; 10 | public final long posLong; 11 | 12 | public BlockPairTypePosition(BlockStatePair pair, long posLong) 13 | { 14 | this.pair = pair; 15 | this.type = pair.type; 16 | this.posLong = posLong; 17 | } 18 | 19 | public BlockPos getBlockPos() 20 | { 21 | return BlockPos.fromPacked(this.posLong); 22 | } 23 | 24 | public static BlockPairTypePosition of(BlockStatePair pair, long chunkPosLong, int chunkRelativePosition) 25 | { 26 | long posLong = PositionUtils.getPackedAbsolutePosition(chunkPosLong, chunkRelativePosition); 27 | return new BlockPairTypePosition(pair, posLong); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/verifier/BlockPairTypePositionComparator.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.verifier; 2 | 3 | import java.util.Comparator; 4 | 5 | import malilib.util.position.BlockPos; 6 | import malilib.util.position.PositionUtils; 7 | 8 | class BlockPairTypePositionComparator implements Comparator 9 | { 10 | protected final int referencePosX; 11 | protected final int referencePosY; 12 | protected final int referencePosZ; 13 | protected final boolean closestFirst; 14 | 15 | public BlockPairTypePositionComparator(BlockPos referencePos, boolean closestFirst) 16 | { 17 | this.closestFirst = closestFirst; 18 | this.referencePosX = referencePos.getX(); 19 | this.referencePosY = referencePos.getY(); 20 | this.referencePosZ = referencePos.getZ(); 21 | } 22 | 23 | @Override 24 | public int compare(BlockPairTypePosition pos1, BlockPairTypePosition pos2) 25 | { 26 | double dist1 = this.getSquareDistance(pos1.posLong); 27 | double dist2 = this.getSquareDistance(pos2.posLong); 28 | 29 | if (dist1 == dist2) 30 | { 31 | return 0; 32 | } 33 | 34 | return dist1 < dist2 == this.closestFirst ? -1 : 1; 35 | } 36 | 37 | protected double getSquareDistance(long posLong) 38 | { 39 | int diffX = PositionUtils.unpackX(posLong) - this.referencePosX; 40 | int diffY = PositionUtils.unpackY(posLong) - this.referencePosY; 41 | int diffZ = PositionUtils.unpackZ(posLong) - this.referencePosZ; 42 | return diffX * diffX + diffY * diffY + diffZ * diffZ; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/verifier/BlockStatePair.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.verifier; 2 | 3 | import net.minecraft.block.state.IBlockState; 4 | 5 | public class BlockStatePair 6 | { 7 | public final VerifierResultType type; 8 | public final IBlockState expectedState; 9 | public final IBlockState foundState; 10 | 11 | public BlockStatePair(VerifierResultType type, IBlockState expectedState, IBlockState foundState) 12 | { 13 | this.type = type; 14 | this.expectedState = expectedState; 15 | this.foundState = foundState; 16 | } 17 | 18 | @Override 19 | public boolean equals(Object o) 20 | { 21 | if (this == o) { return true; } 22 | if (o == null || this.getClass() != o.getClass()) { return false; } 23 | 24 | BlockStatePair that = (BlockStatePair) o; 25 | 26 | return this.type == that.type && 27 | this.expectedState == that.expectedState && 28 | this.foundState == that.foundState; 29 | } 30 | 31 | @Override 32 | public int hashCode() 33 | { 34 | int result = this.type.hashCode(); 35 | result = 31 * result + this.expectedState.hashCode(); 36 | result = 31 * result + this.foundState.hashCode(); 37 | return result; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/verifier/BlockStatePairCount.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.verifier; 2 | 3 | import malilib.util.game.wrap.RegistryUtils; 4 | 5 | public class BlockStatePairCount 6 | { 7 | protected final BlockStatePair pair; 8 | protected final String expectedBlockDisplayName; 9 | protected final String foundBlockDisplayName; 10 | public final int count; 11 | 12 | public BlockStatePairCount(BlockStatePair pair, int count) 13 | { 14 | this.pair = pair; 15 | this.expectedBlockDisplayName = RegistryUtils.getBlockIdStr(pair.expectedState); 16 | this.foundBlockDisplayName = RegistryUtils.getBlockIdStr(pair.foundState); 17 | this.count = count; 18 | } 19 | 20 | public BlockStatePair getPair() 21 | { 22 | return this.pair; 23 | } 24 | 25 | public String getExpectedBlockDisplayName() 26 | { 27 | return this.expectedBlockDisplayName; 28 | } 29 | 30 | public String getFoundBlockDisplayName() 31 | { 32 | return this.foundBlockDisplayName; 33 | } 34 | 35 | public int getCount() 36 | { 37 | return this.count; 38 | } 39 | 40 | public static BlockStatePairCount of(BlockStatePair pair, int count) 41 | { 42 | return new BlockStatePairCount(pair, count); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/verifier/VerifierResultType.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.verifier; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import net.minecraft.block.state.IBlockState; 6 | 7 | import malilib.config.option.DualColorConfig; 8 | import malilib.util.StringUtils; 9 | import malilib.util.data.Color4f; 10 | import litematica.config.Configs; 11 | 12 | public enum VerifierResultType 13 | { 14 | CORRECT_STATE ("litematica.name.schematic_verifier.correct_state", Configs.Colors.VERIFIER_CORRECT), 15 | EXTRA ("litematica.name.schematic_verifier.extra", Configs.Colors.VERIFIER_EXTRA), 16 | MISSING ("litematica.name.schematic_verifier.missing", Configs.Colors.VERIFIER_MISSING), 17 | WRONG_BLOCK ("litematica.name.schematic_verifier.wrong_blocks", Configs.Colors.VERIFIER_WRONG_BLOCK), 18 | WRONG_STATE ("litematica.name.schematic_verifier.wrong_state", Configs.Colors.VERIFIER_WRONG_STATE); 19 | 20 | public static final ImmutableList INCORRECT_TYPES = ImmutableList.of( 21 | VerifierResultType.WRONG_BLOCK, 22 | VerifierResultType.WRONG_STATE, 23 | VerifierResultType.MISSING, 24 | VerifierResultType.EXTRA 25 | ); 26 | 27 | public static final ImmutableList SELECTABLE_CATEGORIES = ImmutableList.of( 28 | VerifierResultType.WRONG_BLOCK, 29 | VerifierResultType.WRONG_STATE, 30 | VerifierResultType.MISSING, 31 | VerifierResultType.EXTRA, 32 | VerifierResultType.CORRECT_STATE 33 | ); 34 | 35 | private final String translationKey; 36 | private final DualColorConfig colorConfig; 37 | 38 | VerifierResultType(String translationKey, DualColorConfig colorConfig) 39 | { 40 | this.translationKey = translationKey; 41 | this.colorConfig = colorConfig; 42 | } 43 | 44 | public Color4f getOverlayColor() 45 | { 46 | return this.colorConfig.getFirstColor(); 47 | } 48 | 49 | public int getTextColor() 50 | { 51 | return this.colorConfig.getSecondColorInt(); 52 | } 53 | 54 | public String getDisplayName() 55 | { 56 | return StringUtils.translate(this.translationKey); 57 | } 58 | 59 | public String getCategoryWidgetTranslationKey() 60 | { 61 | return this.translationKey + ".widget"; 62 | } 63 | 64 | public static VerifierResultType from(IBlockState expectedState, IBlockState foundState) 65 | { 66 | VerifierResultType type = null; 67 | 68 | if (foundState != expectedState) 69 | { 70 | if (expectedState != SchematicVerifier.AIR) 71 | { 72 | if (foundState == SchematicVerifier.AIR) 73 | { 74 | type = VerifierResultType.MISSING; 75 | } 76 | else if (expectedState.getBlock() != foundState.getBlock()) 77 | { 78 | type = VerifierResultType.WRONG_BLOCK; 79 | } 80 | else 81 | { 82 | type = VerifierResultType.WRONG_STATE; 83 | } 84 | } 85 | else if (Configs.Visuals.IGNORE_EXISTING_FLUIDS.getBooleanValue() == false || 86 | foundState.getMaterial().isLiquid() == false) 87 | { 88 | type = VerifierResultType.EXTRA; 89 | } 90 | } 91 | 92 | if (type == null) 93 | { 94 | type = VerifierResultType.CORRECT_STATE; 95 | } 96 | 97 | return type; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/litematica/schematic/verifier/VerifierStatus.java: -------------------------------------------------------------------------------- 1 | package litematica.schematic.verifier; 2 | 3 | import malilib.util.data.RunStatus; 4 | 5 | public class VerifierStatus 6 | { 7 | public final RunStatus status; 8 | public final int processedChunks; 9 | public final int totalChunks; 10 | public final int totalBlocks; 11 | public final int correctBlocks; 12 | 13 | public VerifierStatus(RunStatus status, int processedChunks, int totalChunks, int totalBlocks, int correctBlocks) 14 | { 15 | this.status = status; 16 | this.processedChunks = processedChunks; 17 | this.totalChunks = totalChunks; 18 | this.totalBlocks = totalBlocks; 19 | this.correctBlocks = correctBlocks; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/AreaSelectionSimple.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import javax.annotation.Nullable; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | 8 | import malilib.util.data.json.JsonUtils; 9 | import malilib.util.position.BlockPos; 10 | 11 | public class AreaSelectionSimple extends AreaSelection 12 | { 13 | public AreaSelectionSimple(boolean createDefaultBox) 14 | { 15 | if (createDefaultBox) 16 | { 17 | this.createDefaultBoxIfNeeded(); 18 | } 19 | } 20 | 21 | @Override 22 | public boolean setSelectedSelectionBox(String name) 23 | { 24 | // NO-OP 25 | return false; 26 | } 27 | 28 | @Override 29 | @Nullable 30 | public String createNewSelectionBox(BlockPos pos1, String nameIn) 31 | { 32 | // NO-OP 33 | return null; 34 | } 35 | 36 | @Override 37 | public boolean addSelectionBox(SelectionBox box, boolean replace) 38 | { 39 | // NO-OP 40 | return false; 41 | } 42 | 43 | @Override 44 | public void removeAllSelectionBoxes() 45 | { 46 | // NO-OP 47 | } 48 | 49 | @Override 50 | public boolean removeSelectionBox(String name) 51 | { 52 | // NO-OP 53 | return false; 54 | } 55 | 56 | @Override 57 | public boolean removeSelectedBox() 58 | { 59 | // NO-OP 60 | return false; 61 | } 62 | 63 | private void createDefaultBoxIfNeeded() 64 | { 65 | if (this.selectionBoxes.size() != 1) 66 | { 67 | this.selectionBoxes.clear(); 68 | SelectionBox box = new SelectionBox(BlockPos.ORIGIN, BlockPos.ORIGIN, this.getName()); 69 | this.selectionBoxes.put(box.getName(), box); 70 | this.selectedBoxName = box.getName(); 71 | } 72 | else if (this.selectedBoxName == null || this.selectionBoxes.get(this.selectedBoxName) == null) 73 | { 74 | this.selectedBoxName = this.selectionBoxes.keySet().iterator().next(); 75 | } 76 | } 77 | 78 | @Override 79 | public AreaSelectionSimple copy() 80 | { 81 | return fromJson(this.toJson()); 82 | } 83 | 84 | public static AreaSelectionSimple fromJson(JsonObject obj) 85 | { 86 | AreaSelectionSimple area = new AreaSelectionSimple(false); 87 | 88 | if (JsonUtils.hasArray(obj, "boxes")) 89 | { 90 | JsonArray arr = obj.get("boxes").getAsJsonArray(); 91 | 92 | if (arr.size() > 0) 93 | { 94 | // The simple area will only have one box 95 | JsonElement el = arr.get(0); 96 | 97 | if (el.isJsonObject()) 98 | { 99 | SelectionBox box = SelectionBox.fromJson(el.getAsJsonObject()); 100 | 101 | if (box != null) 102 | { 103 | area.selectionBoxes.put(box.getName(), box); 104 | area.selectedBoxName = box.getName(); 105 | } 106 | } 107 | } 108 | } 109 | 110 | if (JsonUtils.hasString(obj, "name")) 111 | { 112 | area.setName(obj.get("name").getAsString()); 113 | } 114 | 115 | BlockPos pos = JsonUtils.getBlockPos(obj, "origin"); 116 | 117 | if (pos != null) 118 | { 119 | area.setManualOrigin(pos); 120 | } 121 | else 122 | { 123 | area.updateAutomaticOrigin(); 124 | } 125 | 126 | // Make sure the simple area has exactly one box, and that it's selected 127 | area.createDefaultBoxIfNeeded(); 128 | 129 | return area; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/AreaSelectionType.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import malilib.config.value.BaseOptionListConfigValue; 6 | 7 | public class AreaSelectionType extends BaseOptionListConfigValue 8 | { 9 | public static final AreaSelectionType SIMPLE = new AreaSelectionType("simple", "litematica.label.area_selection.selection_type.simple"); 10 | public static final AreaSelectionType MULTI_REGION = new AreaSelectionType("multi_region", "litematica.label.area_selection.selection_type.multi_region"); 11 | 12 | public static final ImmutableList VALUES = ImmutableList.of(SIMPLE, MULTI_REGION); 13 | 14 | public AreaSelectionType(String name, String translationKey) 15 | { 16 | super(name, translationKey); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/BoxCorner.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import java.util.function.BiConsumer; 4 | import java.util.function.Function; 5 | 6 | import malilib.util.position.BlockPos; 7 | 8 | public enum BoxCorner 9 | { 10 | NONE ((box, pos) -> {}, box -> null), 11 | CORNER_1 (CornerDefinedBox::setCorner1, CornerDefinedBox::getCorner1), 12 | CORNER_2 (CornerDefinedBox::setCorner2, CornerDefinedBox::getCorner2); 13 | 14 | public final BiConsumer cornerSetter; 15 | public final Function cornerGetter; 16 | 17 | BoxCorner(BiConsumer cornerSetter, 18 | Function cornerGetter) 19 | { 20 | this.cornerSetter = cornerSetter; 21 | this.cornerGetter = cornerGetter; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/CornerDefinedBox.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import javax.annotation.Nullable; 4 | import com.google.gson.JsonObject; 5 | 6 | import malilib.util.data.json.JsonUtils; 7 | import malilib.util.position.BlockPos; 8 | import malilib.util.position.IntBoundingBox; 9 | import malilib.util.position.Vec3i; 10 | import litematica.util.PositionUtils; 11 | 12 | public class CornerDefinedBox 13 | { 14 | protected BlockPos corner1; 15 | protected BlockPos corner2; 16 | protected Vec3i size; 17 | 18 | public CornerDefinedBox() 19 | { 20 | this(BlockPos.ORIGIN, BlockPos.ORIGIN); 21 | } 22 | 23 | public CornerDefinedBox(BlockPos corner1, BlockPos corner2) 24 | { 25 | this.corner1 = corner1; 26 | this.corner2 = corner2; 27 | 28 | this.updateSize(); 29 | } 30 | 31 | public Vec3i getSize() 32 | { 33 | return this.size; 34 | } 35 | 36 | public BlockPos getCorner1() 37 | { 38 | return this.corner1; 39 | } 40 | 41 | public BlockPos getCorner2() 42 | { 43 | return this.corner2; 44 | } 45 | 46 | public void setCorner1(BlockPos pos) 47 | { 48 | this.corner1 = pos; 49 | this.updateSize(); 50 | } 51 | 52 | public void setCorner2(BlockPos pos) 53 | { 54 | this.corner2 = pos; 55 | this.updateSize(); 56 | } 57 | 58 | public BlockPos getCornerPosition(BoxCorner corner) 59 | { 60 | return corner.cornerGetter.apply(this); 61 | } 62 | 63 | protected void setCornerPosition(BoxCorner corner, BlockPos pos) 64 | { 65 | corner.cornerSetter.accept(this, pos); 66 | } 67 | 68 | public IntBoundingBox asIntBoundingBox() 69 | { 70 | return IntBoundingBox.createProper(this.corner1, this.corner2); 71 | } 72 | 73 | public CornerDefinedBox copy() 74 | { 75 | return new CornerDefinedBox(this.corner1, this.corner2); 76 | } 77 | 78 | protected void updateSize() 79 | { 80 | this.size = PositionUtils.getAreaSize(this.corner1, this.corner2); 81 | } 82 | 83 | @Nullable 84 | public JsonObject toJson() 85 | { 86 | JsonObject obj = new JsonObject(); 87 | 88 | obj.add("pos1", JsonUtils.blockPosToJson(this.corner1)); 89 | obj.add("pos2", JsonUtils.blockPosToJson(this.corner2)); 90 | 91 | return obj; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/SelectionBox.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import java.util.function.Consumer; 4 | import javax.annotation.Nullable; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonPrimitive; 7 | 8 | import malilib.util.data.json.JsonUtils; 9 | import malilib.util.position.BlockPos; 10 | import malilib.util.position.Direction; 11 | 12 | public class SelectionBox extends CornerDefinedBox 13 | { 14 | protected String name; 15 | protected BoxCorner selectedCorner = BoxCorner.NONE; 16 | 17 | public SelectionBox() 18 | { 19 | this(BlockPos.ORIGIN, BlockPos.ORIGIN, "Unnamed"); 20 | } 21 | 22 | public SelectionBox(BlockPos pos1, BlockPos pos2, String name) 23 | { 24 | super(pos1, pos2); 25 | 26 | this.name = name; 27 | } 28 | 29 | public String getName() 30 | { 31 | return this.name; 32 | } 33 | 34 | public void setName(String name) 35 | { 36 | this.name = name; 37 | } 38 | 39 | public boolean isCornerSelected(BoxCorner corner) 40 | { 41 | return this.selectedCorner == corner; 42 | } 43 | 44 | public BoxCorner getSelectedCorner() 45 | { 46 | return this.selectedCorner; 47 | } 48 | 49 | public void setSelectedCorner(BoxCorner corner) 50 | { 51 | this.selectedCorner = corner; 52 | } 53 | 54 | public void offsetSelectedCorner(Direction direction, int amount) 55 | { 56 | BoxCorner corner = this.selectedCorner; 57 | 58 | if (corner == BoxCorner.NONE || corner == BoxCorner.CORNER_1) 59 | { 60 | this.setCorner1(this.corner1.offset(direction, amount)); 61 | } 62 | 63 | if (corner == BoxCorner.NONE || corner == BoxCorner.CORNER_2) 64 | { 65 | this.setCorner2(this.corner2.offset(direction, amount)); 66 | } 67 | } 68 | 69 | @Override 70 | public SelectionBox copy() 71 | { 72 | SelectionBox box = new SelectionBox(this.corner1, this.corner2, this.name); 73 | box.setSelectedCorner(this.selectedCorner); 74 | return box; 75 | } 76 | 77 | @Override 78 | @Nullable 79 | public JsonObject toJson() 80 | { 81 | JsonObject obj = super.toJson(); 82 | 83 | if (obj != null) 84 | { 85 | obj.add("name", new JsonPrimitive(this.name)); 86 | } 87 | 88 | return obj; 89 | } 90 | 91 | @Nullable 92 | public static SelectionBox fromJson(JsonObject obj) 93 | { 94 | if (JsonUtils.hasString(obj, "name")) 95 | { 96 | BlockPos pos1 = JsonUtils.getBlockPos(obj, "pos1"); 97 | BlockPos pos2 = JsonUtils.getBlockPos(obj, "pos2"); 98 | 99 | if (pos1 != null && pos2 != null) 100 | { 101 | return new SelectionBox(pos1, pos2, obj.get("name").getAsString()); 102 | } 103 | } 104 | 105 | return null; 106 | } 107 | 108 | public static void fromJson(JsonObject obj, Consumer boxConsumer) 109 | { 110 | SelectionBox box = fromJson(obj); 111 | 112 | if (box != null) 113 | { 114 | boxConsumer.accept(box); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/SlicedBox.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import malilib.util.MathUtils; 4 | import malilib.util.position.Direction; 5 | 6 | public class SlicedBox extends CornerDefinedBox 7 | { 8 | private Direction sliceDirection = Direction.EAST; 9 | private int sliceStart = 0; 10 | private int sliceEnd = 1; 11 | private int sliceRepeatCount; 12 | 13 | public Direction getSliceDirection() 14 | { 15 | return this.sliceDirection; 16 | } 17 | 18 | /** 19 | * @return the relative start offset from pos1 along the slice direction axis 20 | */ 21 | public int getSliceStart() 22 | { 23 | return this.sliceStart; 24 | } 25 | 26 | /** 27 | * @return the relative end offset (inclusive) from pos1 along the slice direction axis 28 | */ 29 | public int getSliceEnd() 30 | { 31 | return this.sliceEnd; 32 | } 33 | 34 | public int getSliceLength() 35 | { 36 | return Math.abs(this.sliceEnd - this.sliceStart) + 1; 37 | } 38 | 39 | public int getSliceRepeatCount() 40 | { 41 | return this.sliceRepeatCount; 42 | } 43 | 44 | public int getMaxSliceLength() 45 | { 46 | switch (this.sliceDirection.getAxis()) 47 | { 48 | case X: return this.getSize().getX(); 49 | case Y: return this.getSize().getY(); 50 | case Z: return this.getSize().getZ(); 51 | default: return 1; 52 | } 53 | } 54 | 55 | public void setSliceDirection(Direction sliceDirection) 56 | { 57 | this.sliceDirection = sliceDirection; 58 | } 59 | 60 | public void setSliceStart(int sliceStart) 61 | { 62 | this.sliceStart = MathUtils.clamp(sliceStart, 0, this.getMaxSliceLength() - 1); 63 | } 64 | 65 | public void setSliceEnd(int sliceEnd) 66 | { 67 | this.sliceEnd = MathUtils.clamp(sliceEnd, 0, this.getMaxSliceLength() - 1); 68 | } 69 | 70 | public void setSliceRepeatCount(int sliceRepeatCount) 71 | { 72 | this.sliceRepeatCount = sliceRepeatCount; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/litematica/selection/ToolSelectionMode.java: -------------------------------------------------------------------------------- 1 | package litematica.selection; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import malilib.config.value.BaseOptionListConfigValue; 6 | 7 | public class ToolSelectionMode extends BaseOptionListConfigValue 8 | { 9 | public static final ToolSelectionMode EXPAND = new ToolSelectionMode("expand", "litematica.label.area_selection.tool_selection_mode.expand"); 10 | public static final ToolSelectionMode CORNERS = new ToolSelectionMode("corners", "litematica.label.area_selection.tool_selection_mode.corners"); 11 | 12 | public static final ImmutableList VALUES = ImmutableList.of(CORNERS, EXPAND); 13 | 14 | public ToolSelectionMode(String name, String translationKey) 15 | { 16 | super(name, translationKey); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/litematica/task/CreateSchematicTask.java: -------------------------------------------------------------------------------- 1 | package litematica.task; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | import java.util.UUID; 6 | import com.google.common.collect.ImmutableMap; 7 | 8 | import malilib.listener.TaskCompletionListener; 9 | import malilib.overlay.message.MessageDispatcher; 10 | import malilib.util.position.BlockPos; 11 | import malilib.util.position.ChunkPos; 12 | import malilib.util.position.IntBoundingBox; 13 | import litematica.render.infohud.InfoHud; 14 | import litematica.scheduler.tasks.TaskProcessChunkBase; 15 | import litematica.schematic.ISchematic; 16 | import litematica.schematic.util.SchematicCreationUtils; 17 | import litematica.selection.AreaSelection; 18 | import litematica.selection.SelectionBox; 19 | import litematica.util.PositionUtils; 20 | 21 | public class CreateSchematicTask extends TaskProcessChunkBase 22 | { 23 | protected final ImmutableMap subRegions; 24 | protected final Set existingEntities = new HashSet<>(); 25 | protected final ISchematic schematic; 26 | protected final BlockPos origin; 27 | protected final boolean ignoreEntities; 28 | 29 | public CreateSchematicTask(ISchematic schematic, 30 | AreaSelection area, 31 | boolean ignoreEntities, 32 | TaskCompletionListener listener) 33 | { 34 | super("litematica.hud.task_name.save_schematic"); 35 | 36 | this.ignoreEntities = ignoreEntities; 37 | this.schematic = schematic; 38 | this.origin = area.getEffectiveOrigin(); 39 | this.subRegions = area.getAllSelectionBoxesMap(); 40 | this.setCompletionListener(listener); 41 | 42 | this.addPerChunkBoxes(area.getAllSelectionBoxes()); 43 | this.updateInfoHudLinesMissingChunks(this.requiredChunks); 44 | } 45 | 46 | @Override 47 | protected boolean canProcessChunk(ChunkPos pos) 48 | { 49 | return this.areSurroundingChunksLoaded(pos, this.worldClient, 1); 50 | } 51 | 52 | @Override 53 | protected boolean processChunk(ChunkPos pos) 54 | { 55 | ImmutableMap volumes = PositionUtils.getBoxesWithinChunk(pos.x, pos.z, this.subRegions); 56 | SchematicCreationUtils.takeBlocksFromWorldWithinChunk(this.schematic, this.world, volumes, this.subRegions); 57 | 58 | if (this.ignoreEntities == false) 59 | { 60 | SchematicCreationUtils.takeEntitiesFromWorldWithinChunk(this.schematic, this.world, volumes, 61 | this.subRegions, this.existingEntities); 62 | } 63 | 64 | return true; 65 | } 66 | 67 | @Override 68 | protected void onStop() 69 | { 70 | if (this.finished == false) 71 | { 72 | MessageDispatcher.warning().translate("litematica.message.error.schematic_save_interrupted"); 73 | } 74 | 75 | InfoHud.getInstance().removeInfoHudRenderer(this, false); 76 | 77 | this.notifyListener(); 78 | } 79 | 80 | /* 81 | public static class SaveSettings 82 | { 83 | public final SimpleBooleanStorage saveBlocks = new SimpleBooleanStorage(true); 84 | public final SimpleBooleanStorage saveBlockEntities = new SimpleBooleanStorage(true); 85 | public final SimpleBooleanStorage saveEntities = new SimpleBooleanStorage(true); 86 | public final SimpleBooleanStorage saveScheduledBlockTicks = new SimpleBooleanStorage(true); 87 | public final SimpleBooleanStorage saveFromClientWorld = new SimpleBooleanStorage(true); 88 | public final SimpleBooleanStorage saveFromSchematicWorld = new SimpleBooleanStorage(false); 89 | public final SimpleBooleanStorage exposedBlocksOnly = new SimpleBooleanStorage(false); 90 | } 91 | */ 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/litematica/tool/ToolModeData.java: -------------------------------------------------------------------------------- 1 | package litematica.tool; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonPrimitive; 5 | 6 | import malilib.util.data.json.JsonUtils; 7 | 8 | public class ToolModeData 9 | { 10 | // TODO change these into a nicer system with something like INamedJsonSerializable 11 | public static final ActionTargetData DELETE = new ActionTargetData(); 12 | public static final ActionTargetData UPDATE_BLOCKS = new ActionTargetData(); 13 | 14 | public static class ActionTargetData 15 | { 16 | private boolean usePlacement; 17 | 18 | public boolean getUsePlacement() 19 | { 20 | return this.usePlacement; 21 | } 22 | 23 | public void toggleUsePlacement() 24 | { 25 | this.usePlacement = ! this.usePlacement; 26 | } 27 | 28 | public JsonObject toJson() 29 | { 30 | JsonObject obj = new JsonObject(); 31 | obj.add("use_placement", new JsonPrimitive(this.usePlacement)); 32 | return obj; 33 | } 34 | 35 | public void fromJson(JsonObject obj) 36 | { 37 | this.usePlacement = JsonUtils.getBoolean(obj, "use_placement"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/litematica/util/LitematicaDirectories.java: -------------------------------------------------------------------------------- 1 | package litematica.util; 2 | 3 | import java.nio.file.Path; 4 | 5 | import malilib.config.option.BooleanAndFileConfig.BooleanAndFile; 6 | import malilib.config.util.ConfigUtils; 7 | import malilib.overlay.message.MessageDispatcher; 8 | import malilib.util.FileUtils; 9 | import malilib.util.StringUtils; 10 | import litematica.Reference; 11 | import litematica.config.Configs; 12 | 13 | public class LitematicaDirectories 14 | { 15 | public static void createDirectoriesIfMissingOrPrintError(Path dir) 16 | { 17 | if (FileUtils.createDirectoriesIfMissing(dir) == false) 18 | { 19 | String key = "litematica.message.error.failed_to_create_directory"; 20 | MessageDispatcher.error().console().translate(key, dir.toAbsolutePath()); 21 | } 22 | } 23 | 24 | public static Path getDataDirectory(String dirName) 25 | { 26 | return FileUtils.getMinecraftDirectory().resolve("litematica").resolve(dirName); 27 | } 28 | 29 | protected static Path getPerWorldDataDirectory(String dirName) 30 | { 31 | String worldName = StringUtils.getWorldOrServerNameOrDefault("__fallback"); 32 | Path dir = getDataDirectory(dirName).resolve(worldName); 33 | return dir; 34 | } 35 | 36 | public static Path getPerWorldDataBaseDirectory() 37 | { 38 | return LitematicaDirectories.getDataDirectory("world_specific_data"); 39 | } 40 | 41 | public static Path getAreaSelectionsBaseDirectory() 42 | { 43 | String name = StringUtils.getWorldOrServerName(); 44 | Path baseDir = getDataDirectory("area_selections"); 45 | Path dir; 46 | 47 | if (Configs.Generic.AREAS_PER_WORLD.getBooleanValue() && name != null) 48 | { 49 | // The 'area_selections' sub-directory is to prevent showing the world name or server IP in the browser, 50 | // as the root directory name is shown in the navigation widget 51 | dir = baseDir.resolve("per_world").resolve(name); 52 | } 53 | else 54 | { 55 | dir = baseDir.resolve("global"); 56 | } 57 | 58 | createDirectoriesIfMissingOrPrintError(dir); 59 | 60 | return dir; 61 | } 62 | 63 | public static Path getModConfigDirectory() 64 | { 65 | return ConfigUtils.getConfigDirectory().resolve(Reference.MOD_ID); 66 | } 67 | 68 | public static Path getMaterialListDirectory() 69 | { 70 | return getDataDirectory("material_list"); 71 | } 72 | 73 | public static Path getPlacementSaveFilesDirectory() 74 | { 75 | Path dir = getPerWorldDataDirectory("placements"); 76 | createDirectoriesIfMissingOrPrintError(dir); 77 | return dir; 78 | } 79 | 80 | public static Path getDefaultSchematicDirectory() 81 | { 82 | return FileUtils.getMinecraftDirectory().resolve("schematics"); 83 | } 84 | 85 | public static Path getSchematicsBaseDirectory() 86 | { 87 | BooleanAndFile value = Configs.Generic.CUSTOM_SCHEMATIC_DIRECTORY.getValue(); 88 | boolean useCustom = value.booleanValue; 89 | Path dir = null; 90 | 91 | if (useCustom) 92 | { 93 | dir = value.fileValue; 94 | } 95 | 96 | if (useCustom == false || dir == null) 97 | { 98 | dir = getDefaultSchematicDirectory(); 99 | } 100 | 101 | createDirectoriesIfMissingOrPrintError(dir); 102 | 103 | return dir; 104 | } 105 | 106 | public static Path getVCSProjectsBaseDirectory() 107 | { 108 | Path dir = getSchematicsBaseDirectory().resolve("VCS"); 109 | createDirectoriesIfMissingOrPrintError(dir); 110 | return dir; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/litematica/util/Nags.java: -------------------------------------------------------------------------------- 1 | package litematica.util; 2 | 3 | import malilib.overlay.message.NagHelper; 4 | import litematica.config.Configs; 5 | 6 | public class Nags 7 | { 8 | public static final NagHelper HELPER = new NagHelper(Configs.Nags.SHOW_HELPFUL_REMINDERS, 9 | Configs.Nags.SHOW_REMINDERS_DISABLE, 10 | Configs.Nags.SHOW_NEW_USER_EXTRA_NAGS, 11 | "litematica.message.nag.info.disable_helpful_nags", 12 | "litematica.message.nag.new_user_extra_nag"); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/litematica/util/value/BlockInfoAlignment.java: -------------------------------------------------------------------------------- 1 | package litematica.util.value; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import malilib.config.value.BaseOptionListConfigValue; 6 | import malilib.config.value.VerticalAlignment; 7 | 8 | public class BlockInfoAlignment extends BaseOptionListConfigValue 9 | { 10 | public static final BlockInfoAlignment CENTER = new BlockInfoAlignment("center", "litematica.label.alignment.center", VerticalAlignment.CENTER); 11 | public static final BlockInfoAlignment TOP_CENTER = new BlockInfoAlignment("top_center", "litematica.label.alignment.top_center", VerticalAlignment.TOP); 12 | 13 | public static final ImmutableList VALUES = ImmutableList.of(TOP_CENTER, CENTER); 14 | 15 | protected final VerticalAlignment verticalAlign; 16 | 17 | public BlockInfoAlignment(String name, String translationKey, VerticalAlignment verticalAlign) 18 | { 19 | super(name, translationKey); 20 | 21 | this.verticalAlign = verticalAlign; 22 | } 23 | 24 | public VerticalAlignment getVerticalAlign() 25 | { 26 | return this.verticalAlign; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/litematica/util/value/BlockInfoListType.java: -------------------------------------------------------------------------------- 1 | package litematica.util.value; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import malilib.config.value.BaseOptionListConfigValue; 6 | 7 | public class BlockInfoListType extends BaseOptionListConfigValue 8 | { 9 | public static final BlockInfoListType ALL = new BlockInfoListType("all", "litematica.gui.label.block_info_list_type.all"); 10 | public static final BlockInfoListType RENDER_LAYERS = new BlockInfoListType("render_layers", "litematica.gui.label.block_info_list_type.render_layers"); 11 | 12 | public static final ImmutableList VALUES = ImmutableList.of(ALL, RENDER_LAYERS); 13 | 14 | public BlockInfoListType(String name, String translationKey) 15 | { 16 | super(name, translationKey); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/litematica/util/value/OverlayType.java: -------------------------------------------------------------------------------- 1 | package litematica.util.value; 2 | 3 | public enum OverlayType 4 | { 5 | NONE (0), 6 | MISSING (1), 7 | EXTRA (2), 8 | WRONG_STATE (3), 9 | WRONG_BLOCK (4); 10 | 11 | private final int priority; 12 | 13 | OverlayType(int priority) 14 | { 15 | this.priority = priority; 16 | } 17 | 18 | /** 19 | * Higher value means higher render priority over an overlapping lower priority 20 | * @return 21 | */ 22 | public int getRenderPriority() 23 | { 24 | return this.priority; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/litematica/util/value/ReplaceBehavior.java: -------------------------------------------------------------------------------- 1 | package litematica.util.value; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import malilib.config.value.BaseOptionListConfigValue; 6 | 7 | public class ReplaceBehavior extends BaseOptionListConfigValue 8 | { 9 | public static final ReplaceBehavior NONE = new ReplaceBehavior("none", "litematica.gui.label.replace_behavior.none"); 10 | public static final ReplaceBehavior ALL = new ReplaceBehavior("all", "litematica.gui.label.replace_behavior.all"); 11 | public static final ReplaceBehavior WITH_NON_AIR = new ReplaceBehavior("with_non_air", "litematica.gui.label.replace_behavior.with_non_air"); 12 | 13 | public static final ImmutableList VALUES = ImmutableList.of(NONE, ALL, WITH_NON_AIR); 14 | 15 | public ReplaceBehavior(String name, String translationKey) 16 | { 17 | super(name, translationKey); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/litematica/world/ChunkProviderSchematic.java: -------------------------------------------------------------------------------- 1 | package litematica.world; 2 | 3 | import net.minecraft.client.multiplayer.ChunkProviderClient; 4 | import net.minecraft.world.World; 5 | import net.minecraft.world.chunk.Chunk; 6 | 7 | import malilib.util.position.ChunkPos; 8 | import litematica.interfaces.IMixinChunkProviderClient; 9 | 10 | public class ChunkProviderSchematic extends ChunkProviderClient 11 | { 12 | private final World world; 13 | 14 | public ChunkProviderSchematic(World world) 15 | { 16 | super(world); 17 | 18 | this.world = world; 19 | } 20 | 21 | @Override 22 | public Chunk loadChunk(int chunkX, int chunkZ) 23 | { 24 | Chunk chunk = new ChunkSchematic(this.world, chunkX, chunkZ); 25 | 26 | ((IMixinChunkProviderClient) (Object) this).getLoadedChunks().put(ChunkPos.asLong(chunkX, chunkZ), chunk); 27 | chunk.markLoaded(true); 28 | 29 | return chunk; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/litematica/world/SchematicWorldHandler.java: -------------------------------------------------------------------------------- 1 | package litematica.world; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.world.EnumDifficulty; 7 | import net.minecraft.world.GameType; 8 | import net.minecraft.world.WorldSettings; 9 | import net.minecraft.world.WorldType; 10 | 11 | import litematica.render.LitematicaRenderer; 12 | 13 | public class SchematicWorldHandler 14 | { 15 | private static final WorldSettings SETTINGS = new WorldSettings(0L, GameType.CREATIVE, false, false, WorldType.FLAT); 16 | private static final Minecraft MC = Minecraft.getMinecraft(); 17 | @Nullable 18 | private static WorldSchematic world; 19 | 20 | @Nullable 21 | public static WorldSchematic getSchematicWorld() 22 | { 23 | return world; 24 | } 25 | 26 | public static void recreateSchematicWorld(boolean remove) 27 | { 28 | if (remove) 29 | { 30 | world = null; 31 | } 32 | else 33 | { 34 | // Note: The dimension used here must have no skylight, because the custom Chunks don't have those arrays 35 | world = new WorldSchematic(null, SETTINGS, 1, EnumDifficulty.PEACEFUL, MC.profiler); 36 | //this.world.addEventListener(LitematicaRenderer.getInstance().getRenderGlobal()); 37 | } 38 | 39 | LitematicaRenderer.getInstance().onSchematicWorldChanged(world); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/assets/litematica/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maruohon/litematica/dd3dd561e248fa57e1477aed922acb2f6b441848/src/main/resources/assets/litematica/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/litematica/shaders/alpha.frag: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | uniform sampler2D texture; 4 | uniform float alpha_multiplier; 5 | 6 | void main() { 7 | vec4 tex = texture2D(texture, gl_TexCoord[0].xy) * gl_Color; 8 | gl_FragColor = vec4(tex.r, tex.g, tex.b, tex.a * alpha_multiplier); 9 | } -------------------------------------------------------------------------------- /src/main/resources/assets/litematica/textures/gui/gui_widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maruohon/litematica/dd3dd561e248fa57e1477aed922acb2f6b441848/src/main/resources/assets/litematica/textures/gui/gui_widgets.png -------------------------------------------------------------------------------- /src/main/resources/assets/litematica/textures/xcf/gui_widgets.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maruohon/litematica/dd3dd561e248fa57e1477aed922acb2f6b441848/src/main/resources/assets/litematica/textures/xcf/gui_widgets.xcf -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "litematica", 4 | "name": "Litematica", 5 | "version": "${mod_version}", 6 | 7 | "description": "A modern schematic mod with extra creative mode features", 8 | "license": "LGPLv3", 9 | "authors": [ "masa" ], 10 | "contact": { 11 | "homepage": "https://www.curseforge.com/minecraft/mc-mods/litematica", 12 | "issues": "https://github.com/maruohon/litematica/issues", 13 | "sources": "https://github.com/maruohon/litematica", 14 | "discord": "https://discordapp.com/channels/169369095538606080/566649314001158165" 15 | }, 16 | 17 | "icon": "assets/litematica/icon.png", 18 | "environment": "client", 19 | "entrypoints": { 20 | "client-init": [ 21 | "litematica.Litematica" 22 | ], 23 | "modmenu": [ 24 | "litematica.compat.modmenu.ModMenuImpl" 25 | ] 26 | }, 27 | 28 | "mixins": [ 29 | { 30 | "config": "mixins.litematica.json", 31 | "environment": "client" 32 | } 33 | ], 34 | 35 | "depends": { 36 | "minecraft": "1.12.x", 37 | "malilib": ">=0.55.0", 38 | "osl-entrypoints": "*", 39 | "osl-resource-loader": "*" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/mixins.litematica.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "litematica.mixin", 4 | "compatibilityLevel": "JAVA_8", 5 | "minVersion": "0.8", 6 | "client": [ 7 | "IMixinBlockRendererDispatcher", 8 | "IMixinCompiledChunk", 9 | "IMixinDataFixer", 10 | "IMixinItemBlockSpecial", 11 | "IMixinViewFrustum", 12 | "IMixinWorldClient", 13 | "MixinBlock", 14 | "MixinBlockRail", 15 | "MixinChunk", 16 | "MixinChunkProviderClient", 17 | "MixinEntityRenderer", 18 | "MixinGuiContainer", 19 | "MixinGuiEditSign", 20 | "MixinIntegratedServer", 21 | "MixinMinecraft", 22 | "MixinNetHandlerPlayClient", 23 | "MixinPlayerControllerMP", 24 | "MixinRenderGlobal", 25 | "MixinWorld", 26 | "debug.GuiOverlayDebugMixin" 27 | ], 28 | "injectors": { 29 | "defaultRequire": 1 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "pack_format": 3, 4 | "description": "Litematica Resources" 5 | } 6 | } --------------------------------------------------------------------------------