Compare commits

..

9 Commits

Author SHA1 Message Date
Britakee
b6a2a9d515 updated the template so it finds Hytale even when it isn’t in the default path 2026-02-18 08:57:15 +01:00
Britakee
be9968c345 Switch to Groovy DSL; add runServer task
Migrate the project from Gradle Kotlin DSL to Groovy DSL: update README to reference build.gradle/settings.gradle and change code blocks to Groovy. Remove build.gradle.kts. Add runServerJar and runServer tasks in build.gradle that depend on shadowJar and launch the Hytale server (uses hytaleHome paths, assets, and optional user mods). Bump plugin manifest version to 0.0.3 and set a concrete ServerVersion string. Fixed some issues.
2026-02-17 19:21:10 +01:00
Britakee
a1f96f58e6 Merge pull request #22 from hytailor/dev/vscode
Fix gitignore for VS Code
2026-02-17 18:23:34 +01:00
Britakee
839df5f059 Merge pull request #19 from FedericoLeiva12/main
Use official Hytale mavel repository and fix Github Actions workflow
2026-02-17 18:23:15 +01:00
DocW
647fe81537 Track VS Code Java formatter config in repo
Add negated pattern to `.gitignore` for the VS Code Java formatter
configuration file (`.vscode/java-formatter.xml`).
2026-02-01 14:20:58 -05:00
DocW
772dabc708 Track VS Code settings in repository
Add negated pattern to `.gitignore` for the VS Code settings file
(`.vscode/settings.json`).
2026-02-01 14:19:36 -05:00
DocW
bbd0265f2e Allow files in .vscode/ to be included in Git repo
Allow the exclusion of files in the `.vscode/` directory to be
overridden via negated patterns (gitignore patterns beginning with `!`)
by changing the pattern in `.gitignore` from `.vscode/` to `.vscode/*`
(i.e., by ignoring the files in `.vscode/` rather than the `.vscode/`
directory itself).
2026-02-01 14:13:52 -05:00
InvBoy
b0d6afbd4c Remove unused database driver dependencies
Removed unused database driver dependencies.
2026-01-27 23:00:05 -03:00
Federico Leiva
b2045653ef Enhance build configuration and dependencies for Hytale plugin to use HytaleServer.jar from official maven repository. Added permissions in workflows to allow github action to create new releases. 2026-01-27 17:46:48 -03:00
37 changed files with 355 additions and 2099 deletions

View File

