Skip to content

Commit

Permalink
Merge pull request #4 from vorlie/1.0.6-dev
Browse files Browse the repository at this point in the history
- Updated translations.
- Added new features:
  - Mobs will heal a specified amount of health when they attack a player.
  - Added an update checker.
    - An update check will be run in a separate thread when a player joins a world.
- Added useful links to the mod:
  - These will be shown in the mod menu screen.
- Refactor logger messages.
  • Loading branch information
vorlie authored Jan 16, 2025
2 parents 1e9a208 + 6cb1eec commit d5633d0
Show file tree
Hide file tree
Showing 22 changed files with 405 additions and 85 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ yarn_mappings=1.21+build.9
loader_version=0.16.10

# Mod Properties
mod_version=1.0.5
mod_version=1.0.6
maven_group=vorlie.lifedrain
archives_base_name=lifedrain

Expand Down
16 changes: 13 additions & 3 deletions src/client/java/vorlie/lifedrain/LifeDrainClient.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package vorlie.lifedrain;

import net.fabricmc.api.ClientModInitializer;

import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;

import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.random.Random;

import vorlie.lifedrain.config.ConfigManager;
import vorlie.lifedrain.utils.UpdateChecker;

public class LifeDrainClient implements ClientModInitializer {
private long lastLifestealTime = 0; // Time of the last lifesteal
private long COOLDOWN_TIME; // Cooldown time in milliseconds (1 second)
private boolean updateChecked = false;

@Override
public void onInitializeClient() {
Expand All @@ -37,6 +40,13 @@ public void onInitializeClient() {
ConfigManager.load();
COOLDOWN_TIME = ConfigManager.CONFIG.lifestealCooldown;
});

ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.world != null && !updateChecked) {
UpdateChecker.checkForUpdates();
updateChecked = true;
}
});
}

