diff --git a/gradle.properties b/gradle.properties index eec8361..33a957d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ mod_name=Fretux's Skill Engine # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ -mod_version=1.2.2 +mod_version=1.2.3 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java b/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java index f49df8e..ca0feeb 100644 --- a/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java +++ b/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java @@ -1,6 +1,9 @@ package net.fretux.skillengine.network; +import net.fretux.ascend.player.PlayerStats; +import net.fretux.ascend.player.PlayerStatsProvider; import net.fretux.skillengine.SkillEngine; +import net.fretux.skillengine.capability.PlayerSkillData; import net.fretux.skillengine.capability.SkillEngineCapabilities; import net.fretux.skillengine.skilltree.*; import net.minecraft.network.FriendlyByteBuf; @@ -9,67 +12,94 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.PacketDistributor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; public class ServerboundUnlockNodePacket { private final ResourceLocation nodeId; + public ServerboundUnlockNodePacket(ResourceLocation nodeId) { this.nodeId = nodeId; } + public static void encode(ServerboundUnlockNodePacket msg, FriendlyByteBuf buf) { buf.writeResourceLocation(msg.nodeId); } - + public static ServerboundUnlockNodePacket decode(FriendlyByteBuf buf) { return new ServerboundUnlockNodePacket(buf.readResourceLocation()); } public static void handle(ServerboundUnlockNodePacket msg, Supplier ctx) { ctx.get().enqueueWork(() -> { + ServerPlayer player = ctx.get().getSender(); - if (player == null) { - SkillEngine.LOGGER.warn("Unlock packet received but sender was null"); - return; - } + if (player == null) return; + SkillNode node = SkillNodeRegistry.get(msg.nodeId); AbilityNode ability = AbilityNodeRegistry.get(msg.nodeId); - if (node == null && ability == null) { - SkillEngine.LOGGER.warn("Unlock packet for unknown node or ability {}", msg.nodeId); - return; - } - player.getCapability(SkillEngineCapabilities.PLAYER_SKILLS).ifPresent(data -> { - if (node != null) { - SkillEngine.LOGGER.info("Received skill unlock request for {} from {} (points={})", - msg.nodeId, player.getGameProfile().getName(), data.getSkillPoints()); - if (SkillLogic.canUnlock(data, player, node)) { - data.unlockNode(node); + player.getCapability(SkillEngineCapabilities.PLAYER_SKILLS).ifPresent(data -> { + + // === SKILL NODE UNLOCK === + if (node != null) { + if (canUnlockSkillNode(player, data, node)) { + data.unlockNode(node); + PacketHandler.syncSkillsTo(player); PacketHandler.CHANNEL.send( PacketDistributor.PLAYER.with(() -> player), new ClientboundNodeUnlockedPacket(node.getId(), data.getSkillPoints()) ); } else { - SkillEngine.LOGGER.info("Cannot unlock skill node {} for {}", - msg.nodeId, player.getGameProfile().getName()); + SkillEngine.LOGGER.info("[SKILLENGINE] Rejecting unlock for {}", node.getId()); } return; } + + // === ABILITY NODE UNLOCK === if (ability != null) { - SkillEngine.LOGGER.info("Received ability unlock request for {} from {} (points={})", - msg.nodeId, player.getGameProfile().getName(), data.getSkillPoints()); - if (!data.isAbilityUnlocked(ability.getId())) { + if (canUnlockAbility(player, data, ability)) { data.unlockAbility(ability); + PacketHandler.syncSkillsTo(player); PacketHandler.CHANNEL.send( PacketDistributor.PLAYER.with(() -> player), new ClientboundNodeUnlockedPacket(ability.getId(), data.getSkillPoints()) ); } else { - SkillEngine.LOGGER.info("Ability {} already unlocked for {}", - msg.nodeId, player.getGameProfile().getName()); + SkillEngine.LOGGER.info("[SKILLENGINE] Rejecting ability unlock for {}", ability.getId()); } } }); }); ctx.get().setPacketHandled(true); } -} + + private static boolean canUnlockSkillNode(ServerPlayer player, PlayerSkillData data, SkillNode node) { + if (data.isUnlocked(node.getId())) return false; + if (data.getSkillPoints() < node.getCost()) return false; + for (ResourceLocation ex : node.getExclusiveWith()) { + if (data.isUnlocked(ex)) return false; + } + for (ResourceLocation parent : node.getLinks()) { + if (!data.isUnlocked(parent)) return false; + } + PlayerStats stats = player.getCapability(PlayerStatsProvider.PLAYER_STATS).orElse(null); + if (stats == null) return false; + for (var e : node.getPrereqAttributes().entrySet()) { + if (stats.getAttributeLevel(e.getKey()) < e.getValue()) { + return false; + } + } + return true; + } + + + private static boolean canUnlockAbility(ServerPlayer player, PlayerSkillData data, AbilityNode ability) { + if (data.isAbilityUnlocked(ability.getId())) return false; + for (ResourceLocation parent : ability.getLinks()) { + boolean ok = data.isUnlocked(parent) || data.isAbilityUnlocked(parent); + if (!ok) return false; + } + return true; + } +} \ No newline at end of file