@@ -1,8 +1,12 @@
name: Build Plugin name: Build Plugin
permissions:
contents: write
on: on:
push: push:
branches: [ main, develop ] branches: [ main, develop ]
tags: [ 'v*' ]
pull_request: pull_request:
branches: [ main ] branches: [ main ]
@@ -11,37 +15,37 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Java 25 - name: Setup Java 25
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '25' java-version: '25'
distribution: 'temurin' distribution: 'temurin'
- name: Cache Gradle Dependencies - name: Cache Gradle Dependencies
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.gradle path: ~/.gradle
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: | restore-keys: |
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
- name: Grant Execute Permission for Gradlew - name: Grant Execute Permission for Gradlew
run: chmod +x gradlew run: chmod +x gradlew
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew shadowJar run: ./gradlew shadowJar
- name: Run Tests - name: Run Tests
run: ./gradlew test run: ./gradlew test
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: plugin-jar name: plugin-jar
path: build/libs/*.jar path: build/libs/*.jar
release: release:
needs: build needs: build
@@ -49,24 +53,24 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Java 25 - name: Setup Java 25
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '25' java-version: '25'
distribution: 'temurin' distribution: 'temurin'
- name: Grant Execute Permission for Gradlew - name: Grant Execute Permission for Gradlew
run: chmod +x gradlew run: chmod +x gradlew
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew shadowJar run: ./gradlew shadowJar
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: build/libs/*.jar files: build/libs/*.jar
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore vendored
View File

@@ -38,7 +38,9 @@ bin/
/.nb-gradle/ /.nb-gradle/
# VS Code # VS Code
.vscode/ .vscode/*
!.vscode/java-formatter.xml
!.vscode/settings.json
# Mac OS # Mac OS
.DS_Store .DS_Store

View File

@@ -6,7 +6,7 @@ A minimal, ready-to-use template for creating Hytale plugins with modern build t
## Features ## Features
**Modern Build System** - Gradle with Kotlin DSL **Modern Build System** - Gradle with Groovy DSL
**Automated Testing** - Custom Gradle plugin for one-command server testing **Automated Testing** - Custom Gradle plugin for one-command server testing
**Java 25** - Latest Java features **Java 25** - Latest Java features
**ShadowJar** - Automatic dependency bundling **ShadowJar** - Automatic dependency bundling
@@ -51,9 +51,9 @@ Your plugin JAR will be in: `build/libs/TemplatePlugin-1.0.0.jar`
When ready to customize, edit these files: When ready to customize, edit these files:
**`settings.gradle.kts`:** **`settings.gradle`:**
```kotlin ```groovy
rootProject.name = "your-plugin-name" rootProject.name = "your-plugin-name"
``` ```
@@ -139,9 +139,9 @@ TemplatePlugin/
│ └── resources/ │ └── resources/
│ └── manifest.json # Plugin metadata │ └── manifest.json # Plugin metadata
├── .gitignore # Git ignore rules ├── .gitignore # Git ignore rules
├── build.gradle.kts # Build configuration ├── build.gradle # Build configuration
├── gradle.properties # Project properties ├── gradle.properties # Project properties
├── settings.gradle.kts # Project settings ├── settings.gradle # Project settings
├── LICENSE # MIT License ├── LICENSE # MIT License
└── README.md # This file └── README.md # This file
``` ```
@@ -200,9 +200,9 @@ rm -rf run/
### Adding Dependencies ### Adding Dependencies
Edit `build.gradle.kts`: Edit `build.gradle`:
```kotlin ```groovy
dependencies { dependencies {
// Hytale API (provided by server) // Hytale API (provided by server)
compileOnly(files("./HytaleServer.jar")) compileOnly(files("./HytaleServer.jar"))
@@ -221,9 +221,9 @@ dependencies {
**Usage:** **Usage:**
Edit `build.gradle.kts`: Edit `build.gradle`:
```kotlin ```groovy
runHytale { runHytale {
jarUrl = "url to hytale server jar" jarUrl = "url to hytale server jar"
} }
@@ -322,7 +322,7 @@ GitHub Actions will automatically build and create a release with your plugin JA
### Server Won't Start ### Server Won't Start
1. Check that `jarUrl` in `build.gradle.kts` is correct 1. Check that `jarUrl` in `build.gradle` is correct
2. Verify Java 25 is installed: `java -version` 2. Verify Java 25 is installed: `java -version`
3. Check logs in `run/logs/` 3. Check logs in `run/logs/`

View File

@@ -1,6 +1,7 @@
plugins { plugins {
id 'java' id 'java'
id 'org.jetbrains.gradle.plugin.idea-ext' version '1.3' id 'org.jetbrains.gradle.plugin.idea-ext' version '1.3'
id 'com.gradleup.shadow' version '9.3.1'
} }
import org.gradle.internal.os.OperatingSystem import org.gradle.internal.os.OperatingSystem
@@ -8,13 +9,17 @@ import org.gradle.internal.os.OperatingSystem
ext { ext {
if (project.hasProperty('hytale_home')) { if (project.hasProperty('hytale_home')) {
hytaleHome = project.findProperty('hytale_home') hytaleHome = project.findProperty('hytale_home')
} else if (System.getenv('HYTALE_HOME')) {
hytaleHome = System.getenv('HYTALE_HOME')
} else { } else {
def os = OperatingSystem.current() def os = OperatingSystem.current()
if (os.isWindows()) { if (os.isWindows()) {
hytaleHome = "${System.getProperty("user.home")}/AppData/Roaming/Hytale" hytaleHome = "${System.getProperty("user.home")}/AppData/Roaming/Hytale"
} else if (os.isMacOsX()) { }
else if (os.isMacOsX()) {
hytaleHome = "${System.getProperty("user.home")}/Library/Application Support/Hytale" hytaleHome = "${System.getProperty("user.home")}/Library/Application Support/Hytale"
} else if (os.isLinux()) { }
else if (os.isLinux()) {
hytaleHome = "${System.getProperty("user.home")}/.var/app/com.hypixel.HytaleLauncher/data/Hytale" hytaleHome = "${System.getProperty("user.home")}/.var/app/com.hypixel.HytaleLauncher/data/Hytale"
if (!file(hytaleHome).exists()) { if (!file(hytaleHome).exists()) {
hytaleHome = "${System.getProperty("user.home")}/.local/share/Hytale" hytaleHome = "${System.getProperty("user.home")}/.local/share/Hytale"
@@ -23,10 +28,10 @@ ext {
} }
} }
if (!project.hasProperty('hytaleHome')) { def hytaleHomePath = ext.has('hytaleHome') ? hytaleHome : null
throw new GradleException('Your Hytale install could not be detected automatically. If you are on an unsupported platform or using a custom install location, please define the install location using the hytale_home property.'); def hasHytaleHome = (hytaleHomePath != null) && file(hytaleHomePath).exists()
} else if (!file(project.findProperty('hytaleHome')).exists()) { if (!hasHytaleHome) {
throw new GradleException("Failed to find Hytale at the expected location. Please make sure you have installed the game. The expected location can be changed using the hytale_home property. Currently looking in ${project.findProperty('hytaleHome')}") logger.lifecycle("Hytale install not detected; run configs that launch the server will be skipped. Set -Phytale_home=/path/to/Hytale to enable them.")
} }
java { java {
@@ -40,23 +45,28 @@ javadoc {
options.addStringOption('Xdoclint:-missing', '-quiet') options.addStringOption('Xdoclint:-missing', '-quiet')
} }
repositories {
maven {
url = uri("https://cursemaven.com")
}
}
// Adds the Hytale server as a build dependency, allowing you to reference and // Adds the Hytale server as a build dependency, allowing you to reference and
// compile against their code. This requires you to have Hytale installed using // compile against their code without bundling it. When a local install is
// the official launcher for now. // present, we still use its jar for launching the server in IDE run configs.
dependencies { dependencies {
implementation(files("$hytaleHome/install/$patchline/package/game/latest/Server/HytaleServer.jar")) compileOnly("com.hypixel.hytale:Server:$hytale_build")
if (hasHytaleHome) {
runtimeOnly(files("$hytaleHome/install/$patchline/package/game/latest/Server/HytaleServer.jar"))
}
// Your dependencies here
} }
// Create the working directory to run the server if it does not already exist. repositories {
def serverRunDir = file("$projectDir/run") mavenCentral()
if (!serverRunDir.exists()) { maven {
serverRunDir.mkdirs() name = "hytale-release"
url = uri("https://maven.hytale.com/release")
}
maven {
name = "hytale-pre-release"
url = uri("https://maven.hytale.com/pre-release")
}
} }
// Updates the manifest.json file with the latest properties defined in the // Updates the manifest.json file with the latest properties defined in the
@@ -80,54 +90,105 @@ tasks.named('processResources') {
dependsOn 'updatePluginManifest' dependsOn 'updatePluginManifest'
} }
def createServerRunArguments(String srcDir) { tasks.named('shadowJar') {
def programParameters = '--allow-op --disable-sentry --assets="' + "${hytaleHome}/install/$patchline/package/game/latest/Assets.zip" + '"' archiveClassifier.set('')
def modPaths = [] mergeServiceFiles()
if (includes_pack.toBoolean()) {
modPaths << srcDir
}
if (load_user_mods.toBoolean()) {
modPaths << "${hytaleHome}/UserData/Mods"
}
if (!modPaths.isEmpty()) {
programParameters += ' --mods="' + modPaths.join(',') + '"'
}
return programParameters
} }
// Creates a run configuration in IDEA that will run the Hytale server with // Ensure the shaded jar is produced during a normal build.
// your plugin and the default assets. tasks.named('build') {
idea.project.settings.runConfigurations { dependsOn 'shadowJar'
'HytaleServer'(org.jetbrains.gradle.ext.Application) {
mainClass = 'com.hypixel.hytale.Main'
moduleName = project.idea.module.name + '.main'
programParameters = createServerRunArguments(sourceSets.main.java.srcDirs.first().parentFile.absolutePath)
workingDirectory = serverRunDir.absolutePath
}
} }
// Creates a launch.json file for VSCode with the same configuration if (hasHytaleHome) {
tasks.register('generateVSCodeLaunch') { // Create the working directory to run the server if it does not already exist.
def vscodeDir = file("$projectDir/.vscode") def serverRunDir = file("$projectDir/run")
def launchFile = file("$vscodeDir/launch.json") if (!serverRunDir.exists()) {
doLast { serverRunDir.mkdirs()
if (!vscodeDir.exists()) { }
vscodeDir.mkdirs()
def createServerRunArguments = { String srcDir ->
def programParameters = '--allow-op --disable-sentry --assets="' + "${hytaleHome}/install/$patchline/package/game/latest/Assets.zip" + '"'
def modPaths = []
if (includes_pack.toBoolean()) {
modPaths << srcDir
}
if (load_user_mods.toBoolean()) {
modPaths << "${hytaleHome}/UserData/Mods"
}
if (!modPaths.isEmpty()) {
programParameters += ' --mods="' + modPaths.join(',') + '"'
}
return programParameters
}
def serverJar = file("$hytaleHome/install/$patchline/package/game/latest/Server/HytaleServer.jar")
def assetsZip = file("$hytaleHome/install/$patchline/package/game/latest/Assets.zip")
def shadowJarTask = tasks.named('shadowJar')
tasks.register('runServerJar', JavaExec) {
dependsOn shadowJarTask
mainClass = 'com.hypixel.hytale.Main'
classpath = files(serverJar)
workingDir = serverRunDir.absolutePath
doFirst {
def modPaths = [shadowJarTask.get().archiveFile.get().asFile.absolutePath]
if (load_user_mods.toBoolean()) {
modPaths << "${hytaleHome}/UserData/Mods"
}
setArgs([
'--allow-op',
'--disable-sentry',
"--assets=${assetsZip}",
"--mods=${modPaths.join(',')}"
])
}
}
tasks.register('runServer') {
dependsOn 'runServerJar'
}
// Creates a run configuration in IDEA that will run the Hytale server with
// your plugin and the default assets.
idea.project.settings.runConfigurations {
'HytaleServer'(org.jetbrains.gradle.ext.Application) {
mainClass = 'com.hypixel.hytale.Main'
moduleName = project.idea.module.name + '.main'
programParameters = createServerRunArguments(sourceSets.main.java.srcDirs.first().parentFile.absolutePath)
workingDirectory = serverRunDir.absolutePath
}
}
// Creates a launch.json file for VSCode with the same configuration
tasks.register('generateVSCodeLaunch') {
def vscodeDir = file("$projectDir/.vscode")
def launchFile = file("$vscodeDir/launch.json")
doLast {
if (!vscodeDir.exists()) {
vscodeDir.mkdirs()
}
def programParams = createServerRunArguments("\${workspaceFolder}")
def launchConfig = [
version: "0.2.0",
configurations: [
[
type: "java",
name: "HytaleServer",
request: "launch",
mainClass: "com.hypixel.hytale.Main",
args: programParams,
cwd: "\${workspaceFolder}/run"
]
]
]
launchFile.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(launchConfig))
}
}
} else {
tasks.register('generateVSCodeLaunch') {
doLast {
logger.lifecycle("Skipped VSCode launch configuration because hytale_home is not set or the install path is missing.")
} }
def programParams = createServerRunArguments("\${workspaceFolder}")
def launchConfig = [
version : "0.2.0",
configurations: [
[
type : "java",
name : "HytaleServer",
request : "launch",
mainClass: "com.hypixel.hytale.Main",
args : programParams,
cwd : "\${workspaceFolder}/run"
]
]
]
launchFile.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(launchConfig))
} }
} }

View File

@@ -1,87 +0,0 @@
plugins {
id("java-library")
id("com.gradleup.shadow") version "9.3.1"
id("run-hytale")
}
group = findProperty("pluginGroup") as String? ?: "com.example"
version = findProperty("pluginVersion") as String? ?: "1.0.0"
description = findProperty("pluginDescription") as String? ?: "A Hytale plugin template"
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
// Hytale Server API (provided by server at runtime)
compileOnly(files("./libs/HytaleServer.jar"))
// Common dependencies (will be bundled in JAR)
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.jetbrains:annotations:24.1.0")
// Test dependencies
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
// Configure server testing
runHytale {
jarUrl = "./libs/HytaleServer.jar"
assetsPath = "./libs/Assets.zip"
}
tasks {
// Configure Java compilation
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release = 25
}
// Configure resource processing
processResources {
filteringCharset = Charsets.UTF_8.name()
// Replace placeholders in manifest.json
val props = mapOf(
"group" to project.group,
"version" to project.version,
"description" to project.description
)
inputs.properties(props)
filesMatching("manifest.json") {
expand(props)
}
}
// Configure ShadowJar (bundle dependencies)
shadowJar {
archiveBaseName.set(rootProject.name)
archiveClassifier.set("")
// Relocate dependencies to avoid conflicts
relocate("com.google.gson", "com.yourplugin.libs.gson")
// Minimize JAR size (removes unused classes)
minimize()
}
// Configure tests
test {
useJUnitPlatform()
}
// Make build depend on shadowJar
build {
dependsOn(shadowJar)
}
}
// Configure Java toolchain
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}

View File

@@ -1,9 +1,9 @@
# The current version of your project. Please use semantic versioning! # The current version of your project. Please use semantic versioning!
version=0.0.1 version=0.0.2
# The group ID used for maven publishing. Usually the same as your package name # The group ID used for maven publishing. Usually the same as your package name
# but not the same as your plugin group! # but not the same as your plugin group!
maven_group=org.KaiFlo maven_group=org.example
# The version of Java used by your plugin. The game is built on Java 21 but # The version of Java used by your plugin. The game is built on Java 21 but
# actually runs on Java 25. # actually runs on Java 25.
@@ -19,6 +19,11 @@ includes_pack=true
# official launcher. # official launcher.
patchline=release patchline=release
# The exact Hytale build to compile against. Use the build string from the
# launcher (format YYYY.MM.DD-<hash>) so Gradle pulls the matching server jar
# for your selected patchline.
hytale_build=2026.01.27-734d39026
# Determines if the development server should also load mods from the user's # Determines if the development server should also load mods from the user's
# standard mods folder. This lets you test mods by installing them where a # standard mods folder. This lets you test mods by installing them where a
# normal player would, instead of adding them as dependencies or adding them # normal player would, instead of adding them as dependencies or adding them

View File

@@ -0,0 +1,88 @@
package com.example.templateplugin;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import javax.annotation.Nonnull;
import java.util.logging.Level;
/**
* Main plugin class.
*
* TODO: Implement your plugin logic here.
*
* @author YourName
* @version 1.0.0
*/
public class TemplatePlugin extends JavaPlugin {
private static TemplatePlugin instance;
/**
* Constructor - Called when plugin is loaded.
*/
public TemplatePlugin(@Nonnull JavaPluginInit init) {
super(init);
instance = this;
getLogger().at(Level.INFO).log("[TemplatePlugin] Plugin loaded!");
}
/**
* Get plugin instance.
*/
public static TemplatePlugin getInstance() {
return instance;
}
/**
* Called when plugin is set up.
*/
@Override
protected void setup() {
getLogger().at(Level.INFO).log("[TemplatePlugin] Plugin setup!");
// TODO: Initialize your plugin here
// - Load configuration
// - Register event listeners
// - Register commands
// - Start services
registerEvents();
registerCommands();
}
/**
* Called when plugin is enabled.
*/
@Override
protected void start() {
getLogger().at(Level.INFO).log("[TemplatePlugin] Plugin enabled!");
}
/**
* Called when plugin is disabled.
*/
@Override
public void shutdown() {
getLogger().at(Level.INFO).log("[TemplatePlugin] Plugin disabled!");
// TODO: Cleanup your plugin here
// - Save data
// - Stop services
// - Close connections
}
/**
* Register your commands here.
*/
private void registerEvents() {
}
/**
* Register your commands here.
*/
private void registerCommands() {
}
}

