Compare commits

..

28 Commits

Author SHA1 Message Date
6d7c36ab0c removed Kack Energy API from People that can't äääänhhhhä program this emergiabpi hist anergeer bad 2026-02-25 14:44:42 +01:00
Flotschispielt
9bd333d74f Fixed EnergyStorage Bug and implemented it with directional energy extraction 2026-02-13 22:17:46 +01:00
Flotschispielt
ba5f5e11b4 Added Interaction for EnergyStorages 2026-02-08 16:08:29 +01:00
b085d8b6ad Improved ChunkSafeBlockAround 2026-02-08 16:06:24 +01:00
96dee9509b removed IsEndless 2026-02-08 14:38:48 +01:00
f45043f571 Removed unnecessary Data structures, parameters and imports 2026-02-08 14:24:18 +01:00
051940b64b Working Energy Transmission 2026-02-08 14:16:11 +01:00
3950bdab08 Added Somewhat functional Transmission between storages 2026-02-08 02:00:22 +01:00
Flotschispielt
59e8fc1734 Merge remote-tracking branch 'origin/main' 2026-02-07 19:31:52 +01:00
Flotschispielt
7b6fc8ce02 Added EnergyStorage Model and added it to the game 2026-02-07 19:31:44 +01:00
5380b275a8 Added Dispatcher for SourceTicking 2026-02-07 18:55:14 +01:00
Flotschispielt
2e3feb5c61 Merge remote-tracking branch 'origin/main' 2026-02-07 17:58:55 +01:00
Flotschispielt
837d7ceb02 Implemented all required Components 2026-02-07 17:58:13 +01:00
a979a77e3e Improved CubeAround and added setBlockRefTicking 2026-02-07 16:29:30 +01:00
Flotschispielt
9aea3a3e83 added model into asset editor 2026-02-07 14:34:18 +01:00
Flotschispielt
0f496ca03f Merge branch 'main' of https://github.com/KainTim/hytale-solar-cell-plugin 2026-02-07 13:11:53 +01:00
75900d50f9 Implemented transfering between two Solarpanels 2026-02-07 01:58:54 +01:00
486254b6dd Added SolarCell Ticking System 2026-02-07 00:24:59 +01:00
Flotschispielt
2ccc7ba5e3 added first version of solar panel model 2026-02-06 18:16:38 +01:00
6fc02d82b4 Added SolarCellInitializer 2026-02-06 18:14:31 +01:00
4cb965adb9 Added Interfaces and SolarCellComponent 2026-02-06 17:42:04 +01:00
2370f624c7 Add World destruction 2026-02-06 13:31:34 +01:00
790012e4c4 Added Logging for what kind of components a chunk reference has 2026-02-05 23:48:37 +01:00
63482565c5 Run Workflow 2026-02-05 22:23:17 +01:00
66fb1d619c Add Energy Storage as dependency 2026-02-05 22:10:07 +01:00
5ed67d4e0d Updated Manifest and Package names
# Conflicts:
#	src/main/java/org/KaiFlo/SolarCell/ExamplePlugin.java
#	src/main/java/org/KaiFlo/SolarCell/SolarCellPlugin.java
#	src/main/java/org/example/plugin/ExamplePlugin.java
2026-02-05 22:02:17 +01:00
6f20b8268b Updated Manifest and Package names 2026-02-05 21:58:32 +01:00
14012dbb2a Remove old Plugin 2026-02-05 21:50:49 +01:00
37 changed files with 2102 additions and 358 deletions

View File

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

4
.gitignore vendored
View File

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

View File