private void handleLifesteal(PlayerEntity player, HostileEntity mob) {
Expand Down
43 changes: 39 additions & 4 deletions src/client/java/vorlie/lifedrain/ModMenuIntegration.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class ModMenuIntegration implements ModMenuApi {

@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
LOGGER.info("Lifesteal: ModMenuIntegration loaded successfully.");
LOGGER.info("[LifeDrain] ModMenuIntegration loaded successfully.");

return parent -> {
ConfigBuilder builder = ConfigBuilder.create()
Expand All @@ -41,6 +41,41 @@ public ConfigScreenFactory<?> getModConfigScreenFactory() {
.setTooltip(Text.translatable("config.lifedrain.enableParticles.tooltip"))
.build());

// Enable Mob Healing
general.addEntry(entryBuilder
.startBooleanToggle(Text.translatable("config.lifedrain.mobsHealOnHit"), ConfigManager.CONFIG.mobsHealOnHit)
.setDefaultValue(true)
.setSaveConsumer(value -> ConfigManager.CONFIG.mobsHealOnHit = value)
.setTooltip(Text.translatable("config.lifedrain.mobsHealOnHit.tooltip"))
.build());

// Mob Heal Amount (Easy)
general.addEntry(entryBuilder
.startFloatField(Text.translatable("config.lifedrain.mobHealAmountEasy"), ConfigManager.CONFIG.mobHealAmountEasy)
.setDefaultValue(0.5F)
.setMin(0.0F)
.setSaveConsumer(value -> ConfigManager.CONFIG.mobHealAmountEasy = value)
.setTooltip(Text.translatable("config.lifedrain.mobHealAmount.tooltip"))
.build());

// Mob Heal Amount (Normal)
general.addEntry(entryBuilder
.startFloatField(Text.translatable("config.lifedrain.mobHealAmountNormal"), ConfigManager.CONFIG.mobHealAmountNormal)
.setDefaultValue(1.0F)
.setMin(0.0F)
.setSaveConsumer(value -> ConfigManager.CONFIG.mobHealAmountNormal = value)
.setTooltip(Text.translatable("config.lifedrain.mobHealAmount.tooltip"))
.build());

// Mob Heal Amount (Hard)
general.addEntry(entryBuilder
.startFloatField(Text.translatable("config.lifedrain.mobHealAmountHard"), ConfigManager.CONFIG.mobHealAmountHard)
.setDefaultValue(2.0F)
.setMin(0.0F)
.setSaveConsumer(value -> ConfigManager.CONFIG.mobHealAmountHard = value)
.setTooltip(Text.translatable("config.lifedrain.mobHealAmount.tooltip"))
.build());

// Lifesteal Cooldown
general.addEntry(entryBuilder
.startIntField(Text.translatable("config.lifedrain.lifestealCooldown"), ConfigManager.CONFIG.lifestealCooldown)
Expand All @@ -56,7 +91,7 @@ public ConfigScreenFactory<?> getModConfigScreenFactory() {
.setDefaultValue(2.0F)
.setMin(0.0F)
.setSaveConsumer(value -> ConfigManager.CONFIG.baseHealEasy = value)
.setTooltip(Text.translatable("config.lifedrain.baseHealEasy.tooltip"))
.setTooltip(Text.translatable("config.lifedrain.baseHeal.tooltip"))
.build());

// Base Heal (Normal)
Expand All @@ -65,7 +100,7 @@ public ConfigScreenFactory<?> getModConfigScreenFactory() {
.setDefaultValue(1.0F)
.setMin(0.0F)
.setSaveConsumer(value -> ConfigManager.CONFIG.baseHealNormal = value)
.setTooltip(Text.translatable("config.lifedrain.baseHealNormal.tooltip"))
.setTooltip(Text.translatable("config.lifedrain.baseHeal.tooltip"))
.build());

// Base Heal (Hard)
Expand All @@ -74,7 +109,7 @@ public ConfigScreenFactory<?> getModConfigScreenFactory() {
.setDefaultValue(0.5F)
.setMin(0.0F)
.setSaveConsumer(value -> ConfigManager.CONFIG.baseHealHard = value)
.setTooltip(Text.translatable("config.lifedrain.baseHealHard.tooltip"))
.setTooltip(Text.translatable("config.lifedrain.baseHeal.tooltip"))
.build());

// Bonus Heal Multiplier
Expand Down
135 changes: 135 additions & 0 deletions src/client/java/vorlie/lifedrain/utils/UpdateChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package vorlie.lifedrain.utils;

import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;

import net.minecraft.client.MinecraftClient;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Scanner;

public class UpdateChecker {
private static final String MOD_ID = "lifedrain";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
private static final String GITHUB_RELEASES_URL = "https://api.github.com/repos/vorlie/Lifedrain/releases/latest";
private static final String MODRINTH_URL_FORMAT = "https://modrinth.com/mod/lifedrain/version/%s";

public static String getCurrentVersion() {
return FabricLoader.getInstance()
.getModContainer(MOD_ID)
.map(ModContainer::getMetadata)
.map(metadata -> metadata.getVersion().getFriendlyString())
.orElse("Unknown");
}

public static void checkForUpdates() {
Thread updateThread = new Thread(() -> {
try {
LOGGER.info("[LifeDrain] Checking for updates...");
String latestVersion = fetchLatestVersionFromGitHub();
String currentVersion = getCurrentVersion();

// Only notify if the current version is less than the latest version and not a dev version
if (isNewVersionAvailable(currentVersion, latestVersion)) {
LOGGER.info("[LifeDrain] A new version is available: {}", latestVersion);
notifyPlayer(latestVersion, currentVersion);
} else {
LOGGER.info("[LifeDrain] No update found. Current version is up-to-date.");
}
} catch (Exception e) {
LOGGER.error("[LifeDrain] Error checking for updates: {}", e.getMessage(), e);
}
});

// Set the name of the thread
updateThread.setName("LifeDrain thread");

// Start the thread
updateThread.start();
}

private static String fetchLatestVersionFromGitHub() throws IOException {
LOGGER.debug("[LifeDrain] Fetching latest version from GitHub...");
try {
URI githubApiUri = new URI(GITHUB_RELEASES_URL);
HttpURLConnection connection = (HttpURLConnection) githubApiUri.toURL().openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", "LifeDrain-UpdateChecker");

Scanner scanner = new Scanner(connection.getInputStream());
StringBuilder jsonResponse = new StringBuilder();
while (scanner.hasNext()) {
jsonResponse.append(scanner.nextLine());
}
scanner.close();

// Parse the "tag_name" field from the JSON response
String response = jsonResponse.toString();
return response.split("\"tag_name\":\"")[1].split("\"")[0];
} catch (Exception e) {
throw new IOException("[LifeDrain] Failed to fetch the latest version from GitHub: " + e.getMessage(), e);
}
}

private static boolean isNewVersionAvailable(String currentVersion, String latestVersion) {
// If the current version is development or higher than the latest release, skip the update message
return !currentVersion.equals(latestVersion) && !currentVersion.toLowerCase().contains("-dev")
&& compareVersions(currentVersion, latestVersion) < 0;
}

private static int compareVersions(String currentVersion, String latestVersion) {
// Compare version strings
String[] currentParts = currentVersion.split("-");
String[] latestParts = latestVersion.split("-");

// Compare the numeric parts (before the dash)
String[] currentVersionParts = currentParts[0].split("\\.");
String[] latestVersionParts = latestParts[0].split("\\.");

for (int i = 0; i < Math.min(currentVersionParts.length, latestVersionParts.length); i++) {
int currentPart = Integer.parseInt(currentVersionParts[i]);
int latestPart = Integer.parseInt(latestVersionParts[i]);
if (currentPart < latestPart) {
return -1; // current version is lower
} else if (currentPart > latestPart) {
return 1; // current version is higher
}
}
return 0; // versions are equal
}

private static void notifyPlayer(String latestVersion, String currentVersion) {
MinecraftClient client = MinecraftClient.getInstance();
if (client.player != null) {
String modrinthLink = String.format(MODRINTH_URL_FORMAT, latestVersion); // Create the Modrinth link based on the latest version

// Prepare the base message with the version number and Modrinth link
MutableText message = Text.literal("[LifeDrain] A new version is available: ")
.formatted(Formatting.GREEN)
.append(Text.literal(latestVersion).formatted(Formatting.AQUA))
.append(Text.literal(". Download it from "))
.append(Text.literal("Modrinth")
.styled(style -> style.withColor(Formatting.BLUE)
.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, modrinthLink))));

// If current version contains "dev", notify the player that it's a dev version
if (currentVersion.toLowerCase().contains("-dev")) {
message.append(Text.literal(" (Development version, things may not work properly!)").formatted(Formatting.RED));
}

// Send the update notification to the player
client.player.sendMessage(message, false);

LOGGER.info("[LifeDrain] Update message sent to player.");
}
}
}
6 changes: 3 additions & 3 deletions src/main/java/vorlie/lifedrain/LifeDrain.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class LifeDrain implements ModInitializer {

@Override
public void onInitialize() {
LOGGER.info("Lifesteal: LifeDrain mod initialized!");
LOGGER.info("[LifeDrain] LifeDrain mod initialized!");
ConfigManager.load(); // Load configuration

COOLDOWN_TIME = ConfigManager.CONFIG.lifestealCooldown;
Expand All @@ -40,7 +40,7 @@ public void onInitialize() {
// Register the config reload event
ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, success) -> {
ConfigManager.load();
LOGGER.info("Lifesteal: LifeDrain config reloaded.");
LOGGER.info("[LifeDrain] LifeDrain config reloaded.");
COOLDOWN_TIME = ConfigManager.CONFIG.lifestealCooldown;
});

Expand All @@ -67,7 +67,7 @@ private void handleLifesteal(PlayerEntity player, HostileEntity mob) {
float totalHeal = baseHeal + bonusHeal;
player.heal(totalHeal);

LOGGER.info("Lifesteal: {} healed {} (Base: {}, Bonus: {}).",
LOGGER.info("[LifeDrain] {} healed {} (Base: {}, Bonus: {}).",
player.getName().getString(), totalHeal, baseHeal, bonusHeal);

// Update last lifesteal activation time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {

// Execute the command
public static int executeCheckConfigCommand(CommandContext<ServerCommandSource> context) {
LOGGER.info("Checking config for missing values...");
LOGGER.info("[LifeDrain] Checking config for missing values...");

// Check and add missing values in the config
ConfigManager.load(); // Reload the config file
ConfigManager.addMissingFields(ConfigManager.getConfigJson()); // Add missing fields

// Send feedback to the player (command source)
context.getSource().sendFeedback(() -> Text.literal(
"LifeDrain: Config file has been checked and updated. " + "To apply changes, run /reload."
"[LifeDrain] Config file has been checked and updated. " + "To apply changes, run /reload."
), false);

// Save the updated config back to the file
Expand Down
36 changes: 28 additions & 8 deletions src/main/java/vorlie/lifedrain/config/ConfigManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static void load() {
// Now convert the JsonObject to our config class
CONFIG = GSON.fromJson(configJson, LifeDrainConfig.class);
} catch (IOException e) {
LOGGER.error("Error reading config: ", e);
LOGGER.error("[LifeDrain] Error reading config: ", e);
}
} else {
save(); // Save defaults if the config file doesn't exist
Expand All @@ -56,41 +56,61 @@ public static JsonObject getConfigJson() {
public static void addMissingFields(JsonObject configJson) {
// Add new fields if they don't exist
if (!configJson.has("lifestealCooldown")) {
LOGGER.info("Missing 'lifestealCooldown', adding default value.");
LOGGER.info("[LifeDrain] Missing 'lifestealCooldown', adding default value.");
configJson.addProperty("lifestealCooldown", LifeDrainConfig.DEFAULT_LIFESTEAL_COOLDOWN);
}

if (!configJson.has("enableParticles")) {
LOGGER.info("Missing 'enableParticles', adding default value.");
LOGGER.info("[LifeDrain] Missing 'enableParticles', adding default value.");
configJson.addProperty("enableParticles", LifeDrainConfig.DEFAULT_ENABLE_PARTICLES);
}

if (!configJson.has("baseHealEasy")) {
LOGGER.info("Missing 'baseHealEasy', adding default value.");
LOGGER.info("[LifeDrain] Missing 'baseHealEasy', adding default value.");
configJson.addProperty("baseHealEasy", LifeDrainConfig.DEFAULT_BASE_HEAL_EASY);
}

if (!configJson.has("baseHealNormal")) {
LOGGER.info("Missing 'baseHealNormal', adding default value.");
LOGGER.info("[LifeDrain] Missing 'baseHealNormal', adding default value.");
configJson.addProperty("baseHealNormal", LifeDrainConfig.DEFAULT_BASE_HEAL_NORMAL);
}

if (!configJson.has("baseHealHard")) {
LOGGER.info("Missing 'baseHealHard', adding default value.");
LOGGER.info("[LifeDrain] Missing 'baseHealHard', adding default value.");
configJson.addProperty("baseHealHard", LifeDrainConfig.DEFAULT_BASE_HEAL_HARD);
}

if (!configJson.has("bonusHealMultiplier")) {
LOGGER.info("Missing 'bonusHealMultiplier', adding default value.");
LOGGER.info("[LifeDrain] Missing 'bonusHealMultiplier', adding default value.");
configJson.addProperty("bonusHealMultiplier", LifeDrainConfig.DEFAULT_BONUS_HEAL_MULTIPLIER);
}

if (!configJson.has("mobsHealOnHit")) {
LOGGER.info("[LifeDrain] Missing 'mobsHealOnHit', adding default value.");
configJson.addProperty("mobsHealOnHit", LifeDrainConfig.DEFAULT_ENABLE_MOB_HEAL);
}

if (!configJson.has("mobHealAmountEasy")) {
LOGGER.info("[LifeDrain] Missing 'mobHealAmountEasy', adding default value.");
configJson.addProperty("mobHealAmountEasy", LifeDrainConfig.DEFAULT_MOB_HEAL_AMOUNT_EASY);
}

if (!configJson.has("mobHealAmountNormal")) {
LOGGER.info("[LifeDrain] Missing 'mobHealAmountNormal', adding default value.");
configJson.addProperty("mobHealAmountNormal", LifeDrainConfig.DEFAULT_MOB_HEAL_AMOUNT_NORMAL);
}

if (!configJson.has("mobHealAmountHard")) {
LOGGER.info("[LifeDrain] Missing 'mobHealAmountHard', adding default value.");
configJson.addProperty("mobHealAmountHard", LifeDrainConfig.DEFAULT_MOB_HEAL_AMOUNT_HARD);
}
}

public static void save() {
try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
GSON.toJson(CONFIG, writer);
} catch (IOException e) {
LOGGER.error("Error saving config: ", e);
LOGGER.error("[LifeDrain] Error saving config: ", e);
}
}
}
Loading

0 comments on commit d5633d0

Please sign in to comment.