View File

@@ -1,54 +0,0 @@
package org.KaiFlo.SolarCell.Commands;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import org.KaiFlo.SolarCell.Helpers.BlockHelper;
import javax.annotation.Nonnull;
import java.util.Objects;
public class ExampleCommand extends CommandBase {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private final String pluginName;
private final String pluginVersion;
private final OptionalArg<Integer> sizeArg;
public ExampleCommand(String pluginName, String pluginVersion) {
super("test", "Prints a test message from the " + pluginName + " plugin.");
this.setPermissionGroup(GameMode.Adventure); // Allows the command to be used by anyone, not just OP
this.pluginName = pluginName;
this.pluginVersion = pluginVersion;
this.sizeArg = this.withOptionalArg("", "", ArgTypes.INTEGER);
}
@Override
protected void executeSync(@Nonnull CommandContext ctx) {
ctx.sendMessage(Message.raw("Hello from the " + pluginName + " v" + pluginVersion + " plugin!"));
World defaultWorld = Objects.requireNonNull(Universe.get().getDefaultWorld());
defaultWorld.execute(() -> {
Store<EntityStore> store = defaultWorld.getEntityStore().getStore();
var playerRef = ctx.senderAsPlayerRef();
var playerTransform = store.getComponent(Objects.requireNonNull(playerRef), TransformComponent.getComponentType());
Vector3i playerPosition = Objects.requireNonNull(playerTransform).getPosition().toVector3i();
var size = sizeArg.get(ctx);
if (size == null) size = 5;
BlockHelper.executeForCubeAround(playerPosition.x, playerPosition.y, playerPosition.z, size,true, (x, y, z) -> {
defaultWorld.breakBlock(x, y, z, 0);
});
});
}
}

View File

@@ -1,11 +0,0 @@
package org.KaiFlo.SolarCell.Components.EnergyConsumer;
public interface IEnergyConsumer {
float getWorkingCapabilityRatio();
void setWorkingCapabilityRatio(float workingCapabilityRatio);
long getConsumptionPerTick();
void setConsumptionPerTick(long consumptionPerTick);
}

View File

@@ -1,70 +0,0 @@
package org.KaiFlo.SolarCell.Components.EnergyConsumer.Implementations;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Components.EnergyConsumer.IEnergyConsumer;
import org.KaiFlo.SolarCell.SolarCellPlugin;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.HyLogger;
public class EnergyConsumerComponent implements Component<ChunkStore>, IEnergyConsumer {
public static final BuilderCodec<EnergyConsumerComponent> CODEC = BuilderCodec.builder(EnergyConsumerComponent.class, EnergyConsumerComponent::new)
.append(new KeyedCodec<>("ConsumptionPerTick", Codec.LONG),
(component, value) -> component.consumptionPerTick = value,
(component) -> component.consumptionPerTick
)
.addValidator(Validators.greaterThanOrEqual(0L))
.documentation("ConsumptionPerTick defines the Consumers ConsumptionPerTick")
.add()
.build();
private long consumptionPerTick;
private float workingCapabilityRatio;
@Override
public float getWorkingCapabilityRatio() {
return workingCapabilityRatio;
}
@Override
public void setWorkingCapabilityRatio(float workingCapabilityRatio) {
this.workingCapabilityRatio = workingCapabilityRatio;
}
@Override
public long getConsumptionPerTick() {
return consumptionPerTick;
}
@Override
public void setConsumptionPerTick(long consumptionPerTick) {
this.consumptionPerTick = consumptionPerTick;
}
private EnergyConsumerComponent copyFrom(EnergyConsumerComponent other) {
this.consumptionPerTick = other.consumptionPerTick;
this.workingCapabilityRatio = other.workingCapabilityRatio;
return this;
}
@NullableDecl
@Override
public Component<ChunkStore> clone() {
try {
super.clone();
} catch (CloneNotSupportedException e) {
HyLogger.atWarning().log("Cloning of " + this.getClass().getName() + " failed.");
}
return new EnergyConsumerComponent().copyFrom(this);
}
public static ComponentType<ChunkStore, EnergyConsumerComponent> getComponentType() {
return SolarCellPlugin.get().getEnergyConsumerComponentType();
}
}

View File

@@ -1,23 +0,0 @@
package org.KaiFlo.SolarCell.Components.EnergySource;
public interface IEnergySource {
/**
* @return True if energy source is endless, False otherwise
*/
boolean isEndless();
/**
* @return If the energy source is not endless, returns the Capacity of the energy source, otherwise -1
*/
long getEnergyCapacity();
/**
* @return The amount of energy the source produces per Tick
*/
long getGeneratesPerTick();
void setGeneratesPerTick(long generatesPerTick);
void setEnergyCapacity(long energyCapacity);
}

View File

@@ -1,81 +0,0 @@
package org.KaiFlo.SolarCell.Components.EnergySource.Implementations;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Components.EnergySource.IEnergySource;
import org.KaiFlo.SolarCell.SolarCellPlugin;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.HyLogger;
public class EnergySourceComponent implements Component<ChunkStore>, IEnergySource {
public static final BuilderCodec<EnergySourceComponent> CODEC = BuilderCodec.builder(EnergySourceComponent.class, EnergySourceComponent::new)
.append(new KeyedCodec<>("GeneratesPerTick", Codec.LONG),
(component, value) -> component.generatesPerTick = value,
(component) -> component.generatesPerTick
)
.addValidator(Validators.greaterThanOrEqual(0L))
.documentation("GeneratesPerTick defines the Sources GeneratesPerTick")
.add()
.append(new KeyedCodec<>("EnergyCapacity", Codec.LONG),
(component, value) -> component.energyCapacity = value,
(component) -> component.energyCapacity
)
.addValidator(Validators.greaterThanOrEqual(-1L))
.documentation("EnergyCapacity defines how long energy can be produced (Set to -1 if endless energy production)")
.add()
.build();
private long generatesPerTick = 5;
private long energyCapacity = -1;
public static ComponentType<ChunkStore, EnergySourceComponent> getComponentType() {
return SolarCellPlugin.get().getEnergySourceComponentType();
}
private EnergySourceComponent copyFrom(EnergySourceComponent other) {
this.generatesPerTick = other.generatesPerTick;
return this;
}
@NullableDecl
@Override
public Component<ChunkStore> clone() {
try {
super.clone();
} catch (CloneNotSupportedException e) {
HyLogger.atWarning().log("Cloning of " + this.getClass().getName() + " failed.");
}
return new EnergySourceComponent().copyFrom(this);
}
@Override
public boolean isEndless() {
return energyCapacity == -1;
}
@Override
public long getEnergyCapacity() {
return energyCapacity;
}
@Override
public void setEnergyCapacity(long energyCapacity) {
this.energyCapacity = energyCapacity;
}
@Override
public long getGeneratesPerTick() {
return generatesPerTick;
}
@Override
public void setGeneratesPerTick(long generatesPerTick) {
this.generatesPerTick = generatesPerTick;
}
}

