diff --git a/.architectury-transformer/debug.log b/.architectury-transformer/debug.log
new file mode 100644
index 00000000..9c6c5e2d
--- /dev/null
+++ b/.architectury-transformer/debug.log
@@ -0,0 +1 @@
+[Architectury Transformer DEBUG] Closed File Systems for E:\Save\Minecraft_Workspace\VS-Addon-Template\common\build\libs\vs_addition-1.18.2-common-0.0.1-alpha.jar
diff --git a/.checkstyle/checkstyle.xml b/.checkstyle/checkstyle.xml
new file mode 100644
index 00000000..472fc16a
--- /dev/null
+++ b/.checkstyle/checkstyle.xml
@@ -0,0 +1,348 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000..be020b35
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,157 @@
+# Use the latest 2.1 version of CircleCI pipeline process engine.
+# See: https://circleci.com/docs/2.0/configuration-reference
+version: 2.1
+
+orbs:
+ discord: antonioned/discord@0.1.0
+
+executors:
+ java_large:
+ resource_class: large
+ docker:
+ - image: cimg/openjdk:17.0
+ environment:
+ ORG_GRADLE_PROJECT_block_external_repositories: "true"
+
+
+commands:
+ checkout_and_cache:
+ steps:
+ - checkout
+ - run: git submodule sync
+ - run: git submodule update --init
+
+ - restore_cache:
+ keys:
+ - v1-gradle-home-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }}
+ - v1-gradle-home
+ - restore_cache:
+ keys:
+ - v1-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }}
+ save_artifacts:
+ parameters:
+ artifact_name:
+ type: string
+ default: eureka
+ version:
+ type: string
+ default: ${CIRCLE_SHA1}
+ steps:
+ # Rename artifacts to "forge.jar" and "fabric.jar"
+ - run: mkdir artifacts
+ - run: find ./forge/build/libs -regextype posix-extended -regex ".*/<< parameters.artifact_name >>(-[^-]+){2}.jar" -exec bash -c 'cp $0 ./artifacts/<< parameters.artifact_name >>-forge-<< parameters.version >>.jar' {} \;
+ - run: find ./fabric/build/libs -regextype posix-extended -regex ".*/<< parameters.artifact_name >>(-[^-]+){2}.jar" -exec bash -c 'cp $0 ./artifacts/<< parameters.artifact_name >>-fabric-<< parameters.version >>.jar' {} \;
+
+ # Store artifacts
+ - store_artifacts:
+ path: artifacts
+ save_artifacts_and_generate_discord:
+ parameters:
+ artifact_name:
+ type: string
+ default: eureka
+ version:
+ type: string
+ default: ${CIRCLE_SHA1}
+ steps:
+ - save_artifacts:
+ artifact_name: << parameters.artifact_name >>
+ version: << parameters.version >>
+ - run: >-
+ echo 'export DISCORD_MSG="
+ New version, **${CIRCLE_TAG}** |
+ [Download Forge](https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/${CIRCLE_NODE_INDEX}/artifacts/<< parameters.artifact_name >>-forge-<< parameters.version >>.jar) |
+ [Download Fabric](https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/${CIRCLE_NODE_INDEX}/artifacts/<< parameters.artifact_name >>-fabric-<< parameters.version >>.jar)
+ "' >> "$BASH_ENV"
+ save_gradle_cache:
+ steps:
+ - save_cache:
+ key: v1-gradle-home-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }}
+ paths:
+ - ~/.gradle
+ - save_cache:
+ key: v1-gradle-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }}
+ paths:
+ - .gradle
+
+# Define a job to be invoked later in a workflow.
+# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
+jobs:
+ gradle_build:
+ executor: java_large
+ steps:
+ # Checkout repo
+ - checkout_and_cache
+ # Run build
+ - run:
+ name: "Build"
+ command: "./gradlew build --no-daemon --stacktrace"
+ - save_artifacts
+ - save_gradle_cache
+
+ publish_to_maven:
+ executor: java_large
+ steps:
+ # Checkout repo
+ - checkout_and_cache
+ # Run build
+ - run:
+ name: "Build"
+ command: "./gradlew build publish --no-daemon --stacktrace"
+ deploy_to_curse:
+ executor: java_large
+ steps:
+ - checkout_and_cache
+ - run: echo 'export ORG_GRADLE_PROJECT_CustomReleaseVersion=${CIRCLE_TAG}' >> "$BASH_ENV"
+ - run:
+ command: "./gradlew build modrinth curseforge --no-daemon --stacktrace --continue"
+ notify_playtesters:
+ executor: java_large
+ steps:
+ # Checkout repo
+ - checkout_and_cache
+ # Run build
+ - run:
+ name: "Build"
+ command: "./gradlew build --no-daemon --stacktrace"
+ - save_artifacts_and_generate_discord
+ - discord/status:
+ success_message: ${DISCORD_MSG}
+ success_only: true
+ webhook: ${EUREKA_118_WEBHOOK}
+
+# Invoke jobs via workflows
+# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
+workflows:
+ build:
+ jobs:
+ - gradle_build:
+ context:
+ - valkyrien_maven
+
+ - notify_playtesters:
+ filters:
+ tags:
+ only: /^playtest.*/
+ branches:
+ ignore: /.*/
+ context:
+ - discord_webhooks
+ - valkyrien_maven
+ - deploy_to_curse:
+ filters:
+ tags:
+ only: /^release.*/
+ branches:
+ ignore: /.*/
+ context:
+ - curse_publishing
+ - valkyrien_maven
+ - publish_to_maven:
+ filters:
+ branches:
+ only:
+ - /.*\/main/
+ context:
+ - valkyrien_maven
+ - github_maven
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..f61ff7a1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,49 @@
+name: Bug report
+description: Let us know about a bug that occurs without other mods
+title: '
'
+labels: bug
+assignees: []
+body:
+- type: checkboxes
+ attributes:
+ label: This issue occurs when only Valkyrien Skies and addons are installed and no other mods
+ options:
+ - label: I have tested this issue and it occurs when no other mods are installed
+ required: true
+- type: dropdown
+ attributes:
+ label: Minecraft Version
+ description: What Minecraft version does this issue occur on?
+ options:
+ - 1.16
+ - 1.18
+ - 1.19
+ - 1.20
+ validations:
+ required: true
+- type: dropdown
+ attributes:
+ label: Mod Loader
+ description: What mod loader does this issue occur on?
+ options:
+ - Forge
+ - Fabric
+ - Quilt
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Issue description
+ description: Describe what happens, and what you expect to happen instead
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Issue reproduction
+ description: Describe how to reproduce your issue
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Logs
+ description: Go to `.minecraft/logs` and drag and drop the `latest.log` and `debug.log` file into this text field
diff --git a/.github/ISSUE_TEMPLATE/compatibility_issue.yml b/.github/ISSUE_TEMPLATE/compatibility_issue.yml
new file mode 100644
index 00000000..1fa48e82
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/compatibility_issue.yml
@@ -0,0 +1,59 @@
+name: Compatibility issue
+description: Let us know about a bug that occurs when another mod is installed
+title: ''
+labels: compat
+assignees: []
+body:
+- type: markdown
+ attributes:
+ value: |
+ **Note:** do not report issues with the following mods:
+ - Optifine
+ - Magma Server (maybe try Arclight?)
+- type: input
+ attributes:
+ label: Mod Name
+ description: The name of the mod that causes the compatibility issue
+ validations:
+ required: true
+- type: checkboxes
+ attributes:
+ label: This issue occurs when only Valkyrien Skies, addons, and the mod I have specified are installed and no other mods
+ options:
+ - label: I have tested this issue and it occurs with only Valkyrien Skies, addons, and the mod I have specified
+- type: dropdown
+ attributes:
+ label: Minecraft Version
+ description: What Minecraft version does this issue occur on?
+ options:
+ - 1.16
+ - 1.18
+ - 1.19
+ validations:
+ required: true
+- type: dropdown
+ attributes:
+ label: Mod Loader
+ description: What mod loader does this issue occur on?
+ options:
+ - Forge
+ - Fabric
+ - Quilt
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Issue description
+ description: Describe what happens, and what you expect to happen instead
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Issue reproduction
+ description: Describe how to reproduce your issue
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Logs
+ description: Go to `.minecraft/logs` and drag and drop the `latest.log` and `debug.log` file into this text field
diff --git a/.github/actions/setup-gradle/action.yml b/.github/actions/setup-gradle/action.yml
new file mode 100644
index 00000000..1de13c81
--- /dev/null
+++ b/.github/actions/setup-gradle/action.yml
@@ -0,0 +1,20 @@
+name: "Setup Gradle"
+description: "Installs java and restores gradle and build cache"
+
+runs:
+ using: "composite"
+ steps:
+ - name: Install JDK 17 (Temurin)
+ uses: actions/setup-java@v2
+ with:
+ java-version: 17
+ distribution: 'temurin'
+
+ - name: Cache local .gradle and gradle packages
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ **/.gradle
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle.properties') }}
+ restore-keys: ${{ runner.os }}-gradle
diff --git a/.github/workflows/build-1.18.x.yml b/.github/workflows/build-1.18.x.yml
new file mode 100644
index 00000000..46464861
--- /dev/null
+++ b/.github/workflows/build-1.18.x.yml
@@ -0,0 +1,37 @@
+name: Build
+on:
+ push:
+ branches:
+ - '1.18.x/*'
+ pull_request:
+ types: [ opened, synchronize, reopened ]
+jobs:
+ validate-gradle:
+ name: "Validate Gradle wrapper"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true # Clone with vs-core submodule
+ - uses: gradle/wrapper-validation-action@v1
+
+ test-server:
+ name: Test Server
+ strategy:
+ matrix:
+ serverType: [ 'fabric', 'forge' ]
+ runs-on: ubuntu-latest
+ timeout-minutes: 20 # Fail after 20 minutes
+ steps:
+ - name: Shallow Clone (--recurse-submodules)
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+
+ - name: Setup Gradle & Caches
+ uses: "./.github/actions/setup-gradle"
+
+ - name: Test Server
+ uses: ValkyrienSkies/Minecraft-Architectury-Testing@v1.9
+ with:
+ serverType: ${{ matrix.serverType }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..12c0f22e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+build/
+*.ipr
+run/
+*.iws
+out/
+*.iml
+.gradle/
+output/
+bin/
+libs/
+
+.classpath
+.project
+.idea/
+classes/
+.metadata
+.vscode
+.settings
+*.launch
+
+.architectury-transformer
+jars/cbcmodernwarfare*.jar
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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
+
+ http://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.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ea3748ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# VS Addition
+
+A little addon for VS2 ~~, Create and ComputerCraft~~.
+
+Before building, please download `cbcmodernwarfare-forge-1.18.2-0.0.4a.jar` and put it in `forge/jars`.
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..cbd1b908
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,145 @@
+plugins {
+ // Needed for Forge+Fabric
+ id "architectury-plugin" version "3.4.146"
+ id "dev.architectury.loom" version "1.3.355" apply false
+ // Kotlin
+ id "org.jetbrains.kotlin.jvm" version "1.9.10" apply false
+ // Kotlin linter
+ id "org.jlleitschuh.gradle.ktlint" version "10.3.0"
+ // Java linter
+ id "checkstyle"
+
+ id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
+ id "com.modrinth.minotaur" version "2.4.5" apply false
+}
+
+// Determine the version
+String platform = String.valueOf(loader_platform).toLowerCase()
+
+version = minecraft_version + "-" + platform + "-" + mod_version
+
+architectury {
+ minecraft = rootProject.minecraft_version
+}
+
+subprojects {
+ apply plugin: "dev.architectury.loom"
+ // Apply checkstyle and ktlint to check the code style of every sub project
+ apply plugin: "org.jlleitschuh.gradle.ktlint"
+ apply plugin: "checkstyle"
+ apply plugin: "org.jetbrains.kotlin.jvm"
+
+ loom {
+ silentMojangMappingsLicense()
+ }
+
+ repositories {
+ maven { // Ritchie's Projectile Library
+ url = "https://maven.realrobotix.me/master/"
+ content {
+ includeGroup("com.rbasamoyai") // THIS IS IMPORTANT
+ }
+ }
+ maven {
+ name = "ParchmentMC"
+ url = "https://maven.parchmentmc.org"
+ }
+ maven { url = "https://maven.terraformersmc.com/releases/" } // Mod Menu
+ maven {
+ name = 'Kotlin for Forge'
+ url = 'https://thedarkcolour.github.io/KotlinForForge/'
+ }
+ maven {
+ name = 'tterrag maven'
+ url = 'https://maven.tterrag.com/'
+ }
+ maven { url = "https://api.modrinth.com/maven" } // LazyDFU, Suggestion Tweaker
+ maven { url = "https://maven.shedaniel.me/" } // Cloth Config, REI
+ maven { url = "https://mvn.devos.one/snapshots/" } // Fabric Create, Porting Lib, Forge Tags, Milk Lib
+ maven { url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/" } // Forge Config API Port
+ maven { url = "https://maven.tterrag.com/" } // Registrate, Forge Create and Flywheel
+ maven { url = "https://maven.cafeteria.dev/releases" } // Fake Player API
+ maven { url = "https://maven.jamieswhiteshirt.com/libs-release" } // Reach Entity Attributes
+// maven { url = "https://cursemaven.com" } //it has some issue, what's happened?
+ }
+
+ dependencies {
+ minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
+ // The following line declares the mojmap mappings, you may use other mappings as well
+ mappings(loom.layered {
+ officialMojangMappings()
+ parchment("org.parchmentmc.data:parchment-1.18.2:2022.09.04@zip")
+ })
+
+ implementation("org.joml:joml:1.10.4") { transitive = false }
+ implementation("org.joml:joml-primitives:1.10.0") { transitive = false }
+ }
+
+ checkstyle {
+ // configure to use checkstyle v8.41
+ toolVersion "8.41"
+ // Gradle shouldn't fail builds on checkstyle errors
+ ignoreFailures = true
+ // Checkstyle config file is in .checkstyle/checkstyle.xml
+ configFile = file("${rootDir}/.checkstyle/checkstyle.xml")
+ }
+
+ // configure checkstyle, but different
+ // https://docs.gradle.org/current/userguide/checkstyle_plugin.html
+ tasks.withType(Checkstyle) {
+ reports {
+ // Do not output html reports
+ html.required = false
+ // Output xml reports
+ xml.required = true
+ }
+ }
+
+ // configure ktlint
+ ktlint {
+ ignoreFailures = true
+ reporters {
+ // configure to output in checkstyle XML format
+ reporter "checkstyle"
+ }
+ }
+}
+
+allprojects {
+ apply plugin: "java"
+ apply plugin: "architectury-plugin"
+ apply plugin: "maven-publish"
+
+ archivesBaseName = rootProject.archives_base_name
+ version = rootProject.version
+ group = rootProject.maven_group
+
+ repositories {
+ mavenLocal()
+ maven {
+ name = 'Kotlin for Forge'
+ url = 'https://thedarkcolour.github.io/KotlinForForge/'
+ content { includeGroup "thedarkcolour" }
+ }
+ maven {
+ name = "Valkyrien Skies Internal"
+ url = project.vs_maven_url ?: 'https://maven.valkyrienskies.org'
+ if (project.vs_maven_username && project.vs_maven_password) {
+ credentials {
+ username = project.vs_maven_username
+ password = project.vs_maven_password
+ }
+ }
+ }
+ }
+
+ tasks.withType(JavaCompile) {
+ options.encoding = "UTF-8"
+
+ options.release = 17
+ }
+
+ java {
+ withSourcesJar()
+ }
+}
\ No newline at end of file
diff --git a/common/build.gradle b/common/build.gradle
new file mode 100644
index 00000000..d8d50e93
--- /dev/null
+++ b/common/build.gradle
@@ -0,0 +1,93 @@
+architectury {
+ common(rootProject.enabled_platforms.split(","))
+}
+
+loom {
+ accessWidenerPath = file("src/main/resources/vs_addition.accesswidener")
+}
+
+dependencies {
+ // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
+ // Do NOT use other classes from fabric loader
+ modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+
+ // Architectury API
+ modApi "dev.architectury:architectury:${rootProject.architectury_version}"
+
+ //Mixin Extras
+ compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5"))
+
+ // Valkyrien Skies 2
+ modApi("org.valkyrienskies:valkyrienskies-118-common:${rootProject.vs2_version}")
+
+ // VS Core
+ compileOnly("org.valkyrienskies.core:api:${rootProject.vs_core_version}")
+ compileOnly("org.valkyrienskies.core:api-game:${rootProject.vs_core_version}")
+ compileOnly("org.valkyrienskies.core:util:${rootProject.vs_core_version}")
+ compileOnly("org.valkyrienskies.core:impl:${rootProject.vs_core_version}")
+
+ api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10"
+ api "org.jetbrains.kotlin:kotlin-reflect:1.9.10"
+
+ //Common create compats,
+ //We just use a version from a platform and hope the classes exist on both versions and mixins apply correctly
+ modCompileOnly("com.simibubi.create:create-fabric-${minecraft_version}:${create_fabric_version}")
+ { exclude group: 'com.github.AlphaMode', module: 'fakeconfigtoml' }
+ modCompileOnly("net.fabricmc.fabric-api:fabric-api:${fabric_api_version}")
+ modCompileOnly("com.jozufozu.flywheel:flywheel-fabric-${minecraft_version}:${flywheel_version_fabric}")
+ modCompileOnly("io.github.fabricators_of_create:Porting-Lib:${port_lib_version}+${minecraft_version}")
+
+ // CBC
+ // Change the previous section
+ modImplementation("com.rbasamoyai:ritchiesprojectilelib:1.0.0-c5d3ea1+1.18.2-common") { transitive = false }
+ modCompileOnly("maven.modrinth:create-big-cannons:eLFsS1Ui")
+
+ //ComputerCraft
+ modCompileOnly("maven.modrinth:cc-restitched:1.100.8+1.18.2")
+
+ //Eureka
+ modCompileOnly("maven.modrinth:eureka:1.18.2-fabric-1.4.0-beta.1")
+
+ //Clockwork
+ modCompileOnly("maven.modrinth:create-clockwork:8JdnD9wo")
+
+ //Copycat+
+ modCompileOnly("maven.modrinth:copycats:g3m9RPx3")
+}
+
+publishing {
+ publications {
+ mavenCommon(MavenPublication) {
+ groupId = "org.valkyrienskies.eureka"
+ version = project.version
+ artifactId = rootProject.archives_base_name + "-" + project.name
+ from components.java
+ }
+ }
+
+ // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
+ repositories {
+ if (project.vs_maven_username && project.vs_maven_password) {
+ println "Publishing to VS Maven"
+ maven {
+ url = project.vs_maven_url
+ credentials {
+ username = project.vs_maven_username
+ password = project.vs_maven_password
+ }
+ }
+ }
+ // Add repositories to publish to here.
+ if (System.getenv("GITHUB_ACTOR") != null) {
+ println "Publishing to Github Packages"
+ maven {
+ name = "GitHubPackages"
+ url = uri("https://maven.pkg.github.com/ValkyrienSkies/Eureka")
+ credentials {
+ username = System.getenv("GITHUB_ACTOR")
+ password = System.getenv("GITHUB_TOKEN")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/gradle.properties b/common/gradle.properties
new file mode 100644
index 00000000..f148e470
--- /dev/null
+++ b/common/gradle.properties
@@ -0,0 +1,2 @@
+port_lib_version=1.2.677-beta
+port_lib_hash=cca931b
\ No newline at end of file
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/PeripheralCommon.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/PeripheralCommon.java
new file mode 100644
index 00000000..2c5fae76
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/PeripheralCommon.java
@@ -0,0 +1,55 @@
+package io.github.xiewuzhiying.vs_addition.compats.computercraft;
+
+
+import dan200.computercraft.api.peripheral.IPeripheral;
+import io.github.xiewuzhiying.vs_addition.VSAdditionMod;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.peripherals.CannonMountPeripheral;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.peripherals.FlapBearingPeripheral;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.peripherals.ShipHelmPeripheral;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.state.BlockState;
+import org.valkyrienskies.clockwork.ClockworkBlocks;
+import org.valkyrienskies.clockwork.content.contraptions.flap.FlapBearingBlockEntity;
+import org.valkyrienskies.eureka.EurekaBlocks;
+import org.valkyrienskies.eureka.blockentity.ShipHelmBlockEntity;
+import rbasamoyai.createbigcannons.cannon_control.cannon_mount.CannonMountBlockEntity;
+import rbasamoyai.createbigcannons.index.CBCBlocks;
+
+import javax.annotation.Nullable;
+
+public class PeripheralCommon {
+ private boolean c(BlockState arg1, Block arg2) { return arg1.getBlock() == arg2; }
+
+ @Nullable
+ public IPeripheral getPeripheralCommon(Level level, BlockPos blockPos, Direction direction){
+ BlockState s = level.getBlockState(blockPos);
+ BlockEntity be = level.getBlockEntity(blockPos);
+ if (VSAdditionMod.getCBC_ACTIVE() && c(s, CBCBlocks.CANNON_MOUNT.get())) {
+ return new CannonMountPeripheral("cbc_cannon_mount", (CannonMountBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getCLOCKWORK_ACTIVE() && c(s, ClockworkBlocks.FLAP_BEARING.get())) {
+ return new FlapBearingPeripheral("clockwork_flap_bearing", (FlapBearingBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getACACIA_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getCRIMSON_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getBIRCH_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getSPRUCE_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getWARPED_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getJUNGLE_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getOAK_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else if (VSAdditionMod.getEUREKA_ACTIVE() && c(s, EurekaBlocks.INSTANCE.getDARK_OAK_SHIP_HELM().get())) {
+ return new ShipHelmPeripheral("eureka_ship_helm", (ShipHelmBlockEntity) be, level, blockPos, direction);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/VSAdditionCC.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/VSAdditionCC.java
new file mode 100644
index 00000000..1a22d506
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/VSAdditionCC.java
@@ -0,0 +1,12 @@
+package io.github.xiewuzhiying.vs_addition.compats.computercraft;
+
+import dan200.computercraft.shared.computer.core.ServerComputer;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.apis.CryptoAPI;
+import net.minecraft.server.level.ServerLevel;
+
+
+public class VSAdditionCC{
+ public static void applyCCAPIs(ServerComputer computer, ServerLevel level){
+ computer.addAPI(new CryptoAPI());
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/apis/CryptoAPI.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/apis/CryptoAPI.java
new file mode 100644
index 00000000..b9c90029
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/apis/CryptoAPI.java
@@ -0,0 +1,277 @@
+package io.github.xiewuzhiying.vs_addition.compats.computercraft.apis;
+
+import dan200.computercraft.api.lua.IArguments;
+import dan200.computercraft.api.lua.ILuaAPI;
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.LuaFunction;
+
+import javax.crypto.*;
+import javax.crypto.spec.GCMParameterSpec;
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CryptoAPI implements ILuaAPI {
+
+ private final String ALGO = "AES_256/GCM/NoPadding";
+ public final int AES_KEY_SIZE = 256;
+ public final int GCM_IV_LENGTH = 12;
+ public final int TLEN = 128;
+
+ @Override
+ public String[] getNames() {
+ return new String[] {"cpt", "crypto"};
+ }
+
+ private Key generateKey(String key_string) throws NoSuchAlgorithmException, NoSuchProviderException {
+ KeyGenerator kg = KeyGenerator.getInstance("AES");
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
+ sr.setSeed(key_string.getBytes(StandardCharsets.UTF_8));
+ kg.init(AES_KEY_SIZE, sr);
+ return kg.generateKey();
+ }
+
+ private String bytesToHexString(byte[] src) {
+ StringBuilder sb = new StringBuilder();
+ if (src == null || src.length == 0) {
+ return null;
+ }
+ for (int j = 0; j < src.length; j++) {
+ int v = src[j] & 0xFF;
+ String hv = Integer.toHexString(v);
+ if (hv.length() < 2) {
+ sb.append(0);
+ }
+ sb.append(hv);
+ }
+ return sb.toString().toUpperCase();
+ }
+
+ private byte[] hexStringToBytes(String src) {
+ if (src == null || src.equals("")) {
+ return null;
+ }
+ src = src.toUpperCase();
+ int len = src.length() / 2;
+ char[] hexChars = src.toCharArray();
+ byte[] b = new byte[len];
+ for (int i = 0; i < len; i++) {
+ int pos = i * 2;
+ b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+ }
+ return b;
+ }
+
+ private byte charToByte(char c) {
+ return (byte) "0123456789ABCDEF".indexOf(c);
+ }
+
+ @LuaFunction
+ public final Object SHA512HASH(IArguments raw_data) {
+ MessageDigest sha512 = null;
+ try {
+ sha512 = MessageDigest.getInstance("SHA3-512");
+ } catch (NoSuchAlgorithmException ignored) {
+
+ }
+ try {
+ sha512.update(raw_data.getString(0).getBytes(StandardCharsets.UTF_8));
+ } catch (LuaException ignored) {
+
+ }
+ byte[] hash = sha512.digest();
+ return bytesToHexString(hash);
+ }
+
+ @LuaFunction
+ public final Object SHA256HASH(IArguments raw_data) throws LuaException {
+ MessageDigest sha256 = null;
+ try {
+ sha256 = MessageDigest.getInstance("SHA3-256");
+ } catch (NoSuchAlgorithmException ignored) {
+
+ }
+ sha256.update(raw_data.getString(0).getBytes(StandardCharsets.UTF_8));
+ byte[] hash = sha256.digest();
+ return bytesToHexString(hash);
+ }
+
+ @LuaFunction
+ public final Object AESGCMEncrypt(IArguments args) throws LuaException {
+ Cipher cipher = null;
+ try {
+ cipher = Cipher.getInstance(ALGO);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException ignored) {
+
+ }
+ byte[] iv = new byte[GCM_IV_LENGTH];
+ SecureRandom sr = new SecureRandom();
+ sr.nextBytes(iv);
+ try {
+ cipher.init(Cipher.ENCRYPT_MODE, generateKey(args.getString(1)), new GCMParameterSpec(TLEN, iv));
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException ignored) {
+
+ }
+ byte[] textBytes = args.getString(0).getBytes(StandardCharsets.UTF_8);
+ byte[] encryptBytes = new byte[0];
+ try {
+ encryptBytes = cipher.doFinal(textBytes);
+ } catch (IllegalBlockSizeException | BadPaddingException ignored) {
+
+ }
+ byte[] msg = new byte[GCM_IV_LENGTH + encryptBytes.length];
+ System.arraycopy(iv, 0, msg, 0, GCM_IV_LENGTH);
+ System.arraycopy(encryptBytes, 0, msg, GCM_IV_LENGTH, encryptBytes.length);
+ return bytesToHexString(msg);
+ }
+
+ @LuaFunction
+ public final Object AESGCMDecrypt(IArguments args) throws LuaException {
+ byte[] bytes = hexStringToBytes(args.getString(0));
+ byte[] iv = new byte[GCM_IV_LENGTH];
+ byte[] content = new byte[bytes.length - GCM_IV_LENGTH];
+ System.arraycopy(bytes, 0, iv, 0, GCM_IV_LENGTH);
+ System.arraycopy(bytes, GCM_IV_LENGTH, content, 0, content.length);
+ Cipher cipher = null;
+ try {
+ cipher = Cipher.getInstance(ALGO);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException ignored) {
+
+ }
+ GCMParameterSpec params = new GCMParameterSpec(TLEN, iv);
+ try {
+ cipher.init(Cipher.DECRYPT_MODE, generateKey(args.getString(1)), params);
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException ignored) {
+
+ }
+ byte[] finals = new byte[0];
+ try {
+ finals = cipher.doFinal(content);
+ } catch (IllegalBlockSizeException | BadPaddingException ignored) {
+
+ }
+ return new String(finals, StandardCharsets.UTF_8);
+ }
+
+ @LuaFunction
+ public final Map GenECDHKeypair() {
+ KeyPairGenerator kpg = null;
+ try {
+ kpg = KeyPairGenerator.getInstance("EC");
+ } catch (NoSuchAlgorithmException ignored) {
+
+ }
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
+ try {
+ kpg.initialize(ecSpec);
+ } catch (InvalidAlgorithmParameterException ignored) {
+
+ }
+ KeyPair kp = kpg.generateKeyPair();
+ String pri = bytesToHexString(kp.getPrivate().getEncoded());
+ String pub = bytesToHexString(kp.getPublic().getEncoded());
+ Map map = new HashMap<>();
+ map.put("pri", pri);
+ map.put("pub", pub);
+ return map;
+ }
+
+ @LuaFunction
+ public final Object GetECDHFinalKey(IArguments args) throws LuaException {
+ X509EncodedKeySpec x509 = new X509EncodedKeySpec(hexStringToBytes(args.getString(0)));
+ PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(hexStringToBytes(args.getString(1)));
+ KeyFactory kf = null;
+ try {
+ kf = KeyFactory.getInstance("EC");
+ } catch (NoSuchAlgorithmException ignored) {
+
+ }
+ PublicKey pk = null;
+ try {
+ pk = kf.generatePublic(x509);
+ } catch (InvalidKeySpecException ignored) {
+
+ }
+ PrivateKey priK = null;
+ try {
+ priK = kf.generatePrivate(pkcs);
+ } catch (InvalidKeySpecException ignored) {
+
+ }
+ KeyAgreement kam = null;
+ try {
+ kam = KeyAgreement.getInstance("ECDH");
+ } catch (NoSuchAlgorithmException ignored) {
+
+ }
+ try {
+ kam.init(priK);
+ } catch (InvalidKeyException ignored) {
+
+ }
+ try {
+ kam.doPhase(pk, true);
+ } catch (InvalidKeyException ignored) {
+
+ }
+ byte[] sSecret = kam.generateSecret();
+ return bytesToHexString(sSecret);
+ }
+
+ @LuaFunction
+ public final String MakeECDSASign(IArguments args) throws LuaException {
+ String input = args.getString(0);
+ String pri = args.getString(1);
+ String result = "";
+ byte[] signedBytes;
+ try {
+ Signature sign = Signature.getInstance("SHA256withECDSA");
+ PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(hexStringToBytes(pri));
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ PrivateKey key = kf.generatePrivate(pkcs);
+ sign.initSign(key);
+ sign.update(input.getBytes());
+ signedBytes = sign.sign();
+ result = bytesToHexString(signedBytes);
+ } catch (Exception ignored) {
+
+ }
+ return result;
+ }
+
+ @LuaFunction
+ public final boolean CheckECDSASign(IArguments args) throws LuaException {
+ String input = args.getString(0);
+ String pub = args.getString(1);
+ String data = args.getString(2);
+ boolean result = false;
+ try {
+ Signature signature = Signature.getInstance("SHA256withECDSA");
+ X509EncodedKeySpec x509 = new X509EncodedKeySpec(hexStringToBytes(pub));
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ PublicKey key = kf.generatePublic(x509);
+ signature.initVerify(key);
+ signature.update(data.getBytes());
+ result = signature.verify(hexStringToBytes(input));
+ } catch (Exception ignored) {
+ }
+ return result;
+ }
+
+ @LuaFunction
+ public final String Salt() {
+ byte[] b = new byte[0];
+ try {
+ SecureRandom sr = SecureRandom.getInstanceStrong();
+ b = new byte[128];
+ sr.nextBytes(b);
+ } catch (NoSuchAlgorithmException ignored) {
+ }
+ return bytesToHexString(b);
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/apis/DeflateAPI.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/apis/DeflateAPI.java
new file mode 100644
index 00000000..ca8fb441
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/apis/DeflateAPI.java
@@ -0,0 +1,40 @@
+//package io.github.xiewuzhiying.vs_addition.compats.computercraft.apis;
+//
+//import dan200.computercraft.api.lua.ILuaAPI;
+//import dan200.computercraft.api.lua.LuaFunction;
+//
+//import java.io.UnsupportedEncodingException;
+//import java.util.zip.DataFormatException;
+//import java.util.zip.Deflater;
+//import java.util.zip.Inflater;
+//
+//public class DeflateAPI implements ILuaAPI {
+// @Override
+// public String[] getNames() {
+// return new String[] {"deflate"};
+// }
+//
+// @LuaFunction
+// public final Object compression(String input) throws UnsupportedEncodingException {
+// byte[] data = input.getBytes("UTF-8");
+//
+// // Compress the bytes
+// byte[] output = new byte[100];
+// Deflater compresser = new Deflater();
+// compresser.setInput(data);
+// compresser.finish();
+// int compressedDataLength = compresser.deflate(output);
+// compresser.end();
+// return output;
+// }
+//
+// @LuaFunction
+// public final Object decompression(String input, Double compressedDataLength) throws DataFormatException {
+// Inflater decompresser = new Inflater();
+// decompresser.setInput(input.getBytes(), 0, (int) Math.floor(compressedDataLength));
+// byte[] result = new byte[100];
+// int resultLength = decompresser.inflate(result);
+// decompresser.end();
+// return result;
+// }
+//}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/CannonMountPeripheral.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/CannonMountPeripheral.java
new file mode 100644
index 00000000..7a1554eb
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/CannonMountPeripheral.java
@@ -0,0 +1,127 @@
+package io.github.xiewuzhiying.vs_addition.compats.computercraft.peripherals;
+
+import dan200.computercraft.api.lua.IArguments;
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import io.github.xiewuzhiying.vs_addition.mixin.createbigcannons.CannonMountBlockEntityAccessor;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import rbasamoyai.createbigcannons.cannon_control.cannon_mount.CannonMountBlockEntity;
+
+public class CannonMountPeripheral implements IPeripheral {
+
+ private final String type ;
+ private final CannonMountBlockEntity tileEntity;
+
+ private final Level level;
+
+ private final BlockPos worldPosition;
+
+ private final Direction direction;
+
+ public CannonMountPeripheral(String type, CannonMountBlockEntity tileEntity, Level level, BlockPos blockPos, Direction direction) {
+ this.type = type;
+ this.tileEntity = tileEntity;
+ this.level = level;
+ this.worldPosition = blockPos;
+ this.direction = direction;
+ }
+
+ @NotNull
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(@Nullable IPeripheral iPeripheral) {
+ return iPeripheral == this;
+ }
+
+ @Override
+ public Object getTarget() {
+ return this.tileEntity;
+ }
+
+
+ @LuaFunction(mainThread = true)
+ public final void setPitch(double value){
+ if(this.isRunning())
+ this.tileEntity.setPitch((float) value);
+ }
+
+ @LuaFunction(mainThread = true)
+ public final void setYaw(double value){
+ if(this.isRunning())
+ this.tileEntity.setYaw((float) value);
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object assemble(){
+ if(!this.isRunning()) {
+ ((CannonMountBlockEntityAccessor) this.tileEntity).Assemble();
+ return true;
+ }
+ return false;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object disassemble() {
+ if(this.isRunning()) {
+ this.tileEntity.disassemble();
+ this.tileEntity.sendData();
+ return true;
+ }
+ return false;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final void fire() {
+ if(this.isRunning()) {
+ this.tileEntity.getContraption().tryFiringShot();
+ }
+ }
+
+ @LuaFunction
+ public final boolean isRunning(){
+ return this.tileEntity.isRunning();
+ }
+
+ @LuaFunction
+ public final Object getPitchOffset(IArguments partialTicks) throws LuaException {
+ if(this.isRunning()) {
+ double value = partialTicks.optDouble(0).orElse(0.0);
+ return (double) this.tileEntity.getPitchOffset((float) value);
+ }
+ return false;
+ }
+
+ @LuaFunction
+ public final Object getYawOffset(IArguments partialTicks) throws LuaException {
+ if(this.isRunning()) {
+ double value = partialTicks.optDouble(0).orElse(0.0);
+ return (double) this.tileEntity.getYawOffset((float) value);
+ }
+ return false;
+ }
+
+ @LuaFunction
+ public final Object getMaxDepress() {
+ if(this.isRunning()) {
+ return (double) ((CannonMountBlockEntityAccessor) this.tileEntity).GetMaxDepress();
+ }
+ return false;
+ }
+
+ @LuaFunction
+ public final Object getMaxElevate() {
+ if(this.isRunning()) {
+ return (double) ((CannonMountBlockEntityAccessor) this.tileEntity).GetMaxElevate();
+ }
+ return false;
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/FlapBearingPeripheral.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/FlapBearingPeripheral.java
new file mode 100644
index 00000000..01522f93
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/FlapBearingPeripheral.java
@@ -0,0 +1,64 @@
+package io.github.xiewuzhiying.vs_addition.compats.computercraft.peripherals;
+
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.valkyrienskies.clockwork.content.contraptions.flap.FlapBearingBlockEntity;
+
+public class FlapBearingPeripheral implements IPeripheral {
+
+ private final String type ;
+ private final FlapBearingBlockEntity tileEntity;
+
+ public FlapBearingPeripheral(String type, FlapBearingBlockEntity tileEntity, Level level, BlockPos blockPos, Direction direction) {
+ this.type = type;
+ this.tileEntity = tileEntity;
+ }
+
+ @NotNull
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(@Nullable IPeripheral iPeripheral) {
+ return iPeripheral == this;
+ }
+
+ @Override
+ public Object getTarget() {
+ return this.tileEntity;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object setAngle(double angle){
+ if(this.tileEntity.isRunning()) {
+ this.tileEntity.setAngle((float) angle);
+ return true;
+ }
+ return false;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object assemble(){
+ if(!this.tileEntity.isRunning()){
+ this.tileEntity.assemble();
+ return true;
+ }
+ return false;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object disassemble(){
+ if(this.tileEntity.isRunning()){
+ this.tileEntity.disassemble();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/ShipHelmPeripheral.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/ShipHelmPeripheral.java
new file mode 100644
index 00000000..7bf5b863
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/compats/computercraft/peripherals/ShipHelmPeripheral.java
@@ -0,0 +1,52 @@
+package io.github.xiewuzhiying.vs_addition.compats.computercraft.peripherals;
+
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import io.github.xiewuzhiying.vs_addition.mixin.createbigcannons.CannonMountBlockEntityAccessor;
+import io.github.xiewuzhiying.vs_addition.mixin.vs_eureka.ShipHelmBlockEntityAccessor;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.valkyrienskies.eureka.blockentity.ShipHelmBlockEntity;
+import org.valkyrienskies.eureka.ship.EurekaShipControl;
+
+public class ShipHelmPeripheral implements IPeripheral {
+
+ private final String type ;
+ private final ShipHelmBlockEntity tileEntity;
+
+ private final EurekaShipControl shipControl;
+
+ public ShipHelmPeripheral(String type, ShipHelmBlockEntity tileEntity, Level level, BlockPos blockPos, Direction direction) {
+ this.type = type;
+ this.tileEntity = tileEntity;
+ this.shipControl = ((ShipHelmBlockEntityAccessor)(Object) this.tileEntity).GetControl();
+ }
+
+ @NotNull
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(@Nullable IPeripheral iPeripheral) {
+ return iPeripheral == this;
+ }
+
+ @Override
+ public Object getTarget() {
+ return this.tileEntity;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object disassemble() {
+ if(this.tileEntity.getAssembled()){
+ this.tileEntity.disassemble();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/content/wing/CopycatWingBlock.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/content/wing/CopycatWingBlock.java
new file mode 100644
index 00000000..f519fb1c
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/content/wing/CopycatWingBlock.java
@@ -0,0 +1,222 @@
+//package io.github.xiewuzhiying.vs_addition.content.wing;
+//
+//import com.copycatsplus.copycats.CCShapes;
+//import com.copycatsplus.copycats.Copycats;
+//import com.copycatsplus.copycats.content.copycat.base.CTWaterloggedCopycatBlock;
+//import com.simibubi.create.content.schematics.requirement.ISpecialBlockItemRequirement;
+//import com.simibubi.create.content.schematics.requirement.ItemRequirement;
+//import com.simibubi.create.foundation.utility.VoxelShaper;
+//import net.minecraft.core.BlockPos;
+//import net.minecraft.core.Direction;
+//import net.minecraft.server.level.ServerLevel;
+//import net.minecraft.world.InteractionResult;
+//import net.minecraft.world.entity.player.Player;
+//import net.minecraft.world.item.ItemStack;
+//import net.minecraft.world.item.context.BlockPlaceContext;
+//import net.minecraft.world.item.context.UseOnContext;
+//import net.minecraft.world.level.BlockAndTintGetter;
+//import net.minecraft.world.level.BlockGetter;
+//import net.minecraft.world.level.Level;
+//import net.minecraft.world.level.block.Block;
+//import net.minecraft.world.level.block.Mirror;
+//import net.minecraft.world.level.block.Rotation;
+//import net.minecraft.world.level.block.entity.BlockEntity;
+//import net.minecraft.world.level.block.state.BlockState;
+//import net.minecraft.world.level.block.state.StateDefinition;
+//import net.minecraft.world.level.block.state.properties.BlockStateProperties;
+//import net.minecraft.world.level.block.state.properties.DirectionProperty;
+//import net.minecraft.world.level.block.state.properties.IntegerProperty;
+//import net.minecraft.world.level.pathfinder.PathComputationType;
+//import net.minecraft.world.phys.shapes.CollisionContext;
+//import net.minecraft.world.phys.shapes.VoxelShape;
+//import org.jetbrains.annotations.NotNull;
+//import org.jetbrains.annotations.Nullable;
+//
+//import java.util.List;
+//
+//import static net.minecraft.core.Direction.UP;
+//
+//public class CopycatWingBlock extends CTWaterloggedCopycatBlock implements ISpecialBlockItemRequirement {
+//
+//
+// public static final DirectionProperty FACING = BlockStateProperties.FACING;
+// public static final IntegerProperty LAYERS = BlockStateProperties.LAYERS;
+//
+// private static final VoxelShaper[] SHAPE_BY_LAYER = new VoxelShaper[]{CCShapes.EMPTY, CCShapes.LAYER_2PX, CCShapes.LAYER_4PX, CCShapes.LAYER_6PX, CCShapes.LAYER_8PX, CCShapes.LAYER_10PX, CCShapes.LAYER_12PX, CCShapes.LAYER_14PX, CCShapes.LAYER_16PX};
+//
+// public CopycatWingBlock(Properties pProperties) {
+// super(pProperties);
+// registerDefaultState(defaultBlockState().setValue(FACING, UP));
+// }
+//
+// @Override
+// public boolean isIgnoredConnectivitySide(BlockAndTintGetter reader, BlockState state, Direction face, BlockPos fromPos, BlockPos toPos) {
+// Direction facing = state.getValue(FACING);
+// BlockState toState = reader.getBlockState(toPos);
+//
+// return !toState.is(this);
+// }
+//
+// @Override
+// public BlockState getStateForPlacement(BlockPlaceContext context) {
+// BlockState stateForPlacement = super.getStateForPlacement(context);
+// assert stateForPlacement != null;
+// BlockPos blockPos = context.getClickedPos();
+// BlockState state = context.getLevel().getBlockState(blockPos);
+// if (state.is(this)) {
+// if (state.getValue(LAYERS) < 8)
+// return state.cycle(LAYERS);
+// else {
+// Copycats.LOGGER.warn("Can't figure out where to place a layer! Please file an issue if you see this.");
+// return state;
+// }
+// } else {
+// return stateForPlacement.setValue(FACING, context.getNearestLookingDirection().getOpposite());
+// }
+// }
+//
+// @SuppressWarnings("deprecation")
+// @Override
+// public boolean canBeReplaced(@NotNull BlockState pState, BlockPlaceContext pUseContext) {
+// ItemStack itemstack = pUseContext.getItemInHand();
+// if (!itemstack.is(this.asItem())) return false;
+// if (pState.getValue(LAYERS) == 8) return false;
+// return pState.getValue(FACING) == pUseContext.getClickedFace();
+// }
+//
+// @Override
+// public InteractionResult onSneakWrenched(BlockState state, UseOnContext context) {
+// if (state.getValue(LAYERS) <= 1)
+// return super.onSneakWrenched(state, context);
+//
+// Level world = context.getLevel();
+// BlockPos pos = context.getClickedPos();
+// Player player = context.getPlayer();
+// if (world instanceof ServerLevel serverLevel) {
+// if (player != null && !player.isCreative()) {
+// // Respect loot tables
+// List drops = Block.getDrops(state.setValue(LAYERS, 1), serverLevel, pos, world.getBlockEntity(pos), player, context.getItemInHand());
+// for (ItemStack drop : drops) {
+// player.getInventory().placeItemBackInInventory(drop);
+// }
+// }
+// BlockPos up = pos.relative(Direction.UP);
+// // need to call updateShape before setBlock to schedule a tick for water
+// world.setBlockAndUpdate(pos, state.setValue(LAYERS, state.getValue(LAYERS) - 1).updateShape(Direction.UP, world.getBlockState(up), world, pos, up));
+// playRemoveSound(world, pos);
+// }
+// return InteractionResult.SUCCESS;
+// }
+//
+// @Override
+// public ItemRequirement getRequiredItems(BlockState state, BlockEntity blockEntity) {
+// return new ItemRequirement(
+// ItemRequirement.ItemUseType.CONSUME,
+// new ItemStack(asItem(), state.getValue(LAYERS))
+// );
+// }
+//
+//
+// public boolean canConnectTexturesToward(BlockAndTintGetter reader, BlockPos fromPos, BlockPos toPos, BlockState state) {
+// BlockState toState = reader.getBlockState(toPos);
+// if (!toState.is(this)) return false;
+// if (!state.is(this)) return false;
+// Direction facing = state.getValue(FACING);
+//
+// if (toPos.equals(fromPos.relative(facing))) return false;
+//
+// BlockPos diff = fromPos.subtract(toPos);
+// int coord = facing.getAxis().choose(diff.getX(), diff.getY(), diff.getZ());
+//
+// if (!toState.is(this)) return coord != -facing.getAxisDirection().getStep();
+//
+// if (isOccluded(state, toState, facing)) return true;
+// if (toState.setValue(WATERLOGGED, false) == state.setValue(WATERLOGGED, false) && coord == 0) return true;
+// return false;
+// }
+//
+// @Nullable
+// @Override
+// public BlockState getConnectiveMaterial(BlockAndTintGetter reader, BlockState otherState, Direction face, BlockPos fromPos, BlockPos toPos) {
+// return (canConnectTexturesToward(reader, fromPos, toPos, reader.getBlockState(fromPos)) ? getMaterial(reader, toPos) : null);
+// }
+//
+// private static boolean isOccluded(BlockState state, BlockState other, Direction pDirection) {
+// state = state.setValue(WATERLOGGED, false);
+// other = other.setValue(WATERLOGGED, false);
+// Direction facing = state.getValue(FACING);
+// if (facing.getOpposite() == other.getValue(FACING) && pDirection == facing) return true;
+// if (other.getValue(FACING) != facing) return false;
+// return pDirection.getAxis() != facing.getAxis();
+// }
+//
+// @SuppressWarnings("deprecation")
+// @Override
+// public @NotNull BlockState rotate(BlockState state, Rotation rot) {
+// return state.setValue(FACING, rot.rotate(state.getValue(FACING)));
+// }
+//
+// @Override
+// @SuppressWarnings("deprecation")
+// public @NotNull BlockState mirror(BlockState state, Mirror mirrorIn) {
+// return state.rotate(mirrorIn.getRotation(state.getValue(FACING)));
+// }
+//
+// @SuppressWarnings("deprecation")
+// @Override
+// public boolean isPathfindable(@NotNull BlockState pState, @NotNull BlockGetter pLevel, @NotNull BlockPos pPos, @NotNull PathComputationType pType) {
+// return switch (pType) {
+// case LAND -> pState.getValue(LAYERS) < 5 && pState.getValue(FACING).equals(UP);
+// default -> false;
+// };
+// }
+//
+// @Override
+// protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) {
+// super.createBlockStateDefinition(pBuilder.add(FACING).add(LAYERS));
+// }
+//
+// @SuppressWarnings("deprecation")
+// @Override
+// public @NotNull VoxelShape getShape(BlockState pState, @NotNull BlockGetter pLevel, @NotNull BlockPos pPos, @NotNull CollisionContext pContext) {
+// return SHAPE_BY_LAYER[pState.getValue(LAYERS)].get(pState.getValue(FACING));
+// }
+//
+//
+// public boolean supportsExternalFaceHiding(BlockState state) {
+// return true;
+// }
+//
+//
+// public boolean hidesNeighborFace(BlockGetter level, BlockPos pos, BlockState state, BlockState neighborState, Direction dir) {
+// Direction facing = state.getValue(FACING);
+// int layers = state.getValue(LAYERS);
+// if (state.is(this) == neighborState.is(this)) {
+// Direction neighborFacing = neighborState.getValue(FACING);
+// int neighborLayers = neighborState.getValue(LAYERS);
+// if (getMaterial(level, pos).skipRendering(getMaterial(level, pos.relative(dir)), dir.getOpposite())) {
+// return neighborFacing == facing && neighborLayers == layers || // cull the sides if two copycats of the same height are next to each other
+// // cull if both sides have a square block face
+// (neighborFacing == facing.getOpposite() || neighborLayers == 8) && facing == dir.getOpposite() ||
+// (neighborFacing == facing.getOpposite() || layers == 8) && neighborFacing == dir ||
+// layers == 8 && neighborLayers == 8;
+// }
+// }
+// return false;
+// }
+//
+// @Override
+// public @NotNull VoxelShape getOcclusionShape(BlockState pState, BlockGetter level, BlockPos pos) {
+// return SHAPE_BY_LAYER[pState.getValue(LAYERS)].get(pState.getValue(FACING));
+// }
+//
+// @Override
+// public boolean useShapeForLightOcclusion(BlockState state) {
+// return true;
+// }
+//
+// @Override
+// public float getShadeBrightness(BlockState state, BlockGetter level, BlockPos pos) {
+// return state.getValue(LAYERS) == 8 ? 0.2f : 1.0f;
+// }
+//}
\ No newline at end of file
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/content/wing/CopycatWingModel.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/content/wing/CopycatWingModel.java
new file mode 100644
index 00000000..d6218f2b
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/content/wing/CopycatWingModel.java
@@ -0,0 +1,50 @@
+//package io.github.xiewuzhiying.vs_addition.content.wing;
+//
+//import com.copycatsplus.copycats.content.copycat.base.model.SimpleCopycatPart;
+//import com.copycatsplus.copycats.content.copycat.layer.CopycatLayerBlock;
+//import net.minecraft.core.Direction;
+//import net.minecraft.world.level.block.state.BlockState;
+//
+//import static com.copycatsplus.copycats.content.copycat.base.model.QuadHelper.CopycatRenderContext;
+//import static com.copycatsplus.copycats.content.copycat.base.model.QuadHelper.MutableCullFace.*;
+//import static com.copycatsplus.copycats.content.copycat.base.model.QuadHelper.assemblePiece;
+//
+//
+//public class CopycatWingModel implements SimpleCopycatPart {
+//
+// @Override
+// public void emitCopycatQuads(BlockState state, CopycatRenderContext context, BlockState material) {
+// int layer = state.getValue(CopycatWingBlock.LAYERS);
+// Direction facing = state.getValue(CopycatWingBlock.FACING);
+//
+// if (facing.getAxis().isVertical()) {
+// boolean flipY = facing == Direction.DOWN;
+// assemblePiece(
+// context, 0, flipY,
+// vec3(0, 0, 0),
+// aabb(16, layer, 16),
+// cull(UP)
+// );
+// assemblePiece(
+// context, 0, flipY,
+// vec3(0, layer, 0),
+// aabb(16, layer, 16).move(0, 16 - layer, 0),
+// cull(DOWN)
+// );
+// } else {
+// int rot = (int) facing.toYRot();
+// assemblePiece(
+// context, rot, false,
+// vec3(0, 0, 0),
+// aabb(16, 16, layer),
+// cull(SOUTH)
+// );
+// assemblePiece(
+// context, rot, false,
+// vec3(0, 0, layer),
+// aabb(16, 16, layer).move(0, 0, 16 - layer),
+// cull(NORTH)
+// );
+// }
+// }
+//}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/computercraft/MixinRedstoneAPI.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/computercraft/MixinRedstoneAPI.java
new file mode 100644
index 00000000..87150e41
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/computercraft/MixinRedstoneAPI.java
@@ -0,0 +1,31 @@
+package io.github.xiewuzhiying.vs_addition.mixin.computercraft;
+
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.core.apis.IAPIEnvironment;
+import dan200.computercraft.core.apis.RedstoneAPI;
+import dan200.computercraft.core.computer.ComputerSide;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.Shadow;
+
+@Mixin(RedstoneAPI.class)
+public abstract class MixinRedstoneAPI{
+
+ @Mutable
+ @Final
+ @Shadow
+ private final IAPIEnvironment environment;
+
+ public MixinRedstoneAPI(IAPIEnvironment environment) {
+ this.environment = environment;
+ }
+
+ @LuaFunction( { "setAnalogOutput2", "setAnalogueOutput2" } )
+ public final void setAnalogOutput2( ComputerSide side, int value ) throws LuaException
+ {
+ if( value < 0) throw new LuaException( "Expected number in range >= 0" );
+ environment.setOutput( side, value );
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/computercraft/MixinWirelessModemPeripheral.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/computercraft/MixinWirelessModemPeripheral.java
new file mode 100644
index 00000000..27c4bcfa
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/computercraft/MixinWirelessModemPeripheral.java
@@ -0,0 +1,37 @@
+package io.github.xiewuzhiying.vs_addition.mixin.computercraft;
+
+import com.llamalad7.mixinextras.sugar.Local;
+import com.llamalad7.mixinextras.sugar.ref.LocalRef;
+import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
+import dan200.computercraft.shared.peripheral.modem.ModemState;
+import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec3;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import org.valkyrienskies.core.api.ships.Ship;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+
+@Mixin(WirelessModemPeripheral.class)
+public abstract class MixinWirelessModemPeripheral extends ModemPeripheral{
+ protected MixinWirelessModemPeripheral(ModemState state) {
+ super(state);
+ }
+ @Inject(
+ method = "getRange",
+ at = @At(
+ value = "INVOKE",
+ target = "Ldan200/computercraft/shared/peripheral/modem/wireless/WirelessModemPeripheral;getPosition()Lnet/minecraft/world/phys/Vec3;",
+ shift = At.Shift.AFTER
+ )
+ )
+ public void whenPositionOnShip(CallbackInfoReturnable cir, @Local Level world, @Local LocalRef position){
+ final Ship ship = VSGameUtilsKt.getShipManagingPos(world, position.get());
+ if (ship != null) {
+ position.set(VSGameUtilsKt.toWorldCoordinates(ship, position.get()));
+ }
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/copycats/MixinCopycatLayerBlock.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/copycats/MixinCopycatLayerBlock.java
new file mode 100644
index 00000000..934a11d1
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/copycats/MixinCopycatLayerBlock.java
@@ -0,0 +1,94 @@
+//package io.github.xiewuzhiying.vs_addition.mixin.copycats;
+//
+//
+//import com.copycatsplus.copycats.content.copycat.base.CTWaterloggedCopycatBlock;
+//import com.copycatsplus.copycats.content.copycat.layer.CopycatLayerBlock;
+//import com.simibubi.create.AllBlocks;
+//import com.simibubi.create.content.schematics.requirement.ISpecialBlockItemRequirement;
+//import kotlin.Pair;
+//import net.minecraft.core.BlockPos;
+//import net.minecraft.world.item.context.BlockPlaceContext;
+//import net.minecraft.world.level.BlockGetter;
+//import net.minecraft.world.level.Level;
+//import net.minecraft.world.level.block.Block;
+//import net.minecraft.world.level.block.Blocks;
+//import net.minecraft.world.level.block.state.BlockState;
+//import net.minecraft.world.phys.AABB;
+//import net.minecraft.world.phys.shapes.CollisionContext;
+//import net.minecraft.world.phys.shapes.VoxelShape;
+//import org.jetbrains.annotations.NotNull;
+//import org.spongepowered.asm.mixin.Mixin;
+//import org.spongepowered.asm.mixin.Shadow;
+//import org.spongepowered.asm.mixin.Unique;
+//import org.spongepowered.asm.mixin.injection.At;
+//import org.spongepowered.asm.mixin.injection.Inject;
+//import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+//import org.valkyrienskies.core.apigame.world.ShipWorldCore;
+//import org.valkyrienskies.core.apigame.world.chunks.BlockType;
+//import org.valkyrienskies.mod.common.BlockStateInfo;
+//import org.valkyrienskies.mod.common.VSGameUtilsKt;
+//import org.valkyrienskies.mod.common.util.DimensionIdProvider;
+//
+//import java.util.List;
+//
+//@Mixin(CopycatLayerBlock.class)
+//public abstract class MixinCopycatLayerBlock extends CTWaterloggedCopycatBlock implements ISpecialBlockItemRequirement {
+// @Shadow @NotNull public abstract @NotNull VoxelShape getShape(BlockState pState, @NotNull BlockGetter pLevel, @NotNull BlockPos pPos, @NotNull CollisionContext pContext);
+//
+// public MixinCopycatLayerBlock(Properties pProperties) {
+// super(pProperties);
+// }
+//
+// @Unique
+// private static double vs_addition$calculateTotalVolume(final List aabbList) {
+// double totalVolume = 0.0;
+//
+// for (final AABB aabb : aabbList) {
+// final double width = aabb.maxX - aabb.minX;
+// final double height = aabb.maxY - aabb.minY;
+// final double depth = aabb.maxZ - aabb.minZ;
+// totalVolume += width * height * depth;
+// }
+//
+// return totalVolume;
+// }
+//
+// @Inject(
+// method = "canBeReplaced",
+// at = @At(
+// value = "INVOKE",
+// target = "Lnet/minecraft/world/level/block/state/BlockState;getValue(Lnet/minecraft/world/level/block/state/properties/Property;)Ljava/lang/Comparable;",
+// shift = At.Shift.BEFORE
+// )
+//
+// )
+// public void canBeReplaced(BlockState pState, BlockPlaceContext pUseContext, CallbackInfoReturnable cir){
+// final Level pLevel = pUseContext.getLevel();
+// final BlockPos pPos = pUseContext.getClickedPos();
+//
+//
+// final ShipWorldCore shipObjectWorld = VSGameUtilsKt.getShipObjectWorld(pLevel);
+//
+// final Double multiplier = vs_addition$calculateTotalVolume(pState.getShape(pLevel, pPos).toAabbs());
+//
+// final Double Oldmass;
+//
+// final Double Newmass;
+//
+// final Pair State = BlockStateInfo.INSTANCE.get(pLevel.getBlockState(pPos));
+//
+// final Pair NewState = BlockStateInfo.INSTANCE.get(pState);
+//
+// if(getBlockEntity(pLevel, pPos).getMaterial()== AllBlocks.COPYCAT_BASE.getDefaultState()) {
+// Oldmass = BlockStateInfo.INSTANCE.get(Blocks.AIR.defaultBlockState()).getFirst();
+// Newmass = BlockStateInfo.INSTANCE.get(Blocks.AIR.defaultBlockState()).getFirst();
+// }
+// else {
+// Oldmass = BlockStateInfo.INSTANCE.get(getBlockEntity(pLevel, pPos).getMaterial()).getFirst() * multiplier;
+// Newmass = BlockStateInfo.INSTANCE.get(getBlockEntity(pLevel, pPos).getMaterial()).getFirst() * multiplier;
+// }
+//
+// shipObjectWorld.onSetBlock(pPos.getX(), pPos.getY(), pPos.getZ(), ((DimensionIdProvider) pLevel).getDimensionId(), State.getSecond(), NewState.getSecond(), Oldmass, NewState.getFirst());
+// }
+//
+//}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinCopycatBlock.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinCopycatBlock.java
new file mode 100644
index 00000000..81bde22d
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinCopycatBlock.java
@@ -0,0 +1,78 @@
+package io.github.xiewuzhiying.vs_addition.mixin.create;
+
+import com.simibubi.create.AllBlocks;
+import com.simibubi.create.content.decoration.copycat.CopycatBlock;
+import com.simibubi.create.content.decoration.copycat.CopycatBlockEntity;
+import com.simibubi.create.content.equipment.wrench.IWrenchable;
+import com.simibubi.create.foundation.block.IBE;
+import java.util.List;
+import kotlin.Pair;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.AABB;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.At.Shift;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.valkyrienskies.core.apigame.world.ShipWorldCore;
+import org.valkyrienskies.core.apigame.world.chunks.BlockType;
+import org.valkyrienskies.mod.common.BlockStateInfo;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+import org.valkyrienskies.mod.common.util.DimensionIdProvider;
+
+@Mixin(CopycatBlock.class)
+public abstract class MixinCopycatBlock extends Block implements IBE, IWrenchable {
+
+ @Unique
+ private static double vs_addition$calculateTotalVolume(final List aabbList) {
+ double totalVolume = 0.0;
+
+ for (final AABB aabb : aabbList) {
+ final double width = aabb.maxX - aabb.minX;
+ final double height = aabb.maxY - aabb.minY;
+ final double depth = aabb.maxZ - aabb.minZ;
+ totalVolume += width * height * depth;
+ }
+
+ return totalVolume;
+ }
+
+ public MixinCopycatBlock(final Properties pProperties) {
+ super(pProperties);
+ }
+
+ @Inject(
+ at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/level/Level;removeBlockEntity(Lnet/minecraft/core/BlockPos;)V",
+ shift = Shift.BEFORE
+ ),
+ method = "onRemove"
+ )
+ public void onRemove(final BlockState pState, final Level pLevel, final BlockPos pPos, final BlockState pNewState, final boolean pIsMoving,
+ final CallbackInfo ci) {
+
+
+ final ShipWorldCore shipObjectWorld = VSGameUtilsKt.getShipObjectWorld(pLevel);
+
+ final Double multiplier = vs_addition$calculateTotalVolume(pState.getShape(pLevel, pPos).toAabbs());
+
+ final Double Oldmass;
+
+ final Pair State = BlockStateInfo.INSTANCE.get(pState);
+
+ final Pair NewState = BlockStateInfo.INSTANCE.get(pNewState);
+
+ if(getBlockEntity(pLevel, pPos).getMaterial()== AllBlocks.COPYCAT_BASE.getDefaultState())
+ Oldmass = BlockStateInfo.INSTANCE.get(Blocks.AIR.defaultBlockState()).getFirst();
+ else
+ Oldmass = BlockStateInfo.INSTANCE.get(getBlockEntity(pLevel, pPos).getMaterial()).getFirst() * multiplier;
+
+ shipObjectWorld.onSetBlock(pPos.getX(), pPos.getY(), pPos.getZ(), ((DimensionIdProvider) pLevel).getDimensionId(), State.getSecond(), NewState.getSecond(), Oldmass, NewState.getFirst());
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinCopycatBlockEntity.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinCopycatBlockEntity.java
new file mode 100644
index 00000000..a490bbff
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinCopycatBlockEntity.java
@@ -0,0 +1,91 @@
+package io.github.xiewuzhiying.vs_addition.mixin.create;
+
+import com.simibubi.create.AllBlocks;
+import com.simibubi.create.content.decoration.copycat.CopycatBlockEntity;
+import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
+import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
+import java.util.List;
+import kotlin.Pair;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.entity.BlockEntityType;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.AABB;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.At.Shift;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.valkyrienskies.core.apigame.world.ShipWorldCore;
+import org.valkyrienskies.core.apigame.world.chunks.BlockType;
+import org.valkyrienskies.mod.common.BlockStateInfo;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+import org.valkyrienskies.mod.common.util.DimensionIdProvider;
+
+@Mixin(CopycatBlockEntity.class)
+public abstract class MixinCopycatBlockEntity extends SmartBlockEntity{
+
+ @Shadow
+ private BlockState material;
+ @Shadow
+ private ItemStack consumedItem;
+
+ @Unique
+ private static double vs_addition$calculateTotalVolume(final List aabbList) {
+ double totalVolume = 0.0;
+
+ for (final AABB aabb : aabbList) {
+ final double width = aabb.maxX - aabb.minX;
+ final double height = aabb.maxY - aabb.minY;
+ final double depth = aabb.maxZ - aabb.minZ;
+ totalVolume += width * height * depth;
+ }
+
+ return totalVolume;
+ }
+
+
+ public MixinCopycatBlockEntity(BlockEntityType> type, BlockPos pos, BlockState state) {
+ super(type, pos, state);
+ material = AllBlocks.COPYCAT_BASE.getDefaultState();
+ consumedItem = ItemStack.EMPTY;
+ }
+
+ @Inject(
+ method = "setMaterial",
+ at = @At(
+ value = "INVOKE",
+ target = "Lcom/simibubi/create/content/decoration/copycat/CopycatBlockEntity;getBlockState()Lnet/minecraft/world/level/block/state/BlockState;",
+ shift = Shift.AFTER
+ )
+ )
+ public void setMaterial(final BlockState blockState, final CallbackInfo ci) {
+
+ final ShipWorldCore shipObjectWorld = VSGameUtilsKt.getShipObjectWorld(level);
+
+ final Double multiplier = vs_addition$calculateTotalVolume(getBlockState().getShape(level, worldPosition).toAabbs());
+
+ final Pair State = BlockStateInfo.INSTANCE.get(getBlockState());
+
+ final Double Oldmass;
+
+ final Double Newmass;
+
+ if(material==AllBlocks.COPYCAT_BASE.getDefaultState())
+ Oldmass = BlockStateInfo.INSTANCE.get(Blocks.AIR.defaultBlockState()).getFirst();
+ else
+ Oldmass = BlockStateInfo.INSTANCE.get(material).getFirst() * multiplier;
+
+
+ if(blockState==AllBlocks.COPYCAT_BASE.getDefaultState())
+ Newmass = BlockStateInfo.INSTANCE.get(Blocks.AIR.defaultBlockState()).getFirst();
+ else
+ Newmass = BlockStateInfo.INSTANCE.get(blockState).getFirst() * multiplier;
+
+ shipObjectWorld.onSetBlock(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), ((DimensionIdProvider) level).getDimensionId(), State.getSecond(), State.getSecond(), Oldmass, Newmass);
+ }
+
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinDisplayLinkBlockItem.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinDisplayLinkBlockItem.java
new file mode 100644
index 00000000..c5bbdceb
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinDisplayLinkBlockItem.java
@@ -0,0 +1,105 @@
+package io.github.xiewuzhiying.vs_addition.mixin.create;
+
+import com.simibubi.create.content.redstone.displayLink.DisplayLinkBlockItem;
+import com.simibubi.create.foundation.utility.Lang;
+import com.simibubi.create.infrastructure.config.AllConfigs;
+import net.minecraft.ChatFormatting;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Vec3i;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtUtils;
+import net.minecraft.util.Mth;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.BlockItem;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.context.UseOnContext;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+
+
+@Mixin(DisplayLinkBlockItem.class)
+public abstract class MixinDisplayLinkBlockItem extends BlockItem {
+
+ public MixinDisplayLinkBlockItem(Block block, Properties properties) {
+ super(block, properties);
+ }
+
+ @Unique
+ private Level vs_addition$accessedLevel;
+
+ @Override
+ public InteractionResult useOn(UseOnContext pContext) {
+ ItemStack stack = pContext.getItemInHand();
+ BlockPos pos = pContext.getClickedPos();
+ Level level = pContext.getLevel();
+ BlockState state = level.getBlockState(pos);
+ Player player = pContext.getPlayer();
+ vs_addition$accessedLevel = level;
+
+ if (player == null)
+ return InteractionResult.FAIL;
+
+ if (player.isShiftKeyDown() && stack.hasTag()) {
+ if (level.isClientSide)
+ return InteractionResult.SUCCESS;
+ player.displayClientMessage(Lang.translateDirect("display_link.clear"), true);
+ stack.setTag(null);
+ return InteractionResult.SUCCESS;
+ }
+
+ if (!stack.hasTag()) {
+ if (level.isClientSide)
+ return InteractionResult.SUCCESS;
+ CompoundTag stackTag = stack.getOrCreateTag();
+ stackTag.put("SelectedPos", NbtUtils.writeBlockPos(pos));
+ player.displayClientMessage(Lang.translateDirect("display_link.set"), true);
+ stack.setTag(stackTag);
+ return InteractionResult.SUCCESS;
+ }
+
+ CompoundTag tag = stack.getTag();
+ CompoundTag teTag = new CompoundTag();
+
+ BlockPos selectedPos = NbtUtils.readBlockPos(tag.getCompound("SelectedPos"));
+ BlockPos placedPos = pos.relative(pContext.getClickedFace(), state.getMaterial()
+ .isReplaceable() ? 0 : 1);
+
+ if (!selectedPos.closerThan(placedPos, AllConfigs.server().logistics.displayLinkRange.get())) {
+ player.displayClientMessage(Lang.translateDirect("display_link.too_far")
+ .withStyle(ChatFormatting.RED), true);
+ return InteractionResult.FAIL;
+ }
+
+ teTag.put("TargetOffset", NbtUtils.writeBlockPos(selectedPos.subtract(placedPos)));
+ tag.put("BlockEntityTag", teTag);
+
+ InteractionResult useOn = super.useOn(pContext);
+ if (level.isClientSide || useOn == InteractionResult.FAIL)
+ return useOn;
+
+ ItemStack itemInHand = player.getItemInHand(pContext.getHand());
+ if (!itemInHand.isEmpty())
+ itemInHand.setTag(null);
+ player.displayClientMessage(Lang.translateDirect("display_link.success")
+ .withStyle(ChatFormatting.GREEN), true);
+ return useOn;
+ }
+
+ @Redirect(
+ method = "useOn",
+ at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/core/BlockPos;closerThan(Lnet/minecraft/core/Vec3i;D)Z"
+ )
+ )
+ public boolean closerThan(BlockPos instance, Vec3i pVector, double pDistance) {
+ return VSGameUtilsKt.squaredDistanceBetweenInclShips(vs_addition$accessedLevel, instance.getX(), instance.getY(), instance.getZ(), pVector.getX(), pVector.getY(), pVector.getZ()) < Mth.square(pDistance);
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinTreeFertilizerItem.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinTreeFertilizerItem.java
new file mode 100644
index 00000000..f988448d
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/create/MixinTreeFertilizerItem.java
@@ -0,0 +1,123 @@
+package io.github.xiewuzhiying.vs_addition.mixin.create;
+
+import com.simibubi.create.content.equipment.TreeFertilizerItem;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.item.BoneMealItem;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.context.UseOnContext;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.BonemealableBlock;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.BlockStateProperties;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3d;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+import org.valkyrienskies.core.api.ships.ServerShip;
+import org.valkyrienskies.core.api.world.ServerShipWorld;
+import org.valkyrienskies.core.apigame.VSCore;
+import org.valkyrienskies.core.util.datastructures.DenseBlockPosSet;
+import org.valkyrienskies.eureka.util.ShipAssembler;
+import org.valkyrienskies.mod.common.ValkyrienSkiesMod;
+import org.valkyrienskies.mod.common.util.VectorConversionsMCKt;
+import org.valkyrienskies.core.apigame.ShipTeleportData;
+import org.valkyrienskies.core.impl.game.ShipTeleportDataImpl;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.valkyrienskies.mod.common.assembly.ShipAssemblyKt.createNewShipWithBlocks;
+
+@Mixin(TreeFertilizerItem.class)
+public abstract class MixinTreeFertilizerItem extends Item {
+
+ public MixinTreeFertilizerItem(Properties properties) {
+ super(properties);
+ }
+
+ /**
+ * @author xiewuzhiying
+ * @reason try to use ship instead of create new dimension
+ */
+ @Overwrite
+ public @NotNull InteractionResult useOn(UseOnContext context) {
+ BlockState state = context.getLevel()
+ .getBlockState(context.getClickedPos());
+ Block block = state.getBlock();
+ if (block instanceof BonemealableBlock bonemealableBlock && state.is(BlockTags.SAPLINGS)) {
+
+ if (context.getLevel().isClientSide) {
+ BoneMealItem.addGrowthParticles(context.getLevel(), context.getClickedPos(), 100);
+ return InteractionResult.SUCCESS;
+ }
+
+ BlockPos saplingPos = context.getClickedPos();
+ ServerLevel world = (ServerLevel) context.getLevel();
+
+// DenseBlockPosSet set = new DenseBlockPosSet();
+// for (BlockPos pos : BlockPos.betweenClosed(-1, 0, -1, 1, 0, 1)) {
+// if (context.getLevel()
+// .getBlockState(saplingPos.offset(pos))
+// .getBlock() == block) {
+// set.add(saplingPos.offset(pos).getX(), saplingPos.offset(pos).getY(), saplingPos.offset(pos).getZ());
+// set.add(saplingPos.offset(pos).getX(), saplingPos.offset(pos).getY() - 1, saplingPos.offset(pos).getZ());
+// }
+// }
+//
+// ServerShip ship = createNewShipWithBlocks(saplingPos, set, world);
+// ship.setStatic(true);
+//
+// Vector3d position = ship.getTransform().getWorldToShip().transformPosition(VectorConversionsMCKt.toJOMLD(saplingPos));
+
+ bonemealableBlock.performBonemeal(world, world.getRandom(),
+ saplingPos ,
+ withStage(state, 1));
+
+// ShipAssembler.INSTANCE.unfillShip(world, ship, Direction.SOUTH, new BlockPos(position.x, position.y, position.z), saplingPos);
+// ValkyrienSkiesMod.vsCore.deleteShips((ServerShipWorld) ship, Collections.singletonList(ship));
+
+// for (BlockPos pos : world.ket) {
+// BlockPos actualPos = pos.offset(saplingPos).below(10);
+// BlockState newState = world.getBlockState(pos);
+//
+// // Don't replace Bedrock
+// if (context.getLevel()
+// .getBlockState(actualPos)
+// .getDestroySpeed(context.getLevel(), actualPos) == -1)
+// continue;
+// // Don't replace solid blocks with leaves
+// if (!newState.isRedstoneConductor(world, pos)
+// && !context.getLevel()
+// .getBlockState(actualPos)
+// .getCollisionShape(context.getLevel(), actualPos)
+// .isEmpty())
+// continue;
+//
+// context.getLevel()
+// .setBlockAndUpdate(actualPos, newState);
+// }
+
+ if (context.getPlayer() != null && !context.getPlayer()
+ .isCreative())
+ context.getItemInHand()
+ .shrink(1);
+ return InteractionResult.SUCCESS;
+
+ }
+
+ return super.useOn(context);
+ }
+
+ @Shadow
+ private BlockState withStage(BlockState original, int stage) {
+ if (!original.hasProperty(BlockStateProperties.STAGE))
+ return original;
+ return original.setValue(BlockStateProperties.STAGE, 1);
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/CannonMountBlockEntityAccessor.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/CannonMountBlockEntityAccessor.java
new file mode 100644
index 00000000..2883c1de
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/CannonMountBlockEntityAccessor.java
@@ -0,0 +1,18 @@
+package io.github.xiewuzhiying.vs_addition.mixin.createbigcannons;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+import rbasamoyai.createbigcannons.cannon_control.cannon_mount.CannonMountBlockEntity;
+
+@Mixin(CannonMountBlockEntity.class)
+public interface CannonMountBlockEntityAccessor {
+
+ @Invoker("assemble")
+ void Assemble();
+
+ @Invoker("getMaxDepress")
+ float GetMaxDepress();
+
+ @Invoker("getMaxElevate")
+ float GetMaxElevate();
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/MixinMountedAutoCannonContraption.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/MixinMountedAutoCannonContraption.java
new file mode 100644
index 00000000..c56555a0
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/MixinMountedAutoCannonContraption.java
@@ -0,0 +1,82 @@
+package io.github.xiewuzhiying.vs_addition.mixin.createbigcannons;
+
+import io.github.xiewuzhiying.vs_addition.mixin.minecraft.EntityAccessor;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.Vec3;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.valkyrienskies.core.api.ships.ServerShip;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+import org.valkyrienskies.mod.common.util.GameTickForceApplier;
+import org.valkyrienskies.mod.common.util.VectorConversionsMCKt;
+import rbasamoyai.createbigcannons.cannon_control.contraption.AbstractMountedCannonContraption;
+import rbasamoyai.createbigcannons.cannon_control.contraption.ItemCannon;
+import rbasamoyai.createbigcannons.cannon_control.contraption.MountedAutocannonContraption;
+import rbasamoyai.createbigcannons.cannon_control.contraption.PitchOrientedContraptionEntity;
+import rbasamoyai.createbigcannons.munitions.autocannon.AbstractAutocannonProjectile;
+
+@Mixin(MountedAutocannonContraption.class)
+public abstract class MixinMountedAutoCannonContraption extends AbstractMountedCannonContraption implements ItemCannon {
+
+ @Unique
+ private float vs_addition$speed;
+
+ @Unique
+ private Vec3 vs_addition$vector;
+
+ @Unique
+ private ServerShip vs_addition$serverShip;
+
+ @Inject(
+ method = "fireShot",
+ at = @At(
+ value = "INVOKE",
+ target = "Lrbasamoyai/createbigcannons/munitions/autocannon/AbstractAutocannonProjectile;shoot(DDDFF)V",
+ shift = At.Shift.BEFORE
+ )
+ )
+ public void getShip(ServerLevel level, PitchOrientedContraptionEntity entity, CallbackInfo ci){
+ vs_addition$serverShip = (ServerShip) VSGameUtilsKt.getShipObjectManagingPos(entity.level, VectorConversionsMCKt.toJOML(entity.getAnchorVec()));
+ }
+
+
+ @Redirect(
+ method = "fireShot",
+ at = @At(
+ value = "INVOKE",
+ target = "Lrbasamoyai/createbigcannons/munitions/autocannon/AbstractAutocannonProjectile;shoot(DDDFF)V"
+ )
+ )
+ public void shoot(AbstractAutocannonProjectile> instance, double x, double y, double z, float velocity, float inaccuracy) {
+ vs_addition$speed = velocity;
+ vs_addition$vector = (new Vec3(x, y, z)).normalize().add(((EntityAccessor) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy, ((EntityAccessor)(Object) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy, ((EntityAccessor) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy).scale(velocity).add(VectorConversionsMCKt.toMinecraft(vs_addition$serverShip.getVelocity()));
+ Vec3 vec3 = vs_addition$vector.add(VectorConversionsMCKt.toMinecraft(vs_addition$serverShip.getVelocity()));
+ instance.setDeltaMovement(vec3);
+ double d = vs_addition$vector.horizontalDistance();
+ instance.setYRot((float)(Mth.atan2(vs_addition$vector.x, vs_addition$vector.z) * 57.2957763671875));
+ instance.setXRot((float)(Mth.atan2(vs_addition$vector.y, d) * 57.2957763671875));
+ instance.yRotO = instance.getYRot();
+ instance.xRotO = instance.getXRot();
+ }
+
+ @Inject(
+ method = "fireShot",
+ at = @At(
+ value = "INVOKE",
+ target = "Lrbasamoyai/createbigcannons/munitions/autocannon/AbstractAutocannonProjectile;shoot(DDDFF)V",
+ shift = At.Shift.AFTER
+ )
+ )
+ public void recoil(ServerLevel level, PitchOrientedContraptionEntity entity, CallbackInfo ci) {
+ if (vs_addition$serverShip != null) {
+ GameTickForceApplier applier = vs_addition$serverShip.getAttachment(GameTickForceApplier.class);
+ double recoilForce = vs_addition$speed * 1250.0d;
+ applier.applyInvariantForceToPos(vs_addition$serverShip.getTransform().getShipToWorldRotation().transform(VectorConversionsMCKt.toJOML(vs_addition$vector).negate().normalize()).mul(recoilForce), VectorConversionsMCKt.toJOML(entity.getAnchorVec().add(0.5, 0.5, 0.5)).sub(vs_addition$serverShip.getTransform().getPositionInShip()));
+ }
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/MixinMountedBigCannonContraption.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/MixinMountedBigCannonContraption.java
new file mode 100644
index 00000000..6978b483
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/createbigcannons/MixinMountedBigCannonContraption.java
@@ -0,0 +1,80 @@
+package io.github.xiewuzhiying.vs_addition.mixin.createbigcannons;
+
+import io.github.xiewuzhiying.vs_addition.mixin.minecraft.EntityAccessor;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.util.Mth;
+import net.minecraft.world.phys.Vec3;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.valkyrienskies.core.api.ships.ServerShip;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+import org.valkyrienskies.mod.common.util.GameTickForceApplier;
+import org.valkyrienskies.mod.common.util.VectorConversionsMCKt;
+import rbasamoyai.createbigcannons.cannon_control.contraption.AbstractMountedCannonContraption;
+import rbasamoyai.createbigcannons.cannon_control.contraption.MountedBigCannonContraption;
+import rbasamoyai.createbigcannons.cannon_control.contraption.PitchOrientedContraptionEntity;
+import rbasamoyai.createbigcannons.munitions.big_cannon.AbstractBigCannonProjectile;
+
+@Mixin(MountedBigCannonContraption.class)
+public abstract class MixinMountedBigCannonContraption extends AbstractMountedCannonContraption {
+
+ @Unique
+ private float vs_addition$speed;
+
+ @Unique
+ private Vec3 vs_addition$vector;
+
+ @Unique
+ private ServerShip vs_addition$serverShip;
+
+ @Inject(
+ method = "fireShot",
+ at = @At(
+ value = "INVOKE",
+ target = "Lrbasamoyai/createbigcannons/munitions/big_cannon/AbstractBigCannonProjectile;shoot(DDDFF)V",
+ shift = At.Shift.BEFORE
+ )
+ )
+ public void getShip(ServerLevel level, PitchOrientedContraptionEntity entity, CallbackInfo ci){
+ vs_addition$serverShip = (ServerShip) VSGameUtilsKt.getShipObjectManagingPos(entity.level, VectorConversionsMCKt.toJOML(entity.getAnchorVec()));
+ }
+
+ @Redirect(
+ method = "fireShot",
+ at = @At(
+ value = "INVOKE",
+ target = "Lrbasamoyai/createbigcannons/munitions/big_cannon/AbstractBigCannonProjectile;shoot(DDDFF)V"
+ )
+ )
+ public void shoot(AbstractBigCannonProjectile> instance, double x, double y, double z, float velocity, float inaccuracy) {
+ vs_addition$speed = velocity;
+ vs_addition$vector = (new Vec3(x, y, z)).normalize().add(((EntityAccessor) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy, ((EntityAccessor) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy, ((EntityAccessor)(Object) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy).scale(velocity);
+ Vec3 vec3 = vs_addition$vector.add(VectorConversionsMCKt.toMinecraft(vs_addition$serverShip.getVelocity()));
+ instance.setDeltaMovement(vec3);
+ double d = vs_addition$vector.horizontalDistance();
+ instance.setYRot((float)(Mth.atan2(vs_addition$vector.x, vs_addition$vector.z) * 57.2957763671875));
+ instance.setXRot((float)(Mth.atan2(vs_addition$vector.y, d) * 57.2957763671875));
+ instance.yRotO = instance.getYRot();
+ instance.xRotO = instance.getXRot();
+ }
+
+ @Inject(
+ method = "fireShot",
+ at = @At(
+ value = "INVOKE",
+ target = "Lrbasamoyai/createbigcannons/munitions/big_cannon/AbstractBigCannonProjectile;shoot(DDDFF)V",
+ shift = At.Shift.AFTER
+ )
+ )
+ public void recoil(ServerLevel level, PitchOrientedContraptionEntity entity, CallbackInfo ci) {
+ if (vs_addition$serverShip != null) {
+ GameTickForceApplier applier = vs_addition$serverShip.getAttachment(GameTickForceApplier.class);
+ double recoilForce = vs_addition$speed * 50000.0d;
+ applier.applyInvariantForceToPos(vs_addition$serverShip.getTransform().getShipToWorldRotation().transform(VectorConversionsMCKt.toJOML(vs_addition$vector).negate().normalize()).mul(recoilForce), VectorConversionsMCKt.toJOML(entity.getAnchorVec().add(0.5, 0.5, 0.5)).sub(vs_addition$serverShip.getTransform().getPositionInShip()));
+ }
+ }
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/minecraft/EntityAccessor.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/minecraft/EntityAccessor.java
new file mode 100644
index 00000000..eebf3ec0
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/minecraft/EntityAccessor.java
@@ -0,0 +1,14 @@
+package io.github.xiewuzhiying.vs_addition.mixin.minecraft;
+
+
+import net.minecraft.world.entity.Entity;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import java.util.Random;
+
+@Mixin(Entity.class)
+public interface EntityAccessor {
+ @Accessor("random")
+ Random getRandom();
+}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/vs_eureka/EurekaShipControlAccessor.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/vs_eureka/EurekaShipControlAccessor.java
new file mode 100644
index 00000000..6f030a44
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/vs_eureka/EurekaShipControlAccessor.java
@@ -0,0 +1,13 @@
+//package io.github.xiewuzhiying.vs_addition.mixin.vs_eureka;
+//
+//import org.spongepowered.asm.mixin.Mixin;
+//import org.spongepowered.asm.mixin.gen.Invoker;
+//import org.valkyrienskies.eureka.ship.EurekaShipControl;
+//
+//@Mixin(EurekaShipControl.class)
+//public interface EurekaShipControlAccessor {
+// @Invoker("")
+// public class data aaControlData(){
+//
+// }
+//}
diff --git a/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/vs_eureka/ShipHelmBlockEntityAccessor.java b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/vs_eureka/ShipHelmBlockEntityAccessor.java
new file mode 100644
index 00000000..e65cf114
--- /dev/null
+++ b/common/src/main/java/io/github/xiewuzhiying/vs_addition/mixin/vs_eureka/ShipHelmBlockEntityAccessor.java
@@ -0,0 +1,12 @@
+package io.github.xiewuzhiying.vs_addition.mixin.vs_eureka;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+import org.valkyrienskies.eureka.blockentity.ShipHelmBlockEntity;
+import org.valkyrienskies.eureka.ship.EurekaShipControl;
+
+@Mixin(ShipHelmBlockEntity.class)
+public interface ShipHelmBlockEntityAccessor {
+ @Invoker("getControl")
+ EurekaShipControl GetControl();
+}
diff --git a/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionBlockEntityTypes.kt b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionBlockEntityTypes.kt
new file mode 100644
index 00000000..0884dc10
--- /dev/null
+++ b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionBlockEntityTypes.kt
@@ -0,0 +1,27 @@
+//package io.github.xiewuzhiying.vs_addition
+//
+//import com.simibubi.create.content.decoration.copycat.CopycatBlockEntity
+//import com.tterrag.registrate.builders.BlockEntityBuilder
+//import net.minecraft.core.BlockPos
+//import net.minecraft.world.level.block.entity.BlockEntityType
+//import net.minecraft.world.level.block.state.BlockState
+//
+//class VSAdditionBlockEntityTypes {
+// private val REGISTRATE = VSAdditionMod.getRegistrate()
+//
+// val COPYCAT = REGISTRATE.blockEntity("copycat",
+// BlockEntityBuilder.BlockEntityFactory { type: BlockEntityType?, pos: BlockPos?, state: BlockState? ->
+// CopycatBlockEntity(
+// type,
+// pos,
+// state
+// )
+// })
+// .validBlocks(
+// VSAdditionBlocks
+// )
+// .register()
+//
+//
+// fun register() {}
+//}
\ No newline at end of file
diff --git a/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionBlocks.kt b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionBlocks.kt
new file mode 100644
index 00000000..1dcad74e
--- /dev/null
+++ b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionBlocks.kt
@@ -0,0 +1,63 @@
+//package io.github.xiewuzhiying.vs_addition
+//
+//import com.copycatsplus.copycats.CopycatRegistrate
+//import com.copycatsplus.copycats.config.FeatureToggle
+//import com.copycatsplus.copycats.content.copycat.base.model.SimpleCopycatPart
+//import com.copycatsplus.copycats.datagen.CCLootGen
+//import com.simibubi.create.foundation.data.BuilderTransformers
+//import com.simibubi.create.foundation.data.CreateRegistrate
+//import com.simibubi.create.foundation.data.ModelGen
+//import com.tterrag.registrate.builders.BlockBuilder
+//import com.tterrag.registrate.providers.DataGenContext
+//import com.tterrag.registrate.providers.RegistrateBlockstateProvider
+//import com.tterrag.registrate.util.nullness.NonNullFunction
+//import dev.architectury.injectables.annotations.ExpectPlatform
+//import io.github.xiewuzhiying.vs_addition.content.wing.CopycatWingBlock
+//import io.github.xiewuzhiying.vs_addition.content.wing.CopycatWingModel
+//import net.minecraft.client.resources.model.BakedModel
+//import net.minecraft.world.item.BlockItem
+//import net.minecraft.world.level.block.Block
+//import net.minecraft.world.level.block.state.BlockBehaviour
+//
+//@SuppressWarnings("unused")
+//class VSAdditionBlocks {
+// private val REGISTRATE: CopycatRegistrate = VSAdditionMod.getRegistrate()
+//
+//
+// val COPYCAT_WING = REGISTRATE.block("copycat_wing",
+// NonNullFunction { pProperties: BlockBehaviour.Properties ->
+// CopycatWingBlock(
+// pProperties
+// )
+// })
+// .transform>(
+// BuilderTransformers.copycat()
+// )
+// .transform>(
+// FeatureToggle.register>()
+// )
+// .onRegister(CreateRegistrate.blockModel {
+// NonNullFunction { model: BakedModel? ->
+// SimpleCopycatPart.create(
+// model,
+// CopycatWingModel()
+// )
+// }
+// })
+// .loot(CCLootGen.build(CCLootGen.lootForLayers()))
+// .item()
+// .transform>(
+// ModelGen.customItemModel>(
+// "copycat_base",
+// "wing"
+// )
+// )
+// .register()
+//
+// @ExpectPlatform
+// fun getWrappedBlockState(c: DataGenContext?, p: RegistrateBlockstateProvider?, name: String?) {
+// throw AssertionError()
+// }
+//
+// fun register() {}
+//}
\ No newline at end of file
diff --git a/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionCreativeTabs.kt b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionCreativeTabs.kt
new file mode 100644
index 00000000..4ce532fd
--- /dev/null
+++ b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionCreativeTabs.kt
@@ -0,0 +1,39 @@
+//package io.github.xiewuzhiying.vs_addition
+//
+//import com.copycatsplus.copycats.config.FeatureToggle
+//import com.copycatsplus.copycats.mixin_interfaces.CreativeTabExpander
+//import com.tterrag.registrate.util.entry.ItemProviderEntry
+//import net.minecraft.core.NonNullList
+//import net.minecraft.world.item.CreativeModeTab
+//import net.minecraft.world.item.ItemStack
+//import net.minecraft.world.level.ItemLike
+//
+//
+//object VSAdditionCreativeTabs {
+// val MAIN: CreativeModeTab = MainCreativeModeTab()
+// var ITEMS: List>? = null
+//
+// init {
+// ITEMS = java.util.List.of>(
+// VSAdditionBlocks.COPYCAT_WING
+// )
+// }
+//
+// class MainCreativeModeTab :
+// CreativeModeTab((TAB_BUILDING_BLOCKS as CreativeTabExpander).`copycats$expandTabCount`(), "vs_addition.main") {
+// override fun makeIcon(): ItemStack {
+// return VSAdditionBlocks.COPYCAT_WING.asStack()
+// }
+//
+// override fun fillItemList(pItems: NonNullList) {
+// val var2: Iterator<*> = ITEMS!!.iterator()
+// while (var2.hasNext()) {
+// val item = var2.next() as ItemProviderEntry<*>
+// if (FeatureToggle.isEnabled(item.id)) {
+// (item.get() as ItemLike).asItem().fillItemCategory(this, pItems)
+// }
+// }
+// }
+// }
+//}
+//
diff --git a/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionMod.kt b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionMod.kt
new file mode 100644
index 00000000..3e4fa4ec
--- /dev/null
+++ b/common/src/main/kotlin/io/github/xiewuzhiying/vs_addition/VSAdditionMod.kt
@@ -0,0 +1,26 @@
+package io.github.xiewuzhiying.vs_addition
+
+import dev.architectury.platform.Platform
+
+object VSAdditionMod {
+ const val MOD_ID = "vs_addition"
+
+ @JvmStatic var CREATE_ACTIVE = false
+ @JvmStatic var CC_ACTIVE = false
+ @JvmStatic var CLOCKWORK_ACTIVE = false
+ @JvmStatic var CBC_ACTIVE = false
+ @JvmStatic var EUREKA_ACTIVE = false
+
+ @JvmStatic
+ fun init() {
+ CREATE_ACTIVE = Platform.isModLoaded("create")
+ CC_ACTIVE = Platform.isModLoaded("computercraft")
+ CLOCKWORK_ACTIVE = Platform.isModLoaded("vs_clockwork")
+ CBC_ACTIVE = Platform.isModLoaded("createbigcannons")
+ EUREKA_ACTIVE = Platform.isModLoaded("eureka")
+ }
+
+ @JvmStatic
+ fun initClient() {
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/resources/architectury.common.json b/common/src/main/resources/architectury.common.json
new file mode 100644
index 00000000..3ec01b4c
--- /dev/null
+++ b/common/src/main/resources/architectury.common.json
@@ -0,0 +1,3 @@
+{
+ "accessWidener": "vs_addition.accesswidener"
+}
\ No newline at end of file
diff --git a/common/src/main/resources/vs_addition-common.mixins.json b/common/src/main/resources/vs_addition-common.mixins.json
new file mode 100644
index 00000000..0da063fb
--- /dev/null
+++ b/common/src/main/resources/vs_addition-common.mixins.json
@@ -0,0 +1,22 @@
+{
+ "required": true,
+ "package": "io.github.xiewuzhiying.vs_addition.mixin",
+ "compatibilityLevel": "JAVA_17",
+ "mixins": [
+ "computercraft.MixinRedstoneAPI",
+ "computercraft.MixinWirelessModemPeripheral",
+ "create.MixinCopycatBlock",
+ "create.MixinCopycatBlockEntity",
+ "create.MixinDisplayLinkBlockItem",
+ "create.MixinTreeFertilizerItem",
+ "createbigcannons.CannonMountBlockEntityAccessor",
+ "createbigcannons.MixinMountedAutoCannonContraption",
+ "createbigcannons.MixinMountedBigCannonContraption",
+ "minecraft.EntityAccessor",
+ "vs_eureka.ShipHelmBlockEntityAccessor"
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ },
+ "minVersion": "0.8"
+}
diff --git a/common/src/main/resources/vs_addition.accesswidener b/common/src/main/resources/vs_addition.accesswidener
new file mode 100644
index 00000000..13268c32
--- /dev/null
+++ b/common/src/main/resources/vs_addition.accesswidener
@@ -0,0 +1 @@
+accessWidener v2 named
\ No newline at end of file
diff --git a/fabric/build.gradle b/fabric/build.gradle
new file mode 100644
index 00000000..591c52d7
--- /dev/null
+++ b/fabric/build.gradle
@@ -0,0 +1,144 @@
+plugins {
+ id "com.github.johnrengelman.shadow" version "7.1.2"
+ id "com.matthewprenger.cursegradle"
+ id "com.modrinth.minotaur"
+}
+
+apply from: '../gradle-scripts/publish-curseforge.gradle'
+
+architectury {
+ platformSetupLoomIde()
+ fabric()
+}
+
+loom {
+ accessWidenerPath = project(":common").loom.accessWidenerPath
+}
+
+configurations {
+ common
+ shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this.
+ compileClasspath.extendsFrom common
+ runtimeClasspath.extendsFrom common
+ developmentFabric.extendsFrom common
+}
+
+dependencies {
+ modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+ modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
+ modImplementation("net.fabricmc:fabric-language-kotlin:1.10.10+kotlin.1.9.10")
+
+ // Architectury API
+ include(modApi "dev.architectury:architectury-fabric:${rootProject.architectury_version}")
+
+ //Mixin Extras
+ compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5"))
+
+ // Mod menu
+ modImplementation("com.terraformersmc:modmenu:3.2.3")
+ modImplementation("me.shedaniel.cloth:cloth-config:${cloth_config_version}")
+
+ // Valkyrien Skies 2
+ modApi("org.valkyrienskies:valkyrienskies-118-fabric:${rootProject.vs2_version}")
+
+ common(project(path: ":common", configuration: "namedElements")) { transitive false }
+ shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
+
+ // CC Restitched
+ modImplementation("maven.modrinth:cc-restitched:1.100.8+1.18.2")
+
+ // Create compats
+ modImplementation("com.simibubi.create:create-fabric-${minecraft_version}:${create_fabric_version}") {
+ exclude group: 'com.github.AlphaMode', module: 'fakeconfigtoml'
+ }
+ modImplementation("com.jozufozu.flywheel:flywheel-fabric-${minecraft_version}:${flywheel_version_fabric}")
+ modImplementation("com.tterrag.registrate_fabric:Registrate:${registrate_version}")
+ modImplementation("io.github.fabricators_of_create:Porting-Lib:${port_lib_version}+${minecraft_version}")
+ modImplementation("me.alphamode:ForgeTags:${forge_tags_version}")
+ modImplementation("net.minecraftforge:forgeconfigapiport-fabric:${forge_config_api_port_version}")
+ modImplementation("com.jamieswhiteshirt:reach-entity-attributes:${reach_entity_attributes_version}")
+ modImplementation("dev.cafeteria:fake-player-api:${fake_player_api_version}")
+ modImplementation("io.github.tropheusj:milk-lib:${milk_lib_version}")
+
+ // CBC
+ modCompileOnly("maven.modrinth:create-big-cannons:eLFsS1Ui")
+ modImplementation("com.rbasamoyai:ritchiesprojectilelib:1.0.0-c5d3ea1+1.18.2-fabric") { transitive = false }
+
+ //Copycat+
+ modCompileOnly("maven.modrinth:copycats:g3m9RPx3")
+}
+
+processResources {
+ inputs.property "version", project.version
+
+ filesMatching("fabric.mod.json") {
+ expand "version": project.version, "vs2_version": project.vs2_version.substring(0, project.vs2_version.indexOf('+'))
+ }
+}
+
+shadowJar {
+ exclude "architectury.common.json"
+
+ configurations = [project.configurations.shadowCommon]
+ archiveClassifier.set "dev-shadow"
+}
+
+remapJar {
+ injectAccessWidener = true
+ input.set shadowJar.archiveFile
+ dependsOn shadowJar
+ archiveClassifier.set null
+}
+
+jar {
+ archiveClassifier.set "dev"
+}
+
+sourcesJar {
+ def commonSources = project(":common").sourcesJar
+ dependsOn commonSources
+ from commonSources.archiveFile.map { zipTree(it) }
+}
+
+components.java {
+ withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
+ skip()
+ }
+}
+
+publishing {
+ publications {
+ mavenCommon(MavenPublication) {
+ groupId = "org.valkyrienskies.eureka"
+ version = project.version
+ artifactId = rootProject.archives_base_name + "-" + project.name
+ from components.java
+ }
+ }
+
+ // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
+ repositories {
+ if (project.vs_maven_username && project.vs_maven_password) {
+ println "Publishing to VS Maven"
+ maven {
+ url = project.vs_maven_url
+ credentials {
+ username = project.vs_maven_username
+ password = project.vs_maven_password
+ }
+ }
+ }
+ // Add repositories to publish to here.
+ if (System.getenv("GITHUB_ACTOR") != null) {
+ println "Publishing to Github Packages"
+ maven {
+ name = "GitHubPackages"
+ url = uri("https://maven.pkg.github.com/ValkyrienSkies/Eureka")
+ credentials {
+ username = System.getenv("GITHUB_ACTOR")
+ password = System.getenv("GITHUB_TOKEN")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/fabric/gradle.properties b/fabric/gradle.properties
new file mode 100644
index 00000000..ba0b072a
--- /dev/null
+++ b/fabric/gradle.properties
@@ -0,0 +1,18 @@
+loader_platform=Fabric
+create_version=0.5.1-f-build.1333+mc1.18.2
+# https://github.com/Fabricators-of-Create/Create/blob/mc1.18/fabric/dev/gradle.properties
+config_api_id=3943250
+forge_config_api_port_version=3.2.4
+fake_player_api_version=0.3.0
+# https://maven.tterrag.com/com/jozufozu/flywheel/Flywheel-Fabric
+reach_entity_attributes_version=2.1.1
+registrate_version=MC1.18.2-1.1.11
+forge_tags_version=2.1
+milk_lib_version=0.3.2
+port_lib_version=1.2.1304-beta
+# port_lib_hash=cca931b
+night_config_core_version=3.6.3
+night_config_toml_version=3.6.3
+jsr305_version=3.0.2
+# https://modrinth.com/mod/no-indium/
+no_indium_version=1.0.2+1.18.2
\ No newline at end of file
diff --git a/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/VSAdditionModFabric.java b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/VSAdditionModFabric.java
new file mode 100644
index 00000000..d9231373
--- /dev/null
+++ b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/VSAdditionModFabric.java
@@ -0,0 +1,41 @@
+package io.github.xiewuzhiying.vs_addition.fabric;
+
+import dan200.computercraft.api.ComputerCraftAPI;
+import io.github.xiewuzhiying.vs_addition.VSAdditionMod;
+import io.github.xiewuzhiying.vs_addition.fabric.compat.computercraft.FabricPeripheralProvider;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.loader.api.FabricLoader;
+import org.valkyrienskies.mod.fabric.common.ValkyrienSkiesModFabric;
+
+import static io.github.xiewuzhiying.vs_addition.VSAdditionMod.init;
+import static io.github.xiewuzhiying.vs_addition.VSAdditionMod.initClient;
+
+public class VSAdditionModFabric implements ModInitializer {
+ private static boolean VS2_ACTIVE = false;
+
+ @Override
+ public void onInitialize() {
+ // force VS2 to load before eureka
+ VS2_ACTIVE = FabricLoader.getInstance().isModLoaded("valkyrienskies");
+
+ if(VS2_ACTIVE)
+ new ValkyrienSkiesModFabric().onInitialize();
+
+ init();
+
+ if(VSAdditionMod.getCC_ACTIVE())
+ ComputerCraftAPI.registerPeripheralProvider(new FabricPeripheralProvider());
+ }
+
+ @Environment(EnvType.CLIENT)
+ public static class Client implements ClientModInitializer {
+ @Override
+ public void onInitializeClient() {
+
+ initClient();
+ }
+ }
+}
diff --git a/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/ValkyrienPreLaunch.java b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/ValkyrienPreLaunch.java
new file mode 100644
index 00000000..4c8041e1
--- /dev/null
+++ b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/ValkyrienPreLaunch.java
@@ -0,0 +1,12 @@
+package io.github.xiewuzhiying.vs_addition.fabric;
+
+import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
+
+/**
+ * For now, just using this class as an abusive early entrypoint to run the updater
+ */
+public class ValkyrienPreLaunch implements PreLaunchEntrypoint {
+
+ @Override
+ public void onPreLaunch() {}
+}
diff --git a/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/compat/computercraft/FabricPeripheralProvider.java b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/compat/computercraft/FabricPeripheralProvider.java
new file mode 100644
index 00000000..dbc9de6b
--- /dev/null
+++ b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/compat/computercraft/FabricPeripheralProvider.java
@@ -0,0 +1,20 @@
+package io.github.xiewuzhiying.vs_addition.fabric.compat.computercraft;
+
+import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.api.peripheral.IPeripheralProvider;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.PeripheralCommon;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class FabricPeripheralProvider implements IPeripheralProvider {
+
+ @Nullable
+ @Override
+ public IPeripheral getPeripheral(@Nonnull Level level, @Nonnull BlockPos blockPos, @Nonnull Direction direction) {
+ return (new PeripheralCommon()).getPeripheralCommon(level, blockPos, direction);
+ }
+}
diff --git a/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/mixin/computercraft/TileComputerMixin.java b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/mixin/computercraft/TileComputerMixin.java
new file mode 100644
index 00000000..0badafe0
--- /dev/null
+++ b/fabric/src/main/java/io/github/xiewuzhiying/vs_addition/fabric/mixin/computercraft/TileComputerMixin.java
@@ -0,0 +1,29 @@
+package io.github.xiewuzhiying.vs_addition.fabric.mixin.computercraft;
+
+import dan200.computercraft.shared.computer.blocks.TileComputer;
+import dan200.computercraft.shared.computer.core.ServerComputer;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.VSAdditionCC;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.Level;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(TileComputer.class)
+public abstract class TileComputerMixin {
+ @Inject(
+ method = "createComputer",
+ at = @At("RETURN"),
+ cancellable = true,
+ remap = false
+ )
+ private void cc_vs$addAPI(int instanceID, int id, CallbackInfoReturnable cir) {
+ ServerComputer computer = cir.getReturnValue();
+ Level level = computer.getLevel();
+
+ VSAdditionCC.applyCCAPIs(computer, (ServerLevel) level);
+
+ cir.setReturnValue(computer);
+ }
+}
\ No newline at end of file
diff --git a/fabric/src/main/resources/assets/vs_addition/icon.png b/fabric/src/main/resources/assets/vs_addition/icon.png
new file mode 100644
index 00000000..72d59786
Binary files /dev/null and b/fabric/src/main/resources/assets/vs_addition/icon.png differ
diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json
new file mode 100644
index 00000000..0998cef8
--- /dev/null
+++ b/fabric/src/main/resources/fabric.mod.json
@@ -0,0 +1,42 @@
+{
+ "schemaVersion": 1,
+ "id": "vs_addition",
+ "version": "${version}",
+ "name": "VS Addition",
+ "description": "Some little addition for VS2 and other mods.",
+ "authors": [
+ "xiewuzhying"
+ ],
+ "contact": {
+ "homepage": "https://valkyrienskies.org/",
+ "sources": "https://github.com/xiewuzhiying/VS-Addition"
+ },
+ "license": "Apache-2.0",
+ "icon": "assets/vs_addition/icon.png",
+ "environment": "*",
+ "entrypoints": {
+ "main": [
+ "io.github.xiewuzhiying.vs_addition.fabric.VSAdditionModFabric"
+ ],
+ "preLaunch": [
+ "io.github.xiewuzhiying.vs_addition.fabric.ValkyrienPreLaunch"
+ ]
+ },
+ "mixins": [
+ "vs_addition-common.mixins.json",
+ "vs_addition.mixins.json"
+ ],
+ "accessWidener" : "vs_addition-common.accesswidener",
+ "depends": {
+ "minecraft": ">=1.18.2",
+ "fabricloader": ">=0.14.21"
+ },
+ "suggests": {
+ "valkyrienskies": ">=2.1.1-beta.5",
+ "create": ">=0.5.1",
+ "createbigcannons": ">=0.5.2",
+ "computercraft": "*",
+ "vs_clockwork": ">=0.1",
+ "copycats": ">=1.2.5"
+ }
+}
\ No newline at end of file
diff --git a/fabric/src/main/resources/vs_addition.mixins.json b/fabric/src/main/resources/vs_addition.mixins.json
new file mode 100644
index 00000000..7275036a
--- /dev/null
+++ b/fabric/src/main/resources/vs_addition.mixins.json
@@ -0,0 +1,14 @@
+{
+ "required": true,
+ "package": "io.github.xiewuzhiying.vs_addition.fabric.mixin",
+ "compatibilityLevel": "JAVA_17",
+ "mixins": [
+ "computercraft.TileComputerMixin"
+ ],
+ "client": [
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ },
+ "minVersion": "0.8"
+}
diff --git a/forge/build.gradle b/forge/build.gradle
new file mode 100644
index 00000000..4486c8ed
--- /dev/null
+++ b/forge/build.gradle
@@ -0,0 +1,171 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ // Make sure this version matches the one included in Kotlin for Forge
+ classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10'
+ // OPTIONAL Gradle plugin for Kotlin Serialization
+ classpath 'org.jetbrains.kotlin:kotlin-serialization:1.6.10'
+ }
+}
+
+plugins {
+ id "com.github.johnrengelman.shadow" version "7.1.2"
+ id "com.matthewprenger.cursegradle"
+ id "com.modrinth.minotaur"
+}
+
+apply from: '../gradle-scripts/publish-curseforge.gradle'
+
+architectury {
+ platformSetupLoomIde()
+ forge()
+}
+
+loom {
+ accessWidenerPath = project(":common").loom.accessWidenerPath
+
+ forge {
+ convertAccessWideners = true
+ mixinConfig "vs_addition.mixins.json"
+ mixinConfig "vs_addition-common.mixins.json"
+ extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
+ }
+}
+
+configurations {
+ common
+ shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this.
+ compileClasspath.extendsFrom common
+ runtimeClasspath.extendsFrom common
+ developmentForge.extendsFrom common
+}
+
+dependencies {
+ forge "net.minecraftforge:forge:$forge_version"
+
+ // Architectury API
+ include(modApi "dev.architectury:architectury-forge:${rootProject.architectury_version}")
+
+ //Mixin Extras
+ compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.5"))
+ implementation(include("io.github.llamalad7:mixinextras-forge:0.3.5"))
+
+ // Valkyrien Skies 2
+ modApi("org.valkyrienskies:valkyrienskies-118-forge:$vs2_version") { transitive = false }
+
+ // VS Core
+ implementation("org.valkyrienskies.core:api:$vs_core_version")
+ implementation("org.valkyrienskies.core:api-game:$vs_core_version")
+ implementation("org.valkyrienskies.core:util:$vs_core_version")
+ implementation("org.valkyrienskies.core:impl:$vs_core_version")
+
+ common(project(path: ":common", configuration: "namedElements")) { transitive false }
+ shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
+
+ // Kotlin for Forge
+ implementation "thedarkcolour:kotlinforforge:$forge_kotlin_version"
+
+ // Create compats
+ modImplementation("com.simibubi.create:create-${minecraft_version}:${create_version}:slim") { transitive = false }
+ modImplementation("com.jozufozu.flywheel:flywheel-forge-${minecraft_version}:${flywheel_version}")
+ modImplementation("com.tterrag.registrate:Registrate:${registrate_version}")
+
+ // CC Tweaked
+ modCompileOnly("maven.modrinth:cc-tweaked:1.18.2-1.101.3")
+
+ //Framed blocks
+ modCompileOnly("maven.modrinth:framedblocks:5.11.5")
+
+ //Copycat+
+
+ modImplementation("maven.modrinth:copycats:1.18.2-1.2.5")
+
+ //CBC
+ modCompileOnly("maven.modrinth:create-big-cannons:0.5.4")
+ modImplementation("com.rbasamoyai:ritchiesprojectilelib:1.0.0-c5d3ea1+1.18.2-forge") { transitive = false }
+
+ //CBC Modern Warfare
+ modCompileOnly files("jars/cbcmodernwarfare-forge-1.18.2-0.0.4a.jar")
+}
+
+processResources {
+ inputs.property "version", project.version
+
+ filesMatching("META-INF/mods.toml") {
+ expand "version": project.version, "vs2_version": project.vs2_version.substring(0, project.vs2_version.indexOf('+'))
+ }
+}
+
+tasks.withType(JavaCompile).configureEach {
+ // Minecraft 1.18 (1.18-pre2) upwards uses Java 17.
+ it.options.release = 17
+}
+
+shadowJar {
+ exclude "fabric.mod.json"
+ exclude "architectury.common.json"
+
+ configurations = [project.configurations.shadowCommon]
+ archiveClassifier.set "dev-shadow"
+}
+
+remapJar {
+ input.set shadowJar.archiveFile
+ dependsOn shadowJar
+ archiveClassifier.set null
+}
+
+jar {
+ archiveClassifier.set "dev"
+}
+
+sourcesJar {
+ def commonSources = project(":common").sourcesJar
+ dependsOn commonSources
+ from commonSources.archiveFile.map { zipTree(it) }
+}
+
+components.java {
+ withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
+ skip()
+ }
+}
+
+publishing {
+ publications {
+ mavenCommon(MavenPublication) {
+ groupId = "org.valkyrienskies.eureka"
+ version = project.version
+ artifactId = rootProject.archives_base_name + "-" + project.name
+ from components.java
+ }
+ }
+
+ // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
+ repositories {
+ if (project.vs_maven_username && project.vs_maven_password) {
+ println "Publishing to VS Maven"
+ maven {
+ url = project.vs_maven_url
+ credentials {
+ username = project.vs_maven_username
+ password = project.vs_maven_password
+ }
+ }
+ }
+ // Add repositories to publish to here.
+ if (System.getenv("GITHUB_ACTOR") != null) {
+ println "Publishing to Github Packages"
+ maven {
+ name = "GitHubPackages"
+ url = uri("https://maven.pkg.github.com/ValkyrienSkies/Eureka")
+ credentials {
+ username = System.getenv("GITHUB_ACTOR")
+ password = System.getenv("GITHUB_TOKEN")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/forge/gradle.properties b/forge/gradle.properties
new file mode 100644
index 00000000..73cc3a3b
--- /dev/null
+++ b/forge/gradle.properties
@@ -0,0 +1,6 @@
+loom.platform=forge
+loader_platform=Forge
+kotlin.stdlib.default.dependency=false
+create_version=0.5.1.f-345
+flywheel_version=0.6.10-105
+registrate_version=MC1.18.2-1.1.3
\ No newline at end of file
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/ForgePeripheralProvider.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/ForgePeripheralProvider.java
new file mode 100644
index 00000000..70925cd7
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/ForgePeripheralProvider.java
@@ -0,0 +1,22 @@
+package io.github.xiewuzhiying.vs_addition.forge.compat.computercraft;
+
+import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.api.peripheral.IPeripheralProvider;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.PeripheralCommon;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import net.minecraftforge.common.util.LazyOptional;
+import org.jetbrains.annotations.NotNull;
+
+public class ForgePeripheralProvider implements IPeripheralProvider {
+ @NotNull
+ @Override
+ public LazyOptional getPeripheral(@NotNull Level level, @NotNull BlockPos blockPos, @NotNull Direction direction) {
+ IPeripheral peripheral = (new PeripheralCommon()).getPeripheralCommon(level,blockPos,direction);
+ if(peripheral==null) peripheral = (new PeripheralForge().getPeripheralForge(level,blockPos,direction));
+ if(peripheral==null) return LazyOptional.empty();
+ IPeripheral finalPeripheral = peripheral;
+ return LazyOptional.of(() -> finalPeripheral);
+ }
+}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/PeripheralForge.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/PeripheralForge.java
new file mode 100644
index 00000000..3b8085bf
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/PeripheralForge.java
@@ -0,0 +1,30 @@
+package io.github.xiewuzhiying.vs_addition.forge.compat.computercraft;
+
+import dan200.computercraft.api.peripheral.IPeripheral;
+import io.github.xiewuzhiying.vs_addition.forge.compat.computercraft.peripherals.CompactCannonMountPeripheral;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.state.BlockState;
+import riftyboi.cbcmodernwarfare.cannon_control.compact_mount.CompactCannonMountBlockEntity;
+import riftyboi.cbcmodernwarfare.index.CBCModernWarfareBlocks;
+
+import javax.annotation.Nullable;
+
+public class PeripheralForge {
+
+ private boolean c(BlockState arg1, Block arg2) { return arg1.getBlock() == arg2; }
+
+ @Nullable
+ public IPeripheral getPeripheralForge(Level level, BlockPos blockPos, Direction direction){
+ BlockState s = level.getBlockState(blockPos);
+ BlockEntity be = level.getBlockEntity(blockPos);
+ if (c(s, CBCModernWarfareBlocks.COMPACT_MOUNT.get())) {
+ return new CompactCannonMountPeripheral("cbcmf_compact_cannon_mount", (CompactCannonMountBlockEntity) be, level, blockPos, direction);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/peripherals/CompactCannonMountPeripheral.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/peripherals/CompactCannonMountPeripheral.java
new file mode 100644
index 00000000..ec07aaf9
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/compat/computercraft/peripherals/CompactCannonMountPeripheral.java
@@ -0,0 +1,126 @@
+package io.github.xiewuzhiying.vs_addition.forge.compat.computercraft.peripherals;
+
+import dan200.computercraft.api.lua.IArguments;
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import io.github.xiewuzhiying.vs_addition.forge.mixin.cbcmodernwarfare.CompactCannonMountBlockEntityAccessor;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import riftyboi.cbcmodernwarfare.cannon_control.compact_mount.CompactCannonMountBlockEntity;
+
+public class CompactCannonMountPeripheral implements IPeripheral {
+
+ private final String type ;
+ private final CompactCannonMountBlockEntity tileEntity;
+
+ private final Level level;
+
+ private final BlockPos worldPosition;
+
+ private final Direction direction;
+
+ public CompactCannonMountPeripheral(String type, CompactCannonMountBlockEntity tileEntity, Level level, BlockPos blockPos, Direction direction) {
+ this.type = type;
+ this.tileEntity = tileEntity;
+ this.level = level;
+ this.worldPosition = blockPos;
+ this.direction = direction;
+ }
+
+ @NotNull
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(@Nullable IPeripheral iPeripheral) {
+ return iPeripheral == this;
+ }
+
+ @Override
+ public Object getTarget() {
+ return this.tileEntity;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final void setPitch(double value){
+ if(this.isRunning())
+ this.tileEntity.setPitch((float) value);
+ }
+
+ @LuaFunction(mainThread = true)
+ public final void setYaw(double value){
+ if(this.isRunning())
+ this.tileEntity.setYaw((float) value);
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object assemble(){
+ if(!this.isRunning()) {
+ ((CompactCannonMountBlockEntityAccessor) this.tileEntity).Assemble();
+ return true;
+ }
+ return false;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final Object disassemble() {
+ if(this.isRunning()) {
+ this.tileEntity.disassemble();
+ this.tileEntity.sendData();
+ return true;
+ }
+ return false;
+ }
+
+ @LuaFunction(mainThread = true)
+ public final void fire() {
+ if(this.isRunning()) {
+ this.tileEntity.getContraption().tryFiringShot();
+ }
+ }
+
+ @LuaFunction
+ public final boolean isRunning(){
+ return this.tileEntity.isRunning();
+ }
+
+ @LuaFunction
+ public final Object getPitchOffset(IArguments partialTicks) throws LuaException {
+ if(this.isRunning()) {
+ double value = partialTicks.optDouble(0).orElse(0.0);
+ return (double) this.tileEntity.getPitchOffset((float) value);
+ }
+ return false;
+ }
+
+ @LuaFunction
+ public final Object getYawOffset(IArguments partialTicks) throws LuaException {
+ if(this.isRunning()) {
+ double value = partialTicks.optDouble(0).orElse(0.0);
+ return (double) this.tileEntity.getYawOffset((float) value);
+ }
+ return false;
+ }
+
+ @LuaFunction
+ public final Object getMaxDepress() {
+ if(this.isRunning()) {
+ return (double) ((CompactCannonMountBlockEntityAccessor) this.tileEntity).GetMaxDepress();
+ }
+ return false;
+ }
+
+ @LuaFunction
+ public final Object getMaxElevate() {
+ if(this.isRunning()) {
+ return (double) ((CompactCannonMountBlockEntityAccessor) this.tileEntity).GetMaxElevate();
+ }
+ return false;
+ }
+}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/content/redstone/displayLink/target/FramedSignDisplayTarget.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/content/redstone/displayLink/target/FramedSignDisplayTarget.java
new file mode 100644
index 00000000..26d885b6
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/content/redstone/displayLink/target/FramedSignDisplayTarget.java
@@ -0,0 +1,40 @@
+package io.github.xiewuzhiying.vs_addition.forge.content.redstone.displayLink.target;
+
+import com.simibubi.create.content.redstone.displayLink.DisplayLinkContext;
+import com.simibubi.create.content.redstone.displayLink.target.DisplayTarget;
+import com.simibubi.create.content.redstone.displayLink.target.DisplayTargetStats;
+import net.minecraft.network.chat.MutableComponent;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import xfacthd.framedblocks.common.blockentity.FramedSignBlockEntity;
+
+import java.util.List;
+
+public class FramedSignDisplayTarget extends DisplayTarget {
+
+ @Override
+ public void acceptText(int line, List text, DisplayLinkContext context) {
+ BlockEntity be = context.getTargetBlockEntity();
+ if (!(be instanceof FramedSignBlockEntity sign))
+ return;
+
+ boolean changed = false;
+ for (int i = 0; i < text.size() && i + line < 4; i++) {
+ if (i == 0)
+ reserve(i + line, sign, context);
+ if (i > 0 && isReserved(i + line, sign, context))
+ break;
+
+ sign.setLine(i + line, text.get(i));
+ changed = true;
+ }
+
+ if (changed)
+ context.level().sendBlockUpdated(context.getTargetPos(), sign.getBlockState(), sign.getBlockState(), 2);
+ }
+
+ @Override
+ public DisplayTargetStats provideStats(DisplayLinkContext context) {
+ return new DisplayTargetStats(4, 15, this);
+ }
+
+}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/ValkyrienForgeMixinConfigPlugin.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/ValkyrienForgeMixinConfigPlugin.java
new file mode 100644
index 00000000..8460ef94
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/ValkyrienForgeMixinConfigPlugin.java
@@ -0,0 +1,47 @@
+package io.github.xiewuzhiying.vs_addition.forge.mixin;
+
+import java.util.List;
+import java.util.Set;
+import org.objectweb.asm.tree.ClassNode;
+import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
+import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
+
+/**
+ * For now, just using this class as an abusive early entrypoint to run the updater
+ */
+public class ValkyrienForgeMixinConfigPlugin implements IMixinConfigPlugin {
+
+ @Override
+ public void onLoad(final String s) {
+ }
+
+ @Override
+ public String getRefMapperConfig() {
+ return null;
+ }
+
+ @Override
+ public boolean shouldApplyMixin(final String s, final String s1) {
+ return true;
+ }
+
+ @Override
+ public void acceptTargets(final Set set, final Set set1) {
+
+ }
+
+ @Override
+ public List getMixins() {
+ return null;
+ }
+
+ @Override
+ public void preApply(final String s, final ClassNode classNode, final String s1, final IMixinInfo iMixinInfo) {
+
+ }
+
+ @Override
+ public void postApply(final String s, final ClassNode classNode, final String s1, final IMixinInfo iMixinInfo) {
+
+ }
+}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/cbcmodernwarfare/CompactCannonMountBlockEntityAccessor.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/cbcmodernwarfare/CompactCannonMountBlockEntityAccessor.java
new file mode 100644
index 00000000..ddb0a1cf
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/cbcmodernwarfare/CompactCannonMountBlockEntityAccessor.java
@@ -0,0 +1,18 @@
+package io.github.xiewuzhiying.vs_addition.forge.mixin.cbcmodernwarfare;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+import riftyboi.cbcmodernwarfare.cannon_control.compact_mount.CompactCannonMountBlockEntity;
+
+@Mixin(CompactCannonMountBlockEntity.class)
+public interface CompactCannonMountBlockEntityAccessor {
+
+ @Invoker("assemble")
+ void Assemble();
+
+ @Invoker("getMaxDepress")
+ float GetMaxDepress();
+
+ @Invoker("getMaxElevate")
+ float GetMaxElevate();
+}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/cbcmodernwarfare/MixinMountedMediumcannonContraption.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/cbcmodernwarfare/MixinMountedMediumcannonContraption.java
new file mode 100644
index 00000000..e15baf8b
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/cbcmodernwarfare/MixinMountedMediumcannonContraption.java
@@ -0,0 +1,81 @@
+//package io.github.xiewuzhiying.vs_addition.forge.mixin.cbcmodernwarfare;
+//
+//import io.github.xiewuzhiying.vs_addition.mixin.minecraft.EntityAccessor;
+//import net.minecraft.server.level.ServerLevel;
+//import net.minecraft.util.Mth;
+//import net.minecraft.world.phys.Vec3;
+//import org.spongepowered.asm.mixin.Mixin;
+//import org.spongepowered.asm.mixin.Unique;
+//import org.spongepowered.asm.mixin.injection.At;
+//import org.spongepowered.asm.mixin.injection.Inject;
+//import org.spongepowered.asm.mixin.injection.Redirect;
+//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+//import org.valkyrienskies.core.api.ships.ServerShip;
+//import org.valkyrienskies.mod.common.VSGameUtilsKt;
+//import org.valkyrienskies.mod.common.util.GameTickForceApplier;
+//import org.valkyrienskies.mod.common.util.VectorConversionsMCKt;
+//import rbasamoyai.createbigcannons.cannon_control.contraption.AbstractMountedCannonContraption;
+//import rbasamoyai.createbigcannons.cannon_control.contraption.ItemCannon;
+//import rbasamoyai.createbigcannons.cannon_control.contraption.PitchOrientedContraptionEntity;
+//import riftyboi.cbcmodernwarfare.cannon_control.contraption.MountedMediumcannonContraption;
+//import riftyboi.cbcmodernwarfare.munitions.medium_cannon.AbstractMediumcannonProjectile;
+//
+//@Mixin(MountedMediumcannonContraption.class)
+//public abstract class MixinMountedMediumcannonContraption extends AbstractMountedCannonContraption implements ItemCannon {
+//
+// @Unique
+// private float vs_addition$speed;
+//
+// @Unique
+// private Vec3 vs_addition$vector;
+//
+// @Unique
+// private ServerShip vs_addition$serverShip;
+//
+// @Inject(
+// method = "fireShot",
+// at = @At(
+// value = "INVOKE",
+// target = "Lriftyboi/cbcmodernwarfare/munitions/medium_cannon/AbstractMediumcannonProjectile;m_6686_(DDDFF)V",
+// shift = At.Shift.BEFORE
+// )
+// )
+// public void getShip(ServerLevel level, PitchOrientedContraptionEntity entity, CallbackInfo ci){
+// vs_addition$serverShip = (ServerShip) VSGameUtilsKt.getShipObjectManagingPos(entity.level, VectorConversionsMCKt.toJOML(entity.getAnchorVec()));
+// }
+//
+// @Redirect(
+// method = "fireShot",
+// at = @At(
+// value = "INVOKE",
+// target = "Lriftyboi/cbcmodernwarfare/munitions/medium_cannon/AbstractMediumcannonProjectile;m_6686_(DDDFF)V"
+// )
+// )
+// public void shoot(AbstractMediumcannonProjectile> instance, double x, double y, double z, float velocity, float inaccuracy) {
+// vs_addition$speed = velocity;
+// vs_addition$vector = (new Vec3(x, y, z)).normalize().add(((EntityAccessor) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy, ((EntityAccessor)(Object) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy, ((EntityAccessor)(Object) instance).getRandom().nextGaussian() * 0.007499999832361937 * (double)inaccuracy).scale(velocity);
+// Vec3 vec3 = vs_addition$vector.add(VectorConversionsMCKt.toMinecraft(vs_addition$serverShip.getVelocity()));
+// instance.setDeltaMovement(vec3);
+// double d = vs_addition$vector.horizontalDistance();
+// instance.setYRot((float)(Mth.atan2(vs_addition$vector.x, vs_addition$vector.z) * 57.2957763671875));
+// instance.setXRot((float)(Mth.atan2(vs_addition$vector.y, d) * 57.2957763671875));
+// instance.yRotO = instance.getYRot();
+// instance.xRotO = instance.getXRot();
+// }
+//
+// @Inject(
+// method = "fireShot",
+// at = @At(
+// value = "INVOKE",
+// target = "Lriftyboi/cbcmodernwarfare/munitions/medium_cannon/AbstractMediumcannonProjectile;m_6686_(DDDFF)V",
+// shift = At.Shift.AFTER
+// )
+// )
+// public void recoil(ServerLevel level, PitchOrientedContraptionEntity entity, CallbackInfo ci) {
+// if (vs_addition$serverShip != null) {
+// GameTickForceApplier applier = vs_addition$serverShip.getAttachment(GameTickForceApplier.class);
+// double recoilForce = vs_addition$speed * 50000.0d;
+// applier.applyInvariantForceToPos(vs_addition$serverShip.getTransform().getShipToWorldRotation().transform(VectorConversionsMCKt.toJOML(vs_addition$vector).negate().normalize()).mul(recoilForce), VectorConversionsMCKt.toJOML(entity.getAnchorVec().add(0.5, 0.5, 0.5)).sub(vs_addition$serverShip.getTransform().getPositionInShip()));
+// }
+// }
+//}
diff --git a/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/computercraft/TileComputerMixin.java b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/computercraft/TileComputerMixin.java
new file mode 100644
index 00000000..ed98b293
--- /dev/null
+++ b/forge/src/main/java/io/github/xiewuzhiying/vs_addition/forge/mixin/computercraft/TileComputerMixin.java
@@ -0,0 +1,29 @@
+package io.github.xiewuzhiying.vs_addition.forge.mixin.computercraft;
+
+import dan200.computercraft.shared.computer.blocks.TileComputer;
+import dan200.computercraft.shared.computer.core.ServerComputer;
+import io.github.xiewuzhiying.vs_addition.compats.computercraft.VSAdditionCC;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.Level;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(TileComputer.class)
+public abstract class TileComputerMixin {
+ @Inject(
+ method = "createComputer",
+ at = @At("RETURN"),
+ cancellable = true,
+ remap = false
+ )
+ private void cc_vs$addAPI(int id, CallbackInfoReturnable cir) {
+ ServerComputer computer = cir.getReturnValue();
+ Level level = computer.getLevel();
+
+ VSAdditionCC.applyCCAPIs(computer, (ServerLevel) level);
+
+ cir.setReturnValue(computer);
+ }
+}
\ No newline at end of file
diff --git a/forge/src/main/kotlin/io/github/xiewuzhiying/vs_addition/forge/VSAdditionModForge.kt b/forge/src/main/kotlin/io/github/xiewuzhiying/vs_addition/forge/VSAdditionModForge.kt
new file mode 100644
index 00000000..ea85d443
--- /dev/null
+++ b/forge/src/main/kotlin/io/github/xiewuzhiying/vs_addition/forge/VSAdditionModForge.kt
@@ -0,0 +1,75 @@
+package io.github.xiewuzhiying.vs_addition.forge
+
+import com.simibubi.create.content.redstone.displayLink.AllDisplayBehaviours
+import dan200.computercraft.api.ComputerCraftAPI
+import io.github.xiewuzhiying.vs_addition.VSAdditionMod
+import io.github.xiewuzhiying.vs_addition.VSAdditionMod.init
+import io.github.xiewuzhiying.vs_addition.VSAdditionMod.initClient
+import io.github.xiewuzhiying.vs_addition.forge.compat.computercraft.ForgePeripheralProvider
+import io.github.xiewuzhiying.vs_addition.forge.content.redstone.displayLink.target.FramedSignDisplayTarget
+import net.minecraft.resources.ResourceLocation
+import net.minecraftforge.eventbus.api.IEventBus
+import net.minecraftforge.fml.ModList
+import net.minecraftforge.fml.common.Mod
+import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
+import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
+import thedarkcolour.kotlinforforge.forge.MOD_BUS
+import xfacthd.framedblocks.common.FBContent
+
+
+@Mod(VSAdditionMod.MOD_ID)
+class VSAdditionModForge {
+
+ var CREATE_ACTIVE = false
+ var CC_ACTIVE = false
+ var FRAMEDBLOCKS_ACTIVE = false
+ var COPYCATS_ACTIVE = false
+ var CBC_ACTIVE = false
+ var CBCMF_ACTIVE = false
+
+ init {
+ CREATE_ACTIVE = ModList.get().isLoaded("create")
+ CC_ACTIVE = ModList.get().isLoaded("computercraft")
+ FRAMEDBLOCKS_ACTIVE = ModList.get().isLoaded("framedblocks")
+ COPYCATS_ACTIVE = ModList.get().isLoaded("copycats")
+ CBC_ACTIVE = ModList.get().isLoaded("createbigcannnons")
+ CBCMF_ACTIVE = ModList.get().isLoaded("cbcmodernwarfare")
+
+ MOD_BUS.addListener { event: FMLClientSetupEvent? ->
+ clientSetup(
+ event
+ )
+ }
+ MOD_BUS.addListener { event: FMLCommonSetupEvent? ->
+ serverSetup(
+ event
+ )
+ }
+ init()
+ }
+
+ private fun clientSetup(event: FMLClientSetupEvent?) {
+ initClient()
+ }
+
+ private fun serverSetup(event: FMLCommonSetupEvent?) {
+ if(FRAMEDBLOCKS_ACTIVE && CREATE_ACTIVE)
+ AllDisplayBehaviours.assignBlockEntity(
+ AllDisplayBehaviours.register(
+ ResourceLocation(
+ VSAdditionMod.MOD_ID,
+ "framed_sign_display_target"
+ ), FramedSignDisplayTarget()
+ ), FBContent.blockEntityTypeFramedSign.get()
+ )
+
+ if(CC_ACTIVE)
+ ComputerCraftAPI.registerPeripheralProvider(ForgePeripheralProvider())
+ }
+
+
+ companion object {
+ fun getModBus(): IEventBus = MOD_BUS
+ }
+
+}
diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml
new file mode 100644
index 00000000..513ba0d4
--- /dev/null
+++ b/forge/src/main/resources/META-INF/mods.toml
@@ -0,0 +1,77 @@
+modLoader = "kotlinforforge"
+loaderVersion = "[3.12.0,)"
+issueTrackerURL = "https://github.com/xiewuzhiying/VS-Addition/issues"
+license = "Apache-2.0"
+
+[[mods]]
+modId = "vs_addition"
+version = "${version}"
+displayName = "VS Addition"
+authors = ["xiewuzhiying"]
+description = '''
+Some little addition for VS2 and other mods.
+'''
+logoFile = "icon.png"
+
+[[dependencies.vs_addition]]
+modId = "forge"
+mandatory = true
+versionRange = "[40.1.69,)"
+ordering = "NONE"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "minecraft"
+mandatory = true
+versionRange = "[1.18.2,)"
+ordering = "NONE"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "valkyrienskies"
+mandatory = false
+versionRange = "[2.1.1-beta.5,)"
+ordering = "AFTER"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "create"
+mandatory = false
+versionRange = "[0.5.1,)"
+ordering = "AFTER"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "createbigcannons"
+mandatory = false
+versionRange = "[0.5.2,)"
+ordering = "AFTER"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "framedblocks"
+mandatory = false
+versionRange = "[5,)"
+ordering = "AFTER"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "computercraft"
+mandatory = false
+versionRange = "*"
+ordering = "AFTER"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "vs_clockwork"
+mandatory = false
+versionRange = "[0.1,)"
+ordering = "AFTER"
+side = "BOTH"
+
+[[dependencies.vs_addition]]
+modId = "copycats"
+mandatory = false
+versionRange = "[1.2.5,)"
+ordering = "AFTER"
+side = "BOTH"
\ No newline at end of file
diff --git a/forge/src/main/resources/icon.png b/forge/src/main/resources/icon.png
new file mode 100644
index 00000000..72d59786
Binary files /dev/null and b/forge/src/main/resources/icon.png differ
diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta
new file mode 100644
index 00000000..08796647
--- /dev/null
+++ b/forge/src/main/resources/pack.mcmeta
@@ -0,0 +1,6 @@
+{
+ "pack": {
+ "description": "VS Addition",
+ "pack_format": 8
+ }
+}
diff --git a/forge/src/main/resources/vs_addition.mixins.json b/forge/src/main/resources/vs_addition.mixins.json
new file mode 100644
index 00000000..4d54f53b
--- /dev/null
+++ b/forge/src/main/resources/vs_addition.mixins.json
@@ -0,0 +1,14 @@
+{
+ "required": true,
+ "package": "io.github.xiewuzhiying.vs_addition.forge.mixin",
+ "compatibilityLevel": "JAVA_17",
+ "plugin": "io.github.xiewuzhiying.vs_addition.forge.mixin.ValkyrienForgeMixinConfigPlugin",
+ "mixins": [
+ "cbcmodernwarfare.CompactCannonMountBlockEntityAccessor",
+ "computercraft.TileComputerMixin"
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ },
+ "minVersion": "0.8"
+}
diff --git a/gradle-scripts/publish-curseforge.gradle b/gradle-scripts/publish-curseforge.gradle
new file mode 100644
index 00000000..9d7f444a
--- /dev/null
+++ b/gradle-scripts/publish-curseforge.gradle
@@ -0,0 +1,53 @@
+// Determine release type
+def vsReleaseType = 'alpha'
+if (project.hasProperty("CustomReleaseVersion")) {
+ def matches = project.property("CustomReleaseVersion") =~ 'release/(?:\\d+\\.){1,2}\\d+(?:-(alpha|beta).\\d+)?'
+
+ vsReleaseType = matches[0][1] ?: 'release'
+}
+
+def fileDisplayName = "[${project.loader_platform} ${project.minecraft_version}] v${project.version}"
+
+// upgrade curseforge release type if it's an alpha,
+// because alpha can't be installed from the curseforge app
+def curseForgeReleaseType = vsReleaseType == 'alpha' ? 'beta' : vsReleaseType
+
+// Publish to CurseForge
+curseforge {
+ if (project.curse_api_key) {
+ apiKey = project.curse_api_key
+ project {
+ id = project.curse_project_id
+ releaseType = curseForgeReleaseType
+ addGameVersion project.minecraft_version
+ addGameVersion project.loader_platform
+
+ mainArtifact(remapJar.archiveFile.get().asFile) {
+ relations {
+ requiredDependency 'valkyrien-skies'
+ }
+ displayName = fileDisplayName
+ }
+ }
+ }
+ options {
+ forgeGradleIntegration = false
+ javaVersionAutoDetect = false
+ }
+}
+
+// Publish to Modrinth
+modrinth {
+ token = project.modrinth_api_key
+ projectId = project.modrinth_project_id
+ versionType = vsReleaseType
+ versionName = fileDisplayName
+ versionNumber = "${project.minecraft_version}-${project.loader_platform.toLowerCase()}-${project.version}"
+ uploadFile = remapJar
+ gameVersions = [project.minecraft_version]
+ loaders = [project.loader_platform.toLowerCase()]
+
+ dependencies {
+ required.project 'valkyrien-skies'
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..8d7d1a68
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,32 @@
+org.gradle.jvmargs=-Xmx4096M
+# Identity
+mod_name=vs_addition
+mod_id=vs_addition
+mod_version=0.0.1c
+enabled_platforms=fabric,forge
+archives_base_name=vs_addition
+maven_group=io.github.xiewuzhiying
+loader_platform=Common
+# Dependencies
+vs2_version=2.1.1-beta.6+88d9088afa
+vs_core_version=1.1.0+e26d9059c0
+minecraft_version=1.18.2
+architectury_version=4.10.86
+fabric_loader_version=0.15.8
+fabric_api_version=0.77.0+1.18.2
+forge_version=1.18.2-40.2.10
+forge_kotlin_version=3.12.0
+kotlin_version=1.9.10
+cloth_config_version=6.4.90
+create_fabric_version=0.5.1-f-build.1333+mc1.18.2
+flywheel_version_fabric=0.6.9-38
+createbigcannons_version=0.5.2-nightly-e815ca4
+# Maven publishing
+vs_maven_url=
+vs_maven_username=
+vs_maven_password=
+# For release builds
+curse_api_key=
+curse_project_id=
+modrinth_api_key=
+modrinth_project_id=
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..e708b1c0
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 00000000..27313fbc
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/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/master/subprojects/plugins/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
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# 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"'
+
+# 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
+ which java >/dev/null 2>&1 || 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
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ 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
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# 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 100755
index 00000000..107acd32
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@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=.
+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%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+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%"=="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!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..fb04e482
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,52 @@
+pluginManagement {
+ repositories {
+ /*maven {
+ val vs_maven_url: String? by settings
+ val vs_maven_username: String? by settings
+ val vs_maven_password: String? by settings
+
+ name = "Valkyrien Skies Internal"
+ url = uri(vs_maven_url ?: "https://maven.valkyrienskies.org")
+
+ if (vs_maven_username != null && vs_maven_password != null) {
+ credentials {
+ username = vs_maven_username!!
+ password = vs_maven_password!!
+ }
+ }
+ }*/
+ gradlePluginPortal()
+ maven("https://maven.fabricmc.net/") {
+ name = "Fabric"
+ }
+ maven("https://repo.spongepowered.org/repository/maven-public/") {
+ name = "Sponge Snapshots"
+ }
+ maven("https://maven.minecraftforge.net") {
+ name = "Forge"
+ }
+ maven("https://maven.architectury.dev/") {
+ name = "Architectury"
+ }
+ }
+
+ resolutionStrategy {
+ eachPlugin {
+ // If we request Forge, actually give it the correct artifact.
+ if (requested.id.id == "net.minecraftforge.gradle") {
+ useModule("${requested.id}:ForgeGradle:${requested.version}")
+ }
+
+ if (requested.id.namespace?.startsWith("org.jetbrains.kotlin") == true) {
+ val kotlin_version: String by settings
+ useVersion(kotlin_version)
+ }
+ }
+ }
+}
+
+include("common")
+include("fabric")
+include("forge")
+
+rootProject.name = "vs_addition"