Skip to content

Commit 1f3849e

Browse files
committed
First Commit Natural Regeneration
1 parent 7a39937 commit 1f3849e

13 files changed

Lines changed: 637 additions & 165 deletions

README.md

Lines changed: 54 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,78 @@
1-
# 🛠️ Hytale Plugin Template
1+
# Natural Regeneration
22

3-
Welcome to the **Hytale Plugin Template**! This project is a pre-configured foundation for building **Java Plugins**. It streamlines the development process by handling classpath setup, server execution, and asset bundling.
3+
A Hytale server plugin that passively regenerates player health after not taking damage for a configurable period of time.
44

5-
> **⚠️ Early Access Warning**
6-
> Hytale is currently in Early Access. Features, APIs, and this template are subject to frequent changes. Please ensure you are using the latest version of the template for the best experience.
5+
## Features
76

8-
---
7+
- Automatic health regeneration after a delay period
8+
- Fully configurable via in-game commands
9+
- Persistent configuration saved to JSON
10+
- Optimized for server performance
911

10-
## 📋 Prerequisites
12+
## How It Works
1113

12-
Before you begin, ensure your environment is ready:
14+
1. When a player takes damage, their regeneration timer resets
15+
2. After the configured delay (default: 10 seconds), health regeneration begins
16+
3. Health regenerates at the configured rate until the player reaches max health or takes damage again
1317

14-
* **Hytale Launcher**: Installed and updated.
15-
* **Java 25 SDK**: Required for modern Hytale development.
16-
* **IntelliJ IDEA**: (Community or Ultimate) Recommended for full feature support.
18+
## Commands
1719

18-
---
20+
| Command | Description |
21+
|---------|-------------|
22+
| `/nr` | Shows help and available subcommands |
23+
| `/nr status` | Display current configuration |
24+
| `/nr toggle` | Enable or disable regeneration |
25+
| `/nr delay <seconds>` | Set delay before regeneration starts |
26+
| `/nr amount <hp>` | Set HP regenerated per tick |
27+
| `/nr interval <seconds>` | Set time between regeneration ticks |
1928

20-
## 🚀 Quick Start Installation
29+
**Aliases:** `/naturalregeneration`, `/naturalregen`, `/nr`
2130

22-
### 1. Initial Setup (Before Importing)
31+
## Configuration
2332

24-
To avoid IDE caching issues, configure these files **before** you open the project in IntelliJ:
33+
Configuration is automatically saved to `Server/Plugin/Config/NaturalRegeneration.json`
2534

26-
* **`settings.gradle`**: Set your unique project name.
27-
```gradle
28-
rootProject.name = 'MyAwesomePlugin'
35+
| Option | Default | Description |
36+
|--------|---------|-------------|
37+
| `Enabled` | `true` | Whether regeneration is active |
38+
| `DelaySeconds` | `10.0` | Seconds after damage before regen starts |
39+
| `AmountHP` | `1.0` | HP restored per regeneration tick |
40+
| `IntervalSeconds` | `1.0` | Seconds between regeneration ticks |
2941

30-
```
31-
32-
33-
* **`gradle.properties`**: Set your `maven_group` (e.g., `com.yourname`) and starting version.
34-
* **`src/main/resources/manifest.json`**: Update your plugin metadata.
35-
* **CRITICAL:** Ensure the `"Main"` property points exactly to your entry-point class.
36-
37-
38-
39-
### 2. Importing the Project
40-
41-
1. Open IntelliJ IDEA and select **Open**.
42-
2. Navigate to the template folder and click **OK**.
43-
3. Wait for the Gradle sync to finish. This will automatically download dependencies, create a `./run` folder, and generate the **HytaleServer** run configuration.
44-
45-
### 3. Authenticating your Test Server
46-
47-
You **must** authenticate your local server to connect to it:
48-
49-
1. Launch the **HytaleServer** configuration in IDEA.
50-
2. In the terminal, run: `auth login device`.
51-
3. Follow the printed URL to log in via your Hytale account.
52-
4. Once verified, run: `auth persistence Encrypted`.
53-
54-
---
42+
### Example Configuration
5543