View File

@@ -1,26 +0,0 @@
package org.KaiFlo.SolarCell.Components.EnergyStorage;
public interface IEnergyStorage {
long getMaxCapacity();
void setMaxCapacity(long maxCapacity);
long getExtractEnergyPerTick();
void setExtractEnergyPerTick(long extractEnergyPerTick);
long getCurrentEnergyAmount();
void setCurrentEnergyAmount(long currentEnergyAmount);
long getReceiveEnergyPerTick();
void setReceiveEnergyPerTick(long receiveEnergyPerTick);
long extractEnergy(long requiredEnergy);
long receiveEnergy(long inputEnergy);
double getCurrentEnergyToCapacityRatio();
}

View File

@@ -1,136 +0,0 @@
package org.KaiFlo.SolarCell.Components.EnergyStorage.Implementations;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Components.EnergyStorage.IEnergyStorage;
import org.KaiFlo.SolarCell.SolarCellPlugin;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.HyLogger;
public class EnergyStorageComponent implements Component<ChunkStore>, IEnergyStorage {
public static final BuilderCodec<EnergyStorageComponent> CODEC = BuilderCodec.builder(EnergyStorageComponent.class, EnergyStorageComponent::new)
.append(new KeyedCodec<>("MaxCapacity", Codec.LONG),
(component, value) -> component.maxCapacity = value,
(component) -> component.maxCapacity
)
.addValidator(Validators.greaterThanOrEqual(0L))
.documentation("MaxCapacity defines the Storage MaxCapacity")
.add()
.append(new KeyedCodec<>("ExtractEnergyPerTick", Codec.LONG),
(component, value) -> component.extractEnergyPerTick = value,
(component) -> component.extractEnergyPerTick
)
.addValidator(Validators.greaterThanOrEqual(0L))
.documentation("ExtractEnergyPerTick defines the Storage ExtractEnergyPerTick")
.add()
.append(new KeyedCodec<>("ReceiveEnergyPerTick", Codec.LONG),
(component, value) -> component.receiveEnergyPerTick = value,
(component) -> component.receiveEnergyPerTick
)
.addValidator(Validators.greaterThanOrEqual(0L))
.documentation("ReceiveEnergyPerTick defines the Storage ReceiveEnergyPerTick")
.add()
.append(new KeyedCodec<>("CurrentEnergyAmount", Codec.LONG),
(component, value) -> component.currentEnergyAmount = value,
(component) -> component.currentEnergyAmount
)
.addValidator(Validators.greaterThanOrEqual(0L))
.documentation("CurrentEnergyAmount defines the Storage CurrentEnergyAmount")
.add()
.build();
private long maxCapacity;
private long extractEnergyPerTick;
private long currentEnergyAmount;
private long receiveEnergyPerTick;
@Override
public long getMaxCapacity() {
return maxCapacity;
}
@Override
public void setMaxCapacity(long maxCapacity) {
this.maxCapacity = maxCapacity;
}
@Override
public long getExtractEnergyPerTick() {
return extractEnergyPerTick;
}
@Override
public void setExtractEnergyPerTick(long extractEnergyPerTick) {
this.extractEnergyPerTick = extractEnergyPerTick;
}
@Override
public long getCurrentEnergyAmount() {
return currentEnergyAmount;
}
@Override
public void setCurrentEnergyAmount(long currentEnergyAmount) {
this.currentEnergyAmount = currentEnergyAmount;
}
@Override
public long getReceiveEnergyPerTick() {
return receiveEnergyPerTick;
}
@Override
public void setReceiveEnergyPerTick(long receiveEnergyPerTick) {
this.receiveEnergyPerTick = receiveEnergyPerTick;
}
@Override
public double getCurrentEnergyToCapacityRatio(){
return (double) currentEnergyAmount / maxCapacity;
}
@Override
public long extractEnergy(long requiredEnergy) {
var extractedEnergy = Math.min(currentEnergyAmount, Math.min(requiredEnergy, extractEnergyPerTick));
currentEnergyAmount -= extractedEnergy;
return extractedEnergy;
}
@Override
public long receiveEnergy(long inputEnergy) {
var receivedEnergy = Math.min(maxCapacity - currentEnergyAmount, Math.min(inputEnergy, receiveEnergyPerTick));
currentEnergyAmount += receivedEnergy;
return receivedEnergy;
}
private EnergyStorageComponent copyFrom(EnergyStorageComponent other) {
this.maxCapacity = other.maxCapacity;
this.currentEnergyAmount = other.currentEnergyAmount;
this.receiveEnergyPerTick = other.receiveEnergyPerTick;
this.extractEnergyPerTick = other.extractEnergyPerTick;
return this;
}
@NullableDecl
@Override
public Component<ChunkStore> clone() {
try {
super.clone();
} catch (CloneNotSupportedException e) {
HyLogger.atWarning().log("Cloning of " + this.getClass().getName() + " failed.");
}
return new EnergyStorageComponent().copyFrom(this);
}
public static ComponentType<ChunkStore, EnergyStorageComponent> getComponentType() {
return SolarCellPlugin.get().getEnergyStorageComponentType();
}
}

View File

@@ -1,19 +0,0 @@
package org.KaiFlo.SolarCell.Enums;
public enum BlockRotation {
NORTH,
EAST,
SOUTH,
WEST;
public static BlockRotation getEnum(int rotationIndex) {
return switch ( rotationIndex )
{
case 0 -> NORTH;
case 1 -> WEST;
case 2 -> SOUTH;
case 3 -> EAST;
default -> NORTH;
};
}
}

View File

@@ -1,189 +0,0 @@
package org.KaiFlo.SolarCell.Helpers;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Enums.BlockRotation;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import java.util.*;
public class BlockHelper {
public static final HytaleLogger HyLogger = HytaleLogger.getLogger();
public static void executeForCubeAround(int x, int y, int z, int size, boolean own, Callback callback) {
for (int xOffset = 0; xOffset < size; xOffset++) {
for (int yOffset = 0; yOffset < size; yOffset++) {
for (int zOffset = 0; zOffset < size; zOffset++) {
var xPos = (xOffset - size / 2) + x;
var yPos = (yOffset - size / 2) + y;
var zPos = (zOffset - size / 2) + z;
if (!(xPos == x && yPos == y && zPos == z)) {
callback.accept(xPos, yPos, zPos);
} else if (own) {
callback.accept(xPos, yPos, zPos);
}
}
}
}
}
public static void executeForDirection(
int centerX, int centerY, int centerZ,
World world,
CommandBuffer<ChunkStore> commandBuffer,
BlockRotation direction,
ChunkSafeCallback chunkSafeCallback){
Map<Long, List<BlockPos>> positionsByChunk = new HashMap<>();
addBlockInDirectionToPositions(centerX, centerY, centerZ, direction, positionsByChunk);
addBlockDownwardToPositions(centerX, centerY, centerZ, positionsByChunk);
executeChunkSafeCallbackForPositions(world, commandBuffer, chunkSafeCallback, positionsByChunk);
}
private static void addBlockInDirectionToPositions(
int centerX, int centerY, int centerZ, BlockRotation direction, Map<Long, List<BlockPos>> positionsByChunk) {
int xPos = centerX;
int zPos = centerZ;
if(direction == BlockRotation.NORTH || direction == BlockRotation.SOUTH){
zPos += direction == BlockRotation.SOUTH ? 1 : -1;
}else{
xPos += direction == BlockRotation.EAST ? 1 : -1;
}
int chunkX = Math.floorDiv(xPos, 32);
int chunkZ = Math.floorDiv(zPos, 32);
long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ);
int localX = Math.floorMod(xPos, 32);
int localZ = Math.floorMod(zPos, 32);
positionsByChunk
.computeIfAbsent(chunkIndex, _ -> new ArrayList<>())
.add(new BlockPos(xPos, centerY, zPos, localX, localZ));
}
private static void addBlockDownwardToPositions(
int centerX, int centerY, int centerZ, Map<Long, List<BlockPos>> positionsByChunk) {
int yPos = centerY - 1;
int chunkX = Math.floorDiv(centerX, 32);
int chunkZ = Math.floorDiv(centerZ, 32);
long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ);
int localX = Math.floorMod(centerX, 32);
int localZ = Math.floorMod(centerZ, 32);
positionsByChunk
.computeIfAbsent(chunkIndex, _ -> new ArrayList<>())
.add(new BlockPos(centerX, yPos, centerZ, localX, localZ));
}
public static void executeForCubeAroundChunkSafe(
int centerX, int centerY, int centerZ,
int size,
boolean includeCenter,
World world,
CommandBuffer<ChunkStore> commandBuffer,
ChunkSafeCallback chunkSafeCallback
) {
int halfSize = size / 2;
Map<Long, List<BlockPos>> positionsByChunk = new HashMap<>();
for (int xOffset = -halfSize; xOffset <= halfSize; xOffset++) {
for (int yOffset = -halfSize; yOffset <= halfSize; yOffset++) {
for (int zOffset = -halfSize; zOffset <= halfSize; zOffset++) {
int xPos = centerX + xOffset;
int yPos = centerY + yOffset;
int zPos = centerZ + zOffset;
if (!includeCenter && xPos == centerX && yPos == centerY && zPos == centerZ) {
continue;
}
int chunkX = Math.floorDiv(xPos, 32);
int chunkZ = Math.floorDiv(zPos, 32);
long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ);
int localX = Math.floorMod(xPos, 32);
int localZ = Math.floorMod(zPos, 32);
positionsByChunk
.computeIfAbsent(chunkIndex, _ -> new ArrayList<>())
.add(new BlockPos(xPos, yPos, zPos, localX, localZ));
}
}
}
executeChunkSafeCallbackForPositions(world, commandBuffer, chunkSafeCallback, positionsByChunk);
}
private static void executeChunkSafeCallbackForPositions(World world, CommandBuffer<ChunkStore> commandBuffer, ChunkSafeCallback chunkSafeCallback, Map<Long, List<BlockPos>> positionsByChunk) {
world.execute(() -> {
for (var entry : positionsByChunk.entrySet()) {
long chunkIndexCI = entry.getKey();
List<BlockPos> blockPositions = entry.getValue();
WorldChunk worldChunk = world.getChunkIfLoaded(chunkIndexCI);
if (worldChunk == null) continue;
var blockComponentChunk = commandBuffer.getComponent(
worldChunk.getReference(),
BlockComponentChunk.getComponentType()
);
if (blockComponentChunk == null) continue;
for (BlockPos pos : blockPositions) {
int index = ChunkUtil.indexBlockInColumn(
pos.localX(), pos.y(), pos.localZ()
);
var targetRef = blockComponentChunk.getEntityReference(index);
if (targetRef == null) continue;
chunkSafeCallback.accept(
pos.x(), pos.y(), pos.z(),
targetRef,
blockComponentChunk,
worldChunk
);
}
}
});
}
record BlockPos(int x, int y, int z, int localX, int localZ) {}
public interface Callback {
void accept(int x, int y, int z);
}
public interface ChunkSafeCallback {
void accept(int x, int y, int z, Ref<ChunkStore> targetRef, BlockComponentChunk blockComponentChunk, WorldChunk targetChunk);
}
public static void setBlockRefTicking(Ref<ChunkStore> blockRef, @NonNullDecl CommandBuffer<ChunkStore> commandBuffer) {
BlockModule.BlockStateInfo blockInfo = commandBuffer.getComponent(blockRef, BlockModule.BlockStateInfo.getComponentType());
if (blockInfo == null) return;
WorldChunk worldChunk = commandBuffer.getComponent(blockInfo.getChunkRef(), WorldChunk.getComponentType());
if (worldChunk == null) return;
int x = ChunkUtil.xFromBlockInColumn(blockInfo.getIndex());
int y = ChunkUtil.yFromBlockInColumn(blockInfo.getIndex());
int z = ChunkUtil.zFromBlockInColumn(blockInfo.getIndex());
worldChunk.setTicking(x, y, z, true);
}
}

