commit 4c3f441f411d51afcee1e6c6fa71aead2cc3a109 Author: Andrew nuark G Date: Thu May 7 14:25:08 2026 +0700 feat: working staffs of building diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d18fc84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# MacOS DS_Store files +.DS_Store + +# Gradle cache folder +.gradle + +# Gradle build folder +build + +# IntelliJ +out/ +.idea +*.iml +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Common working directory +run +/repo/ diff --git a/.kotlin/errors/errors-1778009600907.log b/.kotlin/errors/errors-1778009600907.log new file mode 100644 index 0000000..d62cafe --- /dev/null +++ b/.kotlin/errors/errors-1778009600907.log @@ -0,0 +1,121 @@ +kotlin version: 2.3.0 +error message: Incremental compilation failed: Storage for [C:\Users\Admin\Documents\repos\mclschcannoncompat\build\kotlin\compileKotlin\cacheable\caches-jvm\jvm\kotlin\source-to-classes.tab] is already registered +java.lang.IllegalStateException: Storage for [C:\Users\Admin\Documents\repos\mclschcannoncompat\build\kotlin\compileKotlin\cacheable\caches-jvm\jvm\kotlin\source-to-classes.tab] is already registered + at org.jetbrains.kotlin.com.intellij.util.io.FilePageCache.registerPagedFileStorage(FilePageCache.java:410) + at org.jetbrains.kotlin.com.intellij.util.io.PagedFileStorage.(PagedFileStorage.java:72) + at org.jetbrains.kotlin.com.intellij.util.io.ResizeableMappedFile.(ResizeableMappedFile.java:68) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentBTreeEnumerator.(PersistentBTreeEnumerator.java:128) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentEnumerator.createDefaultEnumerator(PersistentEnumerator.java:52) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.(PersistentMapImpl.java:165) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.(PersistentMapImpl.java:140) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapBuilder.buildImplementation(PersistentMapBuilder.java:88) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapBuilder.build(PersistentMapBuilder.java:71) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.(PersistentHashMap.java:46) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.(PersistentHashMap.java:72) + at org.jetbrains.kotlin.incremental.storage.LazyStorage.createMap(LazyStorage.kt:62) + at org.jetbrains.kotlin.incremental.storage.LazyStorage.getStorageIfExists(LazyStorage.kt:53) + at org.jetbrains.kotlin.incremental.storage.LazyStorage.get(LazyStorage.kt:76) + at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.get(InMemoryStorage.kt:68) + at org.jetbrains.kotlin.incremental.storage.AppendableInMemoryStorage.get(InMemoryStorage.kt:155) + at org.jetbrains.kotlin.incremental.storage.AppendableInMemoryStorage.get(InMemoryStorage.kt:147) + at org.jetbrains.kotlin.incremental.storage.AppendableSetBasicMap.get(BasicMap.kt:137) + at org.jetbrains.kotlin.incremental.storage.AbstractSourceToOutputMap.getFqNames(SourceToOutputMaps.kt:50) + at org.jetbrains.kotlin.incremental.AbstractIncrementalCache.classesFqNamesBySources(AbstractIncrementalCache.kt:115) + at org.jetbrains.kotlin.incremental.dirtyFiles.ChangesDetectionUtilsKt.getRemovedClassesChanges(changesDetectionUtils.kt:142) + at org.jetbrains.kotlin.incremental.dirtyFiles.JvmSourcesToCompileCalculator.calculateSourcesToCompileWithKnownChanges(JvmSourcesToCompileCalculator.kt:77) + at org.jetbrains.kotlin.incremental.dirtyFiles.JvmSourcesToCompileCalculator.calculateSourcesToCompileImpl(JvmSourcesToCompileCalculator.kt:49) + at org.jetbrains.kotlin.incremental.dirtyFiles.JvmSourcesToCompileCalculator.calculateWithClasspathSnapshot(JvmSourcesToCompileCalculator.kt:97) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.calculateSourcesToCompile(IncrementalJvmCompilerRunner.kt:60) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.calculateSourcesToCompile(IncrementalJvmCompilerRunner.kt:23) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally$lambda$0$compile(IncrementalCompilerRunner.kt:225) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:267) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:119) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:684) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:94) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1810) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360) + at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) + at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) + at java.base/java.security.AccessController.doPrivileged(AccessController.java:714) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721) + at java.base/java.security.AccessController.doPrivileged(AccessController.java:400) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) + Suppressed: java.lang.Exception: Storage[C:\Users\Admin\Documents\repos\mclschcannoncompat\build\kotlin\compileKotlin\cacheable\caches-jvm\jvm\kotlin\source-to-classes.tab] registration stack trace + at org.jetbrains.kotlin.com.intellij.util.io.FilePageCache.registerPagedFileStorage(FilePageCache.java:437) + ... 46 more + + +error message: Daemon compilation failed: null +java.lang.Exception + at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69) + at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:224) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:143) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:107) + at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:75) + at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62) + at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62) + at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) + at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59) + at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53) + at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162) + at org.gradle.internal.Factories$1.create(Factories.java:31) + at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:264) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:128) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:133) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +Caused by: java.io.IOException: Could not delete 'C:\Users\Admin\Documents\repos\mclschcannoncompat\build\kotlin\compileKotlin\cacheable\caches-jvm' + at org.jetbrains.kotlin.incremental.FileUtilsKt.deleteRecursivelyOrThrow(fileUtils.kt:47) + at org.jetbrains.kotlin.incremental.FileUtilsKt.deleteDirectoryContents(fileUtils.kt:38) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.cleanOrCreateDirectories(IncrementalCompilerRunner.kt:321) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:290) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:147) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:684) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:94) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1810) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360) + at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) + at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) + at java.base/java.security.AccessController.doPrivileged(AccessController.java:714) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721) + at java.base/java.security.AccessController.doPrivileged(AccessController.java:400) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720) + ... 3 more + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5de1e9f --- /dev/null +++ b/build.gradle @@ -0,0 +1,207 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'idea' + id 'net.neoforged.moddev' version '2.0.141' + id 'org.jetbrains.kotlin.jvm' version '2.3.0' +} + +version = mod_version +group = mod_group_id + +repositories { + mavenLocal() + maven { + name = 'Kotlin for Forge' + url = 'https://thedarkcolour.github.io/KotlinForForge/' + content { includeGroup "thedarkcolour" } + } + + maven { url = "https://maven.createmod.net" } // Create, Ponder, Flywheel + maven { url = "https://maven.ithundxr.dev/snapshots" } // Registrate + + maven { + name "ModMaven" + url "https://modmaven.dev" + } + maven { + name "CurseForge" + url "https://www.cursemaven.com" + } + maven { + name "MineColonies" + url "https://ldtteam.jfrog.io/ldtteam/modding" + } +} + +base { + archivesName = mod_id +} + +java.toolchain.languageVersion = JavaLanguageVersion.of(21) +kotlin.jvmToolchain(21) + +neoForge { + // Specify the version of NeoForge to use. + version = project.neo_version + + parchment { + mappingsVersion = project.parchment_mappings_version + minecraftVersion = project.parchment_minecraft_version + } + + // This line is optional. Access Transformers are automatically detected + // accessTransformers.add('src/main/resources/META-INF/accesstransformer.cfg') + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + client() + + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + server { + server() + programArgument '--nogui' + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + // This run config launches GameTestServer and runs all registered gametests, then exits. + // By default, the server will crash when no gametests are provided. + // The gametest system is also enabled by default for other run configs under the /test command. + gameTestServer { + type = "gameTestServer" + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + data { + data() + + // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it + // gameDirectory = project.file('run-data') + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + } + + // applies to all the run configs above + configureEach { + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + systemProperty 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + logLevel = org.slf4j.event.Level.DEBUG + } + } + + mods { + // define mod <-> source bindings + // these are used to tell the game which sources are for which mod + // mostly optional in a single mod project + // but multi mod projects should define one per mod + "${mod_id}" { + sourceSet(sourceSets.main) + } + } +} + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + + +dependencies { + implementation 'thedarkcolour:kotlinforforge-neoforge:5.11.0' + +// implementation("com.simibubi.create:create-${minecraft_version}:${create_version}:slim") { transitive = false } +// implementation("net.createmod.ponder:ponder-neoforge:${ponder_version}+mc${minecraft_version}") +// compileOnly("dev.engine-room.flywheel:flywheel-neoforge-api-${minecraft_version}:${flywheel_version}") +// runtimeOnly("dev.engine-room.flywheel:flywheel-neoforge-${minecraft_version}:${flywheel_version}") +// implementation("com.tterrag.registrate:Registrate:${registrate_version}") + + implementation "com.ldtteam:minecolonies:${minecolonies_version}" + implementation "com.ldtteam:structurize:${structurize_version}" + implementation "com.ldtteam:blockui:${blockui_version}" + implementation "curse.maven:domum-ornamentum-527361:${domumornamentum_file}" + + runtimeOnly "curse.maven:sophisticated-core-618298:8046952" + runtimeOnly "curse.maven:sophisticated-storage-619320:8034906" + runtimeOnly "curse.maven:pocket-storage-367734:6834323" + + // Example mod dependency with JEI + // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime + // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" + // compileOnly "mezz.jei:jei-${mc_version}-forge-api:${jei_version}" + // runtimeOnly "mezz.jei:jei-${mc_version}-forge:${jei_version}" + + // Example mod dependency using a mod jar from ./libs with a flat dir repository + // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar + // The group id is ignored when searching -- in this case, it is "blank" + // implementation "blank:coolmod-${mc_version}:${coolmod_version}" + + // Example mod dependency using a file as dependency + // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") + + // Example project dependency using a sister or child project: + // implementation project(":myproject") + + // For more info: + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html +} + +// This block of code expands all declared replace properties in the specified resource targets. +// A missing property will result in an error. Properties are expanded using ${} Groovy notation. +var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) { + var replaceProperties = [minecraft_version : minecraft_version, + minecraft_version_range: minecraft_version_range, + neo_version : neo_version, + neo_version_range : neo_version_range, + loader_version_range : loader_version_range, + mod_id : mod_id, + mod_name : mod_name, + mod_license : mod_license, + mod_version : mod_version, + mod_authors : mod_authors, + mod_description : mod_description] + inputs.properties replaceProperties + expand replaceProperties + from "src/main/templates" + into "build/generated/sources/modMetadata" +} + +// Include the output of "generateModMetadata" as an input directory for the build +// this works with both building through Gradle and the IDE. +sourceSets.main.resources.srcDir generateModMetadata +// To avoid having to run "generateModMetadata" manually, make it run on every project reload +neoForge.ideSyncTask generateModMetadata + +// Example configuration to allow publishing using the maven-publish plugin +publishing { + publications { + register('mavenJava', MavenPublication) { + from components.java + } + } + repositories { + maven { + url "file://${project.projectDir}/repo" + } + } +} + +// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. +idea { + module { + downloadSources = true + downloadJavadoc = true + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..2583458 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,52 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +org.gradle.jvmargs=-Xmx2G +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true +## Environment Properties +# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge +# The Minecraft version must agree with the Neo version to get a valid artifact +minecraft_version=1.21.1 +# The Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft_version_range=[1.21.1,1.22) +# The Neo version must agree with the Minecraft version to get a valid artifact +neo_version=21.1.219 +# The Neo version range can use any version of Neo as bounds +neo_version_range=[21,) +# The loader version range can only use the major version of FML as bounds +loader_version_range=[5.3,) +parchment_minecraft_version=1.21.11 +parchment_mappings_version=2025.12.21-nightly-SNAPSHOT +## Mod Properties +# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} +# Must match the String constant located in the main mod class annotated with @Mod. +mod_id=mclschcannoncompat +# The human-readable display name for the mod. +mod_name=MineColonies Stuff +# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. +mod_license=All Rights Reserved +# The mod version. See https://semver.org/ +mod_version=1.0.0 +# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. +# This should match the base package used for the mod sources. +# See https://maven.apache.org/guides/mini/guide-naming-conventions.html +mod_group_id=xyz.nuark.mcmod +# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. +mod_authors=nuark +# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. +mod_description=This mod adds stuff... and staves! Things I wanted to have, really +# Create stuff version +create_version = 6.0.9-216 +ponder_version = 1.0.81 +flywheel_version = 1.0.6 +registrate_version = MC1.21-1.3.0+67 +# Minecolonies stuff version +minecolonies_version=1.1.1285-1.21.1-snapshot +structurize_version=1.0.810-1.21.1-snapshot +multipiston_version=1.2.51-1.21.1-snapshot +blockui_version=1.0.199-1.21.1-snapshot +domumornamentum_version=1.0.223-snapshot +domumornamentum_file=7231908 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..b740cf1 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ada876e --- /dev/null +++ b/settings.gradle @@ -0,0 +1,11 @@ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = 'https://maven.neoforged.net/releases' } + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} diff --git a/src/generated/resources/.cache/37d63bb25ad2a1f1b3cea75df8f510fa196f89ed b/src/generated/resources/.cache/37d63bb25ad2a1f1b3cea75df8f510fa196f89ed new file mode 100644 index 0000000..2cf11f4 --- /dev/null +++ b/src/generated/resources/.cache/37d63bb25ad2a1f1b3cea75df8f510fa196f89ed @@ -0,0 +1,4 @@ +// 1.21.1 2026-05-05T23:41:11.6384884 Item Models: mclschcannoncompat +52b70fbe9824ebbf99b2c3464e62faeb93a3ce63 assets/mclschcannoncompat/models/item/builder_assistant_staff.json +acd5e3967cb0dcf277da711c0b8f0efbc6271d6c assets/mclschcannoncompat/models/item/builder_assistant_staff_t2.json +d996164a02ec5598f0d421cc1ad0755b29621414 assets/mclschcannoncompat/models/item/builder_assistant_staff_t3.json diff --git a/src/generated/resources/.cache/6e801767522783b716853a5ad8062111be83b958 b/src/generated/resources/.cache/6e801767522783b716853a5ad8062111be83b958 new file mode 100644 index 0000000..0330876 --- /dev/null +++ b/src/generated/resources/.cache/6e801767522783b716853a5ad8062111be83b958 @@ -0,0 +1,2 @@ +// 1.21.1 2026-05-07T13:46:09.9996673 Languages: ru_ru for mod: mclschcannoncompat +3deacd406d126128077ef1e97aa26c68ef1759e7 assets/mclschcannoncompat/lang/ru_ru.json diff --git a/src/generated/resources/.cache/9ba92730b9239f67da1ce4d3431e6be06116236d b/src/generated/resources/.cache/9ba92730b9239f67da1ce4d3431e6be06116236d new file mode 100644 index 0000000..524a983 --- /dev/null +++ b/src/generated/resources/.cache/9ba92730b9239f67da1ce4d3431e6be06116236d @@ -0,0 +1,2 @@ +// 1.21.1 2026-05-07T13:46:10.0056649 Languages: en_us for mod: mclschcannoncompat +a01abd18da057bba316103cb554505de33093034 assets/mclschcannoncompat/lang/en_us.json diff --git a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e new file mode 100644 index 0000000..ad13b51 --- /dev/null +++ b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -0,0 +1,7 @@ +// 1.21.1 2026-05-05T23:41:11.6364906 Recipes +a4d166c9c7d5b081e15495f87588fa70f62bee84 data/mclschcannoncompat/advancement/recipes/tools/rec_fct1.json +c1b2ebefe2e42c4a01db164c8fbf90dd0aadea78 data/mclschcannoncompat/advancement/recipes/tools/rec_fct2.json +909da6934e35b03c890689105c7f29bae51ef330 data/mclschcannoncompat/advancement/recipes/tools/rec_fct3.json +594aec36a3be123023816c430342c9f081fd5bd4 data/mclschcannoncompat/recipe/rec_fct1.json +4d05680c3a8c9923bf0610a8429a821cf57bbfff data/mclschcannoncompat/recipe/rec_fct2.json +e4bfc8d84f08ce1637c0d24090653218ba04f5be data/mclschcannoncompat/recipe/rec_fct3.json diff --git a/src/generated/resources/assets/mclschcannoncompat/lang/en_us.json b/src/generated/resources/assets/mclschcannoncompat/lang/en_us.json new file mode 100644 index 0000000..05c3d04 --- /dev/null +++ b/src/generated/resources/assets/mclschcannoncompat/lang/en_us.json @@ -0,0 +1,6 @@ +{ + "item.asdf.assistantstaff.maxdepth": "Maximum depth: %s", + "item.mclschcannoncompat.builder_assistant_staff": "Builder staff T1", + "item.mclschcannoncompat.builder_assistant_staff_t2": "Builder staff T2", + "item.mclschcannoncompat.builder_assistant_staff_t3": "Builder staff T3" +} \ No newline at end of file diff --git a/src/generated/resources/assets/mclschcannoncompat/lang/ru_ru.json b/src/generated/resources/assets/mclschcannoncompat/lang/ru_ru.json new file mode 100644 index 0000000..9263fba --- /dev/null +++ b/src/generated/resources/assets/mclschcannoncompat/lang/ru_ru.json @@ -0,0 +1,6 @@ +{ + "item.asdf.assistantstaff.maxdepth": "Максимальная блоков: %s", + "item.mclschcannoncompat.builder_assistant_staff": "Посох строителя T1", + "item.mclschcannoncompat.builder_assistant_staff_t2": "Посох строителя T2", + "item.mclschcannoncompat.builder_assistant_staff_t3": "Посох строителя T3" +} \ No newline at end of file diff --git a/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff.json b/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff.json new file mode 100644 index 0000000..245a892 --- /dev/null +++ b/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "mclschcannoncompat:item/builder_assistant_staff" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff_t2.json b/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff_t2.json new file mode 100644 index 0000000..5a40727 --- /dev/null +++ b/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff_t2.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "mclschcannoncompat:item/builder_assistant_staff_t2" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff_t3.json b/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff_t3.json new file mode 100644 index 0000000..26b67f8 --- /dev/null +++ b/src/generated/resources/assets/mclschcannoncompat/models/item/builder_assistant_staff_t3.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "mclschcannoncompat:item/builder_assistant_staff_t3" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct1.json b/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct1.json new file mode 100644 index 0000000..fe0ef77 --- /dev/null +++ b/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct1.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_iron_block": { + "conditions": { + "items": [ + { + "items": "minecraft:iron_block" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "mclschcannoncompat:rec_fct1" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_iron_block" + ] + ], + "rewards": { + "recipes": [ + "mclschcannoncompat:rec_fct1" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct2.json b/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct2.json new file mode 100644 index 0000000..bd883ab --- /dev/null +++ b/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct2.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_diamond_block": { + "conditions": { + "items": [ + { + "items": "minecraft:diamond_block" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "mclschcannoncompat:rec_fct2" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_diamond_block" + ] + ], + "rewards": { + "recipes": [ + "mclschcannoncompat:rec_fct2" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct3.json b/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct3.json new file mode 100644 index 0000000..89125e0 --- /dev/null +++ b/src/generated/resources/data/mclschcannoncompat/advancement/recipes/tools/rec_fct3.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_netherite_block": { + "conditions": { + "items": [ + { + "items": "minecraft:netherite_block" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "mclschcannoncompat:rec_fct3" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_netherite_block" + ] + ], + "rewards": { + "recipes": [ + "mclschcannoncompat:rec_fct3" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct1.json b/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct1.json new file mode 100644 index 0000000..cc52cf1 --- /dev/null +++ b/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct1.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "key": { + "I": { + "item": "minecraft:iron_block" + }, + "S": { + "item": "minecraft:stick" + } + }, + "pattern": [ + " II", + " SI", + "I " + ], + "result": { + "count": 1, + "id": "mclschcannoncompat:builder_assistant_staff" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct2.json b/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct2.json new file mode 100644 index 0000000..1711691 --- /dev/null +++ b/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct2.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "key": { + "D": { + "item": "minecraft:diamond_block" + }, + "I": { + "item": "minecraft:iron_block" + }, + "S": { + "item": "mclschcannoncompat:builder_assistant_staff" + } + }, + "pattern": [ + " DD", + " SD", + "I " + ], + "result": { + "count": 1, + "id": "mclschcannoncompat:builder_assistant_staff_t2" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct3.json b/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct3.json new file mode 100644 index 0000000..15baf33 --- /dev/null +++ b/src/generated/resources/data/mclschcannoncompat/recipe/rec_fct3.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "key": { + "G": { + "item": "minecraft:gold_block" + }, + "I": { + "item": "minecraft:iron_block" + }, + "N": { + "item": "minecraft:netherite_block" + }, + "S": { + "item": "mclschcannoncompat:builder_assistant_staff_t2" + } + }, + "pattern": [ + " NG", + " SN", + "I " + ], + "result": { + "count": 1, + "id": "mclschcannoncompat:builder_assistant_staff_t3" + } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/Mclschcannoncompat.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/Mclschcannoncompat.kt new file mode 100644 index 0000000..8bcbe6f --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/Mclschcannoncompat.kt @@ -0,0 +1,104 @@ +package xyz.nuark.mcmod.mclschcannoncompat + +import net.minecraft.client.Minecraft +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.CreativeModeTabs +import net.neoforged.bus.api.SubscribeEvent +import net.neoforged.fml.ModList +import net.neoforged.fml.common.EventBusSubscriber +import net.neoforged.fml.common.Mod +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent +import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent +import net.neoforged.neoforge.data.event.GatherDataEvent +import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent +import net.neoforged.neoforge.event.RegisterCommandsEvent +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger +import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS +import thedarkcolour.kotlinforforge.neoforge.forge.runForDist +import xyz.nuark.mcmod.mclschcannoncompat.block.ModBlocks +import xyz.nuark.mcmod.mclschcannoncompat.comands.ModCommands +import xyz.nuark.mcmod.mclschcannoncompat.datagen.ModItemModelProvider +import xyz.nuark.mcmod.mclschcannoncompat.datagen.ModLanguageProviders +import xyz.nuark.mcmod.mclschcannoncompat.datagen.ModRecipeProvider +import xyz.nuark.mcmod.mclschcannoncompat.item.ModItems + +@Mod(Mclschcannoncompat.ID) +@EventBusSubscriber +object Mclschcannoncompat { + const val ID = "mclschcannoncompat" + + val LOGGER: Logger = LogManager.getLogger(ID) + + init { + LOGGER.log(Level.INFO, "Hello world!") + + ModBlocks.REGISTRY.register(MOD_BUS) + + val obj = runForDist(clientTarget = { + MOD_BUS.addListener(::onClientSetup) + Minecraft.getInstance() + }, serverTarget = { + MOD_BUS.addListener(::onServerSetup) + "test" + }) + + ModItems.REGISTRY.register(MOD_BUS) + + println(obj) + } + + private fun onClientSetup(@Suppress("unused") event: FMLClientSetupEvent) { + } + + private fun onServerSetup(@Suppress("unused") event: FMLDedicatedServerSetupEvent) { + } + + @SubscribeEvent + fun onCommonSetup(@Suppress("unused") event: FMLCommonSetupEvent) { + } + + @SubscribeEvent + fun buildContents(event: BuildCreativeModeTabContentsEvent) { + if (event.tabKey === CreativeModeTabs.TOOLS_AND_UTILITIES) { + event.accept(ModItems.BUILDER_ASSISTANT_STAFF_T1.get()) + event.accept(ModItems.BUILDER_ASSISTANT_STAFF_T2.get()) + event.accept(ModItems.BUILDER_ASSISTANT_STAFF_T3.get()) + } + } + + @SubscribeEvent + fun registerCommands(event: RegisterCommandsEvent) { + ModCommands.register(event.dispatcher) + } + + @SubscribeEvent + fun onNetworkRegistry(event: RegisterPayloadHandlersEvent) + { + val modVersion = ModList.get().getModContainerById(ID).get().getModInfo().getVersion().toString() + @Suppress("unused") val registry = event.registrar(ID).versioned(modVersion) + } + + @SubscribeEvent + fun gatherData(event: GatherDataEvent) { + val existingFileHelper = event.existingFileHelper + val generator = event.generator + val output = generator.packOutput + val lookupProvider = event.lookupProvider + + generator.addProvider( + event.includeClient(), + ModItemModelProvider(output, existingFileHelper) + ) + ModLanguageProviders.provideProviders(generator, event.includeClient()) + generator.addProvider( + event.includeServer(), + ModRecipeProvider(output, lookupProvider) + ) + } + + fun resource(name: String): ResourceLocation = ResourceLocation.fromNamespaceAndPath(ID, name) +} diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/block/ModBlocks.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/block/ModBlocks.kt new file mode 100644 index 0000000..6d5e6e8 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/block/ModBlocks.kt @@ -0,0 +1,8 @@ +package xyz.nuark.mcmod.mclschcannoncompat.block + +import net.neoforged.neoforge.registries.DeferredRegister +import xyz.nuark.mcmod.mclschcannoncompat.Mclschcannoncompat + +object ModBlocks { + val REGISTRY = DeferredRegister.createBlocks(Mclschcannoncompat.ID) +} diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/comands/ModCommands.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/comands/ModCommands.kt new file mode 100644 index 0000000..f55cf07 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/comands/ModCommands.kt @@ -0,0 +1,10 @@ +package xyz.nuark.mcmod.mclschcannoncompat.comands + +import com.mojang.brigadier.CommandDispatcher +import net.minecraft.commands.CommandSourceStack + + +object ModCommands { + fun register(dispatcher: CommandDispatcher) { + } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModItemModelProvider.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModItemModelProvider.kt new file mode 100644 index 0000000..6d05685 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModItemModelProvider.kt @@ -0,0 +1,15 @@ +package xyz.nuark.mcmod.mclschcannoncompat.datagen + +import net.minecraft.data.PackOutput +import net.neoforged.neoforge.client.model.generators.ItemModelProvider +import net.neoforged.neoforge.common.data.ExistingFileHelper +import xyz.nuark.mcmod.mclschcannoncompat.Mclschcannoncompat +import xyz.nuark.mcmod.mclschcannoncompat.item.ModItems + +class ModItemModelProvider(output: PackOutput, existingFileHelper: ExistingFileHelper) : ItemModelProvider(output, Mclschcannoncompat.ID, existingFileHelper) { + override fun registerModels() { + basicItem(ModItems.BUILDER_ASSISTANT_STAFF_T1.get()) + basicItem(ModItems.BUILDER_ASSISTANT_STAFF_T2.get()) + basicItem(ModItems.BUILDER_ASSISTANT_STAFF_T3.get()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModLanguageProviders.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModLanguageProviders.kt new file mode 100644 index 0000000..127b1b6 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModLanguageProviders.kt @@ -0,0 +1,32 @@ +package xyz.nuark.mcmod.mclschcannoncompat.datagen + +import net.minecraft.data.DataGenerator +import net.minecraft.data.PackOutput +import net.neoforged.neoforge.common.data.LanguageProvider +import xyz.nuark.mcmod.mclschcannoncompat.Mclschcannoncompat +import xyz.nuark.mcmod.mclschcannoncompat.item.ModItems + +object ModLanguageProviders { + class ModRuRuLanguageProvider(output: PackOutput) : LanguageProvider(output, Mclschcannoncompat.ID, "ru_ru") { + override fun addTranslations() { + add(ModItems.BUILDER_ASSISTANT_STAFF_T1.get(), "Посох строителя T1") + add(ModItems.BUILDER_ASSISTANT_STAFF_T2.get(), "Посох строителя T2") + add(ModItems.BUILDER_ASSISTANT_STAFF_T3.get(), "Посох строителя T3") + add("item.asdf.assistantstaff.maxdepth", "Максимальная блоков: %s") + } + } + + class ModEnUsLanguageProvider(output: PackOutput) : LanguageProvider(output, Mclschcannoncompat.ID, "en_us") { + override fun addTranslations() { + add(ModItems.BUILDER_ASSISTANT_STAFF_T1.get(), "Builder staff T1") + add(ModItems.BUILDER_ASSISTANT_STAFF_T2.get(), "Builder staff T2") + add(ModItems.BUILDER_ASSISTANT_STAFF_T3.get(), "Builder staff T3") + add("item.asdf.assistantstaff.maxdepth", "Maximum depth: %s") + } + } + + fun provideProviders(generator: DataGenerator, shouldRun: Boolean) { + generator.addProvider(shouldRun, ModRuRuLanguageProvider(generator.packOutput)) + generator.addProvider(shouldRun, ModEnUsLanguageProvider(generator.packOutput)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModRecipeProvider.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModRecipeProvider.kt new file mode 100644 index 0000000..2f2dee6 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/datagen/ModRecipeProvider.kt @@ -0,0 +1,47 @@ +package xyz.nuark.mcmod.mclschcannoncompat.datagen + +import net.minecraft.core.HolderLookup +import net.minecraft.data.PackOutput +import net.minecraft.data.recipes.RecipeCategory +import net.minecraft.data.recipes.RecipeOutput +import net.minecraft.data.recipes.RecipeProvider +import net.minecraft.data.recipes.ShapedRecipeBuilder +import net.minecraft.world.item.Items +import xyz.nuark.mcmod.mclschcannoncompat.Mclschcannoncompat +import xyz.nuark.mcmod.mclschcannoncompat.item.ModItems +import java.util.concurrent.CompletableFuture + + +class ModRecipeProvider(output: PackOutput, registries: CompletableFuture) : RecipeProvider(output, registries) { + override fun buildRecipes(recipeOutput: RecipeOutput) { + ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, ModItems.BUILDER_ASSISTANT_STAFF_T1.get()) + .pattern(" II") + .pattern(" SI") + .pattern("I ") + .define('I', Items.IRON_BLOCK) + .define('S', Items.STICK) + .unlockedBy("has_iron_block", has(Items.IRON_BLOCK)) + .save(recipeOutput, Mclschcannoncompat.resource("rec_fct1")) + + ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, ModItems.BUILDER_ASSISTANT_STAFF_T2.get()) + .pattern(" DD") + .pattern(" SD") + .pattern("I ") + .define('I', Items.IRON_BLOCK) + .define('D', Items.DIAMOND_BLOCK) + .define('S', ModItems.BUILDER_ASSISTANT_STAFF_T1.get()) + .unlockedBy("has_diamond_block", has(Items.DIAMOND_BLOCK)) + .save(recipeOutput, Mclschcannoncompat.resource("rec_fct2")) + + ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, ModItems.BUILDER_ASSISTANT_STAFF_T3.get()) + .pattern(" NG") + .pattern(" SN") + .pattern("I ") + .define('I', Items.IRON_BLOCK) + .define('N', Items.NETHERITE_BLOCK) + .define('G', Items.GOLD_BLOCK) + .define('S', ModItems.BUILDER_ASSISTANT_STAFF_T2.get()) + .unlockedBy("has_netherite_block", has(Items.NETHERITE_BLOCK)) + .save(recipeOutput, Mclschcannoncompat.resource("rec_fct3")) + } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/item/BuilderAssistantStaff.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/item/BuilderAssistantStaff.kt new file mode 100644 index 0000000..473c09a --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/item/BuilderAssistantStaff.kt @@ -0,0 +1,96 @@ +package xyz.nuark.mcmod.mclschcannoncompat.item + +import com.minecolonies.core.items.ItemAssistantHammer +import net.minecraft.ChatFormatting +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.network.chat.Component +import net.minecraft.world.InteractionHand +import net.minecraft.world.InteractionResult +import net.minecraft.world.InteractionResultHolder +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.TooltipFlag +import net.minecraft.world.item.context.UseOnContext +import net.minecraft.world.level.Level +import xyz.nuark.mcmod.mclschcannoncompat.utils.AssistantTaskQueueHandler + +class BuilderAssistantStaff( + id: String, + properties: Properties, + val reach: Int, +) : ItemAssistantHammer(id, properties.stacksTo(1), reach) { + override fun useOnBlock(player: Player, interactPos: BlockPos) { + player.cooldowns.addCooldown(this, 1200) + + val visited = hashSetOf() + val toScan = ArrayDeque() + + toScan.add(interactPos) + visited.add(interactPos) + + while (!toScan.isEmpty() && visited.size < (reach * MAX_PROPAGATION_DEPTH)) { + if (player.cooldowns.isOnCooldown(this)) { + player.cooldowns.addCooldown(this, 1200) + } + + val current = toScan.removeFirst() + + AssistantTaskQueueHandler.addTask(AssistantTaskQueueHandler.AssistantTask( + playerId = player.id, + blockPos = current, + )) + + Direction.entries.forEach { dir -> + val next = current.relative(dir); + if (!visited.contains(next)) { + visited.add(next) + toScan.addLast(next) + } + } + } + + player.cooldowns.removeCooldown(this) + } + + override fun useOn(context: UseOnContext): InteractionResult { + if (context.level.isClientSide) return InteractionResult.SUCCESS + + val player = context.player ?: return InteractionResult.FAIL + if (player.cooldowns.isOnCooldown(this)) return InteractionResult.FAIL + + val startPos = context.clickedPos.relative(context.clickedFace) + + useOnBlock(player, startPos) + + return InteractionResult.CONSUME + } + + override fun use(level: Level, player: Player, hand: InteractionHand): InteractionResultHolder { + if (!level.isClientSide) { + val interactPos = BlockPos.containing(player.eyePosition.add(player.lookAngle.multiply(3.0, 3.0, 3.0))) + useOnBlock(player, interactPos) + } + + return InteractionResultHolder.success(player.mainHandItem) + } + + override fun appendHoverText( + stack: ItemStack, + ctx: TooltipContext?, + tooltipList: MutableList, + flagIn: TooltipFlag + ) { + tooltipList.add( + Component.translatable("item.asdf.assistantstaff.maxdepth", reach * MAX_PROPAGATION_DEPTH).withStyle(ChatFormatting.BLUE) + ) + tooltipList.add( + Component.translatable("item.minecolonies.assistanthammer.desc").withStyle(ChatFormatting.ITALIC) + .withStyle(ChatFormatting.GRAY) + ) + } + + companion object { + const val MAX_PROPAGATION_DEPTH = 200 + } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/item/ModItems.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/item/ModItems.kt new file mode 100644 index 0000000..a45f3b8 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/item/ModItems.kt @@ -0,0 +1,12 @@ +package xyz.nuark.mcmod.mclschcannoncompat.item + +import net.neoforged.neoforge.registries.DeferredRegister +import xyz.nuark.mcmod.mclschcannoncompat.Mclschcannoncompat + +object ModItems { + val REGISTRY = DeferredRegister.createItems(Mclschcannoncompat.ID) + + val BUILDER_ASSISTANT_STAFF_T1 = REGISTRY.registerItem("builder_assistant_staff") { props -> BuilderAssistantStaff("builder_assistant_staff", props, 1) } + val BUILDER_ASSISTANT_STAFF_T2 = REGISTRY.registerItem("builder_assistant_staff_t2") { props -> BuilderAssistantStaff("builder_assistant_staff_t2", props, 3) } + val BUILDER_ASSISTANT_STAFF_T3 = REGISTRY.registerItem("builder_assistant_staff_t3") { props -> BuilderAssistantStaff("builder_assistant_staff_t3", props, 5) } +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/utils/AssistantTaskQueueHandler.kt b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/utils/AssistantTaskQueueHandler.kt new file mode 100644 index 0000000..5f92323 --- /dev/null +++ b/src/main/kotlin/xyz/nuark/mcmod/mclschcannoncompat/utils/AssistantTaskQueueHandler.kt @@ -0,0 +1,331 @@ +package xyz.nuark.mcmod.mclschcannoncompat.utils + +import com.ldtteam.structurize.blocks.ModBlocks +import com.ldtteam.structurize.blueprints.v1.Blueprint +import com.ldtteam.structurize.placement.SimplePlacementContext +import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler +import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler.ActionProcessingResult +import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers +import com.ldtteam.structurize.util.BlockUtils +import com.minecolonies.api.colony.ICitizenData +import com.minecolonies.api.colony.IColony +import com.minecolonies.api.colony.IColonyManager +import com.minecolonies.api.colony.buildings.ModBuildings +import com.minecolonies.api.colony.interactionhandling.ChatPriority +import com.minecolonies.api.colony.permissions.Action +import com.minecolonies.api.colony.workorders.IWorkOrder +import com.minecolonies.api.util.InventoryUtils +import com.minecolonies.api.util.ItemStackUtils +import com.minecolonies.api.util.constant.ColonyConstants +import com.minecolonies.core.colony.buildings.modules.BuildingModules +import com.minecolonies.core.colony.buildings.workerbuildings.BuildingMiner +import com.minecolonies.core.colony.interactionhandling.SimpleNotificationInteraction +import com.minecolonies.core.placementhandlers.SolidPlaceholderPlacementHandler +import net.minecraft.core.BlockPos +import net.minecraft.core.component.DataComponents +import net.minecraft.network.chat.Component +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.ItemStack +import net.minecraft.world.phys.Vec3 +import net.neoforged.bus.api.SubscribeEvent +import net.neoforged.fml.common.EventBusSubscriber +import net.neoforged.neoforge.capabilities.Capabilities +import net.neoforged.neoforge.event.tick.ServerTickEvent +import net.neoforged.neoforge.items.ComponentItemHandler +import net.neoforged.neoforge.items.wrapper.InvWrapper +import xyz.nuark.mcmod.mclschcannoncompat.Mclschcannoncompat +import java.util.* +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.function.Consumer +import kotlin.collections.ArrayList + +@EventBusSubscriber +object AssistantTaskQueueHandler { + private val QUEUE: Queue = ConcurrentLinkedQueue() + + private const val BLOCKS_PER_TICK: Int = 20 + + fun addTask(task: AssistantTask) { + QUEUE.add(task) + } + + private val EXEC_QUEUE: Queue = ConcurrentLinkedQueue() + + @SubscribeEvent + fun onServerTickPre(@Suppress("unused") event: ServerTickEvent.Pre) { + var runnable: Runnable? + while (EXEC_QUEUE.poll().also { runnable = it } != null) { + runnable!!.run() + } + } + + @SubscribeEvent + fun onServerTickPost(event: ServerTickEvent.Post) { + var processed = 0 + while (!QUEUE.isEmpty() && processed < BLOCKS_PER_TICK) { + val task: AssistantTask? = QUEUE.poll() + Mclschcannoncompat.LOGGER.info("Executing task $task") + if (task != null) { + val player = event.server.playerList.players.firstOrNull { it.id == task.playerId } ?: continue + + EXEC_QUEUE.add { task.run(player) } + } + processed++ + } + } + + + private data class BuildAttemptResult( + val areBlocksToBuildNearby: Boolean, + val didTryBuilding: Boolean + ) + + data class AssistantTask( + val playerId: Int, + val blockPos: BlockPos, + ) { + fun run(player: Player) { + val level = player.level() + val colony = IColonyManager.getInstance().getColonyByPosFromWorld(level, blockPos) + + if (colony == null || level == null || !colony.permissions.hasPermission(player, Action.PLACE_BLOCKS)) { + return + } + + for (workOrder in colony.workManager.workOrders.values) { + if ((workOrder.isClaimed) + && colony.serverBuildingManager + .getBuilding(workOrder.claimedBy) != null && colony.serverBuildingManager + .getBuilding(workOrder.claimedBy) + .buildingType === ModBuildings.builder.get() && workOrder.boundingBox != null && workOrder.boundingBox!! + .inflate(2.0).contains(Vec3.atLowerCornerOf(blockPos)) + ) { + if (workOrder.blueprint == null) { + workOrder.loadBlueprint(colony.world) { _: Blueprint? -> } + return + } + + val handlers: MutableList = ArrayList(PlacementHandlers.handlers) + val solidPlaceHolderHandler = SolidPlaceholderPlacementHandler() + val building = colony.serverBuildingManager.getBuilding(workOrder.claimedBy) + + if (building == null || !building.hasModule(BuildingModules.BUILDER_SETTINGS)) { + return + } + + solidPlaceHolderHandler.replacement = building + .getModule(BuildingModules.BUILDER_SETTINGS) + .getSetting(BuildingMiner.FILL_BLOCK)!! + .value.block.defaultBlockState() + handlers.add(0, solidPlaceHolderHandler) + + val buildAttemptResult = + tryBuildingBlockAndPropagate(player, colony, workOrder, blockPos, handlers) + if (buildAttemptResult.areBlocksToBuildNearby && !buildAttemptResult.didTryBuilding) { + player.displayClientMessage( + Component.translatable("item.minecolonies.assistanthammer.noitems"), + true + ) + } + + break + } + } + } + + private fun tryBuildingBlockAndPropagate( + player: Player, + colony: IColony, + workOrder: IWorkOrder, + interactPos: BlockPos, + handlers: MutableList + ): BuildAttemptResult { + val levelState = player.level().getBlockState(interactPos) + val blockInfo = workOrder.blueprint!!.getBlockInfoAsMap()[interactPos.subtract(workOrder.location).offset(workOrder.blueprint!!.getPrimaryBlockOffset())] + + if (blockInfo == null || blockInfo.state == null // Skipping blueprint empty blocks + || blockInfo.state!!.block == levelState.block // Skipping same block as placed right now + || blockInfo.state!!.block == ModBlocks.blockSubstitution.get() // Skipping substitution + || blockInfo.state!!.block == ModBlocks.blockSolidSubstitution.get() // Skipping substitution + || blockInfo.state!!.block == ModBlocks.blockFluidSubstitution.get() // Skipping substitution + || blockInfo.state!!.block == ModBlocks.blockTagSubstitution.get() + ) { // Skipping substitution) + return BuildAttemptResult(areBlocksToBuildNearby = false, didTryBuilding = false) + } + val requiredItem: MutableList = ArrayList() + + var foundHandler: IPlacementHandler? = null + for (handler in handlers) { + if (handler.canHandle(player.level(), BlockPos.ZERO, blockInfo.state)) { + val itemList = handler.getRequiredItems( + player.level(), + interactPos, + blockInfo.state, + blockInfo.tileEntityData, + SimplePlacementContext(true, workOrder.rotationMirror) + ) + requiredItem.addAll(itemList) + + foundHandler = handler + break + } + } + + if (foundHandler == null) { + requiredItem.add(BlockUtils.getItemStackFromBlockState(blockInfo.state)) + } + + if (requiredItem.size != 1) { + return BuildAttemptResult(areBlocksToBuildNearby = false, didTryBuilding = false) + } + + // Searching item in player's inventory and containers + var fromPlayerInv = false + var fromItemCapHolder = false + var fromContainerCompHolder = false + if (!player.isCreative) { + for (required in requiredItem) { + for (stack in player.getInventory().items) { + if (ItemStackUtils.compareItemStacksIgnoreStackSize(required, stack)) { + fromPlayerInv = true + break + } + + stack.getCapability(Capabilities.ItemHandler.ITEM)?.let { inv -> + for (slot in 0 until inv.slots) { + if (ItemStackUtils.compareItemStacksIgnoreStackSize( + required, + inv.getStackInSlot(slot) + ) + ) { + fromItemCapHolder = true + break + } + } + } + if (fromItemCapHolder) { + break + } + + stack.components.get(DataComponents.CONTAINER)?.let { container -> + for (slot in 0 until container.slots) { + if (ItemStackUtils.compareItemStacksIgnoreStackSize( + required, + container.getStackInSlot(slot) + ) + ) { + fromContainerCompHolder = true + break + } + } + } + if (fromContainerCompHolder) { + break + } + } + } + } + + if (!fromPlayerInv && !fromItemCapHolder && !fromContainerCompHolder && !player.isCreative) { + return BuildAttemptResult(areBlocksToBuildNearby = true, didTryBuilding = false) + } + + if (!levelState.equals(blockInfo.state!!)) { + val hardness = levelState.getDestroySpeed(colony.world, interactPos) + val obstructed = !levelState.isAir + val canDestroy = !levelState.hasBlockEntity() && hardness >= 0f && hardness < 50f + if (obstructed && canDestroy) { + colony.world.destroyBlock(interactPos, true, player) + } + } + + val result = try { + foundHandler!!.handle( + colony.world, + interactPos, + blockInfo.state, + blockInfo.tileEntityData, + SimplePlacementContext(true, workOrder.rotationMirror) + ) + } catch (e: UnsupportedOperationException) { + if (e.message?.contains("ModelData refresh") == true) { + ActionProcessingResult.SUCCESS + } else { + throw e + } + } + + if (result == ActionProcessingResult.SUCCESS) { + // Notifying builder about building requirements + if (!colony.world.isClientSide) { + val building = colony.serverBuildingManager.getBuilding(workOrder.location) + if (building != null) { + building.registerBlockPosition(blockInfo.state!!, interactPos, colony.world) + } + } + + // Consuming player item + if (!player.isCreative) { + if (fromPlayerInv) { + InventoryUtils.removeStacksFromItemHandler( + InvWrapper(player.getInventory()), + requiredItem + ) + } else if (fromItemCapHolder) { + for (stack in player.getInventory().items.filter { it.getCapability(Capabilities.ItemHandler.ITEM) != null }) { + stack.getCapability(Capabilities.ItemHandler.ITEM)?.let { inv -> + if (InventoryUtils.removeStacksFromItemHandler(inv, requiredItem)) { + break + } + } + } + } else if (fromContainerCompHolder) { + for (stack in player.getInventory().items.filter { it.components.get(DataComponents.CONTAINER) != null }) { + stack.components.get(DataComponents.CONTAINER)!!.let { cont -> + val itemCap = ComponentItemHandler( + stack, + DataComponents.CONTAINER, + cont.slots.coerceAtMost(255) + ) + if (InventoryUtils.removeStacksFromItemHandler(itemCap, requiredItem)) { + break + } + } + } + } + } + + if (!colony.world.isClientSide) { + val building = colony.serverBuildingManager.getBuilding(workOrder.claimedBy) + for (stack in requiredItem) { + building.getModule( + BuildingModules.BUILDING_RESOURCES + ).reduceNeededResource(stack, 1) + } + + if (ColonyConstants.rand.nextInt(20) == 0) { + val buildingBuilder = colony.serverBuildingManager.getBuilding(workOrder.claimedBy) + if (buildingBuilder != null) { + buildingBuilder.getModule( + BuildingModules.BUILDER_WORK + ).getAssignedCitizen().forEach(Consumer { citizen: ICitizenData? -> + citizen!!.triggerInteraction( + SimpleNotificationInteraction( + Component.translatable( + "item.minecolonies.assistanthammer.happybuilder" + ), + ChatPriority.CHITCHAT + ) + ) + }) + } + } + } + + return BuildAttemptResult(areBlocksToBuildNearby = true, didTryBuilding = true) + } + + return BuildAttemptResult(areBlocksToBuildNearby = true, didTryBuilding = false) + } + } +} diff --git a/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff.png b/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff.png new file mode 100644 index 0000000..7d923ee Binary files /dev/null and b/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff.png differ diff --git a/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff_t2.png b/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff_t2.png new file mode 100644 index 0000000..6283b76 Binary files /dev/null and b/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff_t2.png differ diff --git a/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff_t3.png b/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff_t3.png new file mode 100644 index 0000000..ab82774 Binary files /dev/null and b/src/main/resources/assets/mclschcannoncompat/textures/item/builder_assistant_staff_t3.png differ diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..bdfd3fa --- /dev/null +++ b/src/main/templates/META-INF/neoforge.mods.toml @@ -0,0 +1,93 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader = "kotlinforforge" #mandatory +# A version range to match for said mod loader - for regular FML @Mod it will be the the FML version. This is currently 47. +loaderVersion = "${loader_version_range}" #mandatory +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license = "${mod_license}" +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +# The modid of the mod +modId = "${mod_id}" #mandatory +# The version number of the mod +version = "${mod_version}" #mandatory +# A display name for the mod +displayName = "${mod_name}" #mandatory +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforge.net/docs/misc/updatechecker/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional +# A URL for the "homepage" for this mod, displayed in the mod UI +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="mclschcannoncompat.png" #optional +# A text field displayed in the mod UI +#credits="" #optional +# A text field displayed in the mod UI +authors = "${mod_authors}" #optional + +# The description text for the mod (multi line!) (#mandatory) +description = '''${mod_description}''' + +# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. +#[[mixins]] +#config="${mod_id}.mixins.json" + +# The [[accessTransformers]] block allows you to declare where your AT file is. +# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg +#[[accessTransformers]] +#file="META-INF/accesstransformer.cfg" + +# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json + +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies."${mod_id}"]] #optional +# the modid of the dependency +modId = "neoforge" #mandatory +# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). +# 'required' requires the mod to exist, 'optional' does not +# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning +type = "required" #mandatory +# Optional field describing why the dependency is required or why it is incompatible +# reason="..." +# The version range of the dependency +versionRange = "${neo_version_range}" #mandatory +# An ordering relationship for the dependency. +# BEFORE - This mod is loaded BEFORE the dependency +# AFTER - This mod is loaded AFTER the dependency +ordering = "NONE" +# Side this dependency is applied on - BOTH, CLIENT, or SERVER +side = "BOTH" +# Here's another dependency +[[dependencies."${mod_id}"]] +modId = "minecraft" +type = "required" +# This version range declares a minimum of the current minecraft version up to but not including the next major version +versionRange = "${minecraft_version_range}" +ordering = "NONE" +side = "BOTH" + +#[[dependencies."${mod_id}"]] +#modId="create" +#type="required" +#versionRange="[6.0.9,6.1.0)" +#ordering="NONE" +#side="BOTH" + +[[dependencies."${mod_id}"]] +modId="minecolonies" +type="required" +versionRange="[1.1.1285-1.21.1-snapshot,)" +ordering="AFTER" +side="BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features."${mod_id}"] +#openGLVersion="[3.2,)"