56-
## 🎮 Developing & Testing
57-
58-
### Running the Server
59-
60-
If you do not see the **HytaleServer** run configuration in the top-right dropdown, click "Edit Configurations..." to unhide it. Press the **Green Play Button** to start, or the **Bug Icon** to start in Debug Mode to enable breakpoints.
61-
62-
### Verifying the Setup
63-
64-
1. Launch your standard Hytale Client.
65-
2. Connect to `Local Server` (127.0.0.1).
66-
3. Type `/test` in-game. If it returns your plugin version, everything is working!
67-
68-
### Bundling Assets
69-
70-
You can include models and textures by placing them in `src/main/resources/Common/` or `src/main/resources/Server/`. These are editable in real-time using the in-game **Asset Editor**.
71-
72-
---
73-
74-
## 📦 Building your Plugin
44+
```json
45+
{
46+
"Enabled": true,
47+
"DelaySeconds": 10.0,
48+
"AmountHP": 1.0,
49+
"IntervalSeconds": 1.0
50+
}
51+
```
7552

76-
To create a shareable `.jar` file for distribution:
53+
## Installation
7754

78-
1. Open the **Gradle Tab** on the right side of IDEA.
79-
2. Navigate to `Tasks` -> `build` -> `build`.
80-
3. Your compiled plugin will be in: `build/libs/your-plugin-name-1.0.0.jar`.
55+
1. Build the plugin JAR file
56+
2. Place the JAR in your server's `plugins` folder
57+
3. Start/restart the server
58+
4. Configure using in-game commands or edit the config file
8159

82-
To install it manually, drop the JAR into `%appdata%/Hytale/UserData/Mods/`.
60+
## Building
8361

84-
---
62+
```bash
63+
./gradlew build
64+
```
8565

86-
## 📚 Advanced Documentation
66+
The compiled JAR will be located in `build/libs/`
8767