View File

@@ -1,16 +0,0 @@
package org.KaiFlo.SolarCell.Helpers;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import java.util.List;
import java.util.Optional;
public class ComponentHelper {
public static<T> Optional<T> getComponentOfType(List<Component<ChunkStore>> components, Class<T> type){
return components.stream()
.filter(x -> x.getClass() == type)
.map(type::cast)
.findFirst();
}
}

View File

@@ -1,75 +0,0 @@
package org.KaiFlo.SolarCell;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Commands.ExampleCommand;
import org.KaiFlo.SolarCell.Components.EnergyConsumer.Implementations.EnergyConsumerComponent;
import org.KaiFlo.SolarCell.Components.EnergySource.Implementations.EnergySourceComponent;
import org.KaiFlo.SolarCell.Components.EnergyStorage.Implementations.EnergyStorageComponent;
import org.KaiFlo.SolarCell.Systems.EnergySource.EnergySourceInitializerSystem;
import org.KaiFlo.SolarCell.Systems.EnergySource.TickingImplementations.SolarCellSourceTicking;
import org.KaiFlo.SolarCell.Systems.EnergyStorage.EnergyStorageInitializerSystem;
import org.KaiFlo.SolarCell.Systems.EnergyStorage.TickingImplementations.BatteryStorageTicking;
import org.KaiFlo.SolarCell.Systems.EnergyTickingSystem;
import javax.annotation.Nonnull;
import java.util.List;
public class SolarCellPlugin extends JavaPlugin {
protected static SolarCellPlugin instance;
public static SolarCellPlugin get() {
return instance;
}
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private ComponentType<ChunkStore, EnergySourceComponent> energySourceComponentType;
private ComponentType<ChunkStore, EnergyConsumerComponent> energyConsumerComponentType;
private ComponentType<ChunkStore, EnergyStorageComponent> energyStorageComponentType;
public SolarCellPlugin(@Nonnull JavaPluginInit init) {
super(init);
LOGGER.atInfo().log("Hello from " + this.getName() + " version " + this.getManifest().getVersion().toString());
}
@Override
protected void setup() {
instance = this;
LOGGER.atInfo().log("Setting up plugin " + this.getName());
energySourceComponentType = this.getChunkStoreRegistry().registerComponent(EnergySourceComponent.class, "EnergySource", EnergySourceComponent.CODEC);
energyConsumerComponentType = this.getChunkStoreRegistry().registerComponent(EnergyConsumerComponent.class, "EnergyConsumer", EnergyConsumerComponent.CODEC);
energyStorageComponentType = this.getChunkStoreRegistry().registerComponent(EnergyStorageComponent.class, "EnergyStorage", EnergyStorageComponent.CODEC);
this.getCommandRegistry().registerCommand(new ExampleCommand(this.getName(), this.getManifest().getVersion().toString()));
var energyTickingSystem = new EnergyTickingSystem()
.withTickingSystemForComponentTypes(List.of(energyStorageComponentType), new BatteryStorageTicking())
.withTickingSystemForComponentTypes(List.of(energySourceComponentType,energyStorageComponentType), new SolarCellSourceTicking());
this.getChunkStoreRegistry().registerSystem(new EnergySourceInitializerSystem());
this.getChunkStoreRegistry().registerSystem(new EnergyStorageInitializerSystem());
this.getChunkStoreRegistry().registerSystem(energyTickingSystem);
this.getCodecRegistry(Interaction.CODEC).register("StorageUseInteraction", StorageIntercationListener.class, StorageIntercationListener.CODEC);
}
public ComponentType<ChunkStore, EnergySourceComponent> getEnergySourceComponentType() {
return energySourceComponentType;
}
public ComponentType<ChunkStore, EnergyConsumerComponent> getEnergyConsumerComponentType() {
return energyConsumerComponentType;
}
public ComponentType<ChunkStore, EnergyStorageComponent> getEnergyStorageComponentType() {
return energyStorageComponentType;
}
}

View File

@@ -1,75 +0,0 @@
package org.KaiFlo.SolarCell;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.protocol.SimpleBlockInteraction;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInteraction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.UseBlockInteraction;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.NotificationUtil;
import com.hypixel.hytale.protocol.BlockPosition;
import org.KaiFlo.SolarCell.Components.EnergyStorage.Implementations.EnergyStorageComponent;
import javax.annotation.Nonnull;
public class StorageIntercationListener extends SimpleInstantInteraction {
public static final BuilderCodec<StorageIntercationListener> CODEC = BuilderCodec.builder(
StorageIntercationListener.class, StorageIntercationListener::new, SimpleInstantInteraction.CODEC
).build();
@Override
protected void firstRun(@Nonnull InteractionType interactionType, @Nonnull InteractionContext interactionContext, @Nonnull CooldownHandler cooldownHandler) {
CommandBuffer<EntityStore> commandBuffer = interactionContext.getCommandBuffer();
Ref<EntityStore> ref = interactionContext.getEntity();
BlockPosition pos = interactionContext.getTargetBlock();
Player player = commandBuffer.getComponent(ref, Player.getComponentType());
World world = commandBuffer.getExternalData().getWorld();
if (interactionType.getValue() == InteractionType.Use.getValue()) {
int chunkX = Math.floorDiv(pos.x, 32);
int chunkZ = Math.floorDiv(pos.z, 32);
int localX = Math.floorMod(pos.x, 32);
int localZ = Math.floorMod(pos.z, 32);
world.execute(() -> {
var chunkStore = world.getChunkStore().getStore();
var targetChunk = world.getChunk(ChunkUtil.indexChunk(chunkX, chunkZ));
if (targetChunk == null) return;
var blockComponentChunk = chunkStore.getComponent(
targetChunk.getReference(),
BlockComponentChunk.getComponentType()
);
if (blockComponentChunk == null) return;
int index = ChunkUtil.indexBlockInColumn(localX, pos.y, localZ);
var targetRef = blockComponentChunk.getEntityReference(index);
if (targetRef == null) return;
var energyStorage = chunkStore.getComponent(targetRef, EnergyStorageComponent.getComponentType());
if (energyStorage == null) return;
NotificationUtil.sendNotificationToUniverse(String.format("%d/%d (%.2f%%)",
energyStorage.getCurrentEnergyAmount(),
energyStorage.getMaxCapacity(),
energyStorage.getCurrentEnergyToCapacityRatio() * 100
));
});
}
}
}