@@ -6,7 +6,7 @@ A minimal, ready-to-use template for creating Hytale plugins with modern build t
## Features
**Modern Build System** - Gradle with Groovy DSL
**Modern Build System** - Gradle with Kotlin DSL
**Automated Testing** - Custom Gradle plugin for one-command server testing
**Java 25** - Latest Java features
**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:
**`settings.gradle`:**
**`settings.gradle.kts`:**
```groovy
```kotlin
rootProject.name = "your-plugin-name"
```
@@ -139,9 +139,9 @@ TemplatePlugin/
│ └── resources/
│ └── manifest.json # Plugin metadata
├── .gitignore # Git ignore rules
├── build.gradle # Build configuration
├── build.gradle.kts # Build configuration
├── gradle.properties # Project properties
├── settings.gradle # Project settings
├── settings.gradle.kts # Project settings
├── LICENSE # MIT License
└── README.md # This file
```
@@ -200,9 +200,9 @@ rm -rf run/
### Adding Dependencies
Edit `build.gradle`:
Edit `build.gradle.kts`:
```groovy
```kotlin
dependencies {
// Hytale API (provided by server)
compileOnly(files("./HytaleServer.jar"))
@@ -221,9 +221,9 @@ dependencies {
**Usage:**
Edit `build.gradle`:
Edit `build.gradle.kts`:
```groovy
```kotlin
runHytale {
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
1. Check that `jarUrl` in `build.gradle` is correct
1. Check that `jarUrl` in `build.gradle.kts` is correct
2. Verify Java 25 is installed: `java -version`
3. Check logs in `run/logs/`

View File

@@ -1,7 +1,6 @@
plugins {
id 'java'
id 'org.jetbrains.gradle.plugin.idea-ext' version '1.3'
id 'com.gradleup.shadow' version '9.3.1'
}
import org.gradle.internal.os.OperatingSystem
@@ -9,17 +8,13 @@ import org.gradle.internal.os.OperatingSystem
ext {
if (project.hasProperty('hytale_home')) {
hytaleHome = project.findProperty('hytale_home')
} else if (System.getenv('HYTALE_HOME')) {
hytaleHome = System.getenv('HYTALE_HOME')
} else {
def os = OperatingSystem.current()
if (os.isWindows()) {
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"
}
else if (os.isLinux()) {
} else if (os.isLinux()) {
hytaleHome = "${System.getProperty("user.home")}/.var/app/com.hypixel.HytaleLauncher/data/Hytale"
if (!file(hytaleHome).exists()) {
hytaleHome = "${System.getProperty("user.home")}/.local/share/Hytale"
@@ -28,10 +23,10 @@ ext {
}
}
def hytaleHomePath = ext.has('hytaleHome') ? hytaleHome : null
def hasHytaleHome = (hytaleHomePath != null) && file(hytaleHomePath).exists()
if (!hasHytaleHome) {
logger.lifecycle("Hytale install not detected; run configs that launch the server will be skipped. Set -Phytale_home=/path/to/Hytale to enable them.")
if (!project.hasProperty('hytaleHome')) {
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.');
} else if (!file(project.findProperty('hytaleHome')).exists()) {
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')}")
}
java {
@@ -45,28 +40,23 @@ javadoc {
options.addStringOption('Xdoclint:-missing', '-quiet')
}
// Adds the Hytale server as a build dependency, allowing you to reference and
// compile against their code without bundling it. When a local install is
// present, we still use its jar for launching the server in IDE run configs.
dependencies {
compileOnly("com.hypixel.hytale:Server:$hytale_build")
if (hasHytaleHome) {
runtimeOnly(files("$hytaleHome/install/$patchline/package/game/latest/Server/HytaleServer.jar"))
repositories {
maven {
url = uri("https://cursemaven.com")
}
// Your dependencies here
}
repositories {
mavenCentral()
maven {
name = "hytale-release"
url = uri("https://maven.hytale.com/release")
}
maven {
name = "hytale-pre-release"
url = uri("https://maven.hytale.com/pre-release")
}
// 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
// the official launcher for now.
dependencies {
implementation(files("$hytaleHome/install/$patchline/package/game/latest/Server/HytaleServer.jar"))
}
// Create the working directory to run the server if it does not already exist.
def serverRunDir = file("$projectDir/run")
if (!serverRunDir.exists()) {
serverRunDir.mkdirs()
}
// Updates the manifest.json file with the latest properties defined in the
@@ -90,105 +80,54 @@ tasks.named('processResources') {
dependsOn 'updatePluginManifest'
}
tasks.named('shadowJar') {
archiveClassifier.set('')
mergeServiceFiles()
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
}
// Ensure the shaded jar is produced during a normal build.
tasks.named('build') {
dependsOn 'shadowJar'
}
if (hasHytaleHome) {
// Create the working directory to run the server if it does not already exist.
def serverRunDir = file("$projectDir/run")
if (!serverRunDir.exists()) {
serverRunDir.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
// 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'
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.")
}
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))
}
}

87
build.gradle.kts Normal file
View File

@@ -0,0 +1,87 @@
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!
version=0.0.2
version=0.0.1
# The group ID used for maven publishing. Usually the same as your package name
# but not the same as your plugin group!
maven_group=org.example
maven_group=org.KaiFlo
# The version of Java used by your plugin. The game is built on Java 21 but
# actually runs on Java 25.
@@ -19,11 +19,6 @@ includes_pack=true
# official launcher.
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
# 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
@@ -34,4 +29,4 @@ load_user_mods=false
# manually. You may also want to use a custom path if you are building in
# a non-standard environment like a build server. The home path should
# the folder that contains the install and UserData folder.
# hytale_home=./test-file
# hytale_home=./test-file

View File

@@ -1,88 +0,0 @@
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

@@ -0,0 +1,54 @@
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

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

View File

@@ -0,0 +1,70 @@
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

@@ -0,0 +1,23 @@
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

@@ -0,0 +1,81 @@
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

@@ -0,0 +1,26 @@
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

@@ -0,0 +1,136 @@
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

@@ -0,0 +1,19 @@
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

@@ -0,0 +1,189 @@
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

@@ -0,0 +1,16 @@
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

@@ -0,0 +1,75 @@
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

@@ -0,0 +1,75 @@
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

@@ -0,0 +1,31 @@
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

@@ -0,0 +1,34 @@
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

@@ -0,0 +1,31 @@
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

@@ -0,0 +1,85 @@
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

@@ -0,0 +1,83 @@
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

@@ -0,0 +1,17 @@
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

@@ -1,29 +0,0 @@
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

@@ -1,27 +0,0 @@
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

@@ -0,0 +1,266 @@
{
"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.

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,162 @@
{
"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.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,217 @@
{
"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

@@ -0,0 +1,77 @@
{
"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

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