88-
For detailed guides on commands, event listeners, and professional patterns, visit our full documentation:
89-
👉 **[Hytale Modding Documentation](https://britakee-studios.gitbook.io/hytale-modding-documentation)**
68+
## Requirements
9069

91-
---
70+
- Hytale Server (compatible version)
9271

93-
## 🆘 Troubleshooting
72+
## License
9473

95-
* **Sync Fails**: Check that your Project SDK is set to **Java 25** via `File > Project Structure`.
96-
* **Cannot Connect**: Ensure you ran the `auth` commands in the server console.
97-
* **Plugin Not Loading**: Double-check your `manifest.json` for typos in the `"Main"` class path.
74+
MIT License
9875

99-
---
76+
## Author
10077

101-
**Need Help?** Visit our full guide here: **[Hytale Modding Documentation](https://britakee-studios.gitbook.io/hytale-modding-documentation)**
78+
Toskan4134

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# The current version of your project. Please use semantic versioning!
2-
version=0.0.2
2+
version=1.0.0
33

44
# The group ID used for maven publishing. Usually the same as your package name
55
# but not the same as your plugin group!
6-
maven_group=org.example
6+
maven_group=org.toskan4134
77

88
# The version of Java used by your plugin. The game is built on Java 21 but
99
# actually runs on Java 25.

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
rootProject.name = 'ExamplePlugin'
1+
rootProject.name = 'NaturalRegeneration'

src/main/java/org/example/plugin/ExampleCommand.java

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/main/java/org/example/plugin/ExamplePlugin.java

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.toskan4134.NaturalRegeneration;
2+
3+
import com.hypixel.hytale.component.ArchetypeChunk;
4+
import com.hypixel.hytale.component.CommandBuffer;
5+
import com.hypixel.hytale.component.Store;
6+
import com.hypixel.hytale.component.query.Query;
7+
import com.hypixel.hytale.logger.HytaleLogger;
8+
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
9+
import com.hypixel.hytale.server.core.modules.entity.damage.DamageEventSystem;
10+
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
11+
12+
import javax.annotation.Nullable;
13+
14+
/**
15+
* ECS system that listens for damage events to track when entities receive damage.
16+
* Extends DamageEventSystem to receive Damage events.
17+
*/
18+
public class DamageListenerSystem extends DamageEventSystem {
19+
20+
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
21+
22+
@Nullable
23+
@Override
24+
public Query<EntityStore> getQuery() {
25+
return Query.any();
26+
}
27+
28+
@Override
29+
public void handle(int entityIndex,
30+
ArchetypeChunk<EntityStore> chunk,
31+
Store<EntityStore> store,
32+
CommandBuffer<EntityStore> commandBuffer,
33+
Damage damage) {
34+
35+
// Get damage info
36+
float damageAmount = damage.getAmount();
37+
38+
if (damageAmount <= 0) {
39+
return; // Not real damage
40+
}
41+
42+
// Record damage in tracker
43+
// entityIndex is the index of the entity that received damage
44+
DamageTracker.onDamageReceived(entityIndex, damageAmount);
45+
46+
LOGGER.atFine().log("Entity " + entityIndex + " received " + damageAmount + " damage");
47+
}
48+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package org.toskan4134.NaturalRegeneration;
2+
3+
import com.hypixel.hytale.logger.HytaleLogger;
4+
5+
import java.util.Iterator;
6+
import java.util.Map;
7+
import java.util.concurrent.ConcurrentHashMap;
8+
9+
/**
10+
* Utility class that tracks when an entity receives damage.
11+
* Stores the timestamp of the last damage received for each entity.
12+
*/
13+
public class DamageTracker {
14+
15+
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
16+
17+
// Map storing last damage time by entity index
18+
private static final Map<Integer, Long> lastDamageTime = new ConcurrentHashMap<>();
19+
20+
// Maximum time to keep records (10 minutes)
21+
private static final long MAX_RECORD_AGE_MS = 600_000L;
22+
23+
// Counter for periodic cleanup
24+
private static long lastCleanupTime = 0L;
25+
private static final long CLEANUP_INTERVAL_MS = 60_000L; // Cleanup every minute
26+
27+
/**
28+
* Records that an entity has received damage.
29+
* @param entityIndex Entity index in the ECS
30+
*/
31+
public static void onDamageReceived(int entityIndex) {
32+
lastDamageTime.put(entityIndex, System.currentTimeMillis());
33+
cleanupIfNeeded();
34+
}
35+
36+
/**
37+
* Records that an entity has received damage with amount.
38+
* @param entityIndex Entity index
39+
* @param damageAmount Amount of damage received
40+
*/
41+
public static void onDamageReceived(int entityIndex, float damageAmount) {
42+
lastDamageTime.put(entityIndex, System.currentTimeMillis());
43+
cleanupIfNeeded();
44+
}
45+
46+
/**
47+
* Gets the time of the last damage received by an entity.
48+
* @param entityIndex Entity index
49+
* @return Timestamp of last damage, or 0 if never damaged
50+
*/
51+
public static long getLastDamageTime(int entityIndex) {
52+
return lastDamageTime.getOrDefault(entityIndex, 0L);
53+
}
54+
55+
/**
56+
* Checks if an entity can regenerate health.
57+
* @param entityIndex Entity index
58+
* @param currentTimeMs Current time in milliseconds
59+
* @param delayMs Required delay in milliseconds since last damage
60+
* @return true if enough time has passed since last damage
61+
*/
62+
public static boolean canRegenerate(int entityIndex, long currentTimeMs, long delayMs) {
63+
Long lastDamage = lastDamageTime.get(entityIndex);
64+
if (lastDamage == null) {
65+
return true; // Never received damage
66+
}
67+
return (currentTimeMs - lastDamage) >= delayMs;
68+
}
69+
70+
/**
71+
* Clears the record for an entity.
72+
* @param entityIndex Entity index
73+
*/
74+
public static void clearEntity(int entityIndex) {
75+
lastDamageTime.remove(entityIndex);
76+
}
77+
78+
/**
79+
* Cleans up old records if enough time has passed.
80+
*/
81+
private static void cleanupIfNeeded() {
82+
long currentTime = System.currentTimeMillis();
83+
if (currentTime - lastCleanupTime < CLEANUP_INTERVAL_MS) {
84+
return;
85+
}
86+
lastCleanupTime = currentTime;
87+
88+
// Clean old entries
89+
Iterator<Map.Entry<Integer, Long>> iterator = lastDamageTime.entrySet().iterator();
90+
while (iterator.hasNext()) {
91+
Map.Entry<Integer, Long> entry = iterator.next();
92+
if (currentTime - entry.getValue() > MAX_RECORD_AGE_MS) {
93+
iterator.remove();
94+
}
95+
}
96+
97+
LOGGER.atFine().log("DamageTracker cleanup: " + lastDamageTime.size() + " active entries");
98+
}
99+
100+
/**
101+
* Clears all records.
102+
*/
103+
public static void clearAll() {
104+
lastDamageTime.clear();
105+
}
106+
107+
/**
108+
* Gets the number of active entries.
109+
*/
110+
public static int getActiveCount() {
111+
return lastDamageTime.size();
112+
}
113+
}

0 commit comments

Comments
 (0)