View File

@@ -1,31 +0,0 @@
package org.KaiFlo.SolarCell.Systems.EnergySource;
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Components.EnergySource.Implementations.EnergySourceComponent;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.setBlockRefTicking;
public class EnergySourceInitializerSystem extends RefSystem<ChunkStore> {
@Override
public void onEntityAdded(@NonNullDecl Ref<ChunkStore> ref, @NonNullDecl AddReason addReason, @NonNullDecl Store<ChunkStore> store, @NonNullDecl CommandBuffer<ChunkStore> commandBuffer) {
setBlockRefTicking(ref, commandBuffer);
}
@Override
public void onEntityRemove(@NonNullDecl Ref ref, @NonNullDecl RemoveReason removeReason, @NonNullDecl Store store, @NonNullDecl CommandBuffer commandBuffer) {
//Nothing to do yet
}
@NullableDecl
@Override
public Query<ChunkStore> getQuery() {
return Query.and(EnergySourceComponent.getComponentType(), BlockModule.BlockStateInfo.getComponentType());
}
}

View File

@@ -1,34 +0,0 @@
package org.KaiFlo.SolarCell.Systems.EnergySource.TickingImplementations;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.protocol.Vector3i;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Components.EnergySource.Implementations.EnergySourceComponent;
import org.KaiFlo.SolarCell.Components.EnergyStorage.Implementations.EnergyStorageComponent;
import org.KaiFlo.SolarCell.Systems.ITickingSystem;
import java.util.List;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.HyLogger;
import static org.KaiFlo.SolarCell.Helpers.ComponentHelper.getComponentOfType;
public class SolarCellSourceTicking implements ITickingSystem {
@Override
public void accept(Ref<ChunkStore> blockRef, List<Component<ChunkStore>> foundComponents, Archetype<ChunkStore> archetype, Vector3i globalPosition, BlockComponentChunk blockComponentChunk, CommandBuffer<ChunkStore> commandBuffer, World world) {
var energyStorage = getComponentOfType(foundComponents, EnergyStorageComponent.class).orElse(null);
if (energyStorage == null) return;
var energySource = getComponentOfType(foundComponents, EnergySourceComponent.class).orElse(null);
if (energySource == null) return;
var received = energyStorage.receiveEnergy(energySource.getGeneratesPerTick());
if (received!= 0){
HyLogger.atInfo().log("Block at " + globalPosition.x+", "+ globalPosition.y+", " +globalPosition.z+" received " + received + " Energy, now at "+energyStorage.getCurrentEnergyAmount());
}
}
}

View File

@@ -1,31 +0,0 @@
package org.KaiFlo.SolarCell.Systems.EnergyStorage;
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.KaiFlo.SolarCell.Components.EnergyStorage.Implementations.EnergyStorageComponent;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.setBlockRefTicking;
public class EnergyStorageInitializerSystem extends RefSystem<ChunkStore> {
@Override
public void onEntityAdded(@NonNullDecl Ref<ChunkStore> ref, @NonNullDecl AddReason addReason, @NonNullDecl Store<ChunkStore> store, @NonNullDecl CommandBuffer<ChunkStore> commandBuffer) {
setBlockRefTicking(ref, commandBuffer);
}
@Override
public void onEntityRemove(@NonNullDecl Ref ref, @NonNullDecl RemoveReason removeReason, @NonNullDecl Store store, @NonNullDecl CommandBuffer commandBuffer) {
//Nothing to do yet
}
@NullableDecl
@Override
public Query<ChunkStore> getQuery() {
return Query.and(EnergyStorageComponent.getComponentType(), BlockModule.BlockStateInfo.getComponentType());
}
}

View File

@@ -1,85 +0,0 @@
package org.KaiFlo.SolarCell.Systems.EnergyStorage.TickingImplementations;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.protocol.Vector3i;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.util.NotificationUtil;
import org.KaiFlo.SolarCell.Components.EnergySource.Implementations.EnergySourceComponent;
import org.KaiFlo.SolarCell.Components.EnergyStorage.Implementations.EnergyStorageComponent;
import org.KaiFlo.SolarCell.Enums.BlockRotation;
import org.KaiFlo.SolarCell.Systems.ITickingSystem;
import java.util.List;
import static org.KaiFlo.SolarCell.Helpers.BlockHelper.*;
import static org.KaiFlo.SolarCell.Helpers.ComponentHelper.getComponentOfType;
public class BatteryStorageTicking implements ITickingSystem {
@Override
public void accept(Ref<ChunkStore> blockRef, List<Component<ChunkStore>> foundComponents, Archetype<ChunkStore> archetype, Vector3i globalPosition, BlockComponentChunk blockComponentChunk, CommandBuffer<ChunkStore> commandBuffer, World world) {
var energyStorage = getComponentOfType(foundComponents, EnergyStorageComponent.class).orElse(null);
if (energyStorage == null) return;
var energySourceComponent = getComponentOfType(foundComponents, EnergySourceComponent.class).orElse(null);
if (energySourceComponent != null) return;
int blockRotationIndex = world.getBlockRotationIndex(globalPosition.x, globalPosition.y, globalPosition.z);
// HyLogger.atInfo().log("Block at " + globalPosition.x+", "+ globalPosition.y+", " +globalPosition.z+": "+blockRotationIndex);
var rotation = BlockRotation.getEnum(blockRotationIndex);
executeForDirection(globalPosition.x, globalPosition.y, globalPosition.z, world, commandBuffer, rotation,
(x, y, z, targetRef, _, _) -> {
if (energyStorage.getCurrentEnergyAmount() >= energyStorage.getMaxCapacity()) return;
var targetEnergyStorage = commandBuffer.getComponent(targetRef, EnergyStorageComponent.getComponentType());
if (targetEnergyStorage == null) return;
if (targetEnergyStorage.getCurrentEnergyAmount() < energyStorage.getCurrentEnergyAmount() && commandBuffer.getComponent(targetRef, EnergySourceComponent.getComponentType()) == null) return;
var extractEnergy = Math.min(energyStorage.getReceiveEnergyPerTick(), targetEnergyStorage.getCurrentEnergyAmount());
transmitEnergy(energyStorage,targetEnergyStorage,extractEnergy,globalPosition, x, y, z);
//Input bei Lüftungsgitter
//INPUT index 0 --> North
//INPUT index 1 --> West
//INPUT index 2 --> South
//INPUT index 3 --> East
}
);
}
private static void transmitEnergy( EnergyStorageComponent energyStorage, EnergyStorageComponent targetEnergyStorage,long extractTarget, Vector3i globalPosition, int x, int y, int z) {
long energy = 1;
if (extractTarget > 1 && !(energyStorage.getCurrentEnergyAmount()+1>=energyStorage.getMaxCapacity())) {
energy = targetEnergyStorage.extractEnergy(extractTarget);
HyLogger.atInfo().log("Extracted " + energy + "/" + extractTarget +
" |" + targetEnergyStorage.getCurrentEnergyAmount() + "| from storage" +
" at Block " + x + ", " + y + ", " + z + ", now at " +
targetEnergyStorage.getCurrentEnergyAmount() + "/" + targetEnergyStorage.getMaxCapacity());
}
long inserted = energyStorage.receiveEnergy(energy);
if (inserted < energy) {
var received = targetEnergyStorage.receiveEnergy(energy - inserted);
HyLogger.atInfo().log("TO MUCH:Inserted " + received + "/" + (energy - inserted) +
" |" + targetEnergyStorage.getCurrentEnergyAmount() + "| into storage" +
" at Block " + x + ", " + y + ", " + z + ", now at " +
targetEnergyStorage.getCurrentEnergyAmount() + "/" + targetEnergyStorage.getMaxCapacity());
}
if (inserted != 0) {
HyLogger.atInfo().log("Inserted " + inserted + "/" + energy +
" |" + energyStorage.getCurrentEnergyAmount() + "| into storage" +
" at Block " + globalPosition.x + ", " + globalPosition.y + ", " + globalPosition.z + ", now at " +
energyStorage.getCurrentEnergyAmount() + "/" + energyStorage.getMaxCapacity());
}
}
}

View File

@@ -1,83 +0,0 @@
package org.KaiFlo.SolarCell.Systems;
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.protocol.Vector3i;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class EnergyTickingSystem extends EntityTickingSystem<ChunkStore> {
private final Map<List<ComponentType<ChunkStore, ?>>, ITickingSystem> componentsToTickingSystem = new HashMap<>();
@Override
public void tick(float v, int archetypeIndex, @NonNullDecl ArchetypeChunk<ChunkStore> archetypeChunk, @NonNullDecl Store<ChunkStore> store, @NonNullDecl CommandBuffer<ChunkStore> commandBuffer) {
var blockSection = archetypeChunk.getComponent(archetypeIndex, BlockSection.getComponentType());
if (blockSection == null) return;
var chunkSection = archetypeChunk.getComponent(archetypeIndex, ChunkSection.getComponentType());
if (chunkSection == null) return;
var blockComponentChunk = commandBuffer.getComponent(chunkSection.getChunkColumnReference(), BlockComponentChunk.getComponentType());
var worldChunk = commandBuffer.getComponent(chunkSection.getChunkColumnReference(), WorldChunk.getComponentType());
if (blockComponentChunk == null || worldChunk == null) return;
var entrySet = componentsToTickingSystem.entrySet();
var foundComponentTypes = new ArrayList<ComponentType<ChunkStore, ?>>();
blockSection.forEachTicking(null, null, chunkSection.getY(), (_, _, localX, localY, localZ, _) -> {
var blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(localX, localY, localZ));
if (blockRef == null) {
return BlockTickStrategy.IGNORED;
}
int globalX = localX + (worldChunk.getX() * 32);
int globalZ = localZ + (worldChunk.getZ() * 32);
var globalPosition = new Vector3i(globalX, localY, globalZ);
var archetype = commandBuffer.getArchetype(blockRef);
foundComponentTypes.clear();
var foundComponents = new ArrayList<Component<ChunkStore>>();
for (int i = 0; i < archetype.length(); i++) {
var type = archetype.get(i);
if (type == null) continue;
foundComponentTypes.add(type);
foundComponents.add(commandBuffer.getComponent(blockRef, type));
}
AtomicBoolean hasAny = new AtomicBoolean(false);
entrySet.stream()
.filter(entry -> foundComponentTypes.containsAll(entry.getKey())).map(Map.Entry::getValue)
.forEach(tickingSystem -> {
hasAny.set(true);
tickingSystem.accept(blockRef, foundComponents, archetype, globalPosition, blockComponentChunk, commandBuffer, worldChunk.getWorld());
});
return hasAny.get() ? BlockTickStrategy.CONTINUE : BlockTickStrategy.IGNORED;
});
}
@NullableDecl
@Override
public Query<ChunkStore> getQuery() {
return Query.and(BlockSection.getComponentType(), ChunkSection.getComponentType());
}
public EnergyTickingSystem withTickingSystemForComponentTypes(List<ComponentType<ChunkStore, ?>> componentTypes, ITickingSystem tickingSystem) {
componentsToTickingSystem.put(componentTypes, tickingSystem);
return this;
}
}

View File

@@ -1,17 +0,0 @@
package org.KaiFlo.SolarCell.Systems;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.protocol.Vector3i;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import java.util.List;
public interface ITickingSystem {
void accept(Ref<ChunkStore> blockRef, List<Component<ChunkStore>> foundComponents, Archetype<ChunkStore> archetype, Vector3i globalPosition, BlockComponentChunk blockComponentChunk, CommandBuffer<ChunkStore> commandBuffer, World world);
}

View File

@@ -0,0 +1,29 @@
package org.example.plugin;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase;
import javax.annotation.Nonnull;
/**
* This is an example command that will simply print the name of the plugin in chat when used.
*/
public class ExampleCommand extends CommandBase {
private final String pluginName;
private final String pluginVersion;
public ExampleCommand(String pluginName, String pluginVersion) {
super("test", "Prints a test message from the " + pluginName + " plugin.");
this.setPermissionGroup(GameMode.Adventure); // Allows the command to be used by anyone, not just OP
this.pluginName = pluginName;
this.pluginVersion = pluginVersion;
}
@Override
protected void executeSync(@Nonnull CommandContext ctx) {
ctx.sendMessage(Message.raw("Hello from the " + pluginName + " v" + pluginVersion + " plugin!"));
}
}

View File

@@ -0,0 +1,27 @@
package org.example.plugin;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import javax.annotation.Nonnull;
/**
* This class serves as the entrypoint for your plugin. Use the setup method to register into game registries or add
* event listeners.
*/
public class ExamplePlugin extends JavaPlugin {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
public ExamplePlugin(@Nonnull JavaPluginInit init) {
super(init);
LOGGER.atInfo().log("Hello from " + this.getName() + " version " + this.getManifest().getVersion().toString());
}
@Override
protected void setup() {
LOGGER.atInfo().log("Setting up plugin " + this.getName());
this.getCommandRegistry().registerCommand(new ExampleCommand(this.getName(), this.getManifest().getVersion().toString()));
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,266 +0,0 @@
{
"nodes": [
{
"id": "1",
"name": "cube",
"position": {"x": 0, "y": 0, "z": 0},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": 0, "y": 4, "z": 0},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 32, "y": 8, "z": 32},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 78, "y": 58},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 78, "y": 66},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 78, "y": 74},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 26, "y": 80},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 32, "y": 32},
"mirror": {"x": true, "y": true},
"angle": 0
},
"bottom": {
"offset": {"x": 32, "y": 32},
"mirror": {"x": true, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
},
{
"id": "2",
"name": "cube",
"position": {"x": 0, "y": 24, "z": 0},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": 0, "y": 4, "z": 0},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 32, "y": 8, "z": 32},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 58, "y": 82},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 26, "y": 88},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 90, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 90, "y": 8},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 64, "y": 32},
"mirror": {"x": true, "y": true},
"angle": 0
},
"bottom": {
"offset": {"x": 64, "y": 32},
"mirror": {"x": true, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
},
{
"id": "3",
"name": "cube",
"position": {"x": 0, "y": 8, "z": 0},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": 0, "y": 8, "z": 0},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 26, "y": 16, "z": 26},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 26, "y": 64},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 64, "y": 26},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 64, "y": 42},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 52, "y": 64},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 26, "y": 90},
"mirror": {"x": true, "y": true},
"angle": 0
},
"bottom": {
"offset": {"x": 90, "y": 0},
"mirror": {"x": true, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
},
{
"id": "4",
"name": "cube",
"position": {"x": 2, "y": 11, "z": -14},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": -2, "y": 5, "z": 0},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 10, "y": 10, "z": 2},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 3, "y": 1},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 0, "y": 1},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 0, "y": 1},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 0, "y": 1},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 0, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"bottom": {
"offset": {"x": 0, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
},
{
"id": "5",
"name": "cube",
"position": {"x": 0, "y": 15, "z": -15},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": 0, "y": 1, "z": 0},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 6, "y": 6, "z": 2},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 0, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 0, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 1, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 0, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 1, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"bottom": {
"offset": {"x": 1, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
}
],
"format": "prop",
"lod": "auto"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,162 +0,0 @@
{
"nodes": [
{
"id": "1",
"name": "cube",
"position": {"x": 1, "y": 0, "z": 0},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": -1, "y": 3, "z": 0},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 22, "y": 6, "z": 26},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 44, "y": 43},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 44, "y": 31},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 44, "y": 49},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 44, "y": 37},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 22, "y": 57},
"mirror": {"x": true, "y": true},
"angle": 0
},
"bottom": {
"offset": {"x": 44, "y": 31},
"mirror": {"x": true, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
},
{
"id": "2",
"name": "cube",
"position": {"x": 0, "y": 5, "z": -2},
"orientation": {"x": 0, "y": 0, "z": 0, "w": 1},
"shape": {
"type": "box",
"offset": {"x": 0, "y": 10, "z": 2.5},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 6, "y": 20, "z": 5},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 44, "y": 55},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 56, "y": 55},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 50, "y": 55},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 0, "y": 57},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 42, "y": 62},
"mirror": {"x": true, "y": true},
"angle": 0
},
"bottom": {
"offset": {"x": 66, "y": 6},
"mirror": {"x": true, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
},
{
"id": "3",
"name": "cube",
"position": {"x": 14, "y": 28, "z": -5},
"orientation": {"x": 0.27983, "y": 0, "z": 0, "w": 0.96005},
"shape": {
"type": "box",
"offset": {"x": -14, "y": 0.5, "z": 5.5},
"stretch": {"x": 1, "y": 1, "z": 1},
"settings": {
"isPiece": false,
"size": {"x": 30, "y": 3, "z": 31},
"isStaticBox": true
},
"textureLayout": {
"back": {
"offset": {"x": 60, "y": 3},
"mirror": {"x": false, "y": false},
"angle": 0
},
"right": {
"offset": {"x": 5, "y": 57},
"mirror": {"x": false, "y": false},
"angle": 0
},
"front": {
"offset": {"x": 5, "y": 60},
"mirror": {"x": false, "y": false},
"angle": 0
},
"left": {
"offset": {"x": 60, "y": 0},
"mirror": {"x": false, "y": false},
"angle": 0
},
"top": {
"offset": {"x": 30, "y": 31},
"mirror": {"x": true, "y": true},
"angle": 0
},
"bottom": {
"offset": {"x": 60, "y": 0},
"mirror": {"x": true, "y": false},
"angle": 0
}
},
"unwrapMode": "custom",
"visible": true,
"doubleSided": false,
"shadingMode": "flat"
}
}
],
"format": "prop",
"lod": "auto"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,217 +0,0 @@
{
"TranslationProperties": {
"Name": "server.items.Bench_Furnace.name",
"Description": "server.items.Bench_Furnace.description"
},
"Icon": "Icons/ItemsGenerated/Bench_Furnace.png",
"Categories": [
"Furniture.Benches"
],
"Recipe": {
"Input": [
{
"ResourceTypeId": "Wood_Trunk",
"Quantity": 6
},
{
"ResourceTypeId": "Rock",
"Quantity": 6
}
],
"Output": [
{
"ItemId": "Bench_Furnace",
"Metadata": {
"BlockState": {
"Type": "processingBench",
"FuelContainer": {
"Capacity": 2
}
}
}
}
],
"BenchRequirement": [
{
"Type": "Crafting",
"Categories": [
"Workbench_Crafting"
],
"Id": "Workbench"
}
],
"TimeSeconds": 3
},
"BlockType": {
"Material": "Solid",
"DrawType": "Model",
"Opacity": "Solid",
"CustomModel": "Items/EnergyStorages/EnergyStorage1.blockymodel",
"CustomModelTexture": [
{
"Texture": "Items/EnergyStorages/texture.png",
"Weight": 1
}
],
"VariantRotation": "NESW",
"Bench": {
"Type": "Processing",
"AllowNoInputProcessing": true,
"Input": [
{
"FilterValidIngredients": true
},
{
"FilterValidIngredients": true
}
],
"Fuel": [
{
"ResourceTypeId": "Fuel",
"Icon": "Icons/Processing/FuelSlotIcon.png"
}
],
"LocalOpenSoundEventId": "SFX_Furnace_Bench_Open",
"LocalCloseSoundEventId": "SFX_Furnace_Bench_Close",
"CompletedSoundEventId": "SFX_Furnace_Bench_Processing_Complete",
"EndSoundEventId": "SFX_Furnace_Bench_Processing_End",
"FailedSoundEventId": "SFX_Furnace_Bench_Processing_Failed",
"BenchUpgradeSoundEventId": "SFX_Workbench_Upgrade_Start_Default",
"BenchUpgradeCompletedSoundEventId": "SFX_Workbench_Upgrade_Complete_Default",
"ExtraOutput": {
"Outputs": [
{
"ItemId": "Ingredient_Charcoal"
}
],
"PerFuelItemsConsumed": 2,
"IgnoredFuelSources": [
{
"ItemId": "Ingredient_Charcoal"
},
{
"ItemId": "Ingredient_Fibre"
},
{
"ItemId": "Ingredient_Tree_Sap"
}
]
},
"OutputSlotsCount": 4,
"Id": "Furnace",
"TierLevels": [
{
"UpgradeRequirement": {
"Material": [
{
"ItemId": "Ingredient_Bar_Copper",
"Quantity": 5
},
{
"ItemId": "Ingredient_Bar_Iron",
"Quantity": 5
},
{
"ItemId": "Ingredient_Bar_Thorium",
"Quantity": 5
},
{
"ItemId": "Ingredient_Bar_Cobalt",
"Quantity": 5
}
],
"TimeSeconds": 3
},
"CraftingTimeReductionModifier": 0.0
},
{
"CraftingTimeReductionModifier": 0.3,
"ExtraInputSlot": 1
}
]
},
"State": {
"Id": "processingBench",
"Definitions": {
"Processing": {
"Looping": true,
"Light": {
"Color": "#B72"
},
"CustomModelTexture": [
{
"Texture": "Blocks/Benches/Furnace_Texture.png",
"Weight": 1
}
],
"Particles": [
{
"SystemId": "Fire_Furnace_On",
"TargetNodeName": "Fire",
"Scale": 0.7,
"PositionOffset": {
"X": 0,
"Z": 0.3
}
}
],
"AmbientSoundEventId": "SFX_Furnace_Bench_Processing",
"CustomModelAnimation": "Blocks/Benches/Furnace_Smelting.blockyanim"
},
"ProcessCompleted": {
"Light": {
"Color": "#B72"
},
"CustomModelTexture": [
{
"Texture": "Blocks/Benches/Furnace_Texture.png",
"Weight": 1
}
],
"AmbientSoundEventId": "SFX_Furnace_Bench_Processing",
"Looping": true,
"CustomModelAnimation": "Blocks/Benches/Furnace_Smelting.blockyanim"
}
}
},
"Gathering": {
"Breaking": {
"GatherType": "Benches"
}
},
"BlockParticleSetId": "Stone",
"ParticleColor": "#5C583E",
"Support": {
"Down": [
{
"FaceType": "Full"
}
]
},
"BlockSoundSetId": "Stone",
"Interactions": {
"Use": "Open_Processing_Bench"
}
},
"PlayerAnimationsId": "Block",
"IconProperties": {
"Scale": 0.365,
"Rotation": [
22.5,
45,
22.5
],
"Translation": [
12,
-17.4
]
},
"Tags": {
"Type": [
"Bench"
]
},
"MaxStack": 1,
"ItemLevel": 1,
"ItemSoundSetId": "ISS_Blocks_Stone"
}

View File

@@ -1,77 +0,0 @@
{
"TranslationProperties": {
"Name": "EnergyStorage Tier 1"
},
"Icon": "Icons/Items/EditorTools/Layers.png",
"IconProperties": {
"Scale": 0.36,
"Rotation": [
22.5,
45,
22.5
],
"Translation": [
12.6,
-24.6
]
},
"BlockType": {
"BlockEntity": {
"Components": {
"EnergyStorage": {
"MaxCapacity": 10000,
"ExtractEnergyPerTick": 500,
"ReceiveEnergyPerTick": 1000,
"CurrentEnergyAmount": 100
}
}
},
"CustomModel": "Items/EnergyStorages/EnergyStorage1.blockymodel",
"CustomModelTexture": [
{
"Texture": "Items/EnergyStorages/texture.png",
"Weight": 1
}
],
"DrawType": "Model",
"Material": "Solid",
"Gathering": {
"Breaking": {
"GatherType": "Woods"
}
},
"Interactions": {
"Use": {
"Interactions": [
{
"Type": "StorageUseInteraction"
}
]
}
},
"BlockSoundSetId": "Wood",
"BlockParticleSetId": "Wood",
"VariantRotation": "NESW",
"ParticleColor": "#3e352a"
},
"Scale": 1,
"ResourceTypes": [
{
"Id": "Fuel"
},
{
"Id": "Charcoal"
}
],
"Tags": {
"Type": [
"Furniture"
],
"Family": [
"Human"
]
},
"Set": "Wardrobe",
"PlayerAnimationsId": "Block",
"ItemSoundSetId": "ISS_Blocks_Wood"
}

View File

@@ -1,89 +0,0 @@
{
"TranslationProperties": {
"Name": "SolarCell Tier 1"
},
"Icon": "Icons/Items/EditorTools/Layers.png",
"IconProperties": {
"Scale": 0.36,
"Rotation": [
22.5,
45,
22.5
],
"Translation": [
12.6,
-24.6
]
},
"BlockType": {
"BlockEntity": {
"Components": {
"EnergyStorage": {
"MaxCapacity": 1000,
"ExtractEnergyPerTick": 100,
"ReceiveEnergyPerTick": 1000,
"CurrentEnergyAmount": 0
},
"EnergySource": {
"EnergyCapacity": -1,
"GeneratesPerTick": 200
}
}
},
"CustomModel": "Items/SolarPanels/SolarCell1.blockymodel",
"CustomModelTexture": [
{
"Texture": "Items/SolarPanels/SolarCell1.png",
"Weight": 1
}
],
"DrawType": "Model",
"Material": "Solid",
"Gathering": {
"Breaking": {
"GatherType": "Woods"
}
},
"Support": {
"Down": [
{
"FaceType": "Full"
}
]
},
"State": {},
"BlockSoundSetId": "Wood",
"BlockParticleSetId": "Wood",
"VariantRotation": "NESW",
"ParticleColor": "#3e352a",
"Interactions": {
"Use": {
"Interactions": [
{
"Type": "StorageUseInteraction"
}
]
}
}
},
"Scale": 1,
"ResourceTypes": [
{
"Id": "Fuel"
},
{
"Id": "Charcoal"
}
],
"Tags": {
"Type": [
"Furniture"
],
"Family": [
"Human"
]
},
"Set": "Wardrobe",
"PlayerAnimationsId": "Block",
"ItemSoundSetId": "ISS_Blocks_Wood"
}

View File

@@ -1,26 +1,22 @@
{ {
"Group": "KaiFlo", "Group": "Example",
"Name": "SolarCell", "Name": "ExamplePlugin",
"Version": "0.0.1", "Version": "0.0.2",
"Description": "A Solar Cell Mod", "Description": "An example plugin for HyTale!",
"Authors": [ "Authors": [
{ {
"Name": "Florian Greindl" "Name": "It's you!"
},
{
"Name": "Tim Kainz"
} }
], ],
"Website": "https://github.com/KainTim/hytale-solar-cell-plugin", "Website": "example.org",
"ServerVersion": "*", "ServerVersion": "2026.02.17-255364b8e",
"Dependencies": { "Dependencies": {
"Hytale:EntityModule": "*",
"Hytale:BlockModule": "*"
}, },
"OptionalDependencies": { "OptionalDependencies": {
}, },
"DisabledByDefault": false, "DisabledByDefault": false,
"Main": "org.KaiFlo.SolarCell.SolarCellPlugin", "Main": "org.example.plugin.ExamplePlugin",
"IncludesAssetPack": true "IncludesAssetPack": true
} }