Registry - Keybinding - Skill - Config modules#48
Registry - Keybinding - Skill - Config modules#48minheragon12345 wants to merge 59 commits into1.21.1/masterfrom
Conversation
…oo small to fit into a future module
# Conflicts: # settings.gradle # testing-common/build.gradle
Idk how to check out forked branch
# Conflicts: # settings.gradle # testing-common/build.gradle # testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java
WalkthroughAdds multiple new subsystems (config, skills, races, attributes, keybinds), extended command argument API, platform entrypoints (Fabric/NeoForge), network payloads for C2S/S2C sync, many mixins/resources/access-wideners, testing/demo content, and extensive Gradle/build/publishing wiring changes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant Internal as InternalSkillPacketActions
participant Network as NetworkManager
participant Server
participant Storage as SkillStorage
Client->>Internal: user input (key/scroll)
Internal->>Network: send RequestSkillActivation/Release/Scroll/Toggle
Network->>Server: deliver packet
Server->>Storage: lookup player's Skills
Storage->>Storage: validate via SkillEvents (permission/mode/cooldown)
Storage->>Storage: update ManasSkillInstance / add TickingSkill
Storage->>Server: mark dirty / persist / schedule sync
sequenceDiagram
autonumber
participant Server
participant Net as ManasConfigNetwork
participant Payload as SyncConfigToClientPayload
participant Client
participant Registry as ConfigRegistry
Server->>Registry: ConfigRegistry.getConfigSyncData()
Server->>Net: ManasConfigNetwork.syncToClients()
Net->>Payload: build payload(map)
Net->>Client: send S2C SyncConfigToClientPayload
Client->>Payload: receive & decode
Payload->>Registry: ConfigRegistry.loadConfigSyncData(map)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 47
🧹 Nitpick comments (50)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java (1)
22-26: Consider making the skillList parameter immutable.While records provide immutable fields, the List itself can still be modified. Consider using
ImmutableListorList.copyOf()in the constructor to prevent external modifications.public record RequestSkillTogglePacket( - List<ResourceLocation> skillList + List<ResourceLocation> skillList ) implements CustomPacketPayload { + public RequestSkillTogglePacket { + skillList = List.copyOf(skillList); // Make defensive copy + }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java (1)
22-26: Add Javadoc to document the record fields.Please add documentation to explain:
- What
heldTickrepresents (duration of skill hold?)- Purpose of
keyNumber(keybinding identifier?)- Format/requirements for
skillListkeybinding-fabric/src/main/resources/fabric.mod.json (1)
1-14: Add homepage URL for better discoverability.The homepage URL is currently empty. Consider adding a link to your GitHub repository or documentation page to help users find more information about the mod.
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/AbstractSkillRegister.java (4)
15-20: Consider eager initialization of the skills register.The current implementation initializes
skillsas null and performs lazy initialization in theskill()method. This pattern could lead to potential race conditions in concurrent scenarios and makes the code harder to reason about.Consider initializing the register in the constructor:
- protected DeferredRegister<ManasSkill> skills = null; + protected final DeferredRegister<ManasSkill> skills; public AbstractSkillRegister(String modId) { super(modId); + this.skills = DeferredRegister.create(modId, SkillAPI.getSkillRegistryKey()); }
21-24: Add method documentation.The
initmethod lacks documentation explaining its purpose, contract with the parent class, and the significance of thebeforeRegistrationparameter.Add Javadoc explaining:
- Method's purpose
- When it's called
- What
beforeRegistrationdoes- Order of operations (super.init followed by skills registration)
+ /** + * Initializes the skill register by executing any pre-registration tasks and registering skills. + * @param beforeRegistration Runnable to execute before registration occurs + */ public void init(final Runnable beforeRegistration) {
34-51: Document the SkillBuilder class and its methods.The inner class and its methods lack documentation explaining their purpose and usage.
Add comprehensive documentation:
+ /** + * Builder class for creating and configuring skill registrations. + * @param <R> The type of the parent register + * @param <T> The type of skill being built + */ public static class SkillBuilder<R extends AbstractSkillRegister<R>, T extends ManasSkill> extends ContentBuilder<T, R> { protected final Supplier<T> skillFactory; + /** + * Creates a new SkillBuilder instance. + * @param register The parent register + * @param name The unique identifier for the skill + * @param skillFactory Factory for creating skill instances + */ private SkillBuilder(R register, String name, Supplier<T> skillFactory) { + /** + * Finalizes the skill registration and returns a registry supplier. + * @return A RegistrySupplier for the registered skill + */ @Override public RegistrySupplier<T> end() { + /** + * Finalizes the skill registration and returns a holder. + * @return A Holder for the registered skill + */ @Override public Holder<T> endAsHolder() {
1-52: Well-designed skill registration system.The implementation provides a robust foundation for skill registration in a modding context. The use of the builder pattern, generic types, and separation of concerns demonstrates good architectural decisions. The class effectively leverages the Architectury API for cross-platform compatibility.
Consider these architectural improvements:
- Add an interface defining the contract for skill registration
- Consider using a factory interface for skill creation to allow for different implementation strategies
- Add events or hooks for registration lifecycle (pre/post registration)
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/ManasCoreSkillFabric.java (1)
11-16: Consider adding error handling and logging.While the implementation follows Fabric's initialization pattern correctly, consider these improvements for better robustness:
- Add try-catch block to handle potential initialization errors
- Add logging to indicate successful initialization
- Consider adding initialization state verification
Example implementation:
public class ManasCoreSkillFabric implements ModInitializer { + private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger("manascore-skill"); @Override public void onInitialize() { - ManasCoreSkill.init(); + try { + LOGGER.info("Initializing ManasCoreSkill module..."); + ManasCoreSkill.init(); + LOGGER.info("ManasCoreSkill module initialized successfully"); + } catch (Exception e) { + LOGGER.error("Failed to initialize ManasCoreSkill module", e); + throw e; // Rethrow to ensure Fabric is aware of initialization failure + } } }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java (1)
50-54: Clarify variable usage ofkeyNumberandmodefor better readability.In the code,
keyNumberis assigned tomode, and both variables are used subsequently. This might cause confusion since they appear to represent the same concept.Consider renaming
keyNumbertomodeif they are intended to be the same, or differentiate them clearly if they represent different concepts. This will improve code readability and maintainability.skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFishingHook.java (1)
23-23: Specify access modifier for the methodThe method
onHitcurrently has package-private visibility. Consider specifying an access modifier (e.g.,privateorpublic) to clarify its intended visibility and adhere to best practices.Apply this diff to specify the access modifier:
- ProjectileDeflection onHit(FishingHook instance, HitResult result, Operation<ProjectileDeflection> original) { + private ProjectileDeflection onHit(FishingHook instance, HitResult result, Operation<ProjectileDeflection> original) {skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinPlayer.java (1)
25-25: Simplify casting of 'this' referenceThe cast
(LivingEntity) (Object) thiscan be simplified to(LivingEntity) thissincePlayerextendsLivingEntity. The extra cast toObjectis unnecessary and can be removed for clarity.Apply this diff to simplify the casting:
- if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, damageSource, changeable).isFalse()) return 0.0F; + if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) this, damageSource, changeable).isFalse()) return 0.0F;skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractHurtingProjectile.java (1)
23-23: Specify access modifier for the methodThe method
onHitcurrently has package-private visibility. Consider specifying an access modifier (e.g.,privateorpublic) to clarify its intended visibility and adhere to best practices.Apply this diff to specify the access modifier:
- ProjectileDeflection onHit(AbstractHurtingProjectile instance, HitResult result, Operation<ProjectileDeflection> original) { + private ProjectileDeflection onHit(AbstractHurtingProjectile instance, HitResult result, Operation<ProjectileDeflection> original) {skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (5)
41-43: Static Fields Should Befinalif They Are Meant to Be ConstantsThe
keyfield is static but not marked asfinal. Ifkeyis intended to be a constant, it should be declared asstatic final.Apply this diff to make
keya constant:-private static StorageKey<SkillStorage> key = null; +private static final StorageKey<SkillStorage> key = null;
88-89: Unnecessary Return After CheckThe method
checkPlayerOnlyEffectsreturns immediately ifpassiveSkillActivateis false, but there's an additional return after the condition which is redundant.Simplify the code by removing the unnecessary return statement.
170-186: Possible Duplicate Skill RegistrationIn the
learnSkillmethod, if a skill instance already exists, the method logs a debug message and returnsfalse. However, it may be helpful to inform the player that they already have the skill.Consider sending a message to the player indicating they have already learned the skill.
245-252: Exception Handling Could Be More SpecificCatching a general
Exceptionin a loop could mask other potential issues.Apply this diff to catch a more specific exception:
try { ManasSkillInstance instance = ManasSkillInstance.fromNBT((CompoundTag) tag); this.skillInstances.put(instance.getSkillId(), instance); -} catch (Exception e) { +} catch (NBTException e) { ManasCoreSkill.LOG.error("Failed to load skill instance from NBT", e); }
265-266: Typo in Method NameresetDirtyThe method
resetDirtymight be better namedmarkCleanto match common conventions.Consider renaming the method for clarity.
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
429-450: Remove redundant constructor and accessors in record.In Java records, the canonical constructor and accessor methods are generated automatically. Explicitly redefining them is unnecessary unless you need custom behavior.
You can simplify the
AttributeTemplaterecord by removing the redundant constructor and accessor methods:public static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { - public AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { - this.id = id; - this.amount = amount; - this.operation = operation; - } public AttributeModifier create(double i) { return new AttributeModifier(this.id, this.amount * i, this.operation); } - - public ResourceLocation id() { - return this.id; - } - - public double amount() { - return this.amount; - } - - public AttributeModifier.Operation operation() { - return this.operation; - } }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
508-510: Clarify the method documentation for damage events.The comment for
onBeingDamagedmentions it is executed beforeonDamageEntity, but in practice,onBeingDamagedis typically called when the entity takes damage, not before it deals damage. Ensure the comments accurately reflect the method's behavior.Consider updating the comment for clarity:
/** - * Gets executed before {@link ManasSkillInstance#onDamageEntity} + * Called when the entity is being damaged. This method is executed before damage is applied. * * @return false will prevent the owner from taking damage. */registry-common/src/main/java/io/github/manasmods/manascore/registry/api/AbstractRegister.java (1)
549-550: Use consistent attribute names in registration.Constructing the attribute name using string formatting can lead to inconsistencies. Consider using
this.id.toString()to ensure the attribute name matches the registry ID.Apply this diff for consistency:
- String name = String.format("%s.attribute.%s", this.id.getNamespace(), this.id.getPath().replaceAll("/", ".")); + String name = this.id.toString();config-common/src/main/java/io/github/manasmods/manascore/config/api/ManasConfig.java (1)
3-7: Add documentation and consider making the path configurable.The abstract class would benefit from:
- JavaDoc explaining its purpose and usage
- Making the parent path configurable through constructor/setter
- Path validation and normalization
Consider this improvement:
+/** + * Base configuration class that provides common functionality for mod configurations. + * Extend this class to create specific configuration handlers for different mod components. + */ public abstract class ManasConfig { + private final String parentPath; + + protected ManasConfig(String parentPath) { + this.parentPath = parentPath != null ? parentPath : "config"; + } + + protected ManasConfig() { + this("config"); + } + public String getParentPath() { - return "config"; + return parentPath; } }registry-common/src/main/java/io/github/manasmods/manascore/registry/ManasCoreRegistry.java (1)
10-14: Add documentation and error handling.The registry initialization would benefit from:
- Class-level JavaDoc explaining its purpose
- Error handling for initialization failures
- Logging for debugging purposes
Consider this improvement:
+import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Core registry initialization for ManasCore. + * Handles the initialization of all registry components. + */ public class ManasCoreRegistry { + private static final Logger LOGGER = LoggerFactory.getLogger(ManasCoreRegistry.class); + public static void init() { + LOGGER.info("Initializing ManasCore Registry"); + try { ManasAttributeRegistry.init(); + LOGGER.info("ManasCore Registry initialization completed"); + } catch (Exception e) { + LOGGER.error("Failed to initialize ManasCore Registry", e); + throw new RuntimeException("Registry initialization failed", e); + } } }registry-fabric/src/main/java/io/github/manasmods/manascore/registry/ManasCoreRegistryFabric.java (1)
10-15: Add documentation and error handling.The Fabric initializer would benefit from:
- Class-level JavaDoc explaining its purpose
- Error handling for initialization failures
- Logging for debugging purposes
Consider this improvement:
+import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Fabric-specific initialization for ManasCore Registry. + * Implements ModInitializer to hook into Fabric's mod loading lifecycle. + */ public class ManasCoreRegistryFabric implements ModInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(ManasCoreRegistryFabric.class); + @Override public void onInitialize() { + LOGGER.info("Initializing ManasCore Registry for Fabric"); + try { ManasCoreRegistry.init(); + LOGGER.info("ManasCore Registry initialization completed for Fabric"); + } catch (Exception e) { + LOGGER.error("Failed to initialize ManasCore Registry for Fabric", e); + throw new RuntimeException("Fabric registry initialization failed", e); + } } }registry-neoforge/src/main/java/io/github/manasmods/manascore/registry/ManasCoreRegistryNeoForge.java (1)
12-14: Consider adding error handling during initialization.The constructor calls
ManasCoreRegistry.init()directly without any error handling. Consider wrapping the initialization in a try-catch block to gracefully handle potential initialization failures.public ManasCoreRegistryNeoForge() { - ManasCoreRegistry.init(); + try { + ManasCoreRegistry.init(); + } catch (Exception e) { + // Use a logging framework instead of System.err in production + System.err.println("Failed to initialize ManasCoreRegistry: " + e.getMessage()); + throw new RuntimeException("Failed to initialize mod", e); + } }keybinding-fabric/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingFabric.java (1)
10-15: Maintain consistency with NeoForge implementation.For consistency with the NeoForge implementation:
- The class should be marked as
finalto prevent inheritance- Consider adding error handling during initialization
-public class ManasCoreKeybindingFabric implements ModInitializer { +public final class ManasCoreKeybindingFabric implements ModInitializer { @Override public void onInitialize() { - ManasCoreKeybinding.init(); + try { + ManasCoreKeybinding.init(); + } catch (Exception e) { + // Use a logging framework instead of System.err in production + System.err.println("Failed to initialize ManasCoreKeybinding: " + e.getMessage()); + throw new RuntimeException("Failed to initialize mod", e); + } } }keybinding-neoforge/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingNeoForge.java (1)
12-14: Consider adding error handling during initialization.The constructor calls
ManasCoreKeybinding.init()directly without any error handling. Consider wrapping the initialization in a try-catch block to gracefully handle potential initialization failures.public ManasCoreKeybindingNeoForge() { - ManasCoreKeybinding.init(); + try { + ManasCoreKeybinding.init(); + } catch (Exception e) { + // Use a logging framework instead of System.err in production + System.err.println("Failed to initialize ManasCoreKeybinding: " + e.getMessage()); + throw new RuntimeException("Failed to initialize mod", e); + } }keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybinding.java (1)
11-17: Strengthen implementation with defensive programming.Consider the following improvements:
- Mark the class as
finalsince it's not designed for inheritance- Add null check for
Platform.getEnvironment()-public class ManasCoreKeybinding { +public final class ManasCoreKeybinding { public static void init() { - if (Platform.getEnvironment() == Env.CLIENT) { + Env currentEnv = Platform.getEnvironment(); + if (currentEnv != null && currentEnv == Env.CLIENT) { ManasCoreKeybindingClient.init(); } } }keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingClient.java (1)
11-15: LGTM! Consider adding class-level documentation.The initialization logic and event registration are well-implemented. Consider adding class-level documentation to explain the client-side responsibility of this class.
Add JavaDoc to explain the class's role:
/** * Client-side initialization handler for the ManasCore keybinding system. * Registers necessary client lifecycle events and initializes the KeybindingManager. */keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingCategory.java (1)
11-18: Add input validation and improve documentation.The class looks good but could benefit from some improvements:
- Add validation for the name parameter
- Enhance documentation for API users
@RequiredArgsConstructor(staticName = "of") +/** + * Represents a category for grouping related keybindings. + * Categories are used to organize keybindings in the game's controls menu. + */ public class KeybindingCategory { private final String name; + /** + * Creates a localization key for this category. + * @return A string in the format "modid.category.categoryname" + */ public String getCategoryString() { + Objects.requireNonNull(name, "Category name cannot be null"); + if (name.isEmpty()) { + throw new IllegalArgumentException("Category name cannot be empty"); + } return String.format("%s.category.%s", ModuleConstants.MOD_ID, this.name); } }skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java (1)
19-26: Document initialization order and add error handling.The initialization sequence looks good, but consider:
- Documenting the required initialization order
- Adding error handling for initialization failures
+ /** + * Initializes the skill system components in the following order: + * 1. SkillRegistry - Registers all available skills + * 2. SkillStorage - Sets up skill data persistence + * 3. ManasSkillNetwork - Initializes networking for skill synchronization + * 4. Client-side initialization (if applicable) + * + * @throws IllegalStateException if any component fails to initialize + */ public static void init() { - SkillRegistry.init(); - SkillStorage.init(); - ManasSkillNetwork.init(); + try { + SkillRegistry.init(); + SkillStorage.init(); + ManasSkillNetwork.init(); + } catch (Exception e) { + LOG.error("Failed to initialize skill system", e); + throw new IllegalStateException("Skill system initialization failed", e); + } if (Platform.getEnvironment() == Env.CLIENT) { - ManasCoreSkillClient.init(); + try { + ManasCoreSkillClient.init(); + } catch (Exception e) { + LOG.error("Failed to initialize client-side skill system", e); + throw new IllegalStateException("Client-side skill system initialization failed", e); + } } }registry-common/src/main/java/io/github/manasmods/manascore/registry/api/Register.java (1)
21-25: Add modId validation and consider thread safety documentation.The implementation looks good, but consider:
- Adding validation for the modId parameter
- Documenting thread safety guarantees
public class Register extends AbstractRegister<Register> { + /** + * Creates a new Register instance for the specified mod. + * This class is not thread-safe and should only be used from the main thread. + * + * @param modId The mod identifier, must not be null or empty + * @throws IllegalArgumentException if modId is null or empty + */ public Register(String modId) { + if (modId == null || modId.trim().isEmpty()) { + throw new IllegalArgumentException("modId must not be null or empty"); + } super(modId); } }skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/Changeable.java (1)
11-15: Consider defensive copying for mutable objectsFor mutable type parameters T, the current implementation doesn't prevent modifications to the stored objects themselves, which could lead to unexpected behavior.
Consider adding a copy constructor or clone method for mutable objects:
protected Changeable(@Nullable T value) { this.original = deepCopy(value); this.value = deepCopy(value); } @SuppressWarnings("unchecked") private T deepCopy(@Nullable T value) { if (value == null) return null; // Implementation depends on type T // Example for Cloneable objects: if (value instanceof Cloneable) { try { return (T) value.getClass().getMethod("clone").invoke(value); } catch (Exception e) { return value; } } return value; }registry-common/src/main/java/io/github/manasmods/manascore/registry/api/ManasAttributeRegistry.java (1)
27-30: Consider adding validation for builder supplierThe
registerNewmethod should validate that the builder supplier produces non-null builders.Consider wrapping the supplier:
@ExpectPlatform public static void registerNew(Supplier<EntityType<? extends LivingEntity>> type, Supplier<AttributeSupplier.Builder> builder) { - throw new AssertionError(); + validateParameters(type, builder); + Supplier<AttributeSupplier.Builder> validatedBuilder = () -> { + AttributeSupplier.Builder result = builder.get(); + if (result == null) { + throw new IllegalStateException("Builder supplier returned null"); + } + return result; + }; + throw new AssertionError("Platform implementation not found for registerNew()"); }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/ManasSkillNetwork.java (1)
14-21: Consider implementing a packet registry pattern.Instead of directly registering packets in the init method, consider implementing a packet registry pattern for better modularity and testability. This would allow easier addition of new packet types and better unit testing capabilities.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java (1)
27-37: Simplify boolean chain and use atomic operations.The current implementation has a complex chain of boolean returns and non-atomic duration increment.
public boolean tick(Skills storage, LivingEntity entity) { - if (!entity.isAlive()) return false; - Optional<ManasSkillInstance> optional = storage.getSkill(skill); - if (optional.isEmpty()) return false; - - ManasSkillInstance instance = optional.get(); - if (reachedMaxDuration(instance, entity)) return false; - - if (!instance.canInteractSkill(entity)) return false; - return instance.onHeld(entity, this.duration++, mode); + return entity.isAlive() + && storage.getSkill(skill) + .filter(instance -> !reachedMaxDuration(instance, entity)) + .filter(instance -> instance.canInteractSkill(entity)) + .map(instance -> instance.onHeld(entity, duration.getAndIncrement(), mode)) + .orElse(false); }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java (1)
8-11: Standardize null-safety annotations.The interface mixes Lombok's @nonnull with JetBrains' @NotNull annotations. Stick to one annotation library for consistency.
import io.github.manasmods.manascore.skill.impl.SkillStorage; -import lombok.NonNull; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.NotNull;skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java (2)
27-31: Consider optimizing the skill iteration process.The current implementation creates a new ArrayList and iterates through all learned skills on every scroll event, which could be inefficient for players with many skills.
Consider:
- Pre-filtering skills that respond to scroll events
- Using a more efficient data structure for frequently accessed skills
- List<ResourceLocation> packetSkills = new ArrayList<>(); - for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) { + List<ResourceLocation> packetSkills = SkillAPI.getSkillsFrom(player).getLearnedSkills().stream() + .filter(skill -> SkillEvents.SKILL_SCROLL.invoker().scroll(skill, player, amountY).isTrue()) + .map(ManasSkillInstance::getSkillId) + .toList(); - if (SkillEvents.SKILL_SCROLL.invoker().scroll(skillInstance, player, amountY).isFalse()) continue; - packetSkills.add(skillInstance.getSkillId()); - }
33-36: Consider rate limiting scroll events.There's no rate limiting on scroll events, which could lead to packet spam if the user scrolls rapidly.
Consider implementing a cooldown mechanism or rate limiting for scroll events to prevent network congestion.
skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/EntityEvents.java (2)
20-26: Add documentation for event fields.The event fields lack documentation explaining their purpose, timing, and usage.
Consider adding JavaDoc for each event field:
+ /** + * Fired before a living entity's tick processing. + * Use this event to modify entity state before the tick occurs. + */ public static Event<LivingTickEvent> LIVING_PRE_TICK = EventFactory.createLoop(); + + /** + * Fired after a living entity's tick processing. + * Use this event to perform actions after the tick is complete. + */ public static Event<LivingTickEvent> LIVING_POST_TICK = EventFactory.createLoop(); // Add similar documentation for other events
59-64: Enhance ProjectileHitResult documentation.The enum comments could be more descriptive to better explain the behavior of each result type.
Consider improving the documentation:
public enum ProjectileHitResult { - DEFAULT, // Hit, damage + possibly continue - HIT, // Hit + damage - HIT_NO_DAMAGE, // Hit - PASS // Pass through + /** + * Default behavior: Projectile hits, applies damage, and may continue based on implementation + */ + DEFAULT, + /** + * Projectile hits and applies damage, then stops + */ + HIT, + /** + * Projectile hits but applies no damage, then stops + */ + HIT_NO_DAMAGE, + /** + * Projectile passes through without interaction + */ + PASS }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
52-56: Consider consolidating packet sending methods.The three packet sending methods have similar structure and environment checks.
Consider creating a private helper method to reduce code duplication:
+ private static void sendPacketIfClient(Runnable packetSender) { + if (Platform.getEnvironment() == Env.CLIENT) { + packetSender.run(); + } + } + public static void skillActivationPacket(int keyNumber) { - if (Platform.getEnvironment() == Env.CLIENT) { - InternalSkillPacketActions.sendSkillActivationPacket(keyNumber); - } + sendPacketIfClient(() -> InternalSkillPacketActions.sendSkillActivationPacket(keyNumber)); }Also applies to: 64-68, 76-80
config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java (1)
48-54: Consider performance optimizations for config retrieval.The current implementation could be improved by:
- Using a more efficient lookup mechanism (e.g., Map) instead of streaming
- Adding caching for frequently accessed configs
+private static final Map<String, ManasConfig> configCache = new ConcurrentHashMap<>(); public static <T extends ManasConfig> T getConfig(Class<T> clazz) { + String key = clazz.getSimpleName().toLowerCase(); + return (T) configCache.computeIfAbsent(key, k -> loadedConfigs.stream() .filter(config -> config.getClass().getSimpleName().equalsIgnoreCase(k)) .findFirst() - .orElse(null)); + .orElse(null)); }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
16-80: Well-designed event system with comprehensive coverage!The event system is well-structured with clear separation of concerns. Consider adding:
- Documentation for the
modeparameter inSkillUpdateCooldownEvent- Documentation for the return values of
EventResultin each event@FunctionalInterface interface SkillUpdateCooldownEvent { + /** + * Updates the cooldown for a skill + * @param skillInstance The skill instance being updated + * @param owner The entity that owns the skill + * @param currentCooldown The current cooldown value + * @param mode The mode of cooldown update: + * 0 = Reset + * 1 = Increment + * 2 = Decrement + * @return EventResult indicating if the cooldown update should proceed + */ EventResult cooldown(ManasSkillInstance skillInstance, LivingEntity owner, int currentCooldown, int mode); }registry-neoforge/src/main/java/io/github/manasmods/manascore/registry/api/neoforge/ManasAttributeRegistryImpl.java (1)
44-69: Consider adding safeguards for event handling.Potential improvements:
- Add validation for builder results
- Consider synchronization for registry clearing
- Add logging for attribute registration failures
static void registerAttributes(final EntityAttributeModificationEvent e) { + if (e == null) { + ManasCoreConfig.LOG.error("EntityAttributeModificationEvent is null"); + return; + } Multimap<EntityType<? extends LivingEntity>, Consumer<AttributeSupplier.Builder>> keyResolvedMap = ArrayListMultimap.create(); REGISTRY.forEach((key, value) -> keyResolvedMap.put(key.get(), value)); e.getTypes().forEach(type -> { AttributeSupplier.Builder builder = new AttributeSupplier.Builder(); if (keyResolvedMap.containsKey(type)) keyResolvedMap.get(type).forEach(consumer -> consumer.accept(builder)); GLOBAL_REGISTRY.forEach(consumer -> consumer.accept(builder)); - builder.build().instances.forEach((attribute, attributeInstance) -> e.add(type, attribute, attributeInstance.getBaseValue())); + try { + AttributeSupplier supplier = builder.build(); + if (supplier != null && supplier.instances != null) { + supplier.instances.forEach((attribute, attributeInstance) -> + e.add(type, attribute, attributeInstance.getBaseValue())); + } + } catch (Exception ex) { + ManasCoreConfig.LOG.error("Failed to build attributes for type: " + type, ex); + } }); synchronized (REGISTRY) { REGISTRY.clear(); } synchronized (GLOBAL_REGISTRY) { GLOBAL_REGISTRY.clear(); } }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/InternalSkillPacketActions.java (3)
31-44: Extract magic numbers and add input validation.Consider these improvements:
- Add validation for keyNumber
- Extract packet-related constants
- Add debug logging for skill activation attempts
+ private static final int MIN_KEY_NUMBER = 0; + private static final int MAX_KEY_NUMBER = 9; // Adjust based on your requirements + public static void sendSkillActivationPacket(int keyNumber) { + if (keyNumber < MIN_KEY_NUMBER || keyNumber > MAX_KEY_NUMBER) { + ManasCoreConfig.LOG.warn("Invalid key number: " + keyNumber); + return; + } var minecraft = Minecraft.getInstance(); Player player = minecraft.player; if (player == null) return; List<ResourceLocation> packetSkills = new ArrayList<>(); for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) { + ManasCoreConfig.LOG.debug("Attempting to activate skill: " + skillInstance.getSkillId()); if (SkillEvents.ACTIVATE_SKILL.invoker().activateSkill(skillInstance, player, keyNumber).isFalse()) continue; packetSkills.add(skillInstance.getSkillId()); }
50-63: Add validation for held ticks parameter.Similar improvements as activation packet, plus validation for held ticks:
+ private static final int MIN_HELD_TICKS = 0; + public static void sendSkillReleasePacket(int keyNumber, int heldTicks) { + if (keyNumber < MIN_KEY_NUMBER || keyNumber > MAX_KEY_NUMBER) { + ManasCoreConfig.LOG.warn("Invalid key number: " + keyNumber); + return; + } + if (heldTicks < MIN_HELD_TICKS) { + ManasCoreConfig.LOG.warn("Invalid held ticks: " + heldTicks); + return; + } var minecraft = Minecraft.getInstance(); Player player = minecraft.player; if (player == null) return;
69-83: Add documentation for toggle conditions.The toggle packet handling looks good but would benefit from better documentation:
+ /** + * Sends a packet to toggle skills that meet the following conditions: + * 1. Player must be present on the client + * 2. Skill must be learned by the player + * 3. Skill must be toggleable (canBeToggled returns true) + * 4. Toggle event must not be cancelled + */ public static void sendSkillTogglePacket() { var minecraft = Minecraft.getInstance(); Player player = minecraft.player; if (player == null) return; List<ResourceLocation> packetSkills = new ArrayList<>();keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java (1)
31-81: Consolidate constructor overloads using builder patternThe class has 6 constructors with varying parameters, which could be simplified using a builder pattern for better maintainability and readability.
Consider implementing a builder:
public static class Builder { private String langKey; private InputConstants.Key defaultKey = InputConstants.UNKNOWN; private KeybindingCategory category; private KeyBindingAction action; private KeyBindingRelease release; public Builder(String langKey, KeybindingCategory category, KeyBindingAction action) { this.langKey = langKey; this.category = category; this.action = action; } public Builder defaultKey(int key) { this.defaultKey = InputConstants.Type.KEYSYM.getOrCreate(key); return this; } public Builder release(KeyBindingRelease release) { this.release = release; return this; } public ManasKeybinding build() { return new ManasKeybinding(langKey, defaultKey, category, action, release); } }config-fabric/src/main/resources/fabric.mod.json (1)
10-12: Add homepage URLThe homepage field is empty. Consider adding the GitHub repository URL or documentation site.
build.gradle (1)
Line range hint
1-85: Consider architectural improvements for better modularity.The build script handles multiple concerns (compilation, publishing, signing, etc.). Consider splitting it into separate Gradle files for better maintainability:
buildSrc/directory for custom plugins and tasks- Separate files for publishing, signing, and platform-specific configurations
Example structure:
buildSrc/ └── src/main/groovy/ ├── manas.java-conventions.gradle ├── manas.publishing-conventions.gradle └── manas.platform-conventions.gradle
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (22)
command-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngcommand-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngconfig-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngconfig-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngconfig-neoforge/src/main/resources/icon.pngis excluded by!**/*.pnginventory-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pnginventory-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngkeybinding-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngkeybinding-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngkeybinding-neoforge/src/main/resources/icon.pngis excluded by!**/*.pngnetwork-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngnetwork-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngregistry-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngregistry-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngregistry-neoforge/src/main/resources/icon.pngis excluded by!**/*.pngskill-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngskill-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngskill-neoforge/src/main/resources/icon.pngis excluded by!**/*.pngstorage-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngstorage-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngtesting-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngtesting-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.png
📒 Files selected for processing (82)
build.gradle(1 hunks)config-common/build.gradle(1 hunks)config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java(1 hunks)config-common/src/main/java/io/github/manasmods/manascore/config/ManasCoreConfig.java(1 hunks)config-common/src/main/java/io/github/manasmods/manascore/config/api/ManasConfig.java(1 hunks)config-common/src/main/resources/architectury.common.json(1 hunks)config-common/src/main/resources/manascore_config.accesswidener(1 hunks)config-common/src/main/resources/manascore_config.mixins.json(1 hunks)config-fabric/src/main/java/io/github/manasmods/manascore/config/fabric/ManasCoreConfigFabric.java(1 hunks)config-fabric/src/main/resources/fabric.mod.json(1 hunks)config-neoforge/gradle.properties(1 hunks)config-neoforge/src/main/java/io/github/manasmods/manascore/config/neoforge/ManasCoreConfigNeoForge.java(1 hunks)config-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)inventory-fabric/src/main/java/io/github/manasmods/manascore/inventory/fabric/ManasCoreInventoryFabric.java(1 hunks)keybinding-common/build.gradle(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybinding.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingClient.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingCategory.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingManager.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java(1 hunks)keybinding-common/src/main/resources/architectury.common.json(1 hunks)keybinding-common/src/main/resources/manascore_keybinding.accesswidener(1 hunks)keybinding-common/src/main/resources/manascore_keybinding.mixins.json(1 hunks)keybinding-fabric/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingFabric.java(1 hunks)keybinding-fabric/src/main/resources/fabric.mod.json(1 hunks)keybinding-fabric/src/main/resources/manascore_keybinding-fabric.mixins.json(1 hunks)keybinding-neoforge/build.gradle(1 hunks)keybinding-neoforge/gradle.properties(1 hunks)keybinding-neoforge/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingNeoForge.java(1 hunks)keybinding-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)registry-common/build.gradle(1 hunks)registry-common/src/main/java/io/github/manasmods/manascore/registry/ManasCoreRegistry.java(1 hunks)registry-common/src/main/java/io/github/manasmods/manascore/registry/api/AbstractRegister.java(1 hunks)registry-common/src/main/java/io/github/manasmods/manascore/registry/api/ManasAttributeRegistry.java(1 hunks)registry-common/src/main/java/io/github/manasmods/manascore/registry/api/Register.java(1 hunks)registry-common/src/main/resources/architectury.common.json(1 hunks)registry-common/src/main/resources/manascore_registry.accesswidener(1 hunks)registry-common/src/main/resources/manascore_registry.mixins.json(1 hunks)registry-fabric/build.gradle(1 hunks)registry-fabric/src/main/java/io/github/manasmods/manascore/registry/ManasCoreRegistryFabric.java(1 hunks)registry-fabric/src/main/java/io/github/manasmods/manascore/registry/api/fabric/ManasAttributeRegistryImpl.java(1 hunks)registry-fabric/src/main/resources/fabric.mod.json(1 hunks)registry-fabric/src/main/resources/manascore_registry-fabric.mixins.json(1 hunks)registry-neoforge/build.gradle(1 hunks)registry-neoforge/gradle.properties(1 hunks)registry-neoforge/src/main/java/io/github/manasmods/manascore/registry/ManasCoreRegistryNeoForge.java(1 hunks)registry-neoforge/src/main/java/io/github/manasmods/manascore/registry/api/neoforge/ManasAttributeRegistryImpl.java(1 hunks)registry-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)settings.gradle(1 hunks)skill-common/build.gradle(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/AbstractSkillRegister.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/data/SkillTagProvider.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/InternalSkillPacketActions.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/ManasSkillNetwork.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinLivingEntity.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinPlayer.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/Changeable.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/EntityEvents.java(1 hunks)skill-common/src/main/resources/architectury.common.json(1 hunks)skill-common/src/main/resources/manascore_skill.accesswidener(1 hunks)skill-common/src/main/resources/manascore_skill.mixins.json(1 hunks)skill-fabric/build.gradle(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/ManasCoreSkillFabric.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractArrow.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractHurtingProjectile.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFireworkRocketEntity.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFishingHook.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLivingEntity.java(1 hunks)
⛔ Files not processed due to max files limit (24)
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLlamaSpit.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinMob.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinPlayer.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinShulkerBullet.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinThrowableProjectile.java
- skill-fabric/src/main/resources/fabric.mod.json
- skill-fabric/src/main/resources/manascore_skill-fabric.mixins.json
- skill-neoforge/build.gradle
- skill-neoforge/gradle.properties
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/ManasCoreSkillNeoForge.java
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java
- skill-neoforge/src/main/resources/META-INF/neoforge.mods.toml
- testing-common/build.gradle
- testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/client/KeybindingTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/client/ManasCoreTestingClient.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/configs/SkillConfig.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/configs/TestConfig.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/module/ConfigModuleTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRegister.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestTags.java
- testing-neoforge/src/main/java/io/github/manasmods/manascore/testing/neoforge/ManasCoreTestingNeoForge.java
✅ Files skipped from review due to trivial changes (23)
- config-common/src/main/resources/manascore_config.accesswidener
- skill-common/src/main/resources/manascore_skill.accesswidener
- registry-fabric/build.gradle
- keybinding-common/build.gradle
- keybinding-common/src/main/resources/manascore_keybinding.accesswidener
- keybinding-neoforge/gradle.properties
- inventory-fabric/src/main/java/io/github/manasmods/manascore/inventory/fabric/ManasCoreInventoryFabric.java
- config-neoforge/src/main/java/io/github/manasmods/manascore/config/neoforge/ManasCoreConfigNeoForge.java
- skill-common/src/main/resources/architectury.common.json
- keybinding-common/src/main/resources/architectury.common.json
- config-neoforge/gradle.properties
- config-common/src/main/resources/architectury.common.json
- config-common/src/main/java/io/github/manasmods/manascore/config/ManasCoreConfig.java
- keybinding-common/src/main/resources/manascore_keybinding.mixins.json
- registry-common/src/main/resources/architectury.common.json
- config-common/src/main/resources/manascore_config.mixins.json
- registry-neoforge/gradle.properties
- registry-common/src/main/resources/manascore_registry.mixins.json
- registry-fabric/src/main/resources/manascore_registry-fabric.mixins.json
- keybinding-fabric/src/main/resources/manascore_keybinding-fabric.mixins.json
- config-fabric/src/main/java/io/github/manasmods/manascore/config/fabric/ManasCoreConfigFabric.java
- registry-common/build.gradle
- skill-common/src/main/resources/manascore_skill.mixins.json
🔇 Additional comments (42)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java (2)
1-21: LGTM! Well-structured package and imports.The package structure clearly indicates client-to-server communication, and the imports are properly organized with cross-platform networking support via Architectury API.
60-62: LGTM! Clean type method implementation.The method is properly annotated and returns the correct type.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java (4)
1-21: LGTM! Well-organized package structure and imports.The package name and imports are well-organized, following Java conventions. The c2s package name clearly indicates this is a client-to-server packet.
27-28: LGTM! Proper packet type and codec implementation.The packet type and codec are correctly implemented following Minecraft's networking patterns.
30-38: LGTM! Consistent encoding and decoding implementation.The constructor and encode method maintain perfect symmetry in their read/write operations, ensuring reliable packet transmission.
49-51: LGTM! Correct type() method implementation.The type() method correctly implements the CustomPacketPayload interface requirement.
keybinding-fabric/src/main/resources/fabric.mod.json (2)
22-25: Verify the existence of mixin configuration files.Please ensure both mixin configuration files exist and are properly configured:
${mod_id}.mixins.json${mod_id}-fabric.mixins.json✅ Verification successful
✓ Mixin configuration files verified
Both mixin configuration files exist in the repository:
manascore_keybinding.mixins.jsonin keybinding-commonmanascore_keybinding-fabric.mixins.jsonin keybinding-fabric🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for mixin configuration files fd -e json -x grep -l "\"package\": \".*mixin.*\"" {}Length of output: 817
15-21: Verify if client-side initialization is required.The
cliententrypoints array is empty, but since this is a keybinding module, it might need client-side initialization. Please verify if any client-specific setup is required.✅ Verification successful
Client entrypoint is not required for this implementation
The keybinding system uses Architectury's client event system (
ClientTickEvent) in the common module, which handles the client-side initialization internally. The empty client entrypoints array is correct for this implementation.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the keybinding implementation has client-specific code ast-grep --pattern 'class $_ implements ClientModInitializer { $$$ }' # Check for client-only annotations or methods rg -l "(@Environment|@OnlyIn).*\.CLIENT"Length of output: 209
Script:
#!/bin/bash # Check the keybinding implementation class ast-grep --pattern 'class ManasCoreKeybindingFabric { $$$ }' # Look for keybinding-related code rg -l "KeyBinding|registerKeyBinding|onKeyPress" # Check if there are any client-specific method calls or registrations rg "MinecraftClient|ClientTickEvent" -A 3Length of output: 1445
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/AbstractSkillRegister.java (1)
1-14: LGTM! Well-structured package and imports.The file has proper copyright notice, well-organized imports, and follows good package naming conventions.
keybinding-neoforge/src/main/resources/META-INF/neoforge.mods.toml (4)
14-14: Verify the existence of the icon file.Ensure that the referenced
icon.pngfile exists in the correct resources directory.✅ Verification successful
Icon file verification successful
The referenced
icon.pngfile exists at the correct location in the resources directory.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if icon.png exists in the resources directory fd -g 'icon.png' keybinding-neoforge/src/main/resources/Length of output: 104
1-2: Consider constraining the loader version range.The version range
[2,)is very broad and might include incompatible future versions. Consider setting an upper bound to avoid potential compatibility issues with future NeoForge versions.
19-19: Consider constraining the NeoForge version range.Similar to the loader version, the NeoForge version range
[21,)is very broad. Consider setting an upper bound to avoid potential compatibility issues.
30-35: Verify Architectury dependency ordering.The Architectury dependency is set to load
AFTERwhile others areNONE. Verify this ordering is consistent with other modules and necessary for proper functionality.✅ Verification successful
Architectury dependency ordering is correct and consistent
The "AFTER" ordering for Architectury is consistently implemented across all neoforge modules and follows a logical pattern where core dependencies use "NONE" while framework dependencies use "AFTER". This ensures proper initialization order.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check Architectury dependency ordering in other modules rg -g '*.toml' 'modId.*=.*"architectury".*\n.*type.*=.*"required".*\n.*ordering.*=.*"AFTER"' .Length of output: 288
Script:
#!/bin/bash # Search for Architectury dependencies with multiline mode echo "=== Searching for all Architectury dependencies ===" rg -U --multiline -g '*.toml' '(?s)\[\[dependencies\.[^\]]+\]\].*?modId.*=.*"architectury".*?side.*=.*"BOTH"' . echo -e "\n=== Searching specifically for different ordering values ===" rg -U --multiline -g '*.toml' 'modId.*=.*"architectury".*ordering.*=.*"[^"]+"' .Length of output: 18408
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/ManasCoreSkillFabric.java (3)
1-5: LGTM! License header is properly formatted.The copyright notice and GPL-3.0 license declaration are correctly specified.
6-10: LGTM! Package and imports are well-organized.The package structure follows Java conventions, and imports are minimal and necessary.
13-15: Verify integration with other modules.The PR introduces multiple modules (Registry, Keybinding, Config), but we only see the Skill module initialization here. Ensure that:
- The initialization order is correct if there are dependencies between modules
- All modules are properly initialized in the Fabric environment
Let's verify the module integration:
✅ Verification successful
Module initialization is correctly implemented.
The current implementation follows best practices:
- Each module handles its own initialization and dependencies
- Client/Server components are properly separated
- No cross-module initialization dependencies require specific ordering
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other module initializers echo "Searching for other module initializers..." rg -l "implements ModInitializer" --type java # Check for potential module dependencies echo "Checking for module dependencies..." rg -l "ManasCoreRegistry|ManasCoreKeybinding|ManasCoreConfig" --type javaLength of output: 2293
Script:
#!/bin/bash # Check initialization code in ModInitializer implementations echo "Checking ModInitializer implementations..." for file in $(rg -l "implements ModInitializer" --type java); do echo "=== $file ===" rg -A 5 "onInitialize\(\)" "$file" done # Look for cross-module dependencies in initialization echo -e "\nChecking for cross-module dependencies..." ast-grep --pattern 'class $_ implements ModInitializer { $$$ public void onInitialize() { $$$ } $$$ }'Length of output: 5090
Script:
#!/bin/bash # Check the content of init() methods in common modules echo "Checking init() methods in common modules..." for module in "Registry" "Keybinding" "Skill" "Storage" "Network" "Inventory" "Command"; do echo "=== Manascore${module} init() ===" rg -A 10 "class ManasCore${module}" --type java rg -A 10 "init\(\)" "*common*/src/main/java/*/${module,,}*/ManasCore${module}.java" || true doneLength of output: 25552
registry-neoforge/src/main/resources/META-INF/neoforge.mods.toml (3)
7-14: Verify resource files and template variables.
- Ensure that
icon.pngexists in the resources directory.- Verify that
${mod_id},${version}, and${mod_name}variables are properly defined.Run this script to verify resources and variables:
✅ Verification successful
Resource files and template variables verified successfully
The
icon.pngfile exists in the correct location, and the template variables follow the standard mod development pattern used consistently across the project.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for icon.png in resources directories echo "Checking for icon.png..." fd -g 'icon.png' 'registry-neoforge/src' # Check for template variable definitions echo "Checking for template variable definitions..." rg -l '\$\{mod_(id|name)\}|\$\{version\}' | while read -r file; do echo "Found variable references in: $file" rg 'mod_(id|name)|version.*=' "$file" doneLength of output: 9106
3-4: Add issue tracker URL and verify license variable.
- The empty issue tracker URL might make it difficult for users to report issues.
- Ensure that the
${license}variable is properly defined in your build configuration.Run this script to verify the license variable resolution:
16-35: Verify dependency version constraints.The dependency configuration looks good with appropriate ordering for architectury. Please verify:
- The NeoForge version constraint "[21,)" is appropriate for your mod's features
- The template variables
${minecraft_version}and${architectury_version}are properly definedRun this script to verify version variables:
✅ Verification successful
Version constraints and variables are properly configured ✓
The dependency configuration is correct:
- Template variables are properly defined in gradle.properties
- NeoForge version constraint "[21,)" is appropriate for the used version 21.1.28
- Minecraft and Architectury version ranges are correctly templated
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for version variable definitions echo "Checking for version variable definitions..." rg -l '\$\{(minecraft|architectury)_version\}' | while read -r file; do echo "Found version references in: $file" rg '(minecraft|architectury).*version.*=' "$file" doneLength of output: 5526
Script:
#!/bin/bash # Check build configuration files for version definitions echo "Checking build configuration files..." # First, find all gradle and properties files echo "=== Gradle and Properties files ===" fd -e gradle -e properties # Then search for version definitions echo -e "\n=== Version definitions ===" rg -i '(minecraft|architectury).*version\s*=' --type properties --type groovyLength of output: 1538
Script:
#!/bin/bash echo "=== Checking root gradle.properties ===" cat gradle.properties echo -e "\n=== Checking module gradle.properties ===" for f in */gradle.properties; do if [ -f "$f" ]; then echo "=== $f ===" cat "$f" fi doneLength of output: 3152
skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinPlayer.java (1)
29-32: Ensure inject and modify variable mixins do not conflictBoth
modifyDamage(lines 23-27) andcancelActuallyHurt(lines 29-32) are targeting theactuallyHurtmethod at the same injection point. Confirm that these mixins do not interfere with each other and execute in the intended order.Consider reviewing the mixin priority or adjusting the injection points to prevent potential conflicts.
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFireworkRocketEntity.java (1)
29-31: Appropriate use of@Uniqueand@NullableannotationsThe declaration of
onHitEventResultwith@Uniqueand@Nullableis correct. Using@Uniqueensures that the mixin introduces this field without causing conflicts, and@Nullableaccurately represents that the field can benull.skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinLivingEntity.java (1)
51-54: Validate method parameters incancelActuallyHurtIn the
cancelActuallyHurtmethod, ensure that the parametersDamageSource damageSourceandfloat damageAmountmatch those of theactuallyHurtmethod inLivingEntity. If they do not match, the injection may fail.Run the following script to verify the method signature:
✅ Verification successful
Parameters in
cancelActuallyHurtare correctly definedThe parameter types in the Mixin injection match the target method signature as evidenced by the fully qualified method reference in the
@Injectannotation. The code would fail to compile if there was a mismatch.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the parameters of the `actuallyHurt` method in LivingEntity. # Search for the method signature in the codebase. rg -A 3 'void actuallyHurt\(DamageSource damageSrc, float damageAmount\)' src/Length of output: 125
Script:
#!/bin/bash # First, find all Java files recursively fd -e java . | xargs rg -l "actuallyHurt.*DamageSource.*float" # Then use ast-grep to find the exact method signature ast-grep --pattern 'void actuallyHurt(DamageSource $_, float $_) { $$$ }'Length of output: 142
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractArrow.java (1)
81-85: Possible Unintended Behavior inonImpulseSetMethodThe
onImpulseSetmethod setsthis.onHitEventResulttonullregardless of the condition. Ifthis.onHitEventResultis notnull, andoriginal.call(instance, value)is not called, this might skip necessary logic.Please verify if the intended behavior is to always reset
onHitEventResulttonull. If not, adjust the condition accordingly.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (2)
42-52:⚠️ Potential issueNull Check Missing for Optional in
LIVING_CHANGE_TARGETEventIn the
LIVING_CHANGE_TARGETevent handler, the code checksif (!changeableTarget.isPresent()) return EventResult.pass();, but later useschangeableTarget.get()without verifying if it's present, which could lead to aNoSuchElementException.Apply this diff to safely handle the optional:
if (!changeableTarget.isPresent()) return EventResult.pass(); -LivingEntity owner = changeableTarget.get(); +LivingEntity owner = changeableTarget.orElse(null); +if (owner == null) return EventResult.pass(); for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(owner).getLearnedSkills()) {Likely invalid or redundant comment.
63-72:⚠️ Potential issueIncorrect Casting in
SKILL_DAMAGE_PRE_CALCULATIONEventThe code casts
source.getEntity()toLivingEntitywithout checking ifsource.getEntity()is indeed an instance ofLivingEntity, which could lead to aClassCastException.Apply this diff to ensure safe casting:
-if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); +Entity sourceEntity = source.getEntity(); +if (!(sourceEntity instanceof LivingEntity owner)) return EventResult.pass();Likely invalid or redundant comment.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
52-58: Potential Issue with Damage Calculation Event RegistrationThe events
SKILL_DAMAGE_PRE_CALCULATION,SKILL_DAMAGE_CALCULATION, andSKILL_DAMAGE_POST_CALCULATIONare invoked sequentially even if one returnsinterruptFalse(). This might not be the intended behavior.Please verify if the intention is to stop the chain of event invocations when one of them returns
interruptFalse(). If so, consider restructuring the logic to respect the interruption.registry-common/src/main/resources/manascore_registry.accesswidener (1)
1-4: Document the purpose of widening access to these fields.While the access widener syntax is correct, please add comments explaining:
- Why these fields need to be exposed
- Potential security implications
- How these fields will be used by the mod
Please ensure these fields are still present in the target Minecraft version:
✅ Verification successful
Access widening is justified by implementation requirements
The widened fields are essential for the mod's core functionality:
AttributeSupplier.instancesis used to manage attribute system integration across platformsMobEffect.particleFactoryenables custom particle effects for mod-specific status effects🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the existence of fields in decompiled Minecraft code # Note: This assumes the presence of a decompiled Minecraft codebase # Search for AttributeSupplier.instances rg -l "class AttributeSupplier" | xargs rg "Map.*instances" # Search for MobEffect.particleFactory rg -l "class MobEffect" | xargs rg "Function.*particleFactory"Length of output: 126
Script:
#!/bin/bash # Check for mod configuration files and documentation echo "=== Searching for mod configuration files ===" fd -e toml -e json "^(fabric|forge|mod|gradle)\." --exec head -n 20 {} echo -e "\n=== Searching for mapping references ===" rg -l "net\.minecraft\.(world|entity)" --type yaml --type properties echo -e "\n=== Checking for documentation ===" fd "README" --exec cat {}Length of output: 4560
Script:
#!/bin/bash # Search for usage of the widened fields echo "=== Searching for AttributeSupplier.instances usage ===" rg -l "AttributeSupplier" --type java rg "instances" --type java -C 3 echo -e "\n=== Searching for MobEffect.particleFactory usage ===" rg -l "MobEffect" --type java rg "particleFactory" --type java -C 3 echo -e "\n=== Looking for related registry code ===" fd . -e java --full-path ".*registry.*" --exec head -n 50 {}Length of output: 9043
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/ManasSkillNetwork.java (1)
15-20: Consider adding synchronization for packet handling.The network packet handlers should be thread-safe as they might be called concurrently from multiple client connections.
Run this script to check if packet handlers implement thread safety:
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java (1)
50-50: Consider thread safety in forEachSkill implementation.The forEachSkill method could lead to deadlocks or inconsistent state if the consumer modifies the skill storage.
Consider providing a read-only view of skills during iteration or documenting thread-safety requirements.
config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java (1)
16-22: Well-structured configuration setup!Good choice of thread-safe collections and proper Gson configuration for JSON handling.
registry-neoforge/src/main/java/io/github/manasmods/manascore/registry/api/neoforge/ManasAttributeRegistryImpl.java (1)
26-42: Excellent implementation of attribute registry!Good practices observed:
- Thread-safe collections
- Lazy evaluation with suppliers
- Clear separation of registry types
config-common/build.gradle (1)
1-3: LGTM!The Loom configuration for access widener path follows standard Minecraft modding practices.
registry-neoforge/build.gradle (1)
11-11: Verify mod_id project property is definedThe
project.mod_idis used to construct the access widener filename. Ensure this property is defined in the project's gradle.properties or build script.✅ Verification successful
Property
mod_idis properly defined in build configurationThe
mod_idproperty is dynamically constructed in the root build.gradle for all subprojects using the pattern${rootProject.archives_name}_$module_name, wheremodule_nameis derived from the project path. This ensures unique and consistent mod IDs across all modules.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if mod_id is defined in gradle.properties if ! grep -q "mod_id=" gradle.properties; then echo "Warning: mod_id property not found in gradle.properties" fiLength of output: 156
Script:
#!/bin/bash # Check build.gradle and settings.gradle for mod_id definition echo "=== Checking build.gradle ===" rg "mod_id" registry-neoforge/build.gradle -A 2 -B 2 echo -e "\n=== Checking settings.gradle ===" if [ -f "settings.gradle" ]; then rg "mod_id" settings.gradle fi echo -e "\n=== Checking for other gradle files ===" fd "build.gradle" --type fLength of output: 1220
Script:
#!/bin/bash echo "=== Checking root build.gradle for project configuration ===" rg "subprojects|allprojects|mod_id" build.gradle -A 3 -B 3 echo -e "\n=== Looking for gradle.properties files ===" fd "gradle.properties" --type f echo -e "\n=== Checking root project files ===" ls -la *.gradle *.properties 2>/dev/nullLength of output: 2479
keybinding-neoforge/build.gradle (1)
11-11: Verify mod_id project property is definedSame concern as in registry-neoforge/build.gradle regarding the mod_id property definition.
skill-fabric/build.gradle (1)
10-13: Verify the scope of dependenciesThe dependencies on network-common and storage-common look appropriate for a skill system. However, consider if these dependencies should be marked as
apiinstead ofimplementationif they expose public APIs that skill-fabric consumers might need to access directly.✅ Verification successful
Dependencies can remain as 'implementation'
The skill-fabric module doesn't expose any network or storage APIs through its public interface. It only provides Fabric-specific integration through mixins and delegates core functionality to the main skill module. The current dependency scope is appropriate.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if any public APIs from network-common or storage-common are exposed through skill-fabric's public interface rg -t java "import.*(?:network|storage).*" ./skill-fabric/src/main/java/Length of output: 74
Script:
#!/bin/bash # Check if the directory exists and list Java files echo "=== Checking directory structure ===" fd -e java . skill-fabric/src/ echo -e "\n=== Checking all imports to understand package structure ===" fd -e java . skill-fabric/src/ -x rg -l "^import" echo -e "\n=== List all package declarations to understand structure ===" fd -e java . skill-fabric/src/ -x rg "^package"Length of output: 3319
Script:
#!/bin/bash # Check content of main class and mixins for any network/storage related code echo "=== Checking ManasCoreSkillFabric.java ===" cat skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/ManasCoreSkillFabric.java echo -e "\n=== Checking all imports in all files ===" fd -e java . skill-fabric/src/ -x cat {} | rg "^import"Length of output: 6803
skill-common/build.gradle (1)
1-3: Verify the access widener file existsThe Loom configuration references an access widener file. Let's ensure it exists and contains the necessary wideners.
✅ Verification successful
Access widener file exists and is properly configured ✅
The file exists with the correct format header (
accessWidener v2 named). While currently empty, this is valid as wideners can be added as needed during development.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the access widener file exists and examine its contents if [ -f "skill-common/src/main/resources/manascore_skill.accesswidener" ]; then echo "Access widener file exists" cat "skill-common/src/main/resources/manascore_skill.accesswidener" else echo "Access widener file is missing" fiLength of output: 228
settings.gradle (1)
Line range hint
17-21: LGTM! Module structure follows the established patternThe new modules (registry, keybinding, skill, config) are properly integrated into the existing module structure, maintaining consistency with the common/fabric/neoforge pattern.
✅ Verification successful
✓ Module structure is complete and properly implemented
All modules (registry, keybinding, skill, config) are properly structured with:
- Common variants containing core APIs
- Platform-specific implementations for both Fabric and NeoForge
- Consistent naming patterns and package structure
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that all module directories exist for module in registry keybinding skill config; do for variant in common fabric neoforge; do if [ ! -d "$module-$variant" ]; then echo "Missing directory: $module-$variant" fi done doneLength of output: 1070
Script:
#!/bin/bash # Check for source files in module directories for module in registry keybinding skill config; do for variant in common fabric neoforge; do echo "=== Checking $module-$variant ===" fd -e java -e kt . "$module-$variant" || true done doneLength of output: 7338
config-fabric/src/main/resources/fabric.mod.json (1)
28-28: Consider the Java version requirementRequiring Java 21 (released in September 2023) might be too restrictive for many users. Consider if Java 17 (the current LTS version) would be sufficient for your needs.
registry-fabric/src/main/resources/fabric.mod.json (1)
28-29: ** Consider the Java version requirement**Same concern as in config-fabric: requiring Java 21 might be too restrictive. Consider if Java 17 would be sufficient.
config-neoforge/src/main/resources/META-INF/neoforge.mods.toml (2)
16-35: Verify dependency version constraints.The dependency version ranges use open-ended constraints (
[21,),[${minecraft_version},),[${architectury_version},)). This could potentially lead to compatibility issues with future versions.Consider adding upper bounds to version ranges for better version control:
- versionRange = "[21,)" + versionRange = "[21,22)" - versionRange = "[${minecraft_version},)" + versionRange = "[${minecraft_version},${minecraft_version_next})" - versionRange = "[${architectury_version},)" + versionRange = "[${architectury_version},${architectury_version_next})"
37-38: Verify mixin configuration file existence.Ensure that the referenced mixin configuration file exists and is properly configured.
✅ Verification successful
Mixin configuration verified successfully ✓
The referenced mixin configuration file exists at
config-common/src/main/resources/manascore_config.mixins.jsonand is properly configured with all required fields.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the mixin configuration file exists fd -e json -x grep -l "\"package\": \".*${mod_id}.*\"" {}Length of output: 812
Script:
#!/bin/bash # Get the mod_id from neoforge.mods.toml echo "=== Mod ID from neoforge.mods.toml ===" cat config-neoforge/src/main/resources/META-INF/neoforge.mods.toml | grep -A 1 "^modId" echo -e "\n=== Looking for mixin files in neoforge paths ===" fd -e json . config-neoforge/Length of output: 366
Script:
#!/bin/bash # Look for gradle files defining mod_id echo "=== Searching for mod_id in gradle files ===" rg "mod_id" -A 2 --glob "*.gradle*" echo -e "\n=== Looking for mixin configuration templates ===" fd ".*mixins?.json.*" config-neoforge/Length of output: 1772
Script:
#!/bin/bash echo "=== Searching for archives_name in gradle files ===" rg "archives_name" --glob "*.gradle*" -A 2 echo -e "\n=== Looking for config module mixin files ===" fd "manascore.*config.*mixins?.json"Length of output: 664
Script:
#!/bin/bash echo "=== Checking mixin configuration content ===" cat config-common/src/main/resources/manascore_config.mixins.jsonLength of output: 390
build.gradle (1)
84-85: Review mixin configuration changes.Two significant changes to the mixin configuration:
- Setting
useLegacyMixinAp = falseindicates a switch to the modern mixin annotation processor- Commenting out
defaultRefmapNamemight affect mixin reference mappingLet's verify the impact of these changes:
✅ Verification successful
Mixin configuration changes are safe to proceed ✅
The changes are well-aligned with modern Minecraft mod development practices:
- Setting
useLegacyMixinAp = falseis the recommended approach for new projects- Removing the custom refmap name is safe as the modern mixin annotation processor handles refmap generation automatically for each module
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for mixin configurations across the project fd -e json -x grep -l "\"refmap\": " {} # Check for any custom refmap references in code rg -l "refmap.*json"Length of output: 77
Script:
#!/bin/bash # Check for mixin usage in the codebase rg "@Mixin" -A 2 # Look for mixin configuration classes ast-grep --pattern 'class $_ implements IMixinConfigPlugin'Length of output: 10593
...main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java
Show resolved
Hide resolved
...main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java
Show resolved
Hide resolved
...ain/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java
Show resolved
Hide resolved
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/AbstractSkillRegister.java
Outdated
Show resolved
Hide resolved
config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java
Outdated
Show resolved
Hide resolved
config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java
Outdated
Show resolved
Hide resolved
...nding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractHurtingProjectile.java (1)
27-27: 🛠️ Refactor suggestionUse '==' operator for enum comparison instead of 'Objects.equals'
When comparing enum constants in Java, it's recommended to use the
==operator rather thanObjects.equals, as enums are singletons and==provides a more efficient and type-safe comparison.- if (!Objects.equals(resultChangeable.get(), EntityEvents.ProjectileHitResult.DEFAULT)) return deflectionChangeable.get(); + if (resultChangeable.get() != EntityEvents.ProjectileHitResult.DEFAULT) return deflectionChangeable.get();keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java (1)
16-16:⚠️ Potential issueConsider thread safety for shared state
The static
PRESSED_KEYBINDINGSHashMap lacks synchronization, which could lead to race conditions in a game environment with multiple input events.
🧹 Nitpick comments (11)
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLlamaSpit.java (2)
1-4: Update the copyright year.The copyright notice shows the year 2025, which is in the future. Consider updating it to the current year or the year when the code was written.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
22-30: Add method documentation and improve null safety.The method implementation is solid, but could benefit from these improvements:
- Add Javadoc explaining the purpose and behavior of the method
- Make the null check more explicit
Consider applying these changes:
+ /** + * Wraps the hit detection of LlamaSpit to allow modification through events. + * + * @param instance The LlamaSpit entity instance + * @param result The original hit result + * @param original The original operation to be wrapped + * @return The resulting ProjectileDeflection + */ @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/LlamaSpit;hitTargetOrDeflectSelf(Lnet/minecraft/world/phys/HitResult;)Lnet/minecraft/world/entity/projectile/ProjectileDeflection;")) ProjectileDeflection onHit(LlamaSpit instance, HitResult result, Operation<ProjectileDeflection> original) { Changeable<EntityEvents.ProjectileHitResult> resultChangeable = Changeable.of(EntityEvents.ProjectileHitResult.DEFAULT); Changeable<ProjectileDeflection> deflectionChangeable = Changeable.of(ProjectileDeflection.NONE); EntityEvents.PROJECTILE_HIT.invoker().hit(result, instance, deflectionChangeable, resultChangeable); - if (!Objects.equals(resultChangeable.get(), EntityEvents.ProjectileHitResult.DEFAULT)) return deflectionChangeable.get(); + // If the event modified the result, return the modified deflection + if (resultChangeable.get() != null && resultChangeable.get() != EntityEvents.ProjectileHitResult.DEFAULT) { + return deflectionChangeable.get(); + } original.call(instance, result); return deflectionChangeable.get(); }skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractHurtingProjectile.java (2)
2-2: Update the copyright yearThe copyright year is set to 2025, but it should be the current year or the year when the code was written.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
22-23: Add method documentationAdd Javadoc to explain the purpose of this method, its parameters, and return value. This will help other developers understand the mixin's functionality.
+ /** + * Wraps the hitTargetOrDeflectSelf method to allow modification of projectile hit behavior. + * @param instance The projectile instance + * @param result The hit result + * @param original The original operation + * @return The projectile deflection result + */ @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/AbstractHurtingProjectile;hitTargetOrDeflectSelf(Lnet/minecraft/world/phys/HitResult;)Lnet/minecraft/world/entity/projectile/ProjectileDeflection;"))keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingCategory.java (1)
11-13: Add parameter validation and documentation.The
nameparameter lacks validation and documentation. Consider:
- Adding parameter validation to prevent null/empty values
- Adding Javadoc to document the expected format/constraints
@RequiredArgsConstructor(staticName = "of") public class KeybindingCategory { + /** + * @param name The category name (non-null, non-empty) + * @throws IllegalArgumentException if name is null or empty + */ private final String name; + + private static String validateName(String name) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Category name cannot be null or empty"); + } + return name; + }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
301-305: Correct Javadoc syntax for@paramtagThe Javadoc comment incorrectly uses
{@param heldTicks}. The correct syntax for documenting parameters is@param parameterName description.Apply this diff to fix the Javadoc:
- * Called when the {@link LivingEntity} owning this Skill releases the skill activation button after {@param heldTicks}. + * Called when the {@link LivingEntity} owning this Skill releases the skill activation button after a certain number of ticks. + * + * @param heldTicks The number of ticks the skill activation button was held.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/InternalSkillPacketActions.java (2)
31-83: Reduce code duplication in packet action methods.The packet action methods share similar patterns. Consider extracting common logic into a helper method:
+ private static <T> void processSkillPacket(int keyNumber, + Function<ManasSkillInstance, Boolean> skillFilter, + BiFunction<Integer, List<ResourceLocation>, T> packetCreator) { + var minecraft = Minecraft.getInstance(); + Player player = minecraft.player; + if (player == null) return; + List<ResourceLocation> packetSkills = new ArrayList<>(); + + for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) { + if (skillFilter.apply(skillInstance)) { + packetSkills.add(skillInstance.getSkillId()); + } + } + + if (!packetSkills.isEmpty()) { + NetworkManager.sendToServer(packetCreator.apply(keyNumber, packetSkills)); + } + }This would simplify the implementation of each packet action method.
34-34: Add logging for failed packet operations.Consider adding debug logging when packet operations fail (e.g., null player) to aid in troubleshooting.
+import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class InternalSkillPacketActions { + private static final Logger LOGGER = LoggerFactory.getLogger(InternalSkillPacketActions.class); public static void sendSkillActivationPacket(int keyNumber) { var minecraft = Minecraft.getInstance(); Player player = minecraft.player; - if (player == null) return; + if (player == null) { + LOGGER.debug("Cannot send skill activation packet: player is null"); + return; + }Also applies to: 53-53, 72-72
skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/EntityEvents.java (2)
20-27: Enhance event documentation.Consider adding more detailed documentation for each event, including:
- When the event is fired
- Expected usage scenarios
- Example implementations
+ /** + * Fired before a living entity's tick processing. + * Use this event to modify entity behavior at the start of each tick. + */ public static Event<LivingTickEvent> LIVING_PRE_TICK = EventFactory.createLoop(); + + /** + * Fired after a living entity's tick processing. + * Use this event to perform post-tick modifications or calculations. + */ public static Event<LivingTickEvent> LIVING_POST_TICK = EventFactory.createLoop();
59-64: Improve ProjectileHitResult enum documentation.The enum states would benefit from more detailed documentation explaining their use cases and implications.
public enum ProjectileHitResult { - DEFAULT, // Hit, damage + possibly continue - HIT, // Hit + damage - HIT_NO_DAMAGE, // Hit - PASS // Pass through + /** + * Default behavior: Hit the target, apply damage, and possibly continue trajectory + */ + DEFAULT, + /** + * Hit the target and apply damage, then stop + */ + HIT, + /** + * Hit the target without applying damage + */ + HIT_NO_DAMAGE, + /** + * Pass through the target without interaction + */ + PASS }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (1)
32-119: Enhance error handling in event handlers.Consider adding try-catch blocks around event handler logic to prevent exceptions from propagating to the event system.
public static void init() { EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> { + try { for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { if (!instance.canInteractSkill(entity)) continue; if (!instance.onEffectAdded(entity, source, changeableTarget)) return EventResult.interruptFalse(); } return EventResult.pass(); + } catch (Exception e) { + LOGGER.error("Error processing LIVING_EFFECT_ADDED event", e); + return EventResult.pass(); + } });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (82)
command-common/build.gradle(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/CommandAnnotationHandler.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/PlatformCommandUtils.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/Command.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/CommandArgumentRegistrationEvent.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/CommandRegistry.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/Execute.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/Permission.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Bool.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/DoubleNumber.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Enum.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/FloatNumber.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/IntNumber.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Literal.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/LongNumber.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Sender.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Text.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Uuid.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandArgumentRegistry.java(1 hunks)command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandCreationContext.java(1 hunks)command-fabric/build.gradle(1 hunks)command-fabric/src/main/java/io/github/manasmods/manascore/command/fabric/ManasCoreCommandFabric.java(1 hunks)command-fabric/src/main/java/io/github/manasmods/manascore/command/fabric/PlatformCommandUtilsImpl.java(1 hunks)command-fabric/src/main/java/io/github/manasmods/manascore/command/fabric/integrations/FabricPermissionsApiIntegration.java(1 hunks)command-neoforge/build.gradle(1 hunks)command-neoforge/gradle.properties(1 hunks)command-neoforge/src/main/java/io/github/manasmods/manascore/command/neoforge/ManasCoreCommandNeoForge.java(1 hunks)command-neoforge/src/main/java/io/github/manasmods/manascore/command/neoforge/PlatformCommandUtilsImpl.java(1 hunks)config-neoforge/src/main/java/io/github/manasmods/manascore/config/neoforge/ManasCoreConfigNeoForge.java(1 hunks)inventory-common/build.gradle(1 hunks)inventory-common/src/main/java/io/github/manasmods/manascore/inventory/VanillaInventoryTab.java(1 hunks)inventory-common/src/main/java/io/github/manasmods/manascore/inventory/api/AbstractInventoryTab.java(1 hunks)inventory-common/src/main/java/io/github/manasmods/manascore/inventory/client/ManasCoreInventoryClient.java(1 hunks)inventory-common/src/main/java/io/github/manasmods/manascore/inventory/client/widget/InventoryTabSwitcherWidget.java(1 hunks)inventory-common/src/main/java/io/github/manasmods/manascore/inventory/mixin/MixinAbstractContainerScreen.java(1 hunks)inventory-fabric/build.gradle(1 hunks)inventory-fabric/src/main/java/io/github/manasmods/manascore/inventory/fabric/ManasCoreInventoryFabric.java(1 hunks)inventory-neoforge/build.gradle(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybinding.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingClient.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingCategory.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingManager.java(1 hunks)keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java(1 hunks)keybinding-fabric/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingFabric.java(1 hunks)keybinding-neoforge/build.gradle(1 hunks)keybinding-neoforge/gradle.properties(1 hunks)keybinding-neoforge/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingNeoForge.java(1 hunks)network-neoforge/build.gradle(1 hunks)settings.gradle(2 hunks)skill-common/build.gradle(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/data/SkillTagProvider.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/InternalSkillPacketActions.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/ManasSkillNetwork.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinLivingEntity.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinPlayer.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/Changeable.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/EntityEvents.java(1 hunks)skill-fabric/build.gradle(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/ManasCoreSkillFabric.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractArrow.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractHurtingProjectile.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFireworkRocketEntity.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFishingHook.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLivingEntity.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLlamaSpit.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinMob.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinPlayer.java(1 hunks)
⛔ Files not processed due to max files limit (27)
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinShulkerBullet.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinThrowableProjectile.java
- skill-neoforge/build.gradle
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/ManasCoreSkillNeoForge.java
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java
- storage-common/build.gradle
- storage-common/src/main/java/io/github/manasmods/manascore/storage/ManasCoreStorage.java
- storage-common/src/main/java/io/github/manasmods/manascore/storage/impl/StorageManager.java
- storage-common/src/main/java/io/github/manasmods/manascore/storage/mixin/MixinServerLevel.java
- storage-fabric/build.gradle
- storage-fabric/src/main/java/io/github/manasmods/manascore/storage/fabric/ManasCoreStorageFabric.java
- storage-fabric/src/main/java/io/github/manasmods/manascore/storage/fabric/mixin/MixinPlayerChunkSender.java
- storage-neoforge/src/main/java/io/github/manasmods/manascore/storage/neoforge/ManasCoreStorageNeoForge.java
- testing-common/build.gradle
- testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/client/KeybindingTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/client/ManasCoreTestingClient.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/module/InventoryTabsTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/module/StorageModuleTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestTags.java
- testing-fabric/build.gradle
- testing-fabric/src/main/java/io/github/manasmods/manascore/testing/fabric/ManasCoreTestingFabric.java
- testing-neoforge/build.gradle
- testing-neoforge/src/main/java/io/github/manasmods/manascore/testing/neoforge/ManasCoreTestingNeoForge.java
✅ Files skipped from review due to trivial changes (38)
- command-neoforge/build.gradle
- command-fabric/build.gradle
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Literal.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Bool.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Enum.java
- command-neoforge/gradle.properties
- inventory-neoforge/build.gradle
- command-common/src/main/java/io/github/manasmods/manascore/command/PlatformCommandUtils.java
- inventory-common/src/main/java/io/github/manasmods/manascore/inventory/VanillaInventoryTab.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Uuid.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/LongNumber.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/CommandRegistry.java
- inventory-common/src/main/java/io/github/manasmods/manascore/inventory/api/AbstractInventoryTab.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/Permission.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/CommandArgumentRegistrationEvent.java
- command-common/build.gradle
- command-fabric/src/main/java/io/github/manasmods/manascore/command/fabric/ManasCoreCommandFabric.java
- command-fabric/src/main/java/io/github/manasmods/manascore/command/fabric/integrations/FabricPermissionsApiIntegration.java
- network-neoforge/build.gradle
- inventory-fabric/build.gradle
- command-neoforge/src/main/java/io/github/manasmods/manascore/command/neoforge/PlatformCommandUtilsImpl.java
- command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandArgumentRegistry.java
- inventory-common/src/main/java/io/github/manasmods/manascore/inventory/client/widget/InventoryTabSwitcherWidget.java
- command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandCreationContext.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/Execute.java
- command-common/src/main/java/io/github/manasmods/manascore/command/CommandAnnotationHandler.java
- command-fabric/src/main/java/io/github/manasmods/manascore/command/fabric/PlatformCommandUtilsImpl.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Text.java
- command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java
- command-neoforge/src/main/java/io/github/manasmods/manascore/command/neoforge/ManasCoreCommandNeoForge.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/FloatNumber.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/IntNumber.java
- inventory-common/src/main/java/io/github/manasmods/manascore/inventory/client/ManasCoreInventoryClient.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/DoubleNumber.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/Command.java
- inventory-common/build.gradle
- inventory-common/src/main/java/io/github/manasmods/manascore/inventory/mixin/MixinAbstractContainerScreen.java
- command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/Sender.java
🚧 Files skipped from review as they are similar to previous changes (33)
- keybinding-neoforge/gradle.properties
- inventory-fabric/src/main/java/io/github/manasmods/manascore/inventory/fabric/ManasCoreInventoryFabric.java
- skill-common/build.gradle
- settings.gradle
- keybinding-fabric/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingFabric.java
- keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingClient.java
- keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybinding.java
- skill-fabric/build.gradle
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/ManasCoreSkillFabric.java
- keybinding-neoforge/build.gradle
- keybinding-neoforge/src/main/java/io/github/manasmods/manascore/keybinding/ManasCoreKeybindingNeoForge.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLivingEntity.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/data/SkillTagProvider.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/ManasSkillNetwork.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/utils/Changeable.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFishingHook.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFireworkRocketEntity.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractArrow.java
- keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingManager.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java
- config-neoforge/src/main/java/io/github/manasmods/manascore/config/neoforge/ManasCoreConfigNeoForge.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinPlayer.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinLivingEntity.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java
🔇 Additional comments (12)
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLlamaSpit.java (2)
20-21: Well-structured Mixin class!The class follows Mixin best practices with proper annotation and abstract declaration.
22-30: Verify consistent implementation across projectile mixins.Since this Mixin modifies core projectile behavior, we should ensure consistency with other projectile-related mixins.
Let's check for similar patterns in other projectile mixins:
✅ Verification successful
Implementation verified as consistent with projectile mixin patterns
The implementation follows the standard pattern used across other projectile mixins in the codebase, particularly matching MixinThrowableProjectile and MixinShulkerBullet exactly. MixinAbstractArrow has justified variations due to arrow-specific mechanics but maintains the same core event-driven approach.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other projectile-related mixins and their event usage echo "Checking for other projectile mixins..." rg -l "ProjectileDeflection" --type java echo "Checking for PROJECTILE_HIT event usage..." rg -l "PROJECTILE_HIT" --type java echo "Checking for similar @WrapOperation patterns..." ast-grep --pattern 'class $_ { @WrapOperation(method = "tick") $_(ProjectileDeflection $_) { $$$ } }'Length of output: 2654
Script:
#!/bin/bash # Check implementation patterns in other projectile mixins echo "Checking MixinThrowableProjectile implementation..." rg -A 10 "@WrapOperation.*tick" skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinThrowableProjectile.java echo "Checking MixinAbstractArrow implementation..." rg -A 10 "@WrapOperation.*tick" skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractArrow.java echo "Checking MixinShulkerBullet implementation..." rg -A 10 "@WrapOperation.*tick" skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinShulkerBullet.javaLength of output: 4257
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinAbstractHurtingProjectile.java (1)
27-29: Simplify return value handlingThe current implementation calls the original operation but ignores its result, always returning the deflection from the changeable. Consider whether the original operation's result should be used when no custom deflection is set.
- if (resultChangeable.get() != EntityEvents.ProjectileHitResult.DEFAULT) return deflectionChangeable.get(); - original.call(instance, result); - return deflectionChangeable.get(); + ProjectileDeflection originalDeflection = original.call(instance, result); + return deflectionChangeable.get() != ProjectileDeflection.NONE ? deflectionChangeable.get() : originalDeflection;Let's verify if there are any other mixins or event handlers that might rely on the current behavior:
keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/KeybindingCategory.java (1)
15-17: Add null check for ModuleConstants.MOD_ID.The
getCategoryString()method assumesModuleConstants.MOD_IDis non-null. Consider adding a null check.keybinding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java (1)
15-127: Add missing @OverRide annotationsThe class extends
KeyMappingbut lacks@Overrideannotations on inherited methods.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
87-89: Use correct constructor forResourceLocationThe method
ResourceLocation.fromNamespaceAndPathmay not exist. Instead, you should use the constructornew ResourceLocation(namespace, path)to create aResourceLocationinstance.Apply this diff to fix the issue:
- return ResourceLocation.fromNamespaceAndPath(id.getNamespace(), "icons/skills/" + id.getPath()); + return new ResourceLocation(id.getNamespace(), "icons/skills/" + id.getPath());skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinPlayer.java (1)
20-25: Ensure correct use of@ModifyVariableand local variablesThe use of
@ModifyVariablewith@Local(argsOnly = true)assumes thatdamageSourceis a local variable within the target method at the specified injection point. Verify thatdamageSourceis indeed available as a local variable at ordinal 6 in theactuallyHurtmethod ofPlayerclass. If not, this mixin may fail at runtime.Please confirm the availability of
damageSourceat the injection point. You can use the following script to decompile the target method and inspect the local variables:skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinMob.java (1)
19-25: Verify parameters in@WrapOperationmethodIn the mixin method
onSetTarget, ensure that the parameters match the expected signature when using@WrapOperation. TheOperationparameter should be last, and@WrapOperationmethods may require additional annotations or adjustments depending on the Mixin version.Please verify that the method signature correctly matches the expectations of the Mixin framework. Refer to the Mixin documentation for
@WrapOperationto ensure compatibility.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (2)
19-22: LGTM! Well-structured utility class.The class follows the utility class pattern with a private constructor to prevent instantiation.
42-44: Add runtime null validation.While the method uses @nonnull annotation, it lacks runtime validation for the entity parameter.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (2)
25-30: Correct Generic Type Parameters in Registrar Builder.The generic type parameter specification in the Registrar builder needs correction.
110-118: Potential Null Pointer Exception inPROJECTILE_HITEvent.The event handler needs null checking for the entity from hitResult.
...nding-common/src/main/java/io/github/manasmods/manascore/keybinding/api/ManasKeybinding.java
Show resolved
Hide resolved
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java
Show resolved
Hide resolved
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java
Show resolved
Hide resolved
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (18)
skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java (3)
1-4: Update the copyright year to 2024.The copyright notice shows 2025, which is incorrect as it's currently 2024.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
36-44: Add validation for negative damage values.The modified damage value should be validated to ensure it's not negative.
if (EntityEvents.LIVING_DAMAGE.invoker().damage(e.getEntity(), e.getSource(), changeableDamage).isFalse()) { e.setNewDamage(0); } else { - e.setNewDamage(changeableDamage.get()); + float newDamage = changeableDamage.get(); + e.setNewDamage(Math.max(0, newDamage)); }
20-21: Add class-level documentation.The class would benefit from documentation explaining its role in the skill system and how it bridges NeoForge events with the ManasCore skill system.
@EventBusSubscriber +/** + * Bridges NeoForge events with ManasCore's skill system. + * This class listens to NeoForge events and translates them into skill system events, + * allowing skills to modify entity targeting, damage, and projectile behavior. + */ public class NeoForgeCommonEventInvoker {skill-neoforge/src/main/resources/META-INF/neoforge.mods.toml (1)
3-3: Add issue tracker URL.An empty issue tracker URL makes it difficult for users to report issues. Consider adding the GitHub repository's issue tracker URL.
-issueTrackerURL = "" +issueTrackerURL = "https://github.com/ManasMods/ManasCore/issues"testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (2)
40-44: Consider adding @OverRide annotations to overridden methodsAdding
@Overrideannotations to methods that override superclass methods enhances code readability and ensures that any discrepancies in method signatures are caught at compile time.
126-138: Review damage handling for balance considerationsThe method
onDamageEntitymodifies damage amounts significantly, such as instant-killing Creepers and increasing damage to Players tenfold. While acceptable for testing, consider the impact on game balance if this code is used beyond testing scenarios.keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/KeybindingCategory.java (2)
11-14: Add input validation and documentation for the name parameter.Consider adding:
- Input validation to ensure the name parameter is not null or empty
- JavaDoc explaining the expected format and constraints for the name parameter
@RequiredArgsConstructor(staticName = "of") public class KeybindingCategory { + /** + * @param name The category name. Must not be null or empty. + * Should only contain lowercase letters, numbers, and underscores. + */ private final String name; + + public static KeybindingCategory of(String name) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Category name must not be null or empty"); + } + return new KeybindingCategory(name.trim()); + }
15-17: Consider externalizing the category string format.The hardcoded format string could be problematic for internationalization. Consider making it configurable.
+ private static final String CATEGORY_FORMAT = "%s.category.%s"; + public String getCategoryString() { - return String.format("%s.category.%s", ModuleConstants.MOD_ID, this.name); + return String.format(CATEGORY_FORMAT, ModuleConstants.MOD_ID, this.name); }keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/ManasKeybinding.java (2)
98-103: Add cleanup mechanism for stale entries.The current implementation might lead to memory leaks if a key release event is missed. Consider adding a cleanup mechanism or timeout for stale entries in the
PRESSED_KEYBINDINGSmap.this.action = () -> { if (!PRESSED_KEYBINDINGS.containsKey(this)) { + // Clean up stale entries older than 5 minutes + long currentTime = System.currentTimeMillis(); + PRESSED_KEYBINDINGS.entrySet().removeIf(entry -> + (currentTime - entry.getValue()) > 300000); PRESSED_KEYBINDINGS.put(this, System.currentTimeMillis()); action.onPress(); } };
107-108: Consider using System.nanoTime() for better precision.For accurate duration measurements,
System.nanoTime()provides better precision thancurrentTimeMillis(). This is especially important for gaming input handling.- long start = PRESSED_KEYBINDINGS.remove(this); - long end = System.currentTimeMillis(); + long start = PRESSED_KEYBINDINGS.remove(this); + long end = System.nanoTime(); + long durationNanos = end - start; + long durationMillis = durationNanos / 1_000_000;keybind-fabric/src/main/resources/fabric.mod.json (2)
10-12: Consider adding homepage URL.The homepage field is currently empty. Consider adding the GitHub repository URL for better discoverability.
"contact": { - "homepage": "" + "homepage": "https://github.com/ManasMods/ManasCore" },
28-28: Consider supporting older Java versions.Java 21 is a very recent version. Consider supporting Java 17 (LTS) to increase adoption, as many users might not have upgraded yet.
keybind-neoforge/src/main/resources/META-INF/neoforge.mods.toml (1)
3-3: Add issue tracker URL.The issue tracker URL is empty. Consider adding the GitHub issues URL for better user support.
-issueTrackerURL = "" +issueTrackerURL = "https://github.com/ManasMods/ManasCore/issues"build.gradle (1)
270-278: Consider using a more robust approach for modifying configuration files.The current string-based modification of configuration files could be fragile. Consider:
- Using a proper TOML parser for neoforge.mods.toml
- Adding error handling for malformed files
- Validating the modified content before writing
Example approach using a TOML parser:
def toml = new TomlParser().parse(file('META-INF/neoforge.mods.toml')) if (project.name.startsWith("skill")) { toml.accessWidener = "manascore_skill-neoforge.accesswidener" } file.text = toml.write()keybind-neoforge/src/main/java/io/github/manasmods/manascore/keybind/neoforge/ManasCoreKeybindNeoForge.java (2)
1-4: Update the copyright year to the current year.The copyright year is set to 2025, which is in the future. Consider updating it to 2024 or using a range (e.g., 2024-2025) if you want to future-proof it.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
12-17: Enhance robustness and documentation.Consider the following improvements:
- Add JavaDoc to explain the class purpose and why inheritance is prevented
- Add error handling around the init() call
- Add logging to indicate successful initialization
Here's a suggested implementation:
+/** + * NeoForge entry point for the ManasCore Keybinding module. + * This class is marked final as it serves as a singleton mod instance. + */ @Mod(ModuleConstants.MOD_ID) public final class ManasCoreKeybindNeoForge { + private static final Logger LOGGER = LogUtils.getLogger(); + public ManasCoreKeybindNeoForge() { + try { ManasCoreKeybind.init(); + LOGGER.info("ManasCore Keybinding module initialized successfully"); + } catch (Exception e) { + LOGGER.error("Failed to initialize ManasCore Keybinding module", e); + throw e; + } } }skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/mixin/MixinPlayer.java (1)
28-32: Extract common damage handling logicThis code duplicates the logic from MixinLivingEntity. Consider extracting the common damage handling logic into a shared utility class.
Create a new utility class:
public class DamageHandlingUtils { public static boolean handleDamage(LivingEntity entity, DamageSource source, float amount, CallbackInfo ci, Stack<DamageContainer> containers) { Changeable<Float> changeable = Changeable.of(amount); if (EntityEvents.LIVING_HURT.invoker().hurt(entity, source, changeable).isFalse()) { ci.cancel(); return false; } Float newDamage = changeable.get(); if (newDamage != null && !containers.isEmpty()) { containers.peek().setNewDamage(newDamage); } return true; } }Then use it in both mixins:
void onHurt(DamageSource source, float amount, CallbackInfo ci) { - Changeable<Float> changeable = Changeable.of(amount); - if (EntityEvents.LIVING_HURT.invoker().hurt((Player) (Object) this, source, changeable).isFalse()) ci.cancel(); - else damageContainers.peek().setNewDamage(changeable.get()); + DamageHandlingUtils.handleDamage((Player)(Object)this, source, amount, ci, damageContainers); }skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinPlayer.java (1)
20-40: Consider a unified cross-platform damage handling approachThe current implementation duplicates damage handling logic across four different mixin classes (NeoForge and Fabric for both Player and LivingEntity).
Consider these improvements:
- Create a common damage handling interface:
public interface DamageHandler { float modifyDamage(float amount, DamageSource source, LivingEntity entity); boolean shouldCancelDamage(float amount, DamageSource source, LivingEntity entity); float modifyTotalDamage(float amount, DamageSource source, LivingEntity entity); }
- Implement platform-specific handlers:
public class FabricDamageHandler implements DamageHandler { // Fabric-specific implementation } public class NeoForgeDamageHandler implements DamageHandler { // NeoForge-specific implementation }
- Use a factory pattern to get the appropriate handler:
public class DamageHandlerFactory { public static DamageHandler getHandler() { // Return platform-specific handler } }This approach would:
- Reduce code duplication
- Make platform differences explicit
- Simplify maintenance
- Make testing easier
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
keybind-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngkeybind-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngkeybind-neoforge/src/main/resources/icon.pngis excluded by!**/*.png
📒 Files selected for processing (32)
build.gradle(1 hunks)keybind-common/build.gradle(1 hunks)keybind-common/src/main/java/io/github/manasmods/manascore/keybind/ManasCoreKeybind.java(1 hunks)keybind-common/src/main/java/io/github/manasmods/manascore/keybind/ManasCoreKeybindClient.java(1 hunks)keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/KeybindingCategory.java(1 hunks)keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/KeybindingManager.java(1 hunks)keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/ManasKeybinding.java(1 hunks)keybind-common/src/main/resources/architectury.common.json(1 hunks)keybind-common/src/main/resources/manascore_keybind.accesswidener(1 hunks)keybind-common/src/main/resources/manascore_keybind.mixins.json(1 hunks)keybind-fabric/build.gradle(1 hunks)keybind-fabric/src/main/java/io/github/manasmods/manascore/keybind/fabric/ManasCoreKeybindFabric.java(1 hunks)keybind-fabric/src/main/resources/fabric.mod.json(1 hunks)keybind-neoforge/build.gradle(1 hunks)keybind-neoforge/gradle.properties(1 hunks)keybind-neoforge/src/main/java/io/github/manasmods/manascore/keybind/neoforge/ManasCoreKeybindNeoForge.java(1 hunks)keybind-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)settings.gradle(2 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinLivingEntity.java(1 hunks)skill-common/src/main/resources/manascore_skill.accesswidener(1 hunks)skill-common/src/main/resources/manascore_skill.mixins.json(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLivingEntity.java(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinPlayer.java(1 hunks)skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java(1 hunks)skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/mixin/MixinLivingEntity.java(1 hunks)skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/mixin/MixinPlayer.java(1 hunks)skill-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)skill-neoforge/src/main/resources/manascore_skill-neoforge.mixins.json(1 hunks)testing-common/build.gradle(2 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/client/KeybindingTest.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java(3 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java(1 hunks)
✅ Files skipped from review due to trivial changes (7)
- keybind-neoforge/gradle.properties
- keybind-common/src/main/resources/architectury.common.json
- keybind-common/src/main/resources/manascore_keybind.accesswidener
- keybind-fabric/build.gradle
- keybind-common/build.gradle
- keybind-common/src/main/resources/manascore_keybind.mixins.json
- skill-neoforge/src/main/resources/manascore_skill-neoforge.mixins.json
🚧 Files skipped from review as they are similar to previous changes (4)
- skill-common/src/main/resources/manascore_skill.mixins.json
- skill-common/src/main/resources/manascore_skill.accesswidener
- skill-common/src/main/java/io/github/manasmods/manascore/skill/mixin/MixinLivingEntity.java
- settings.gradle
🔇 Additional comments (19)
skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java (1)
20-24: Well-structured event invoker setup!Good practices observed:
- Appropriate use of @EventBusSubscriber annotation
- Private constructor prevents instantiation, which is correct for a utility class
skill-neoforge/src/main/resources/META-INF/neoforge.mods.toml (4)
14-14: Verify logo file existence.Ensure that
icon.pngexists in the correct resources directory.✅ Verification successful
Logo file verification successful
The referenced
icon.pngexists at the correct location in the resources directory.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the icon file exists in the resources directory fd -t f "icon.png" skill-neoforge/src/main/resources/Length of output: 96
19-19: Consider constraining NeoForge version range.Similar to the loader version, the NeoForge version range
[21,)is very broad. Consider setting an upper bound to ensure compatibility.
1-2: Consider constraining the loader version range.The version range
[2,)is very broad and might include incompatible future versions. Consider setting an upper bound to ensure compatibility.✅ Verification successful
Consider using a more specific loader version range
The current range
[2,)is too broad. Based on the NeoForge version patterns, consider using[2,3)to ensure compatibility with the current major version while preventing potential issues with future major versions.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check the latest NeoForge versions to determine an appropriate upper bound curl -s https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml | grep -oP '<version>\K[^<]+'Length of output: 10141
37-42: Verify circular dependency.The
manascore_networkdependency might create a circular dependency since it's using the same version as this module. Please verify the dependency structure.✅ Verification successful
No circular dependency detected
The dependency structure appears to be hierarchical, with
manascore_networkserving as a core module that other feature modules depend on. Using the same version across modules is a common practice in monorepo setups where components are released together. No reverse dependencies were found that would create a circular relationship.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for potential circular dependencies in other mod configuration files fd -t f "mods.toml" | xargs rg "modId.*=.*manascore_skill"Length of output: 58
Script:
#!/bin/bash # Find the mod_id value and dependencies in both directions echo "=== Searching for mod_id definition ===" fd -t f "mods.toml" | xargs rg "mod_id.*=|modId.*=.*skill" echo -e "\n=== Searching for manascore dependencies ===" fd -t f "mods.toml" | xargs rg "modId.*=.*manascore" echo -e "\n=== Searching for version definition ===" fd -t f "gradle.properties" | xargs catLength of output: 1568
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (3)
43-43: Verify the usage ofResourceLocation.withDefaultNamespaceIn the constructor, you use
ResourceLocation.withDefaultNamespace("skill.speed"). Please verify that this method correctly creates aResourceLocationwith the intended namespace. If not, consider usingnew ResourceLocation("skill.speed")for clarity.
118-124: Ensure damage cancellation works as expectedIn the
onBeingDamagedmethod, returningfalsewhen the source is cactus indicates that the damage should be canceled. Confirm that this behavior appropriately prevents damage from cacti in the game logic.
150-157: Check healing logic in damage taken eventIn the
onTakenDamagemethod, when the owner is sneaking, they heal the amount of damage taken. Ensure this logic aligns with intended gameplay mechanics and doesn't introduce unintended invincibility.keybind-fabric/src/main/java/io/github/manasmods/manascore/keybind/fabric/ManasCoreKeybindFabric.java (1)
10-14: Initialization code is correctly implementedThe
ManasCoreKeybindFabricclass correctly implements theModInitializerinterface and callsManasCoreKeybind.init()inonInitialize().keybind-common/src/main/java/io/github/manasmods/manascore/keybind/ManasCoreKeybind.java (1)
11-16: Proper client-side initialization checkThe
init()method appropriately checks the environment and initializes client-specific keybinds when running on the client side.keybind-common/src/main/java/io/github/manasmods/manascore/keybind/ManasCoreKeybindClient.java (1)
11-15: Clean implementation of client-side initialization!The class follows single responsibility principle and correctly uses Architectury's event system for client-side initialization.
keybind-neoforge/build.gradle (1)
6-12: LGTM! Build configuration is properly set up.The access widener path is correctly configured and the jar remapping is properly set up.
testing-common/build.gradle (1)
11-14: LGTM! Module additions align with PR objectives.The new modules
:skill,:keybind, and:confighave been properly added to theincludedProjectslist, maintaining consistency with the existing structure.keybind-fabric/src/main/resources/fabric.mod.json (1)
16-21: Verify if client entrypoints are needed for keybinding functionality.The keybinding module typically requires client-side initialization, but the client entrypoints array is empty.
keybind-neoforge/src/main/java/io/github/manasmods/manascore/keybind/neoforge/ManasCoreKeybindNeoForge.java (2)
6-10: LGTM!The package structure follows Java conventions, and imports are minimal and necessary.
14-16: Verify the initialization sequence.Based on the AI summary,
ManasCoreKeybind.init()should only execute client-side code when in the client environment. Let's verify this implementation:✅ Verification successful
Initialization sequence is properly implemented
The code correctly ensures client-side initialization by checking the environment before executing client-specific code. This follows the established pattern used throughout the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the initialization sequence and environment handling # Check ManasCoreKeybind.init() implementation ast-grep --pattern 'class ManasCoreKeybind { $$$ static void init() { $$$ } $$$ }' # Check for proper environment checks rg -A 5 'Env\.CLIENT'Length of output: 5669
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinLivingEntity.java (2)
33-38: Add null check and document the ordinal valueThe previous review comment about adding null checks and documenting the ordinal value is still valid.
21-26:⚠️ Potential issueAdd null check for damage modification
Similar to the NeoForge implementation, add null safety checks.
Apply these changes:
float modifyDamage(float amount, @Local(argsOnly = true) DamageSource damageSource) { Changeable<Float> changeable = Changeable.of(amount); - if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, damageSource, changeable).isFalse()) return 0.0F; - return changeable.get(); + if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, damageSource, changeable).isFalse()) { + return 0.0F; + } + Float newDamage = changeable.get(); + return newDamage != null ? newDamage : amount; }Likely invalid or redundant comment.
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinPlayer.java (1)
1-40: Enhance test coverage for damage handlingThe damage handling modifications are critical to gameplay, but only basic tests are present in
test_coderabbit_num.py.Consider adding these test scenarios:
- Null damage values
- Empty damage containers
- Event cancellation
- Platform-specific behavior differences
Run this script to analyze current test coverage:
...e/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java
Show resolved
Hide resolved
...e/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java
Show resolved
Hide resolved
keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/KeybindingManager.java
Show resolved
Hide resolved
keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/KeybindingManager.java
Show resolved
Hide resolved
keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/KeybindingManager.java
Show resolved
Hide resolved
keybind-common/src/main/java/io/github/manasmods/manascore/keybind/api/ManasKeybinding.java
Show resolved
Hide resolved
testing-common/src/main/java/io/github/manasmods/manascore/testing/client/KeybindingTest.java
Show resolved
Hide resolved
...ing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java
Show resolved
Hide resolved
...orge/src/main/java/io/github/manasmods/manascore/skill/neoforge/mixin/MixinLivingEntity.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (21)
attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinLivingEntity.java (1)
24-26: Injection point atHEADcan cause conflicts with other mixins.
Injecting at the head ofhurtis fine but can conflict if other mixins also inject at the head for the same method in a different mod. Consider using a more precise injection point (e.g., BEFORE a particular line or method call) if you foresee potential mod conflicts.attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinPlayer.java (2)
26-37: Consider extracting repeated critical-hit logic into a shared helper method.
The logic for determining multipliers, chance, and triggering critical events appears similar to the code in lines 42-55. Extracting this logic to a single private or utility method would reduce duplication and improve maintainability.Example refactor sketch:
+private float applyCritLogic(LivingEntity attacker, LivingEntity target, float baseDamage) { + Changeable<Float> multiplier = Changeable.of((float) attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER)); + Changeable<Double> chance = Changeable.of(attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) / 100); + if (AttributeEvents.CRITICAL_ATTACK_CHANCE_EVENT.invoker().applyCrit(attacker, target, 1, multiplier, chance).isFalse()) { + return baseDamage; + } + if (target.getRandom().nextFloat() > chance.get()) { + return baseDamage; + } + ManasCoreAttributeUtils.triggerCriticalAttackEffect(target, attacker); + return baseDamage * multiplier.get(); +}
42-55: Clarify method name or add documentation for damage modifications.
The method signaturegetCritChanceDamagealso adjusts the final damage to incorporate enchantment or events. Consider documenting the exact logic and side effects, or rename to reflect that it may perform advanced critical-hit checks that include enchantment.attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/ManasCoreAttributeRegisterImpl.java (4)
1-4: Update the copyright year.The copyright year is set to 2025, which is in the future. Consider updating it to the current year (2024).
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
25-27: Improve entityTypes initialization.Consider the following improvements:
- Add
@SuppressWarnings("unchecked")to acknowledge the unchecked cast- Make the field
finalfor immutability- Combine filter and map operations for better readability
+ @SuppressWarnings("unchecked") - private static final List<EntityType<? extends LivingEntity>> entityTypes = BuiltInRegistries.ENTITY_TYPE.stream() - .filter(DefaultAttributes::hasSupplier).map(entityType -> (EntityType<? extends LivingEntity>) entityType) - .collect(Collectors.toList()); + private static final List<EntityType<? extends LivingEntity>> ENTITY_TYPES = BuiltInRegistries.ENTITY_TYPE.stream() + .filter(entityType -> DefaultAttributes.hasSupplier(entityType)) + .map(entityType -> (EntityType<? extends LivingEntity>) entityType) + .collect(Collectors.toUnmodifiableList());
29-35: Enhance registration methods with validation and documentation.The registration methods could benefit from:
- Null parameter validation
- Duplicate entry prevention
- JavaDoc documentation
+ /** + * Registers an attribute to be applied to player entities. + * @param holder The attribute holder to register + * @throws NullPointerException if holder is null + */ public static void registerToPlayers(Holder<Attribute> holder) { + Objects.requireNonNull(holder, "Attribute holder cannot be null"); + if (!PLAYER_REGISTRY.contains(holder)) { PLAYER_REGISTRY.add(holder); + } } + /** + * Registers an attribute to be applied to all living entities. + * @param holder The attribute holder to register + * @throws NullPointerException if holder is null + */ public static void registerToGeneric(Holder<Attribute> holder) { + Objects.requireNonNull(holder, "Attribute holder cannot be null"); + if (!GENERIC_REGISTRY.contains(holder)) { GENERIC_REGISTRY.add(holder); + } }
22-61: Consider architectural improvements for robustness.The current implementation could benefit from the following architectural improvements:
- Add logging for debugging and monitoring
- Implement registration verification
- Add error handling for registration failures
- Consider adding a registration status query mechanism
Would you like me to provide a detailed implementation for these improvements?
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/ManasCoreAttribute.java (1)
3-7: Add class-level documentation.The initialization logic is clean and follows good separation of concerns. Consider adding Javadoc to describe the class's purpose and initialization sequence.
Add class documentation:
+/** + * Common entry point for ManasCore attribute system initialization. + * This class delegates the initialization to platform-specific implementations + * through the ManasCoreAttributeRegister. + */ public class ManasCoreAttribute {attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/ManasCoreAttributeNeoForge.java (1)
7-12: Add error handling around initialization.While the implementation follows NeoForge conventions, consider adding error handling around the initialization call to gracefully handle potential failures.
@Mod(ModuleConstants.MOD_ID) public final class ManasCoreAttributeNeoForge { public ManasCoreAttributeNeoForge() { + try { ManasCoreAttribute.init(); + } catch (Exception e) { + LOGGER.error("Failed to initialize ManasCore attributes", e); + throw new RuntimeException("Failed to initialize ManasCore attributes", e); + } } + + private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(ManasCoreAttributeNeoForge.class); }attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/ManasCoreAttributeFabric.java (2)
1-4: Fix copyright year.The copyright year is set to 2025, which appears to be incorrect as it's in the future.
/* - * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods * GNU General Public License 3 */
11-16: Improve consistency with NeoForge implementation.Consider the following improvements for consistency:
- Mark class as final (like NeoForge implementation)
- Add error handling around initialization
- Add logging support
-public class ManasCoreAttributeFabric implements ModInitializer { +public final class ManasCoreAttributeFabric implements ModInitializer { + private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(ManasCoreAttributeFabric.class); + @Override public void onInitialize() { + try { ManasCoreAttribute.init(); + } catch (Exception e) { + LOGGER.error("Failed to initialize ManasCore attributes", e); + throw new RuntimeException("Failed to initialize ManasCore attributes", e); + } } }attribute-common/src/main/java/io/github/manasmods/manascore/attribute/ManasCoreAttributeRegister.java (1)
12-27: Add class-level documentation to explain the platform-specific implementation pattern.The class serves as a platform-agnostic registration point for attributes, but this isn't immediately clear to other developers. Consider adding class-level JavaDoc to explain:
- The purpose of the register
- How platform-specific implementations should be provided
- Usage examples for each method
+/** + * Platform-agnostic attribute registration handler. + * Platform-specific implementations must be provided for each method. + * <p> + * Usage: + * - Use {@link #registerToPlayers} to register attributes that apply to players + * - Use {@link #registerToGeneric} to register attributes that apply to any entity + * - Call {@link #init} during mod initialization to set up the attribute system + */ public class ManasCoreAttributeRegister {attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/Changeable.java (2)
11-48: Enhance thread safety and null handling.The current implementation has several areas for improvement:
- Consider using
volatileorAtomicReferenceinstead of method-level synchronization- Add immutable access to the original value
- Strengthen null handling with Optional
+/** + * Thread-safe wrapper for mutable values that tracks changes from an original value. + * @param <T> The type of the wrapped value + */ public class Changeable<T> { @Nullable private final T original; - private T value; + private volatile T value; protected Changeable(@Nullable T value) { this.original = value; this.value = value; } public static <T> Changeable<T> of(@Nullable T value) { return new Changeable<>(value); } - @Synchronized @Nullable public T get() { return value; } - @Synchronized public void set(@Nullable T value) { this.value = value; } + /** + * @return The original value this instance was created with + */ + @Nullable + public T getOriginal() { + return original; + } + + /** + * @return Optional wrapper of the current value + */ + public Optional<T> getOptional() { + return Optional.ofNullable(value); + } public boolean isPresent() { return value != null; } public boolean isEmpty() { return value == null; } public boolean hasChanged() { - if (original == null) return value != null; - return !original.equals(value); + return !Objects.equals(original, value); } }
25-34: Consider adding atomic operations for thread safety.The current synchronized methods could be replaced with atomic operations for better performance in concurrent scenarios.
- @Synchronized - @Nullable - public T get() { - return value; - } - - @Synchronized - public void set(@Nullable T value) { - this.value = value; - } + private final AtomicReference<T> valueRef; + + protected Changeable(@Nullable T value) { + this.original = value; + this.valueRef = new AtomicReference<>(value); + } + + @Nullable + public T get() { + return valueRef.get(); + } + + public void set(@Nullable T value) { + valueRef.set(value); + } + + public boolean compareAndSet(@Nullable T expect, @Nullable T update) { + return valueRef.compareAndSet(expect, update); + }attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java (2)
27-29: Consider adding validation for critical attack chance.While the range is set correctly (0-100), consider adding runtime validation in case the attribute is modified during gameplay to ensure it stays within bounds.
public static final Holder<Attribute> CRITICAL_ATTACK_CHANCE = registerGenericAttribute(ModuleConstants.MOD_ID, "critical_attack_chance", "manascore.attribute.critical_attack_chance", - 0, 0, 100, true, Attribute.Sentiment.POSITIVE); + 0, 0, 100, true, Attribute.Sentiment.POSITIVE) { + @Override + public double sanitizeValue(double value) { + return Mth.clamp(value, 0.0D, 100.0D); + } +};
31-37: Consider consolidating duplicate registration methods.The
registerPlayerAttributeandregisterGenericAttributemethods are nearly identical except for the final registration call. Consider refactoring to reduce code duplication.+private static Holder<Attribute> registerAttribute(String modID, String id, String name, double amount, + double min, double max, boolean syncable, + Attribute.Sentiment sentiment, + Consumer<Holder<Attribute>> registrationHandler) { + Holder<Attribute> attribute = Registry.registerForHolder(BuiltInRegistries.ATTRIBUTE, + getResourceKey(modID, id), + new RangedAttribute(name, amount, min, max) + .setSyncable(syncable) + .setSentiment(sentiment)); + registrationHandler.accept(attribute); + return attribute; +} public static Holder<Attribute> registerPlayerAttribute(String modID, String id, String name, double amount, double min, double max, boolean syncable, Attribute.Sentiment sentiment) { - Holder<Attribute> attribute = Registry.registerForHolder(BuiltInRegistries.ATTRIBUTE, - getResourceKey(modID, id), - new RangedAttribute(name, amount, min, max) - .setSyncable(syncable) - .setSentiment(sentiment)); - ManasCoreAttributeRegister.registerToPlayers(attribute); - return attribute; + return registerAttribute(modID, id, name, amount, min, max, syncable, sentiment, + ManasCoreAttributeRegister::registerToPlayers); } public static Holder<Attribute> registerGenericAttribute(String modID, String id, String name, double amount, double min, double max, boolean syncable, Attribute.Sentiment sentiment) { - Holder<Attribute> attribute = Registry.registerForHolder(BuiltInRegistries.ATTRIBUTE, - getResourceKey(modID, id), - new RangedAttribute(name, amount, min, max) - .setSyncable(syncable) - .setSentiment(sentiment)); - ManasCoreAttributeRegister.registerToGeneric(attribute); - return attribute; + return registerAttribute(modID, id, name, amount, min, max, syncable, sentiment, + ManasCoreAttributeRegister::registerToGeneric); }Also applies to: 39-45
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributeUtils.java (1)
52-60: Consider caching trigonometric calculations.The
getLookTowardVecmethod performs multiple trigonometric calculations that could be optimized by caching intermediate results.public static Vec3 getLookTowardVec(Player player, double distance) { float f = player.getXRot(); float g = player.getYRot(); - float h = Mth.cos(-g * 0.017453292F - 3.1415927F); - float i = Mth.sin(-g * 0.017453292F - 3.1415927F); - float j = -Mth.cos(-f * 0.017453292F); - float k = Mth.sin(-f * 0.017453292F); + float yawRad = -g * 0.017453292F - 3.1415927F; + float pitchRad = -f * 0.017453292F; + float h = Mth.cos(yawRad); + float i = Mth.sin(yawRad); + float j = -Mth.cos(pitchRad); + float k = Mth.sin(pitchRad); return new Vec3(i * j * distance, k * distance, h * j * distance); }attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java (1)
25-38: Consider extracting critical hit logic to a separate method.The
applyEntityCritmethod contains complex critical hit logic that could be extracted for better maintainability and reusability.+private static boolean shouldApplyCriticalHit(LivingEntity attacker, LivingEntity target, + Changeable<Float> multiplier, + Changeable<Double> chance) { + if (AttributeEvents.CRITICAL_ATTACK_CHANCE_EVENT.invoker() + .applyCrit(attacker, target, 1, multiplier, chance) + .isFalse()) { + return false; + } + return target.getRandom().nextFloat() <= chance.get(); +} @SubscribeEvent(priority = EventPriority.HIGH) public static void applyEntityCrit(final LivingIncomingDamageEvent e) { if (!(e.getSource().getDirectEntity() instanceof LivingEntity attacker)) return; if (attacker instanceof Player) return; LivingEntity target = e.getEntity(); Changeable<Float> multiplier = Changeable.of((float) attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER)); Changeable<Double> chance = Changeable.of(attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) / 100); - if (AttributeEvents.CRITICAL_ATTACK_CHANCE_EVENT.invoker().applyCrit(attacker, target, 1, multiplier, chance).isFalse()) return; - - if (target.getRandom().nextFloat() > chance.get()) return; + if (!shouldApplyCriticalHit(attacker, target, multiplier, chance)) return; ManasCoreAttributeUtils.triggerCriticalAttackEffect(target, attacker); e.setAmount(e.getAmount() * multiplier.get()); }attribute-fabric/src/main/resources/fabric.mod.json (1)
11-12: Add homepage URL for better discoverability.The homepage field is empty. Consider adding a URL to the project's GitHub repository or documentation.
testing-common/src/main/resources/assets/manascore/lang/en_us.json (1)
8-11: Consider more descriptive potion names.The potion names are generic ("Test Potion", etc.). Consider using more descriptive names that reflect their effects.
- "item.minecraft.lingering_potion.effect.lmao_potion": "Test Lingering Potion", - "item.minecraft.splash_potion.effect.lmao_potion": "Test Splash Potion", - "item.minecraft.potion.effect.lmao_potion": "Test Potion", - "item.minecraft.tipped_arrow.effect.lmao_potion": "Test Tipped Arrow", + "item.minecraft.lingering_potion.effect.lmao_potion": "Test Explosive Lingering Potion", + "item.minecraft.splash_potion.effect.lmao_potion": "Test Explosive Splash Potion", + "item.minecraft.potion.effect.lmao_potion": "Test Explosive Potion", + "item.minecraft.tipped_arrow.effect.lmao_potion": "Test Explosive Arrow",attribute-neoforge/src/main/resources/META-INF/neoforge.mods.toml (1)
16-35: Consider using consistent version constraint format.The version constraints use different formats:
- NeoForge: Hardcoded
"[21,)"- Minecraft:
"[${minecraft_version},)"- Architectury:
"[${architectury_version},)"Consider using placeholders for NeoForge version as well to maintain consistency and ease version updates.
- versionRange = "[21,)" + versionRange = "[${neoforge_version},)"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
attribute-fabric/src/main/resources/assets/manascore/icon.pngis excluded by!**/*.pngattribute-fabric/src/main/resources/assets/manascore/logo.pngis excluded by!**/*.pngattribute-neoforge/src/main/resources/icon.pngis excluded by!**/*.png
📒 Files selected for processing (30)
attribute-common/build.gradle(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/ManasCoreAttribute.java(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/ManasCoreAttributeRegister.java(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/AttributeEvents.java(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/Changeable.java(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributeUtils.java(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java(1 hunks)attribute-common/src/main/resources/architectury.common.json(1 hunks)attribute-common/src/main/resources/assets/manascore/lang/en_us.json(1 hunks)attribute-common/src/main/resources/manascore_attribute.accesswidener(1 hunks)attribute-common/src/main/resources/manascore_attribute.mixins.json(1 hunks)attribute-fabric/build.gradle(1 hunks)attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/ManasCoreAttributeFabric.java(1 hunks)attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/ManasCoreAttributeRegisterImpl.java(1 hunks)attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinLivingEntity.java(1 hunks)attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinPlayer.java(1 hunks)attribute-fabric/src/main/resources/fabric.mod.json(1 hunks)attribute-fabric/src/main/resources/manascore_attribute-fabric.mixins.json(1 hunks)attribute-neoforge/build.gradle(1 hunks)attribute-neoforge/gradle.properties(1 hunks)attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/ManasCoreAttributeNeoForge.java(1 hunks)attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/ManasCoreAttributeRegisterImpl.java(1 hunks)attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java(1 hunks)attribute-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java(1 hunks)settings.gradle(2 hunks)testing-common/build.gradle(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestAttributeRegistry.java(1 hunks)testing-common/src/main/resources/assets/manascore/lang/en_us.json(1 hunks)
✅ Files skipped from review due to trivial changes (7)
- attribute-neoforge/gradle.properties
- attribute-common/build.gradle
- attribute-fabric/build.gradle
- attribute-common/src/main/resources/assets/manascore/lang/en_us.json
- attribute-fabric/src/main/resources/manascore_attribute-fabric.mixins.json
- attribute-common/src/main/resources/architectury.common.json
- attribute-common/src/main/resources/manascore_attribute.mixins.json
🚧 Files skipped from review as they are similar to previous changes (3)
- settings.gradle
- testing-common/build.gradle
- config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java
🔇 Additional comments (18)
attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinLivingEntity.java (3)
1-4: License header looks appropriate.
No issues found with the licensing information.
8-9: Confirm mixin-extras version compatibility.
Make sure your project's build.gradle or equivalent includes the correct version of Mixin Extras, as this library is required for@Localinjection. Otherwise, this code may fail at runtime.
27-38: Enhance coverage of non-playerLivingEntityattacks.
- The code currently excludes players (
Playerinstance check), which seems intentional. However, note that indirect player attacks (e.g., arrows) are ignored. If you want to handle those, consider an explicit check for projectiles.- Ensure attribute values are valid (non-null), thus preventing edge cases where the attributes might not be present.
- Consider using the attacker’s random for the critical chance rather than the target’s random, to more consistently reflect the attacker’s capabilities.
attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinPlayer.java (3)
1-4: Add or confirm inclusion of complete license texts.
While the header references the GNU General Public License v3, ensure that the project fully includes and distributes the relevant GPL license texts in accordance with compliance guidelines.
25-25: Verify local injection fornewAmount.
Injecting atHEADand referencing a local float might fail if the expected local variable doesn't exist or is re-ordered in the bytecode. Confirm that the method's local variable structure matches your injection site by verifying any relevant local variable mapping or specifying the correctordinalif needed.
57-60: Handle unexpected or absent attribute values.
When overriding the vanilla 1.5F critical multiplier, ensure that any missing or zero attribute value is handled gracefully to avoid inadvertently causing zero or negative multipliers. A sensible default or a safety clamp might be beneficial.attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/ManasCoreAttributeRegisterImpl.java (1)
37-60: 🛠️ Refactor suggestionImprove initialization robustness and documentation.
Several improvements could enhance the initialization logic:
- The null check suggests potential nulls in ENTITY_TYPES, which should be prevented during list creation
- Registry clearing could cause issues if init() is called multiple times
- Comments could be more descriptive
Consider this refactored implementation:
public static void init() { + // Prevent multiple initialization + if (!GENERIC_REGISTRY.isEmpty() || !PLAYER_REGISTRY.isEmpty()) { + LOGGER.warn("Attribute registration has already been initialized"); + return; + } + LifecycleEvent.SETUP.register(() -> { - entityTypes.forEach(entityType -> { - if (entityType == null) return; + ENTITY_TYPES.forEach(entityType -> { AttributeSupplier.Builder builder = new AttributeSupplier.Builder(); - // Apply existing attributes + // Copy vanilla attributes to preserve default behavior if (DefaultAttributes.hasSupplier(entityType)) { DefaultAttributes.getSupplier(entityType).instances.forEach((attribute, attributeInstance) -> { builder.add(attribute, attributeInstance.getBaseValue()); }); } + // Apply player-specific attributes if this is a player entity if (entityType.equals(EntityType.PLAYER)) PLAYER_REGISTRY.forEach(builder::add); - // Apply global custom attributes + // Apply global attributes to all entity types GENERIC_REGISTRY.forEach(builder::add); - // Register the attributes + // Register the combined attribute set with Fabric FabricDefaultAttributeRegistry.register(entityType, builder); }); - // Clear the registry + // Clear registries to prevent duplicate registrations and free memory PLAYER_REGISTRY.clear(); GENERIC_REGISTRY.clear(); }); }Let's verify that init() is only called once:
attribute-common/src/main/resources/manascore_attribute.accesswidener (1)
1-2: LGTM! Verify access widener configuration.The access widener configuration is correctly structured and follows Minecraft modding best practices. The field access modification is appropriate for attribute registration purposes.
Run this script to verify the access widener configuration is properly recognized:
✅ Verification successful
Access widener configuration verified and correctly integrated
The access widener is properly configured in the build system and follows Fabric modding best practices. The configuration is clean, minimal, and correctly referenced in the build files.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify access widener configuration is properly referenced in build files # Check if access widener path is configured in build.gradle files rg -l "accessWidenerPath.*manascore_attribute.accesswidener" --type gradleLength of output: 106
Script:
#!/bin/bash # Check the actual access widener configuration in build.gradle cat attribute-common/build.gradleLength of output: 202
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java (1)
23-25: Review the default value and maximum limit for critical damage multiplier.The critical damage multiplier has a maximum value of 1024x which seems excessive and could lead to balance issues. Consider reducing this to a more reasonable value (e.g., 5x or 10x) to prevent potential exploits.
attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/ManasCoreAttributeRegisterImpl.java (1)
19-20: LGTM! Thread-safe implementation using CopyOnWriteArrayList.Good choice using CopyOnWriteArrayList for thread-safe concurrent modifications during registration.
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (3)
193-195: LGTM: Block entity creation is properly implemented.The block entity creation is correctly implemented using the registered block entity type.
161-176: Consider adding safety checks for explosion radius.The explosion radius (
g) is set to a random value between 10.0F and 12.0F, which is quite large and could cause significant damage. Consider adding safety checks or configuration options.
62-67: Review attribute modifier values for balance.The attribute modifiers for critical damage and chance seem extremely high:
- Critical damage multiplier: 10x (1000% increase)
- Critical attack chance: 50% (50% increase)
These values might be too powerful for gameplay balance, even in a testing environment.
attribute-neoforge/build.gradle (1)
6-8: LGTM: Access widener configuration is correct.The access widener path is properly inherited from the common module.
attribute-fabric/src/main/resources/fabric.mod.json (1)
27-31: Verify Java version compatibility.The mod requires Java 21, which is a very recent version. This might limit compatibility with some mod loaders and other mods.
attribute-neoforge/src/main/resources/META-INF/neoforge.mods.toml (3)
1-4: Add issue tracker URL and verify license placeholder.
- The empty issue tracker URL should be populated to help users report issues.
- Ensure that the
${license}placeholder is correctly replaced during the build process.Run this script to verify the license placeholder replacement:
✅ Verification successful
Add issue tracker URL - license placeholder is correctly configured
The
${license}placeholder is properly configured inbuild.gradleand will be replaced with "GPLv3" during the build process. However, the issue tracker URL is still empty and should be set to "https://github.com/ManasMods/ManasCore" to match the project's repository.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify license placeholder replacement in build process # Check build configuration files for license variable definition echo "Checking for license variable in build files..." fd -e gradle -e properties | xargs grep -l "license" # Check if there are any remaining unresolved placeholders in built artifacts echo "Checking for unresolved placeholders in built artifacts..." fd -e toml | xargs grep -l "\${license}"Length of output: 916
Script:
#!/bin/bash # Check build.gradle for license configuration echo "=== build.gradle license configuration ===" rg "license" build.gradle -A 3 -B 3 # Check if this is a common pattern in other similar projects echo -e "\n=== Checking root project structure ===" ls -laLength of output: 3582
37-38: Verify mixin configuration file existence.Ensure that the mixin configuration file exists and is properly named according to the mod ID.
Run this script to verify the mixin configuration:
✅ Verification successful
Mixin configuration is correctly set up ✓
The mixin configuration references
manascore_attribute.mixins.jsonwhich exists in the common module atattribute-common/src/main/resources/manascore_attribute.mixins.json. This is a valid setup as NeoForge can use the mixin configuration from the common module.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify mixin configuration file # First, find the mod_id value from build files echo "Checking for mod_id in build files..." fd -e gradle -e properties | xargs grep "mod_id" # Then, check for the corresponding mixins.json file echo "Checking for mixins.json file..." fd "mixins.json" -t fLength of output: 1864
Script:
#!/bin/bash # Get archives_name from gradle.properties and construct expected mod_id echo "Getting archives_name from gradle.properties..." grep "archives_name" gradle.properties # Check for any mixin config file specifically for attribute-neoforge echo -e "\nChecking for attribute-neoforge specific mixin configs..." fd . attribute-neoforge/src/main/resources -t fLength of output: 461
6-14: Verify placeholders and logo file existence.
- Ensure that all placeholders (
${mod_id},${version},${mod_name}) are properly configured in the build system.- Verify that
icon.pngexists in the correct resources directory.Run this script to verify the configuration:
✅ Verification successful
✓ Placeholders and icon file configuration verified
All placeholders are properly configured in the build system and the icon file exists at the correct location. The build process will correctly populate:
${version}from version 4.0.0.0${mod_id}as "manascore_attribute"${mod_name}as "ManasCore - Attribute"🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify mod metadata configuration # Check build configuration files for variable definitions echo "Checking for mod variables in build files..." fd -e gradle -e properties | xargs grep -l "mod_id\|version\|mod_name" # Verify icon.png existence echo "Checking for icon.png..." fd "icon.png" -t fLength of output: 1465
Script:
#!/bin/bash # Check variable definitions in main configuration files echo "=== Checking gradle.properties ===" cat gradle.properties echo -e "\n=== Checking root build.gradle ===" cat build.gradle echo -e "\n=== Checking attribute-neoforge/build.gradle ===" cat attribute-neoforge/build.gradleLength of output: 11202
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/AttributeEvents.java
Show resolved
Hide resolved
...mmon/src/main/java/io/github/manasmods/manascore/testing/registry/TestAttributeRegistry.java
Outdated
Show resolved
Hide resolved
...ommon/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributeUtils.java
Show resolved
Hide resolved
...in/java/io/github/manasmods/manascore/attribute/neoforge/ManasCoreAttributeRegisterImpl.java
Show resolved
Hide resolved
...c/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java
Show resolved
Hide resolved
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java
Show resolved
Hide resolved
testing-common/src/main/resources/assets/manascore/lang/en_us.json
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (8)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java (1)
32-40: Clamp delta and validate map entries during decode.Defensive decoding prevents disconnects/crashes from malicious packets and normalizes data early. This also addresses earlier feedback on delta validation.
public RequestSkillScrollPacket(FriendlyByteBuf buf) { - this(buf.readDouble(), validateList(buf.readMap(FriendlyByteBuf::readResourceLocation, FriendlyByteBuf::readInt))); + this( + validateDelta(buf.readDouble()), + validateList(buf.readMap(FriendlyByteBuf::readResourceLocation, FriendlyByteBuf::readInt)) + ); } private static Map<ResourceLocation, Integer> validateList(Map<ResourceLocation, Integer> map) { - int maxSize = 100; - if (map.size() > maxSize) throw new IllegalArgumentException("Skill map exceeds maximum size of " + maxSize); - return map; + if (map.size() > MAX_SKILLS_PER_PACKET) { + throw new IllegalArgumentException("Skill map exceeds maximum size of " + MAX_SKILLS_PER_PACKET); + } + // Drop negative modes defensively + map.entrySet().removeIf(e -> e.getValue() == null || e.getValue() < 0); + return map; } + +private static double validateDelta(double d) { + if (Double.isNaN(d) || Double.isInfinite(d)) return 0.0; + return Math.max(-MAX_ABS_SCROLL_DELTA, Math.min(MAX_ABS_SCROLL_DELTA, d)); +}skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (2)
304-306: Mark dirty after replacing cooldown list (state change).public void setCoolDownList(List<Integer> list) { - this.cooldownList = list; + this.cooldownList = new ArrayList<>(list); + markDirty(); }
115-122: ValidateskillLocationbefore registry lookup.
ResourceLocation.tryParsecan return null; current code could NPE or mislead error messages.public static ManasSkillInstance fromNBT(CompoundTag tag) throws NullPointerException { - ResourceLocation skillLocation = ResourceLocation.tryParse(tag.getString("skill")); - ManasSkill skill = SkillAPI.getSkillRegistry().get(skillLocation); + ResourceLocation skillLocation = ResourceLocation.tryParse(tag.getString("skill")); + if (skillLocation == null) { + throw new IllegalArgumentException("Invalid skill ID in NBT data."); + } + ManasSkill skill = SkillAPI.getSkillRegistry().get(skillLocation); if (skill == null) throw new IllegalArgumentException("Skill not found in registry: " + skillLocation); ManasSkillInstance instance = skill.createDefaultInstance(); instance.deserialize(tag); return instance; }testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (2)
37-43: Use a stable UUID for attribute modifier to prevent duplicates.public class TestSkill extends ManasSkill { + private static final java.util.UUID SPEED_MODIFIER_UUID = + java.util.UUID.fromString("60ab112f-9d82-4f45-9fc9-799d7d2658b8"); public TestSkill() { super(); ManasCoreTesting.LOG.info("Created skill!"); - this.addHeldAttributeModifier(Attributes.MOVEMENT_SPEED, "test_speed", -0.95, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL); + this.addHeldAttributeModifier(Attributes.MOVEMENT_SPEED, SPEED_MODIFIER_UUID, -0.95, AttributeModifier.Operation.MULTIPLY_TOTAL); }
141-146: Avoid applying extra damage directly; mutate the event amount instead.- if (target instanceof Creeper creeper && skillConfig.instaKillCreeper) { - creeper.hurt(owner.level().damageSources().dragonBreath(), 100F); - ManasCoreTesting.LOG.info("No creeper"); + if (target instanceof Creeper && skillConfig != null && skillConfig.instaKillCreeper) { + amount.set(100F); // lethal once in the normal pipeline + ManasCoreTesting.LOG.info("No creeper");skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (3)
104-109: Use the supported ResourceLocation constructor.fromNamespaceAndPath may not exist in your mappings; prefer the two-arg constructor.
- return ResourceLocation.fromNamespaceAndPath(id.getNamespace(), "icons/skills/" + id.getPath()); + return new ResourceLocation(id.getNamespace(), "icons/skills/" + id.getPath());
485-515: Refactor AttributeTemplate to UUID + name and remove unsupported ResourceLocation ctor usages.Aligns with AttributeModifier API; also replaces fromNamespaceAndPath with the standard constructor in convenience paths.
- public static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { - public AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { - this.id = id; - this.amount = amount; - this.operation = operation; - } - - public AttributeTemplate(String id, double amount, AttributeModifier.Operation operation) { - this(ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, id), amount, operation); - } - - public AttributeModifier create(double i) { - return new AttributeModifier(this.id, this.amount * i, this.operation); - } - - public AttributeModifier create(ResourceLocation location, double i) { - return new AttributeModifier(location, this.amount * i, this.operation); - } - - public ResourceLocation id() { - return this.id; - } - - public double amount() { - return this.amount; - } - - public AttributeModifier.Operation operation() { - return this.operation; - } - } + public static record AttributeTemplate(java.util.UUID id, String name, double amount, AttributeModifier.Operation operation) { + public AttributeTemplate(java.util.UUID id, String name, double amount, AttributeModifier.Operation operation) { + this.id = id; + this.name = name; + this.amount = amount; + this.operation = operation; + } + + public AttributeTemplate(ResourceLocation key, String name, double amount, AttributeModifier.Operation operation) { + this(java.util.UUID.nameUUIDFromBytes(key.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8)), name, amount, operation); + } + + public AttributeTemplate(String keyPath, String name, double amount, AttributeModifier.Operation operation) { + this(new ResourceLocation(ModuleConstants.MOD_ID, keyPath), name, amount, operation); + } + }
233-239: AttributeModifier id/name types are incorrect; switch to UUID + name (and adjust API).Minecraft AttributeModifier expects (UUID id, String name, double amount, Operation). Using ResourceLocation will not compile and breaks removeModifier.
- public void addHeldAttributeModifier(Holder<Attribute> holder, ResourceLocation resourceLocation, double amount, AttributeModifier.Operation operation) { - this.attributeModifiers.put(holder, new AttributeTemplate(resourceLocation, amount, operation)); - } - - public void addHeldAttributeModifier(Holder<Attribute> holder, String id, double amount, AttributeModifier.Operation operation) { - this.attributeModifiers.put(holder, new AttributeTemplate(id, amount, operation)); - } + public void addHeldAttributeModifier(Holder<Attribute> holder, java.util.UUID id, String name, double amount, AttributeModifier.Operation operation) { + this.attributeModifiers.put(holder, new AttributeTemplate(id, name, amount, operation)); + } + + // Convenience: derive a stable UUID from a namespaced key + public void addHeldAttributeModifier(Holder<Attribute> holder, ResourceLocation key, String name, double amount, AttributeModifier.Operation operation) { + java.util.UUID derived = java.util.UUID.nameUUIDFromBytes(key.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8)); + this.attributeModifiers.put(holder, new AttributeTemplate(derived, name, amount, operation)); + }
🧹 Nitpick comments (13)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java (3)
25-31: Promote limits to named constants for reuse (client/server consistency).Define shared MAX constants here so both decode and client code can enforce identical caps.
public record RequestSkillScrollPacket( double delta, Map<ResourceLocation, Integer> skillList ) implements CustomPacketPayload { - public static final CustomPacketPayload.Type<RequestSkillScrollPacket> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, "request_skill_scroll")); + // Caps to protect server from large/abusive inputs + public static final int MAX_SKILLS_PER_PACKET = 100; + public static final double MAX_ABS_SCROLL_DELTA = 20.0; + public static final CustomPacketPayload.Type<RequestSkillScrollPacket> TYPE = + new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, "request_skill_scroll")); public static final StreamCodec<FriendlyByteBuf, RequestSkillScrollPacket> STREAM_CODEC = CustomPacketPayload.codec(RequestSkillScrollPacket::encode, RequestSkillScrollPacket::new);
53-71: Reduce repeated storage.markDirty() calls.Mark once after processing all entries for minor perf/IO savings.
- Skills storage = SkillAPI.getSkillsFrom(player); - for (Map.Entry<ResourceLocation, Integer> entry : skillList.entrySet()) { + Skills storage = SkillAPI.getSkillsFrom(player); + boolean changed = false; + for (Map.Entry<ResourceLocation, Integer> entry : skillList.entrySet()) { storage.getSkill(entry.getKey()).ifPresent(skillInstance -> { @@ - skill.onScroll(player, deltaChangeable.get(), modeChangeable.get()); - storage.markDirty(); + skill.onScroll(player, deltaChangeable.get(), modeVal); + changed = true; }); } + if (changed) storage.markDirty();
47-71: Guard processing block with try/catch to avoid crashing the server thread.Third-party handlers can throw; isolate per-entry to keep the server stable. Add logging as appropriate for your logging facility.
- for (Map.Entry<ResourceLocation, Integer> entry : skillList.entrySet()) { - storage.getSkill(entry.getKey()).ifPresent(skillInstance -> { + for (Map.Entry<ResourceLocation, Integer> entry : skillList.entrySet()) { + try { + storage.getSkill(entry.getKey()).ifPresent(skillInstance -> { // ... - }); + }); + } catch (Exception e) { + // TODO: replace with mod logger + e.printStackTrace(); + } }skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java (1)
36-41: Clamp delta and align with server constants before sending.Normalize the scroll delta to match server-side validation.
- if (!packetSkills.isEmpty()) { - NetworkManager.sendToServer(new RequestSkillScrollPacket(amountY, packetSkills)); + if (!packetSkills.isEmpty()) { + double clamped = Math.max(-RequestSkillScrollPacket.MAX_ABS_SCROLL_DELTA, + Math.min(RequestSkillScrollPacket.MAX_ABS_SCROLL_DELTA, amountY)); + NetworkManager.sendToServer(new RequestSkillScrollPacket(clamped, packetSkills)); return EventResult.interruptFalse(); }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (2)
103-108: Normalize cooldown list size on deserialize.Keep list length consistent with
getModes()to avoid index issues elsewhere.this.cooldownList = Arrays.stream(tag.getIntArray("CooldownList")).boxed().collect(Collectors.toList()); + int expected = this.getModes(); + if (this.cooldownList.size() != expected) { + List<Integer> norm = new ArrayList<>(Math.max(expected, 0)); + for (int i = 0; i < expected; i++) { + norm.add(i < this.cooldownList.size() ? this.cooldownList.get(i) : 0); + } + this.cooldownList = norm; + }
375-381: Avoid double markDirty in getOrCreateTag.
setTagalready marks dirty; the extra call is redundant.public CompoundTag getOrCreateTag() { if (this.tag == null) { this.setTag(new CompoundTag()); - this.markDirty(); } return this.tag; }testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (4)
100-103: High-frequency logging in onTick can spam logs.Consider gating behind debug or sampling.
- if (living.isShiftKeyDown()) ManasCoreTesting.LOG.info("You're sneaky"); + if (living.isShiftKeyDown()) { + if (living.level().getGameTime() % 40 == 0) ManasCoreTesting.LOG.debug("You're sneaky"); + }
141-153: Defensive null-check for config.
ConfigRegistry.getConfigcan return null in tests; avoid NPE.- SkillConfig skillConfig = ConfigRegistry.getConfig(SkillConfig.class); + SkillConfig skillConfig = ConfigRegistry.getConfig(SkillConfig.class); + if (skillConfig == null) { + ManasCoreTesting.LOG.warn("SkillConfig not registered; skipping damage tweaks."); + return true; + }
165-172: Null-safe heal amount.Guard against unexpected null in changeable.
- if (owner.isShiftKeyDown()) { - owner.heal(amount.get()); + if (owner.isShiftKeyDown()) { + Float heal = amount.get(); + if (heal != null) owner.heal(heal);
133-139: Modern DamageTypes API not presentI don’t see any
DamageTypesimports or usages in the codebase; you’re on the olderdamageSources()API. Continue using the existingsource.equals(…)checks for now, and when you upgrade to a modern Minecraft version that includes the newDamageTypesregistry (e.g. 1.20+), migrate tosource.is(DamageTypes.CACTUS)to avoid brittle equality comparisons.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
78-80: Allow cooldown mutation in the event.If handlers are expected to modify cooldowns, pass a Changeable instead of a plain int.
- interface SkillUpdateCooldownEvent { - EventResult cooldown(ManasSkillInstance skillInstance, LivingEntity owner, int currentCooldown, int mode); - } + interface SkillUpdateCooldownEvent { + EventResult cooldown(ManasSkillInstance skillInstance, LivingEntity owner, Changeable<Integer> currentCooldown, int mode); + }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (2)
81-87: Make getName non-null at call sites or return a safe fallback.You mark getName() as @nullable but most usages assume non-null. Either document the null case clearly or return a fallback component to reduce null handling overhead in consumers.
295-315: Minor: wording consistency (“owning” vs “owing”).Javadoc repeatedly says “owing this Skill”; should be “owning this Skill”.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
gradle.properties(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- gradle.properties
🧰 Additional context used
🧬 Code graph analysis (6)
skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java (3)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-619)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (2)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (2)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java (3)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-619)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (6)
config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java (1)
ConfigRegistry(13-100)network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(20-66)testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java (1)
ManasCoreTesting(19-33)testing-common/src/main/java/io/github/manasmods/manascore/testing/configs/SkillConfig.java (1)
SkillConfig(7-20)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestTags.java (1)
TestTags(14-16)
🔇 Additional comments (4)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
150-153: hashCode/equals alignment looks good.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
16-30: Event layout looks solid for scroll and activation flows.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (2)
277-293: removeAttributeModifiers: use UUID and consider symmetry in client sync.
- removeModifier must use UUID.
- Consider sending an attributes update packet when adding as well, for parity.
- attributeInstance.removeModifier(entry.getValue().id()); + attributeInstance.removeModifier(entry.getValue().id());If addHeldAttributeModifiers does not always sync on your target platform/mappings, mirror the packet send there as well.
461-463: EntityEvents is in the same package—no import needed
The enumProjectileHitResultis nested inEntityEventswithinio.github.manasmods.manascore.skill.api, so the reference resolves without additional imports.Likely an incorrect or invalid review comment.
| public MutableComponent getChatDisplayName(boolean withDescription) { | ||
| Style style = Style.EMPTY.withColor(ChatFormatting.GRAY); | ||
| if (withDescription) { | ||
| MutableComponent hoverMessage = this.getName().append("\n"); | ||
| hoverMessage.append(this.getSkillDescription().withStyle(ChatFormatting.GRAY)); | ||
| hoverMessage.append("\n").append(Component.literal(SkillAPI.getSkillRegistry().getId(this).toString()).withStyle(ChatFormatting.DARK_GRAY)); | ||
| style = style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverMessage)); | ||
| } | ||
|
|
||
| MutableComponent component = Component.literal("[").append(this.getName()).append("]"); | ||
| return component.withStyle(style); | ||
| } |
There was a problem hiding this comment.
Fix potential NPEs in chat display name construction.
getName() and getRegistryName() can be null; current code appends/uses them without checks.
- public MutableComponent getChatDisplayName(boolean withDescription) {
+ public MutableComponent getChatDisplayName(boolean withDescription) {
Style style = Style.EMPTY.withColor(ChatFormatting.GRAY);
if (withDescription) {
- MutableComponent hoverMessage = this.getName().append("\n");
- hoverMessage.append(this.getSkillDescription().withStyle(ChatFormatting.GRAY));
- hoverMessage.append("\n").append(Component.literal(SkillAPI.getSkillRegistry().getId(this).toString()).withStyle(ChatFormatting.DARK_GRAY));
+ final ResourceLocation id = this.getRegistryName();
+ final MutableComponent name = Objects.requireNonNullElseGet(this.getName(),
+ () -> id != null ? Component.literal(id.toString()) : Component.literal("unregistered"));
+ MutableComponent hoverMessage = name.copy().append("\n");
+ hoverMessage.append(this.getSkillDescription().withStyle(ChatFormatting.GRAY));
+ if (id != null) {
+ hoverMessage.append("\n").append(Component.literal(id.toString()).withStyle(ChatFormatting.DARK_GRAY));
+ }
style = style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverMessage));
}
- MutableComponent component = Component.literal("[").append(this.getName()).append("]");
+ final MutableComponent baseName = Objects.requireNonNullElseGet(this.getName(),
+ () -> {
+ ResourceLocation id = this.getRegistryName();
+ return id != null ? Component.literal(id.toString()) : Component.literal("unregistered");
+ });
+ MutableComponent component = Component.literal("[").append(baseName).append("]");
return component.withStyle(style);
}Committable suggestion skipped: line range outside the PR's diff.
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| if (o == null || getClass() != o.getClass()) return false; | ||
| ManasSkill skill = (ManasSkill) o; | ||
| return Objects.equals(this.getRegistryName(), skill.getRegistryName()); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
equals() treats two unregistered skills as equal; also add hashCode().
If both registry names are null, Objects.equals(null, null) returns true for distinct instances. Fall back to identity when id is null, and implement hashCode().
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ManasSkill skill = (ManasSkill) o;
- return Objects.equals(this.getRegistryName(), skill.getRegistryName());
+ final ResourceLocation a = this.getRegistryName();
+ final ResourceLocation b = skill.getRegistryName();
+ if (a == null || b == null) return false;
+ return a.equals(b);
}
+
+ @Override
+ public int hashCode() {
+ final ResourceLocation id = this.getRegistryName();
+ return id != null ? id.hashCode() : System.identityHashCode(this);
+ }🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java
around lines 120 to 126, equals currently treats two unregistered skills (null
registryName) as equal and there is no hashCode implementation; change equals to
check the registryName values first and if both registryName are non-null
compare them, but if the registryName is null fall back to identity comparison
(return this == o), and add a matching hashCode() that returns
registryName.hashCode() when registryName is non-null and otherwise returns
System.identityHashCode(this).
| public void addHeldAttributeModifiers(ManasSkillInstance instance, LivingEntity entity, int mode) { | ||
| if (this.attributeModifiers.isEmpty()) return; | ||
|
|
||
| AttributeMap attributeMap = entity.getAttributes(); | ||
| for (Map.Entry<Holder<Attribute>, AttributeTemplate> entry : this.attributeModifiers.entrySet()) { | ||
| AttributeInstance attributeInstance = attributeMap.getInstance(entry.getKey()); | ||
|
|
||
| if (attributeInstance == null) continue; | ||
| attributeInstance.removeModifier(entry.getValue().id()); | ||
| attributeInstance.addOrUpdateTransientModifier(entry.getValue().create(instance.getAttributeModifierAmplifier(entity, entry.getKey(), entry.getValue(), mode))); | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix bad call target and apply/remove modifiers with correct types.
- Should call this.getAttributeModifierAmplifier(...)
- removeModifier expects UUID
- Construct AttributeModifier with UUID + name
- AttributeMap attributeMap = entity.getAttributes();
+ AttributeMap attributeMap = entity.getAttributes();
for (Map.Entry<Holder<Attribute>, AttributeTemplate> entry : this.attributeModifiers.entrySet()) {
AttributeInstance attributeInstance = attributeMap.getInstance(entry.getKey());
if (attributeInstance == null) continue;
- attributeInstance.removeModifier(entry.getValue().id());
- attributeInstance.addOrUpdateTransientModifier(entry.getValue().create(instance.getAttributeModifierAmplifier(entity, entry.getKey(), entry.getValue(), mode)));
+ AttributeTemplate tpl = entry.getValue();
+ attributeInstance.removeModifier(tpl.id());
+ double amp = this.getAttributeModifierAmplifier(instance, entity, entry.getKey(), tpl, mode);
+ attributeInstance.addOrUpdateTransientModifier(new AttributeModifier(tpl.id(), tpl.name(), tpl.amount() * amp, tpl.operation()));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public void addHeldAttributeModifiers(ManasSkillInstance instance, LivingEntity entity, int mode) { | |
| if (this.attributeModifiers.isEmpty()) return; | |
| AttributeMap attributeMap = entity.getAttributes(); | |
| for (Map.Entry<Holder<Attribute>, AttributeTemplate> entry : this.attributeModifiers.entrySet()) { | |
| AttributeInstance attributeInstance = attributeMap.getInstance(entry.getKey()); | |
| if (attributeInstance == null) continue; | |
| attributeInstance.removeModifier(entry.getValue().id()); | |
| attributeInstance.addOrUpdateTransientModifier(entry.getValue().create(instance.getAttributeModifierAmplifier(entity, entry.getKey(), entry.getValue(), mode))); | |
| } | |
| } | |
| public void addHeldAttributeModifiers(ManasSkillInstance instance, LivingEntity entity, int mode) { | |
| if (this.attributeModifiers.isEmpty()) return; | |
| AttributeMap attributeMap = entity.getAttributes(); | |
| for (Map.Entry<Holder<Attribute>, AttributeTemplate> entry : this.attributeModifiers.entrySet()) { | |
| AttributeInstance attributeInstance = attributeMap.getInstance(entry.getKey()); | |
| if (attributeInstance == null) continue; | |
| AttributeTemplate tpl = entry.getValue(); | |
| attributeInstance.removeModifier(tpl.id()); | |
| double amp = this.getAttributeModifierAmplifier(instance, entity, entry.getKey(), tpl, mode); | |
| attributeInstance.addOrUpdateTransientModifier(new AttributeModifier(tpl.id(), tpl.name(), tpl.amount() * amp, tpl.operation())); | |
| } | |
| } |
| ManasSkillInstance skill = skillChangeable.get(); | ||
| if (skill == null || deltaChangeable.isEmpty()) return; | ||
| if (!skill.canScroll(player, modeChangeable.get())) return; | ||
| if (!skill.canInteractSkill(player)) return; | ||
|
|
There was a problem hiding this comment.
Null-safe mode handling to avoid NPE on auto-unboxing.
modeChangeable.get() can be null after event mutation; auto-unboxing to int would NPE.
- ManasSkillInstance skill = skillChangeable.get();
- if (skill == null || deltaChangeable.isEmpty()) return;
- if (!skill.canScroll(player, modeChangeable.get())) return;
+ ManasSkillInstance skill = skillChangeable.get();
+ if (skill == null || deltaChangeable.isEmpty() || modeChangeable.isEmpty()) return;
+ int modeVal = Math.max(0, modeChangeable.get());
+ if (!skill.canScroll(player, modeVal)) return;
- if (!skill.canInteractSkill(player)) return;
+ if (!skill.canInteractSkill(player)) return;
- skill.onScroll(player, deltaChangeable.get(), modeChangeable.get());
+ skill.onScroll(player, deltaChangeable.get(), modeVal);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ManasSkillInstance skill = skillChangeable.get(); | |
| if (skill == null || deltaChangeable.isEmpty()) return; | |
| if (!skill.canScroll(player, modeChangeable.get())) return; | |
| if (!skill.canInteractSkill(player)) return; | |
| ManasSkillInstance skill = skillChangeable.get(); | |
| if (skill == null || deltaChangeable.isEmpty() || modeChangeable.isEmpty()) return; | |
| int modeVal = Math.max(0, modeChangeable.get()); | |
| if (!skill.canScroll(player, modeVal)) return; | |
| if (!skill.canInteractSkill(player)) return; | |
| skill.onScroll(player, deltaChangeable.get(), modeVal); |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java
around lines 62 to 66, the code calls modeChangeable.get() and relies on
auto-unboxing which can NPE if the event mutated it to null; update the logic to
read Integer mode = modeChangeable.get() once, check mode for null (and return
or handle a sensible default) before using it in any canScroll call or any
int-context, and replace modeChangeable.get() uses with the null-checked local
variable so no auto-unboxing occurs on a null value.
| Map<ResourceLocation, Integer> packetSkills = new HashMap<>(); | ||
| for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) { | ||
| Changeable<Integer> mode = Changeable.of(0); | ||
| if (SkillEvents.SKILL_SCROLL_CLIENT.invoker().scroll(skillInstance, player, mode, amountY).isFalse()) continue; | ||
| if (!skillInstance.canScroll(player, mode.get())) continue; | ||
| packetSkills.put(skillInstance.getSkillId(), mode.get()); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Null-safe mode and early cap during collection.
Avoid NPE from mode.get() and cap the number of skills collected to the server’s limit.
- Map<ResourceLocation, Integer> packetSkills = new HashMap<>();
- for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) {
+ Map<ResourceLocation, Integer> packetSkills = new HashMap<>();
+ int collected = 0;
+ for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) {
Changeable<Integer> mode = Changeable.of(0);
if (SkillEvents.SKILL_SCROLL_CLIENT.invoker().scroll(skillInstance, player, mode, amountY).isFalse()) continue;
- if (!skillInstance.canScroll(player, mode.get())) continue;
- packetSkills.put(skillInstance.getSkillId(), mode.get());
+ if (mode.isEmpty()) continue;
+ int m = Math.max(0, mode.get());
+ if (!skillInstance.canScroll(player, m)) continue;
+ packetSkills.put(skillInstance.getSkillId(), m);
+ if (++collected >= RequestSkillScrollPacket.MAX_SKILLS_PER_PACKET) break;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Map<ResourceLocation, Integer> packetSkills = new HashMap<>(); | |
| for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) { | |
| Changeable<Integer> mode = Changeable.of(0); | |
| if (SkillEvents.SKILL_SCROLL_CLIENT.invoker().scroll(skillInstance, player, mode, amountY).isFalse()) continue; | |
| if (!skillInstance.canScroll(player, mode.get())) continue; | |
| packetSkills.put(skillInstance.getSkillId(), mode.get()); | |
| } | |
| Map<ResourceLocation, Integer> packetSkills = new HashMap<>(); | |
| int collected = 0; | |
| for (ManasSkillInstance skillInstance : SkillAPI.getSkillsFrom(player).getLearnedSkills()) { | |
| Changeable<Integer> mode = Changeable.of(0); | |
| if (SkillEvents.SKILL_SCROLL_CLIENT.invoker().scroll(skillInstance, player, mode, amountY).isFalse()) continue; | |
| if (mode.isEmpty()) continue; | |
| int m = Math.max(0, mode.get()); | |
| if (!skillInstance.canScroll(player, m)) continue; | |
| packetSkills.put(skillInstance.getSkillId(), m); | |
| if (++collected >= RequestSkillScrollPacket.MAX_SKILLS_PER_PACKET) break; | |
| } |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkillClient.java
around lines 28-34, make the mode access null-safe and stop collecting once the
server cap is reached: replace direct mode.get() calls with mode.orElse(0) (use
the safe default when passing into
SkillEvents.SKILL_SCROLL_CLIENT.invoker().scroll(...) and
skillInstance.canScroll(...)), and after adding to packetSkills check the server
limit (e.g., if (packetSkills.size() >= SkillAPI.getPacketSkillLimit()) break;)
so you early-exit the loop once the maximum allowed skills for the packet is
reached.
| public boolean canScroll(ManasSkillInstance instance, LivingEntity entity) { | ||
| return entity.isShiftKeyDown(); | ||
| } | ||
|
|
||
| public boolean canIgnoreCoolDown(ManasSkillInstance instance, LivingEntity entity, int mode) { | ||
| return mode == 1 && entity.isShiftKeyDown(); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Confirm override signature for canScroll.
This overload lacks mode; ensure you also override the 3-arg variant used by ManasSkillInstance to avoid default behavior.
🏁 Script executed:
#!/bin/bash
# Verify ManasSkill.canScroll signatures and TestSkill overrides
rg -n -C2 -g '!**/build/**' -P '\bclass\s+ManasSkill\b|\bcanScroll\s*\('Length of output: 4429
Override the 3-arg canScroll signature in TestSkill
TestSkill currently defines only a 2-arg canScroll(ManasSkillInstance, LivingEntity), but ManasSkill declares public boolean canScroll(ManasSkillInstance, LivingEntity, int mode) (which defaults to false). Replace or add the 3-arg override in TestSkill.java so the intended scrolling behavior is actually invoked.
🤖 Prompt for AI Agents
In
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java
around lines 60 to 66, the class only defines the 2-arg canScroll method but
ManasSkill declares a 3-arg canScroll(ManasSkillInstance, LivingEntity, int)
which is currently not overridden; add or replace with the 3-arg override so the
intended behavior is used: implement public boolean canScroll(ManasSkillInstance
instance, LivingEntity entity, int mode) and return the correct condition (e.g.,
mode == 1 && entity.isShiftKeyDown()) so scrolling respects the mode parameter.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (1)
60-67: Harden ticking with try/catch to avoid server crashes.If
tickRacethrows, it can take down the server loop. Wrap the call and log.- tickRace(entity, storage); + try { + tickRace(entity, storage); + } catch (Exception e) { + log.error("Error ticking race for entity {}: {}", entity, e.getMessage(), e); + }
🧹 Nitpick comments (3)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (3)
80-82: Clamp cooldown to zero to avoid negative values.Prevents underflow into negative cooldowns.
- instance.setCooldown(instance.getCooldown() - 1); + instance.setCooldown(Math.max(0, instance.getCooldown() - 1));
40-41: Tighten visibility of tickingRaces to prevent accidental cross-thread misuse.Expose helpers instead of a public mutable collection.
-public static final Multimap<UUID, TickingRace> tickingRaces = ArrayListMultimap.create(); +private static final Multimap<UUID, TickingRace> tickingRaces = ArrayListMultimap.create();If external access is needed, add controlled methods (add/remove/clear) on this class.
120-121: Health reset on race change — confirm intended UX.Resetting to full health can affect combat balance. If only max-health changes should be applied, consider clamping instead:
owner.setHealth(Math.min(owner.getHealth(), owner.getMaxHealth())).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
gradle.properties(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- gradle.properties
🧰 Additional context used
🧬 Code graph analysis (1)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (6)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(20-66)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
ManasRaceInstance(43-497)
🔇 Additional comments (2)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (2)
108-116: Verify evolution semantics when reusing the same instance.When
evolution == trueandinstance == race, the evolution copy (deserialize/serialize) andonRaceEvolutionare skipped. If evolutions may reuse an instance, this path won’t run. Confirm this can’t happen or guard for it.Would you like a targeted patch that handles “same-instance” evolutions explicitly?
48-55: No changes needed: Races declares markDirty() The Races interface at race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java line 69 defines void markDirty(), so the call compiles as written.
| EntityEvents.LIVING_POST_TICK.register(entity -> { | ||
| Level level = entity.level(); | ||
| if (level.isClientSide()) return; | ||
| Races storage = RaceAPI.getRaceFrom(entity); | ||
| handleRaceTick(entity, level, storage); | ||
| if (entity instanceof Player player) handleRaceHeldTick(player, storage); | ||
| storage.markDirty(); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Reduce network churn: don’t mark storage dirty every server tick.
storage.markDirty() is called every tick for every living entity, causing unnecessary sync traffic. Move it into the race-tick path so it runs at most once per second (or only when something actually changes).
Apply:
@@
EntityEvents.LIVING_POST_TICK.register(entity -> {
Level level = entity.level();
if (level.isClientSide()) return;
Races storage = RaceAPI.getRaceFrom(entity);
handleRaceTick(entity, level, storage);
if (entity instanceof Player player) handleRaceHeldTick(player, storage);
- storage.markDirty();
});
@@
- private static void handleRaceTick(LivingEntity entity, Level level, Races storage) {
+ private static void handleRaceTick(LivingEntity entity, Level level, Races storage) {
MinecraftServer server = level.getServer();
if (server == null) return;
boolean shouldTickRace = server.getTickCount() % INSTANCE_UPDATE == 0;
if (!shouldTickRace) return;
- tickRace(entity, storage);
+ tickRace(entity, storage);
+ // Only mark dirty on the (throttled) race-tick to cut sync volume by ~20x.
+ storage.markDirty();
}Also applies to: 60-67
| if (raceMessage.isPresent()) getOwner().sendSystemMessage(raceMessage.get()); | ||
| race.markDirty(); | ||
| race.addAttributeModifiers(owner); | ||
| owner.setHealth(owner.getMaxHealth()); | ||
| race.onRaceSet(owner); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Send race messages only to players and avoid double-applying attribute modifiers.
sendSystemMessageshould target players; non-player entities don’t support it.- Re-applying modifiers when
instance == racecan cause duplicate modifiers.
- if (raceMessage.isPresent()) getOwner().sendSystemMessage(raceMessage.get());
- race.markDirty();
- race.addAttributeModifiers(owner);
+ if (raceMessage.isPresent() && owner instanceof Player p) {
+ p.sendSystemMessage(raceMessage.get());
+ }
+ race.markDirty();
+ if (instance != race) {
+ race.addAttributeModifiers(owner);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (raceMessage.isPresent()) getOwner().sendSystemMessage(raceMessage.get()); | |
| race.markDirty(); | |
| race.addAttributeModifiers(owner); | |
| owner.setHealth(owner.getMaxHealth()); | |
| race.onRaceSet(owner); | |
| // only send the race-change message to actual players | |
| if (raceMessage.isPresent() && owner instanceof Player p) { | |
| p.sendSystemMessage(raceMessage.get()); | |
| } | |
| race.markDirty(); | |
| // avoid re-applying modifiers if this race was already set | |
| if (instance != race) { | |
| race.addAttributeModifiers(owner); | |
| } | |
| owner.setHealth(owner.getMaxHealth()); | |
| race.onRaceSet(owner); |
🤖 Prompt for AI Agents
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java
around lines 117 to 121: ensure system messages are only sent to player entities
and avoid re-applying attribute modifiers when the race hasn't actually changed;
wrap the sendSystemMessage call in an instanceof check (only call it if owner is
a Player) and guard the race.addAttributeModifiers(owner) call so it only runs
when the stored/previous race instance differs from the new race (i.e., only
apply modifiers on actual race change) or otherwise remove existing modifiers
before re-adding to prevent duplicates.
| if (teleport.get() && getOwner() instanceof ServerPlayer player) SpawnPointHelper.teleportToNewSpawn(player); | ||
| markDirty(); |
There was a problem hiding this comment.
Fix potential NPE when reading Changeable teleport.
Changeable.get() is nullable. Auto-unboxing in if (teleport.get() && ...) can NPE if a listener sets it to null.
- if (teleport.get() && getOwner() instanceof ServerPlayer player) SpawnPointHelper.teleportToNewSpawn(player);
+ if (Boolean.TRUE.equals(teleport.get()) && getOwner() instanceof ServerPlayer player) {
+ SpawnPointHelper.teleportToNewSpawn(player);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (teleport.get() && getOwner() instanceof ServerPlayer player) SpawnPointHelper.teleportToNewSpawn(player); | |
| markDirty(); | |
| if (Boolean.TRUE.equals(teleport.get()) && getOwner() instanceof ServerPlayer player) { | |
| SpawnPointHelper.teleportToNewSpawn(player); | |
| } | |
| markDirty(); |
🤖 Prompt for AI Agents
In
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java
around lines 126-127, the expression if (teleport.get() && getOwner() instanceof
ServerPlayer player) can throw an NPE because Changeable<Boolean>.get() may
return null; replace the condition with a null-safe check such as
Boolean.TRUE.equals(teleport.get()) && getOwner() instanceof ServerPlayer player
so the Boolean is compared safely, keep the
SpawnPointHelper.teleportToNewSpawn(player) call and ensure markDirty() remains
after the conditional.
| @Override | ||
| public void load(CompoundTag data) { | ||
| if (!data.contains(RACE_KEY)) return; | ||
| this.raceInstance = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); | ||
| } |
There was a problem hiding this comment.
NB: Loading NBT can crash on bad data; also reapply attributes after load.
ManasRaceInstance.fromNBT can throw (e.g., unknown race id). Wrap and log; on success, reapply attribute modifiers server-side to prevent stale stats after chunk/relog.
@Override
public void load(CompoundTag data) {
if (!data.contains(RACE_KEY)) return;
- this.raceInstance = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY));
+ try {
+ ManasRaceInstance loaded = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY));
+ this.raceInstance = loaded;
+ LivingEntity owner = this.getOwner();
+ if (!owner.level().isClientSide()) {
+ loaded.addAttributeModifiers(owner);
+ }
+ } catch (Exception e) {
+ log.error("Failed to load race instance for {}. Clearing race. Data: {}", this.getOwner(), data, e);
+ this.raceInstance = null;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| public void load(CompoundTag data) { | |
| if (!data.contains(RACE_KEY)) return; | |
| this.raceInstance = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); | |
| } | |
| @Override | |
| public void load(CompoundTag data) { | |
| if (!data.contains(RACE_KEY)) return; | |
| try { | |
| ManasRaceInstance loaded = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); | |
| this.raceInstance = loaded; | |
| LivingEntity owner = this.getOwner(); | |
| if (!owner.level().isClientSide()) { | |
| loaded.addAttributeModifiers(owner); | |
| } | |
| } catch (Exception e) { | |
| log.error("Failed to load race instance for {}. Clearing race. Data: {}", this.getOwner(), data, e); | |
| this.raceInstance = null; | |
| } | |
| } |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (8)
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (4)
54-56: Guard isShiftKeyDown() to Player to avoid crashes on non-player entitiesUse a Player check where sneaking is read.
Apply:
- return entity.isShiftKeyDown(); + return entity instanceof Player player && player.isShiftKeyDown();- return mode == 1 && entity.isShiftKeyDown(); + return mode == 1 && entity instanceof Player player && player.isShiftKeyDown();- if (living.isShiftKeyDown()) ManasCoreTesting.LOG.info("You're sneaky"); + if (living instanceof Player player && player.isShiftKeyDown()) ManasCoreTesting.LOG.info("You're sneaky");- if (living.isShiftKeyDown()) { + if (living instanceof Player player && player.isShiftKeyDown()) {- if (owner.isShiftKeyDown()) { + if (owner instanceof Player player && player.isShiftKeyDown()) {- if (owner.isShiftKeyDown() && target instanceof Villager) { + if (owner instanceof Player player && player.isShiftKeyDown() && target instanceof Villager) {Also applies to: 66-68, 102-104, 185-191, 167-172, 158-164
43-44: Use a stable ResourceLocation id for the attribute modifierA plain string id is brittle; prefer a namespaced id to ensure consistent add/remove.
Apply:
+ // import: net.minecraft.resources.ResourceLocation + // import: io.github.manasmods.manascore.skill.ModuleConstants - this.addHeldAttributeModifier(Attributes.MOVEMENT_SPEED, "test_speed", -0.95, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL); + this.addHeldAttributeModifier( + Attributes.MOVEMENT_SPEED, + ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, "test_speed"), + -0.95, + AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL + );
62-64: Wrong override signature: canScroll is never calledBase API defines canScroll(ManasSkillInstance, LivingEntity, int). Your 2-arg method won’t be invoked, so scrolling is effectively disabled.
Apply:
- public boolean canScroll(ManasSkillInstance instance, LivingEntity entity) { - return entity.isShiftKeyDown(); - } + public boolean canScroll(ManasSkillInstance instance, LivingEntity entity, int mode) { + return entity instanceof Player player && player.isShiftKeyDown(); + }
145-148: Don’t directly damage creepers inside onDamageEntity; set amount insteadCalling creeper.hurt here double-applies damage and breaks the event contract.
Apply:
- if (target instanceof Creeper creeper && skillConfig.instaKillCreeper) { - creeper.hurt(owner.level().damageSources().dragonBreath(), 100F); - ManasCoreTesting.LOG.info("No creeper"); - } else if (target instanceof IronGolem) { + if (target instanceof Creeper && skillConfig.instaKillCreeper) { + amount.set(100F); // lethal; processed once by the pipeline + ManasCoreTesting.LOG.info("No creeper"); + } else if (target instanceof IronGolem) {skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
44-44: Synchronize tickingSkills or confine all access to the main threadArrayListMultimap is not thread-safe. If any hook runs off-thread, this can race.
Apply:
+import com.google.common.collect.Multimaps; @@ - public static final Multimap<UUID, TickingSkill> tickingSkills = ArrayListMultimap.create(); + public static final Multimap<UUID, TickingSkill> tickingSkills = + Multimaps.synchronizedListMultimap(ArrayListMultimap.create());Optionally make tickingSkills private and expose helper methods to mutate safely.
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (3)
66-74: Guard config access and use safer, saner attribute modifiers.
- Avoid potential NPE/empty list when reading stacks from config; clamp to [1,64].
- Critical damage/chance modifiers are extreme and the chance uses a multiplicative op against a 0 base (likely no effect). Use moderate values and correct ops.
- public static final RegistrySupplier<Item> TEST_ITEM = ITEMS.register("test_item", - () -> new Item(new Item.Properties().arch$tab(TESTING_TAB) - .stacksTo(ConfigRegistry.getConfig(TestConfig.class).random_lists.numberLists.intList.getFirst()) - .attributes(ItemAttributeModifiers.builder() - .add(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 10, - AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL), EquipmentSlotGroup.OFFHAND) - .add(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 50, - AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL), EquipmentSlotGroup.OFFHAND).build()))); + public static final RegistrySupplier<Item> TEST_ITEM = ITEMS.register("test_item", + () -> { + TestConfig cfg = ConfigRegistry.getConfig(TestConfig.class); + int stacks = 64; + if (cfg != null && cfg.random_lists != null && cfg.random_lists.numberLists != null + && cfg.random_lists.numberLists.intList != null && !cfg.random_lists.numberLists.intList.isEmpty()) { + int v = cfg.random_lists.numberLists.intList.get(0); + stacks = Math.max(1, Math.min(64, v)); + } + return new Item(new Item.Properties().arch$tab(TESTING_TAB) + .stacksTo(stacks) + .attributes(ItemAttributeModifiers.builder() + // 1.5x base crit damage + .add(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER, + new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), + 1.5, AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND) + // +15% crit chance (attribute is 0..100); ADD_VALUE avoids multiplying a 0 base. + .add(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE, + new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), + 15, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.OFFHAND) + .build())); + });
134-153: Add null checks, remove unused locals, and surface user feedback in drop event.Prevents NPEs and gives clear UX when an action succeeds/fails.
- PlayerEvent.DROP_ITEM.register((player, entity) -> { - //Test giving Skills - if (entity.getItem().is(Items.DIAMOND)) { - Skills storage = SkillAPI.getSkillsFrom(player); - Registrar<ManasSkill> skills = SkillAPI.getSkillRegistry(); - RegistrySupplier<TestSkill> testSkill = TEST_SKILL; - if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { - ManasCoreTesting.LOG.info("Added Tested Skill!"); - } - - } else if (entity.getItem().is(Items.EMERALD)) { - Skills storage = SkillAPI.getSkillsFrom(player); - storage.forgetSkill(RegistryTest.TEST_SKILL.get(), Component.literal("Forgot Tested Skill!")); - } else if (entity.getItem().is(Items.GOLD_INGOT)) { - Races storage = RaceAPI.getRaceFrom(player); - storage.setRace(RegistryTest.TEST_RACE.getId(), true, Component.literal("Set to Test Race!")); - } - - return EventResult.pass(); - }); + PlayerEvent.DROP_ITEM.register((player, entity) -> { + if (entity.getItem().is(Items.DIAMOND)) { + Skills storage = SkillAPI.getSkillsFrom(player); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get skills storage for {}", player.getScoreboardName()); + return EventResult.pass(); + } + if (TEST_SKILL == null || TEST_SKILL.get() == null) { + ManasCoreTesting.LOG.error("Test skill is not registered"); + return EventResult.pass(); + } + if (storage.learnSkill(TEST_SKILL.get())) { + ManasCoreTesting.LOG.info("Added Tested Skill!"); + player.displayClientMessage(Component.literal("Added Tested Skill!"), true); + } else { + ManasCoreTesting.LOG.warn("Failed to learn test skill"); + } + } else if (entity.getItem().is(Items.EMERALD)) { + Skills storage = SkillAPI.getSkillsFrom(player); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get skills storage for {}", player.getScoreboardName()); + return EventResult.pass(); + } + storage.forgetSkill(TEST_SKILL.get(), Component.literal("Forgot Tested Skill!")); + } else if (entity.getItem().is(Items.GOLD_INGOT)) { + Races storage = RaceAPI.getRaceFrom(player); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get race storage for {}", player.getScoreboardName()); + return EventResult.pass(); + } + if (!RaceAPI.getRaceRegistry().containsKey(TEST_RACE.getId())) { + ManasCoreTesting.LOG.error("Test race is not registered"); + return EventResult.pass(); + } + storage.setRace(TEST_RACE.getId(), true, Component.literal("Set to Test Race!")); + player.displayClientMessage(Component.literal("Race set to Test Race"), true); + } + return EventResult.pass(); + });
178-191: Cap death-explosion radius; use identity compare for effect type.Prevents unexpectedly large blasts in test worlds and simplifies the comparison.
- float g = 10.0F + entity.getRandom().nextFloat() * 2.0F; - if (this.equals(RegistryTest.TEST_MOB_EFFECT_PARTICLE.get())) + float baseRadius = 3.0F; + float randomAddition = entity.getRandom().nextFloat() * 1.0F; + float g = Math.min(baseRadius + randomAddition, 4.0F); + if (this == RegistryTest.TEST_MOB_EFFECT_PARTICLE.get()) level.explode(entity, null, AbstractWindCharge.EXPLOSION_DAMAGE_CALCULATOR, d, e, f, g, false, Level.ExplosionInteraction.TRIGGER, ParticleTypes.GUST_EMITTER_SMALL, ParticleTypes.GUST_EMITTER_LARGE, SoundEvents.BREEZE_WIND_CHARGE_BURST); else level.explode(entity, Explosion.getDefaultDamageSource(level, entity), null, d, e, f, g, false, Level.ExplosionInteraction.MOB, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE);
🧹 Nitpick comments (9)
race-common/src/main/java/io/github/manasmods/manascore/race/mixin/MixinServerPlayer.java (2)
20-25: Call original once; then conditionally copy state (also clamp health and keep absorption).Reduces duplication, avoids future drift, and guards against health > max. Copying absorption keeps client/server visuals consistent after End exit.
Apply:
- if (!endConquered) original.call(instance, oldPlayer, endConquered); - else { - original.call(instance, oldPlayer, endConquered); - instance.getAttributes().assignAllValues(oldPlayer.getAttributes()); - instance.setHealth(oldPlayer.getHealth()); - } + original.call(instance, oldPlayer, endConquered); + if (endConquered) { + instance.getAttributes().assignAllValues(oldPlayer.getAttributes()); + instance.setHealth(Math.min(oldPlayer.getHealth(), instance.getMaxHealth())); + instance.setAbsorptionAmount(oldPlayer.getAbsorptionAmount()); + }
16-16: Rename class for target clarity.The mixin targets PlayerList but the class is named MixinServerPlayer. Consider MixinPlayerList_Respawn or similar for maintainability.
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (1)
167-172: Healing equals incoming damage may nullify damage or cause odd interactionsonTakenDamage heals for the same amount, effectively canceling damage and possibly interacting poorly with other modifiers. Consider capping or tagging sources.
Would you like a mode/threshold-based example that avoids full negation except for specific sources?
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
190-205: Forgetting a skill should also clean up attribute modifiers/toggle stateCurrent flow sends messages and removes the instance, but may leave transient modifiers applied.
Apply:
if (forgetMessage.isPresent()) getOwner().sendSystemMessage(forgetMessage.get()); instance.onForgetSkill(this.getOwner()); instance.markDirty(); - this.getLearnedSkills().remove(instance); + // Ensure transient state is cleared before removal + for (int mode = 0; mode < instance.getModes(); mode++) { + instance.removeAttributeModifiers(getOwner(), mode); + } + instance.setToggled(false); + this.getLearnedSkills().remove(instance); this.hasRemovedSkills = true; markDirty();testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (5)
61-64: Prefer translatable text for the creative tab title.Use a translation key to avoid hardcoded, non-localized UI strings.
- CreativeTabRegistry.create(Component.literal("Testing Creative Tab").withStyle(ChatFormatting.RED), + CreativeTabRegistry.create(Component.translatable("itemGroup.manascore.testing").withStyle(ChatFormatting.RED), () -> new ItemStack(RegistryTest.TEST_ITEM.get())))
86-89: Tighten generics on block entity type.Use the concrete type for stronger compile-time checks.
- public static final RegistrySupplier<BlockEntityType<?>> TEST_BLOCK_ENTITY = BLOCK_ENTITIES.register("test_block_entity", + public static final RegistrySupplier<BlockEntityType<TestBlockEntity>> TEST_BLOCK_ENTITY = BLOCK_ENTITIES.register("test_block_entity", () -> BlockEntityType.Builder.of(TestBlockEntity::new, RegistryTest.TEST_BLOCK.get()).build(null));
90-94: Align entity type generics and consider category.
- Use concrete TestEntity generic.
- Consider MobCategory.CREATURE for a Villager-derived entity unless you intentionally need MONSTER rules.
- public static final RegistrySupplier<EntityType<? extends Villager>> TEST_ENTITY_TYPE = ENTITY_TYPES.register("test_entity", - () -> EntityType.Builder.of(TestEntity::new, MobCategory.MONSTER).fireImmune() + public static final RegistrySupplier<EntityType<TestEntity>> TEST_ENTITY_TYPE = ENTITY_TYPES.register("test_entity", + () -> EntityType.Builder.of(TestEntity::new, MobCategory.CREATURE).fireImmune() .sized(1F, 1F).clientTrackingRange(4).updateInterval(10).build("test_entity"));
96-104: Attribute bounds sanity check.Adding +3 WATER_MOVEMENT_EFFICIENCY might exceed expected ranges and produce odd movement. If intentional for tests, ignore; otherwise reduce.
156-160: Use the provided entityType parameter.Avoid hard-coding the static type; use the constructor parameter for correctness and reuse.
- public TestEntity(EntityType<TestEntity> entityType, Level level) { - super(TEST_ENTITY_TYPE.get(), level); + public TestEntity(EntityType<TestEntity> entityType, Level level) { + super(entityType, level); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
gradle.properties(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/mixin/MixinServerPlayer.java(1 hunks)race-common/src/main/resources/manascore_race.mixins.json(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- race-common/src/main/resources/manascore_race.mixins.json
- gradle.properties
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java
🧰 Additional context used
🧬 Code graph analysis (4)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (10)
config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java (1)
ConfigRegistry(13-100)network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(20-66)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
ManasSkill(56-524)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-623)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java (1)
ManasCoreTesting(19-33)testing-common/src/main/java/io/github/manasmods/manascore/testing/configs/SkillConfig.java (1)
SkillConfig(7-20)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (1)
RegistryTest(59-214)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestTags.java (1)
TestTags(14-16)
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (10)
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java (1)
ManasCoreAttributes(16-61)config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java (1)
ConfigRegistry(13-100)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
RaceRegistry(27-106)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java (1)
ManasCoreTesting(19-33)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestAttributeRegistry.java (1)
TestAttributeRegistry(13-21)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (1)
TestSkill(39-203)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRace.java (1)
TestRace(42-155)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java (1)
TestRaceEvolved(32-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (6)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java (1)
ManasCoreSkill(16-27)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(20-66)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-623)
🔇 Additional comments (5)
race-common/src/main/java/io/github/manasmods/manascore/race/mixin/MixinServerPlayer.java (2)
23-24: Verify client sync of attributes/health post-respawn.Depending on mappings/version, direct AttributeMap mutation may not auto-sync to the player’s own client. Validate that the client immediately shows the correct max health, current health, and modifiers after End respawn. If desync occurs, explicitly broadcast attribute changes or schedule a sync on login tick.
17-19: Stability: ensure the call site is unique across mappings.If multiple restoreFrom invokes ever appear in PlayerList.respawn, add selector constraints (e.g., ordinal) to avoid wrapping the wrong call. Confirm across Fabric/NeoForge 1.21.1 mappings.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
50-56: Damage pipeline ordering looks goodPre/Calc/Post events are chained with early interrupts; this matches expected semantics.
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
16-31: Event surface is coherent and minimalGood separation: result-based for gating, loop for post-tick. SKILL_MASTERY addition fits well.
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (1)
105-117: LGTM on registries.Skill and race registries are correctly wired via Registrar keys; suppliers look good.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java
Outdated
Show resolved
Hide resolved
| .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), | ||
| 69, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND) | ||
| .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), | ||
| 420, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND).build()))); |
There was a problem hiding this comment.
Duplicate modifier IDs on the same item can collide.
Both modifiers use the same ResourceLocation ID "test_critical_chance". Give each a unique, stable ID.
- .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"),
+ .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_attr_player"),
69, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND)
- .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"),
+ .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_attr_all"),
420, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND).build())));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), | |
| 69, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND) | |
| .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), | |
| 420, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND).build()))); | |
| .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier( | |
| ResourceLocation.withDefaultNamespace("test_attr_player"), | |
| 69, AttributeModifier.Operation.ADD_VALUE), | |
| EquipmentSlotGroup.MAINHAND) | |
| .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier( | |
| ResourceLocation.withDefaultNamespace("test_attr_all"), | |
| 420, AttributeModifier.Operation.ADD_VALUE), | |
| EquipmentSlotGroup.MAINHAND) | |
| .build()))); |
🤖 Prompt for AI Agents
In
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java
around lines 81 to 84, two AttributeModifier entries use the same
ResourceLocation ID "test_critical_chance", which can collide; change each
modifier to use a unique, stable ResourceLocation ID (for example
"test_critical_chance_player" and "test_critical_chance_all" or define
descriptive constants) so each modifier has a distinct identifier and update any
related references accordingly.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java (1)
43-49: Gate test command registration to dev/debug builds.ManasCoreTesting.init() is called unconditionally from platform entrypoints and then calls CommandModuleTest.init(), which registers test commands — this exposes test commands in non-dev builds. Guard the registration with a dev-only check or config switch.
Locations to update:
- testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java — init() registers TestCommand (lines ~43–49).
- testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java — line 27: CommandModuleTest.init().
- testing-fabric/src/main/java/io/github/manasmods/manascore/testing/fabric/ManasCoreTestingFabric.java — line 14: ManasCoreTesting.init().
- testing-neoforge/src/main/java/io/github/manasmods/manascore/testing/neoforge/ManasCoreTestingNeoForge.java — line 16: ManasCoreTesting.init().
Change either: (A) add a dev-check in ManasCoreTesting.init() before calling CommandModuleTest.init(), or (B) add a dev-check inside CommandModuleTest.init(). Use the existing Platform.getEnv() or a configurable dev flag.
♻️ Duplicate comments (5)
testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java (5)
70-79: Add user feedback and exception handling when learning a skill; fix log name usage.- Entity entity = selector.findSingleEntity(sender); - if (entity instanceof LivingEntity living) { - Skills storage = SkillAPI.getSkillsFrom(living); - if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) - ManasCoreTesting.LOG.info("Added Test Skill to " + entity.getName()); - } + Entity entity = selector.findSingleEntity(sender); + if (entity instanceof LivingEntity living) { + Skills storage = SkillAPI.getSkillsFrom(living); + try { + if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { + ManasCoreTesting.LOG.info("Added Test Skill to {}", entity.getName().getString()); + sender.sendSuccess(() -> Component.literal("Successfully added skill to entity"), true); + } else { + sender.sendFailure(Component.literal("Entity already has this skill")); + } + } catch (Exception e) { + ManasCoreTesting.LOG.error("Failed to add skill to entity", e); + sender.sendFailure(Component.literal("Failed to add skill to entity: " + e.getMessage())); + } + }
83-89: Teleportation: add try/catch and clear player-only feedback.- if (sender.getPlayer() != null) - SpawnPointHelper.teleportToAcrossDimensions(sender.getPlayer(), Level.OVERWORLD, - pos.getX(), pos.getY(), pos.getZ(), xRot.getRotation(sender).x, yRot.getRotation(sender).y); + if (sender.getPlayer() != null) { + try { + SpawnPointHelper.teleportToAcrossDimensions(sender.getPlayer(), Level.OVERWORLD, + pos.getX(), pos.getY(), pos.getZ(), xRot.getRotation(sender).x, yRot.getRotation(sender).y); + sender.sendSuccess(() -> Component.literal("Teleported successfully"), true); + } catch (Exception e) { + ManasCoreTesting.LOG.error("Failed to teleport player", e); + sender.sendFailure(Component.literal("Teleportation failed: " + e.getMessage())); + } + } else { + sender.sendFailure(Component.literal("Only players can be teleported")); + }
93-99: Teleportation across dimensions: add error handling and user feedback.- if (sender.getPlayer() != null) - SpawnPointHelper.teleportToAcrossDimensions(sender.getPlayer(), dimension, pos.x(), pos.y(), pos.z(), 0, 0); - return true; + if (sender.getPlayer() != null) { + try { + SpawnPointHelper.teleportToAcrossDimensions(sender.getPlayer(), dimension, pos.x(), pos.y(), pos.z(), 0, 0); + sender.sendSuccess(() -> Component.literal("Teleported successfully to dimension"), true); + } catch (Exception e) { + ManasCoreTesting.LOG.error("Failed to teleport player to dimension", e); + sender.sendFailure(Component.literal("Teleportation failed: " + e.getMessage())); + } + } else { + sender.sendFailure(Component.literal("Only players can be teleported")); + } + return true;
102-111: Skill removal: validate player/skill and add success/failure messaging; prefer parameterized log.- ManasSkill skill = SkillAPI.getSkillRegistry().get(location); - if (skill != null && sender.getPlayer() != null) { - SkillAPI.getSkillsFrom(sender.getPlayer()).forgetSkill(skill); - ManasCoreTesting.LOG.info("Removed Test Skill from " + sender.getPlayer().getName()); - } + ManasSkill skill = SkillAPI.getSkillRegistry().get(location); + if (sender.getPlayer() == null) { + sender.sendFailure(Component.literal("Only players can have skills removed")); + return true; + } + if (skill == null) { + sender.sendFailure(Component.literal("Skill not found: " + location)); + return true; + } + try { + boolean removed = SkillAPI.getSkillsFrom(sender.getPlayer()).forgetSkill(skill); + if (removed) { + ManasCoreTesting.LOG.info("Removed Test Skill from {}", sender.getPlayer().getName().getString()); + sender.sendSuccess(() -> Component.literal("Successfully removed skill"), true); + } else { + sender.sendFailure(Component.literal("Player doesn't have this skill")); + } + } catch (Exception e) { + ManasCoreTesting.LOG.error("Failed to remove skill", e); + sender.sendFailure(Component.literal("Failed to remove skill: " + e.getMessage())); + }
114-126: Item creation/gifting: handle errors and inventory-full cases; add user feedback.- if (sender.getPlayer() != null) { - ItemStack stack = itemInput.createItemStack(5, false); - stack.enchant(location, 100); - sender.getPlayer().addItem(stack); - sender.getPlayer().addItem(itemInput2.createItemStack(4, false)); - sender.getPlayer().addItem(itemInput3.createItemStack(3, false)); - } + if (sender.getPlayer() != null) { + try { + ItemStack stack = itemInput.createItemStack(5, false); + stack.enchant(location, 100); + boolean added1 = sender.getPlayer().addItem(stack); + boolean added2 = sender.getPlayer().addItem(itemInput2.createItemStack(4, false)); + boolean added3 = sender.getPlayer().addItem(itemInput3.createItemStack(3, false)); + if (added1 && added2 && added3) { + sender.sendSuccess(() -> Component.literal("Items added to inventory"), true); + } else { + sender.sendFailure(Component.literal("One or more items could not be added (inventory may be full)")); + } + } catch (Exception e) { + ManasCoreTesting.LOG.error("Failed to create or add item", e); + sender.sendFailure(Component.literal("Failed to create or add item: " + e.getMessage())); + } + } else { + sender.sendFailure(Component.literal("Only players can receive items")); + }
🧹 Nitpick comments (2)
testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java (2)
55-57: Add permission gating tofooroot command.Root
withPermshas no@Permission; gate it like the subcommand to prevent accidental exposure.@Execute +@Permission(value = "manascore.command.test", permissionLevel = Permission.PermissionLevel.GAMEMASTER) public boolean withPerms(@SenderArg CommandSourceStack sender) {
150-159: Avoid NPE from auto-unboxingBooleanin ternary.Use null-safe comparison.
- .append(bool ? "true" : "false") + .append(Boolean.TRUE.equals(bool) ? "true" : "false")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
gradle.properties(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java(15 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java
- gradle.properties
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java
🧰 Additional context used
🧬 Code graph analysis (1)
testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java (5)
race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
ManasSkill(56-524)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java (1)
ManasCoreTesting(19-33)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (1)
RegistryTest(59-214)
🔇 Additional comments (2)
testing-common/src/main/java/io/github/manasmods/manascore/testing/module/CommandModuleTest.java (2)
62-67: Permission on subcommand looks good.
320-324: Enum extension looks fine.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (4)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
338-340: CallmarkDirty()after replacing the cooldown list.This method modifies the instance's state but doesn't mark it dirty for synchronization, unlike other state-manipulating methods (e.g.,
setCoolDown,setMastery,setToggled). This inconsistency could lead to the cooldown list not being synchronized properly.Apply this diff to add the missing
markDirty()call:public void setCoolDownList(List<Integer> list) { this.cooldownList = list; + markDirty(); }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (1)
104-112: Add null check forhitResult.getEntity()to prevent NPE.The code assumes that
hitResult.getEntity()is not null. IfhitResultdoes not contain an entity, theinstanceofcheck will pass null through toLivingEntity, which would then be cast incorrectly in subsequent code. While theinstanceofcheck itself won't throw an NPE, it's safer to explicitly verify the entity is non-null.Apply this diff to add an explicit null check:
EntityEvents.PROJECTILE_HIT.register((result, projectile, deflectionChangeable, hitResultChangeable) -> { if (!(result instanceof EntityHitResult hitResult)) return; - if (!(hitResult.getEntity() instanceof LivingEntity hitEntity)) return; + Entity entity = hitResult.getEntity(); + if (entity == null || !(entity instanceof LivingEntity hitEntity)) return; for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(hitEntity).getLearnedSkills()) {skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (2)
45-45: ReplaceArrayListMultimapwith a thread-safe alternative fortickingSkills.The
tickingSkillsmultimap is accessed and modified from multiple event hooks (e.g.,PLAYER_QUITat line 84,handleSkillHeldTickat line 158, andhandleSkillReleaseat line 246). If any code path runs off the main thread or during concurrent event processing, data races may occur.ArrayListMultimapis not thread-safe.Consider using
Multimaps.synchronizedMultimapto wrap the multimap, or switch to aConcurrentHashMap<UUID, Collection<TickingSkill>>with thread-safe collections:-public static final Multimap<UUID, TickingSkill> tickingSkills = ArrayListMultimap.create(); +public static final Multimap<UUID, TickingSkill> tickingSkills = + Multimaps.synchronizedMultimap(ArrayListMultimap.create());Or:
+import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + -public static final Multimap<UUID, TickingSkill> tickingSkills = ArrayListMultimap.create(); +private static final Map<UUID, Collection<TickingSkill>> tickingSkills = new ConcurrentHashMap<>();Don't forget to add the import:
import com.google.common.collect.Multimaps;
246-248: Fix type mismatch inremoveIfpredicate—released skills are not removed.At line 247, the code compares
tickingSkill.getSkill()withskill.getSkill()using==. Based on the context and theTickingSkillclass (which likely stores aResourceLocation),tickingSkill.getSkill()returns aResourceLocation, whileskill.getSkill()returns aManasSkillobject. This type mismatch means the equality check will always be false, causing released skills to leak intickingSkills.Apply this diff to compare by skill ID:
UUID ownerID = getOwner().getUUID(); if (tickingSkills.containsKey(ownerID)) - tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.getSkill() == skill.getSkill()); + tickingSkills.get(ownerID).removeIf(tickingSkill -> + tickingSkill.getSkill().equals(skill.getSkillId()));
🧹 Nitpick comments (4)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (2)
39-67: Consider documenting null safety and the Changeable pattern.The functional interfaces follow a generally good pattern, but consider these improvements:
Inconsistent return type:
ProjectileHitEventreturnsvoidbut providesChangeableparameters. Most other void-return events (likeLivingTickEvent) don't allow modifications. Consider returningEventResultif the event is meant to be cancellable.Missing null safety documentation: Document whether parameters like
Entity sourceinLivingEffectAddedEventcan be null, and whetherChangeabletargets can be set to null.Document the Changeable pattern: Add JavaDoc explaining that handlers should use
Changeable.set()to modify values and that the original value is preserved for reference.Example documentation:
/** * Called when a projectile hits something. * Handlers can modify the deflection and hit result behavior. * * @param hitResult The hit result (never null) * @param projectile The projectile entity (never null) * @param deflection Changeable deflection behavior (never null, value may be null) * @param result Changeable hit result (never null, defaults to DEFAULT) */ @FunctionalInterface public interface ProjectileHitEvent { void hit(HitResult hitResult, Projectile projectile, Changeable<ProjectileDeflection> deflection, Changeable<ProjectileHitResult> result); }
69-74: Convert inline comments to JavaDoc for better IDE support.The enum values have helpful inline comments, but converting them to JavaDoc would provide better IDE integration and documentation generation.
public enum ProjectileHitResult { - DEFAULT, // Hit, damage + possibly continue - HIT, // Hit + damage - HIT_NO_DAMAGE, // Hit - PASS // Pass through + /** Hit with damage, and possibly continue through the target. */ + DEFAULT, + /** Hit with damage and stop. */ + HIT, + /** Hit without dealing damage. */ + HIT_NO_DAMAGE, + /** Pass through without hitting. */ + PASS }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (1)
26-112: Consider adding error handling for skill event interactions.The event handlers invoke skill instance methods (e.g.,
onEffectAdded,onBeingTargeted,onDamageEntity) without catching potential exceptions. If a skill's implementation throws an unexpected exception, it could interrupt the entire event processing chain and affect other skills or game systems.While adding try-catch blocks to every handler would add verbosity, consider wrapping the skill interaction calls in a helper method that logs and suppresses exceptions, or document that skill implementations must not throw unchecked exceptions. For example:
private static boolean safeSkillInteraction(ManasSkillInstance instance, Supplier<Boolean> interaction, String eventName) { try { return interaction.get(); } catch (Exception e) { log.error("Error in skill " + instance.getSkillId() + " during " + eventName, e); return true; // or false, depending on desired behavior } }Then use it in handlers:
if (!safeSkillInteraction(instance, () -> instance.onEffectAdded(entity, source, changeableTarget), "EFFECT_ADDED")) { return EventResult.interruptFalse(); }race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
43-54: Remove redundant null check
Theif (owner == null)check at line 46 can be dropped—changeableTarget.isPresent()already guaranteesget()returns non-null.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
gradle.properties(2 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java
🧰 Additional context used
🧬 Code graph analysis (5)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (5)
race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRace.java (1)
ManasRace(59-435)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
ManasRaceInstance(43-497)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (2)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (2)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (5)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-699)
🔇 Additional comments (15)
gradle.properties (2)
21-21: Verify NeoForge 21.1.209 compatibility.We’ve confirmed that NeoForge 21.1.209 exists in the Maven repository. Review the 21.1.209 release notes for any breaking changes affecting the newly added modules.
10-10: Verify mod_version downgradegradle.properties:10 – The project version (
mod_version) was changed from 4.0.0.0 to 3.0.2.5. Downgrading the version when adding new features is unexpected. Confirm this downgrade is intentional or update to the correct version.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
1-20: LGTM! Clean imports and package structure.The package declaration and imports are well-organized. All imported classes are utilized within the file.
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (2)
145-152: LGTM: Null handling infromNBThas been addressed.The null check for skill lookup at line 148 properly handles the case when a skill is not found in the registry by throwing an
IllegalArgumentExceptionwith a descriptive message.
180-183: LGTM: Theequals/hashCodecontract is now consistent.Both methods now use
skillIdandregistryKey, ensuring that equal objects have the same hash code.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (2)
169-169: LGTM:skillInstancesuses a thread-safeConcurrentHashMap.The use of
ConcurrentHashMapforskillInstancesensures thread-safe access and modification, which is appropriate given the potential for concurrent event processing.
268-274: Good: Error handling inloadmethod prevents corruption from invalid NBT data.The try-catch block around
ManasSkillInstance.fromNBTat line 269 ensures that a single malformed skill instance won't prevent the entire storage from loading. This is a robust approach to handling corrupted or outdated NBT data.race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (8)
1-26: LGTM!Package structure and imports are appropriate for the registry and event wiring implementation.
32-41: LGTM!The LIVING_EFFECT_ADDED handler correctly retrieves the race instance, validates activation eligibility, and delegates to the race's effect hook with proper pass/interrupt semantics.
56-65: LGTM!The SKILL_DAMAGE_POST_CALCULATION handler correctly identifies the attacking entity, retrieves its race, and delegates damage modification to the race's attack hook.
67-75: LGTM!The LIVING_DAMAGE handler correctly retrieves the race of the damaged entity and delegates to the race's hurt hook with proper event flow.
77-85: LGTM!The LIVING_DEATH handler correctly retrieves the race of the dying entity and delegates to the race's death hook with proper interrupt semantics.
87-100: Verify the conqueredEnd logic.The handler skips attribute restoration, health reset, and spawn teleportation when
conqueredEndis true. This may be intentional (End portal return is not a "death" respawn), but ensure this aligns with the expected race behavior when a player returns from the End.Additionally, the
canActivateAbilitycheck on line 98 gates theonRespawnhook but not the attribute/health restoration on lines 93-95. Verify if attribute restoration should also be gated bycanActivateAbility.
103-104: LGTM!The private constructor correctly enforces static-only usage of the registry class.
27-30: Approve registry key castThe unchecked cast mirrors the established pattern in SkillRegistry and aligns with the registrar builder’s type guarantees, so no modifications are required.
| public class EntityEvents { | ||
| public static Event<LivingTickEvent> LIVING_PRE_TICK = EventFactory.createLoop(); | ||
| public static Event<LivingTickEvent> LIVING_POST_TICK = EventFactory.createLoop(); | ||
| public static Event<LivingEffectAddedEvent> LIVING_EFFECT_ADDED = EventFactory.createEventResult(); | ||
| public static Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET = EventFactory.createEventResult(); | ||
|
|
||
| public static Event<EntityEvent.LivingHurt> LIVING_PRE_DAMAGED = EventFactory.createEventResult(); | ||
| public static Event<EntityEvent.LivingHurt> LIVING_ON_BEING_DAMAGED = EventFactory.createEventResult(); | ||
| public static Event<LivingHurtEvent> LIVING_HURT = EventFactory.createEventResult(); | ||
| public static Event<LivingDamageEvent> LIVING_DAMAGE = EventFactory.createEventResult(); | ||
| public static Event<ProjectileHitEvent> PROJECTILE_HIT = EventFactory.createLoop(); | ||
|
|
||
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_FIRST = EventFactory.createEventResult(); | ||
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_HIGH = EventFactory.createEventResult(); | ||
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_NORMAL = EventFactory.createEventResult(); | ||
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_LOW = EventFactory.createEventResult(); | ||
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_LAST = EventFactory.createEventResult(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add documentation to clarify event usage and ordering.
The event declarations lack JavaDoc comments explaining:
- The execution order and purpose of similar events (e.g.,
LIVING_PRE_DAMAGED,LIVING_ON_BEING_DAMAGED,LIVING_HURT,LIVING_DAMAGE) - When to use each event type
- The priority levels for death events
Additionally, there's an inconsistency: LIVING_PRE_DAMAGED and LIVING_ON_BEING_DAMAGED use EntityEvent.LivingHurt, while LIVING_HURT and LIVING_DAMAGE use custom interfaces. Consider using custom interfaces consistently for clarity and flexibility.
Example documentation:
+ /**
+ * Fired before damage calculation. Use {@link EntityEvent.LivingHurt}.
+ */
public static Event<EntityEvent.LivingHurt> LIVING_PRE_DAMAGED = EventFactory.createEventResult();
+ /**
+ * Fired during damage processing. Use {@link EntityEvent.LivingHurt}.
+ */
public static Event<EntityEvent.LivingHurt> LIVING_ON_BEING_DAMAGED = EventFactory.createEventResult();
+ /**
+ * Fired when entity is hurt. Allows modification of damage amount via {@link Changeable}.
+ */
public static Event<LivingHurtEvent> LIVING_HURT = EventFactory.createEventResult();
+ /**
+ * Fired when entity takes damage. Final chance to modify damage via {@link Changeable}.
+ */
public static Event<LivingDamageEvent> LIVING_DAMAGE = EventFactory.createEventResult();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public class EntityEvents { | |
| public static Event<LivingTickEvent> LIVING_PRE_TICK = EventFactory.createLoop(); | |
| public static Event<LivingTickEvent> LIVING_POST_TICK = EventFactory.createLoop(); | |
| public static Event<LivingEffectAddedEvent> LIVING_EFFECT_ADDED = EventFactory.createEventResult(); | |
| public static Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET = EventFactory.createEventResult(); | |
| public static Event<EntityEvent.LivingHurt> LIVING_PRE_DAMAGED = EventFactory.createEventResult(); | |
| public static Event<EntityEvent.LivingHurt> LIVING_ON_BEING_DAMAGED = EventFactory.createEventResult(); | |
| public static Event<LivingHurtEvent> LIVING_HURT = EventFactory.createEventResult(); | |
| public static Event<LivingDamageEvent> LIVING_DAMAGE = EventFactory.createEventResult(); | |
| public static Event<ProjectileHitEvent> PROJECTILE_HIT = EventFactory.createLoop(); | |
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_FIRST = EventFactory.createEventResult(); | |
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_HIGH = EventFactory.createEventResult(); | |
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_NORMAL = EventFactory.createEventResult(); | |
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_LOW = EventFactory.createEventResult(); | |
| public static Event<EntityEvent.LivingDeath> DEATH_EVENT_LAST = EventFactory.createEventResult(); | |
| /** | |
| * Fired before damage calculation. Use {@link EntityEvent.LivingHurt}. | |
| */ | |
| public static Event<EntityEvent.LivingHurt> LIVING_PRE_DAMAGED = EventFactory.createEventResult(); | |
| /** | |
| * Fired during damage processing. Use {@link EntityEvent.LivingHurt}. | |
| */ | |
| public static Event<EntityEvent.LivingHurt> LIVING_ON_BEING_DAMAGED = EventFactory.createEventResult(); | |
| /** | |
| * Fired when entity is hurt. Allows modification of damage amount via {@link Changeable}. | |
| */ | |
| public static Event<LivingHurtEvent> LIVING_HURT = EventFactory.createEventResult(); | |
| /** | |
| * Fired when entity takes damage. Final chance to modify damage via {@link Changeable}. | |
| */ | |
| public static Event<LivingDamageEvent> LIVING_DAMAGE = EventFactory.createEventResult(); |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java
around lines 21 to 37, add JavaDoc comments above each Event field explaining
its purpose and execution ordering (e.g., pre -> on-being-damaged -> hurt ->
damage) and when to use each; document which events are loop vs result-type and
specify the intended priority/sequence for the death events (e.g., FIRST, HIGH,
NORMAL, LOW, LAST) so callers understand ordering; and make the event interface
types consistent by switching LIVING_PRE_DAMAGED and LIVING_ON_BEING_DAMAGED to
use the same custom LivingHurt-like interfaces as LIVING_HURT and LIVING_DAMAGE
(or conversely consolidate to a single custom interface for all hurt/damage
events), then update imports and any references accordingly.
| clone.subInstances = this.subInstances; | ||
| clone.parentSkill = this.parentSkill; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Create defensive copies of subInstances and parentSkill in the copy method.
The copy() method creates a defensive copy of cooldownList but directly assigns the subInstances map and parentSkill reference. This creates shared mutable state between the original instance and its copy, which could lead to unexpected behavior when one instance modifies these fields.
Apply this diff to create defensive copies:
clone.cooldownList = new ArrayList<>(this.cooldownList);
clone.removeTime = this.removeTime;
clone.masteryPoint = this.masteryPoint;
clone.toggled = this.toggled;
if (this.tag != null) clone.tag = this.tag.copy();
- clone.subInstances = this.subInstances;
+ clone.subInstances = new HashMap<>(this.subInstances);
clone.parentSkill = this.parentSkill;
return clone;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java
around lines 75-76, the copy() method assigns subInstances and parentSkill by
reference which shares mutable state; instead make defensive copies: create a
new Map and populate it with copies of each entry (e.g., iterate
this.subInstances and put(key, value.copy()) to avoid sharing nested
ManasSkillInstance objects), and set parentSkill to a copied instance (e.g.,
this.parentSkill == null ? null : this.parentSkill.copy()) rather than assigning
the original reference.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (13)
race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java (1)
47-51: Component parameter is ignored in evolution call.The
componentparameter at line 47 is accepted but not passed tosetRace()at line 50. This prevents custom evolution messages from being displayed, as flagged in a previous review.Apply this diff:
default boolean evolveRace(@NotNull ResourceLocation raceId, @Nullable MutableComponent component) { ManasRace race = RaceAPI.getRaceRegistry().get(raceId); if (race == null) return false; - return setRace(race.createDefaultInstance(), true, false); + return setRace(race.createDefaultInstance(), true, false, component); }race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceAbilityActivationPacket.java (2)
34-35: Add clarifying comment for empty encode method.The empty
encodemethod could benefit from a comment explaining that this packet carries no data.</review_comment_end -->
37-56: Add error handling for packet processing.The handler lacks try-catch error handling. If
onActivateAbility,checkAndMarkDirty, or race storage operations throw exceptions, they could propagate and affect the server.Consider wrapping the logic in a try-catch block:
public void handle(NetworkManager.PacketContext context) { if (context.getEnvironment() != Env.SERVER) return; context.queue(() -> { Player player = context.getPlayer(); if(player == null) return; + try { Races storage = RaceAPI.getRaceFrom(player); Optional<ManasRaceInstance> optional = storage.getRace(); if (optional.isEmpty()) return; ManasRaceInstance instance = optional.get(); if (RaceEvents.ACTIVATE_ABILITY.invoker().activateAbility(instance, player).isFalse()) return; if (!instance.canActivateAbility(player)) return; if (instance.isOnCooldown()) return; instance.onActivateAbility(player); RaceStorage.tickingRaces.put(player.getUUID(), new TickingRace()); storage.checkAndMarkDirty(instance); + } catch (Exception e) { + log.error("Failed to handle race ability activation for player {}", player.getName().getString(), e); + } }); }</review_comment_end -->
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (3)
118-118: Restrict system messages to Player entities only.
LivingEntity.sendSystemMessagemay not behave as intended for non-player entities. Since race messages are user-facing, restrict them to players.Apply this diff:
- if (raceMessage.isPresent()) getOwner().sendSystemMessage(raceMessage.get()); + if (raceMessage.isPresent() && getOwner() instanceof Player player) { + player.sendSystemMessage(raceMessage.get()); + }</review_comment_end -->
127-127: Fix potential NPE from auto-unboxing.
Changeable.get()returns@Nullable Boolean. Auto-unboxingteleport.get()in the condition will throw NPE if a listener sets it to null.Apply this diff:
- if (teleport.get() && getOwner() instanceof ServerPlayer player) SpawnPointHelper.teleportToNewSpawn(player); + if (Boolean.TRUE.equals(teleport.get()) && getOwner() instanceof ServerPlayer player) { + SpawnPointHelper.teleportToNewSpawn(player); + }</review_comment_end -->
138-142: Add error handling and attribute reapplication in load.
ManasRaceInstance.fromNBTcan throw exceptions on malformed data, and loaded race attributes are not reapplied to the entity, potentially leaving the entity with stale stats.Apply this diff:
@Override public void load(CompoundTag data) { if (!data.contains(RACE_KEY)) return; - this.raceInstance = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); + try { + ManasRaceInstance loaded = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); + this.raceInstance = loaded; + LivingEntity owner = this.getOwner(); + if (!owner.level().isClientSide()) { + loaded.addAttributeModifiers(owner); + } + } catch (Exception e) { + log.error("Failed to load race for entity {}. Clearing race. Data: {}", this.getOwner(), data, e); + this.raceInstance = null; + } }</review_comment_end -->
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (2)
127-137: Past NPE concern is not valid for modern Java.The previously flagged null pointer concern on line 129 is not an issue. The
instanceofpattern matching (Java 14+) handles null correctly—ifhitResult.getEntity()returns null, theinstanceofcheck returns false without throwing an NPE.Verify that the project targets Java 14 or later:
#!/bin/bash # Check Gradle build files for Java version configuration rg -n "sourceCompatibility|targetCompatibility|javaVersion|JavaVersion" --type gradle
23-23: Unsafe cast indicates type system issue.The unchecked cast to
ResourceKey<Registry<ManasSkill>>suggests the builder on line 22 may not be producing the expected type. This is related to the previously flagged builder syntax issue.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (2)
45-45: Duplicate: tickingSkills still lacks thread-safety.The static
tickingSkillsmultimap is accessed across event hooks (lines 62, 84-91, 161-170, 250-251) without synchronization. If any event runs off the main thread or if concurrent player actions occur, data races may arise. UseMultimaps.synchronizedMultimap()or a thread-safe alternative.
250-251: Duplicate: Type mismatch prevents ticking skill removal.Comparing
tickingSkill.getSkill()(aManasSkill) toskill.getSkill()(also aManasSkill) using==reference equality will fail to match unless they're the exact same object instance. If the skill instances are different objects representing the same skill type, ticking entries will leak.Apply this diff:
UUID ownerID = getOwner().getUUID(); if (tickingSkills.containsKey(ownerID)) - tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.getSkill() == skill.getSkill()); + tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.getSkill().equals(skill.getSkill()));skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java (2)
62-62: Duplicate: Unsynchronized access to tickingSkills.Directly calling
tickingSkills.put()without synchronization can cause concurrent modification issues if multiple threads access this map. This is the same concurrency concern flagged for line 45 in SkillStorage.
26-28: Validate keyNumber to prevent misuse.The
keyNumberfield is read from the network without validation. Negative values or extremely large values could cause unexpected behavior in downstream code.Add validation in the handle method:
if (mode < 0 || mode >= skill.getModes()) return; + if (keyNumber < 0 || keyNumber > 10) return; // Adjust max as needed if (skill.onCoolDown(mode) && !skill.canIgnoreCoolDown(player, mode)) return;skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java (1)
62-68: Duplicate: NPE risk from auto-unboxing null Integer.Lines 64 and 67 call
modeChangeable.get()which returnsInteger. If the event listener sets it to null, auto-unboxing tointwill throw NPE. Add a null check before using the mode value.Apply this diff:
ManasSkillInstance skill = skillChangeable.get(); - if (skill == null || deltaChangeable.isEmpty()) return; - if (!skill.canScroll(player, modeChangeable.get())) return; + if (skill == null || deltaChangeable.isEmpty() || modeChangeable.isEmpty()) return; + Integer mode = modeChangeable.get(); + if (mode == null || mode < 0) return; + if (!skill.canScroll(player, mode)) return; if (!skill.canInteractSkill(player)) return; - skill.onScroll(player, deltaChangeable.get(), modeChangeable.get()); + skill.onScroll(player, deltaChangeable.get(), mode); storage.checkAndMarkDirty(skill);
🧹 Nitpick comments (4)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
44-60: Redundant null check after Optional.isPresent().Lines 46-47 check
if (owner == null)immediately afterchangeableTarget.get(), butchangeableTarget.isPresent()at line 45 already guarantees a non-null value. This check is redundant and can be removed.Apply this diff:
EntityEvents.LIVING_CHANGE_TARGET.register((entity, changeableTarget) -> { if (!changeableTarget.isPresent()) return EventResult.pass(); LivingEntity owner = changeableTarget.get(); - if (owner == null) return EventResult.pass(); Races storage = RaceAPI.getRaceFrom(entity);race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java (1)
8-12: Inconsistent null annotation libraries.The file mixes Lombok's
@NonNull(line 8) with JetBrains'@NotNull(line 11). While functionally similar, mixing annotation libraries can cause confusion and inconsistent behavior across tools.Standardize on one library. Given that Lombok is already a project dependency, consider using
@NonNullconsistently:import lombok.NonNull; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable;Then update all
@NotNullusages to@NonNull:- default boolean setRace(@NotNull ResourceLocation raceId, boolean teleportToSpawn) { + default boolean setRace(@NonNull ResourceLocation raceId, boolean teleportToSpawn) {Also applies to lines 23, 43, 47.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (1)
40-40: Rename variable for clarity.The variable
owneris misleading—it represents the new target entity, not an owner. Rename tonewTargetortargetto improve readability.Apply this diff:
- LivingEntity owner = changeableTarget.get(); - if (owner == null) return EventResult.pass(); + LivingEntity newTarget = changeableTarget.get(); + if (newTarget == null) return EventResult.pass(); Skills storage = SkillAPI.getSkillsFrom(entity); for (ManasSkillInstance instance : storage.getLearnedSkills()) { - if (!instance.canInteractSkill(owner)) continue; + if (!instance.canInteractSkill(newTarget)) continue; if (!instance.onBeingTargeted(changeableTarget, entity)) {skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
267-269: Document resetExistingData flag lifecycleExplain in
SkillStorage(e.g. in Javadoc or inline comments onload/saveOutdated) that the"resetExistingData"flag is only set bysaveOutdatedwhen migrating removed skills and is omitted by the normalsave(freshCompoundTag), so it only triggers a single clear.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
gradle.properties(2 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceAbilityActivationPacket.java(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceAbilityReleasePacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- gradle.properties
- race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceAbilityReleasePacket.java
🧰 Additional context used
🧬 Code graph analysis (10)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (2)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (6)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java (1)
ManasCoreSkill(16-27)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-699)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java (3)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-699)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceAbilityActivationPacket.java (3)
race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
ManasRaceInstance(43-497)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/impl/TickingRace.java (1)
TickingRace(18-40)
race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java (1)
race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java (3)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-699)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (6)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
ManasRaceInstance(43-497)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java (4)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-699)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java (1)
TickingSkill(19-47)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java (1)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (3)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)
🔇 Additional comments (12)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
30-120: Consistent event handler pattern with proper state management.The event handlers follow a clean, consistent pattern:
- Retrieve race storage from entity
- Early return if no race is present
- Check ability activation eligibility
- Invoke race instance callbacks
- Mark storage dirty when state changes
This design properly integrates with the race lifecycle and ensures state synchronization across client and server.
race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java (2)
23-27: Proper null handling for registry lookups.The addition of the null check at line 25 addresses the potential
NullPointerExceptionfrom failed registry lookups that was flagged in previous reviews. The method now safely returnsfalsewhen the race ID is not found.
71-73: Efficient dirty-checking helper method.The
checkAndMarkDirty()helper cleanly encapsulates the conditional dirty-marking pattern used throughout the event handlers inRaceRegistry.java, improving code reuse and readability.race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceAbilityActivationPacket.java (1)
48-50: Good addition of cooldown check.The cooldown validation (Line 50) prevents activating abilities during cooldown periods, improving the activation flow.
</review_comment_end -->
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (2)
48-72: Improved dirty-marking strategy.The refactored approach eliminates per-tick
markDirty()calls for all entities (Lines 48-54) and instead marks dirty conditionally intickRace(Line 89) and for held abilities (Line 71). This reduces unnecessary synchronization overhead.</review_comment_end -->
110-110: Good check to avoid redundant attribute operations.The condition
instance != null && instance != race(Line 110) prevents unnecessary attribute removal and re-application when setting the same race, addressing a past concern.</review_comment_end -->
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (2)
173-173: Thread-safe map is good, but verify read-side visibility.Using
ConcurrentHashMapforskillInstancesis appropriate for multi-threaded modifications. However, ensure that all reads (lines 180-182, 208-209, etc.) see the latest writes without additional synchronization, asConcurrentHashMapguarantees happens-before relationships for put/get operations.
224-224: Incorrect removal from collection.Line 224 calls
this.getLearnedSkills().remove(instance)on the values collection returned byskillInstances.values(). Collections.values() returns a view that doesn't support removal by value; it only supports iterator-based removal. This will likely throwUnsupportedOperationException.Apply this diff:
- this.getLearnedSkills().remove(instance); + this.skillInstances.remove(instance.getSkillId());Likely an incorrect or invalid review comment.
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java (1)
29-35: Documentation for sync parameter is now present.The documentation added at lines 29-34 clearly explains the
syncparameter's behavior. This addresses the previous review concern.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillTogglePacket.java (1)
37-61: Toggle logic looks correct.The packet handler properly:
- Validates server-side execution (line 38)
- Checks for null player (line 41)
- Uses event system for interception (line 45)
- Validates skill interaction permission (line 49)
- Toggles state and invokes lifecycle callbacks (lines 51-57)
- Marks storage dirty (line 58)
The previous review concerns about list size validation don't apply since this now handles a single skill.
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java (1)
57-57: Mode validation is present.Line 57 validates that
modeis within bounds[0, skill.getModes()), addressing the previous review concern about mode validation.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillScrollPacket.java (1)
36-40: Size validation is present.The
validateListmethod properly limits the map to 100 entries, preventing DoS attacks via excessive skill updates.
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java
Outdated
Show resolved
Hide resolved
| default boolean learnSkill(@NotNull ResourceLocation skillId) { | ||
| return learnSkill(SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance()); | ||
| } | ||
|
|
||
| default boolean learnSkill(@NotNull ResourceLocation skillId, MutableComponent component) { | ||
| return learnSkill(SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance(), component); | ||
| } |
There was a problem hiding this comment.
Potential NPE if skill not found in registry.
Lines 38 and 42 call SkillAPI.getSkillRegistry().get(skillId), which may return null if the skill ID is not registered. Calling createDefaultInstance() on a null will throw NPE.
Add null checks or document that callers must ensure valid skill IDs:
default boolean learnSkill(@NotNull ResourceLocation skillId) {
+ ManasSkill skill = SkillAPI.getSkillRegistry().get(skillId);
+ if (skill == null) return false;
- return learnSkill(SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance());
+ return learnSkill(skill.createDefaultInstance());
}
default boolean learnSkill(@NotNull ResourceLocation skillId, MutableComponent component) {
+ ManasSkill skill = SkillAPI.getSkillRegistry().get(skillId);
+ if (skill == null) return false;
- return learnSkill(SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance(), component);
+ return learnSkill(skill.createDefaultInstance(), component);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| default boolean learnSkill(@NotNull ResourceLocation skillId) { | |
| return learnSkill(SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance()); | |
| } | |
| default boolean learnSkill(@NotNull ResourceLocation skillId, MutableComponent component) { | |
| return learnSkill(SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance(), component); | |
| } | |
| default boolean learnSkill(@NotNull ResourceLocation skillId) { | |
| ManasSkill skill = SkillAPI.getSkillRegistry().get(skillId); | |
| if (skill == null) return false; | |
| return learnSkill(skill.createDefaultInstance()); | |
| } | |
| default boolean learnSkill(@NotNull ResourceLocation skillId, MutableComponent component) { | |
| ManasSkill skill = SkillAPI.getSkillRegistry().get(skillId); | |
| if (skill == null) return false; | |
| return learnSkill(skill.createDefaultInstance(), component); | |
| } |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/Skills.java
around lines 37 to 43, the calls to
SkillAPI.getSkillRegistry().get(skillId).createDefaultInstance() can NPE if the
registry returns null; update both default methods to first fetch the skill
entry into a local variable, check for null, and handle it consistently (e.g.,
return false or throw an IllegalArgumentException with a clear message) before
calling createDefaultInstance(); ensure both overloads use the same
null-handling behavior and include a clear log or exception message mentioning
the missing skillId.
| SkillEvents.SKILL_DAMAGE_PRE_CALCULATION.register((storage, target, source, amount) -> { | ||
| if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | ||
|
|
||
| Skills ownerStorage = SkillAPI.getSkillsFrom(owner); | ||
| for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) { | ||
| if (!instance.canInteractSkill(owner)) continue; | ||
| if (!instance.onDamageEntity(owner, target, source, amount)) { | ||
| storage.checkAndMarkDirty(instance); | ||
| return EventResult.interruptFalse(); | ||
| } else storage.checkAndMarkDirty(instance); | ||
| } | ||
| return EventResult.pass(); | ||
| }); |
There was a problem hiding this comment.
Critical: Wrong storage object used for dirty tracking.
Lines 73 and 75 call storage.checkAndMarkDirty(instance), but instance belongs to ownerStorage, not storage. This causes the target's skill storage to be marked dirty instead of the owner's storage.
Apply this diff to fix the issue:
Skills ownerStorage = SkillAPI.getSkillsFrom(owner);
for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) {
if (!instance.canInteractSkill(owner)) continue;
if (!instance.onDamageEntity(owner, target, source, amount)) {
- storage.checkAndMarkDirty(instance);
+ ownerStorage.checkAndMarkDirty(instance);
return EventResult.interruptFalse();
- } else storage.checkAndMarkDirty(instance);
+ } else ownerStorage.checkAndMarkDirty(instance);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| SkillEvents.SKILL_DAMAGE_PRE_CALCULATION.register((storage, target, source, amount) -> { | |
| if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | |
| Skills ownerStorage = SkillAPI.getSkillsFrom(owner); | |
| for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) { | |
| if (!instance.canInteractSkill(owner)) continue; | |
| if (!instance.onDamageEntity(owner, target, source, amount)) { | |
| storage.checkAndMarkDirty(instance); | |
| return EventResult.interruptFalse(); | |
| } else storage.checkAndMarkDirty(instance); | |
| } | |
| return EventResult.pass(); | |
| }); | |
| SkillEvents.SKILL_DAMAGE_PRE_CALCULATION.register((storage, target, source, amount) -> { | |
| if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | |
| Skills ownerStorage = SkillAPI.getSkillsFrom(owner); | |
| for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) { | |
| if (!instance.canInteractSkill(owner)) continue; | |
| if (!instance.onDamageEntity(owner, target, source, amount)) { | |
| ownerStorage.checkAndMarkDirty(instance); | |
| return EventResult.interruptFalse(); | |
| } else ownerStorage.checkAndMarkDirty(instance); | |
| } | |
| return EventResult.pass(); | |
| }); |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java
around lines 66 to 78, the code calls storage.checkAndMarkDirty(instance) when
iterating the owner's learned skills, which incorrectly marks the target's
SkillStorage dirty; change both calls to
ownerStorage.checkAndMarkDirty(instance) so the owner's Skills storage is
updated instead, preserving the existing control flow (i.e., call
ownerStorage.checkAndMarkDirty(instance) before returning
EventResult.interruptFalse() and in the else branch).
| SkillEvents.SKILL_DAMAGE_POST_CALCULATION.register((storage, target, source, amount) -> { | ||
| if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | ||
|
|
||
| Skills ownerStorage = SkillAPI.getSkillsFrom(owner); | ||
| for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) { | ||
| if (!instance.canInteractSkill(owner)) continue; | ||
| if (!instance.onTouchEntity(owner, target, source, amount)) { | ||
| storage.checkAndMarkDirty(instance); | ||
| return EventResult.interruptFalse(); | ||
| } else storage.checkAndMarkDirty(instance); | ||
| } | ||
| return EventResult.pass(); | ||
| }); |
There was a problem hiding this comment.
Critical: Wrong storage object used for dirty tracking.
Lines 87 and 89 have the same bug as the PRE_CALCULATION handler—storage.checkAndMarkDirty(instance) should use ownerStorage instead.
Apply this diff:
Skills ownerStorage = SkillAPI.getSkillsFrom(owner);
for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) {
if (!instance.canInteractSkill(owner)) continue;
if (!instance.onTouchEntity(owner, target, source, amount)) {
- storage.checkAndMarkDirty(instance);
+ ownerStorage.checkAndMarkDirty(instance);
return EventResult.interruptFalse();
- } else storage.checkAndMarkDirty(instance);
+ } else ownerStorage.checkAndMarkDirty(instance);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| SkillEvents.SKILL_DAMAGE_POST_CALCULATION.register((storage, target, source, amount) -> { | |
| if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | |
| Skills ownerStorage = SkillAPI.getSkillsFrom(owner); | |
| for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) { | |
| if (!instance.canInteractSkill(owner)) continue; | |
| if (!instance.onTouchEntity(owner, target, source, amount)) { | |
| storage.checkAndMarkDirty(instance); | |
| return EventResult.interruptFalse(); | |
| } else storage.checkAndMarkDirty(instance); | |
| } | |
| return EventResult.pass(); | |
| }); | |
| SkillEvents.SKILL_DAMAGE_POST_CALCULATION.register((storage, target, source, amount) -> { | |
| if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | |
| Skills ownerStorage = SkillAPI.getSkillsFrom(owner); | |
| for (ManasSkillInstance instance : ownerStorage.getLearnedSkills()) { | |
| if (!instance.canInteractSkill(owner)) continue; | |
| if (!instance.onTouchEntity(owner, target, source, amount)) { | |
| ownerStorage.checkAndMarkDirty(instance); | |
| return EventResult.interruptFalse(); | |
| } else ownerStorage.checkAndMarkDirty(instance); | |
| } | |
| return EventResult.pass(); | |
| }); |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java
around lines 80 to 92, the dirty-tracking calls use the wrong skills storage
variable: replace both occurrences of storage.checkAndMarkDirty(instance) with
ownerStorage.checkAndMarkDirty(instance) so the owner's Skills container is
marked dirty after the instance is modified; keep the surrounding control
flow/returns unchanged.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
1-92: Consider adding Javadoc for this public API.This is a public API interface without any documentation. Consider adding Javadoc comments to describe:
- When each event is fired in the skill lifecycle
- The semantics of
Changeableparameters (which values can be modified by handlers)- Thread safety and side (client/server/both) for each event
- Return value semantics for EventResult
Example documentation structure:
/** * Central event hub for the Manas skill system. * <p> * Events are fired at various points in the skill lifecycle to allow mods * to observe and modify skill behavior. */ public interface SkillEvents { /** * Fired after the skill system has initialized. * This is a loop event (all handlers are invoked). */ Event<Runnable> POST_INIT = EventFactory.createLoop(); /** * Fired when a skill is being unlocked for a player. * Handlers can modify the unlock message or cancel the unlock. * * @see UnlockSkillEvent */ Event<UnlockSkillEvent> UNLOCK_SKILL = EventFactory.createEventResult(); // ... document remaining events }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
race-common/src/main/java/io/github/manasmods/manascore/race/ManasCoreRace.java(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java
🧰 Additional context used
🧬 Code graph analysis (3)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (1)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)
race-common/src/main/java/io/github/manasmods/manascore/race/ManasCoreRace.java (2)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
RaceRegistry(24-125)race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/ManasRaceNetwork.java (1)
ManasRaceNetwork(13-22)
race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java (1)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)
🔇 Additional comments (3)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (3)
16-31: LGTM! Event factory usage is consistent with functional interface signatures.The event declarations correctly use
createLoop()for void-returning handlers (POST_INIT, SKILL_POST_TICK) andcreateEventResult()for EventResult-returning handlers. The naming is clear and follows conventions.
89-92: Keep Skills storage parameter in SkillDamageCalculationEvent
All invocations ofcalculate(…)consistently pass aSkillsinstance, indicating handlers expect access to the full storage rather than a single skill.
59-67: No change needed: client-side delta remains immutable. The client scroll event only filters scroll actions and handles mode changes; mutable delta adjustments happen server-side viaChangeable<Double>.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (9)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (5)
119-119: Only send system messages to Player entities.
sendSystemMessageis available onLivingEntitybut is primarily intended for players. Non-player entities (mobs) don't display chat messages, so this call will be ineffective for them and could cause issues depending on the implementation.Apply this diff to guard the message sending:
- if (raceMessage.isPresent()) getOwner().sendSystemMessage(raceMessage.get()); + if (raceMessage.isPresent() && owner instanceof Player player) { + player.sendSystemMessage(raceMessage.get()); + }
121-121: Consider guarding attribute addition when race hasn't changed.If
setRaceis called with the same race instance that's already active (i.e.,instance == race), line 111 correctly skips removal, but line 121 still callsaddAttributeModifiers. This could lead to duplicate modifiers if the same race is set multiple times.Consider adding a check to skip attribute reapplication when the race hasn't changed:
if (raceMessage.isPresent() && owner instanceof Player player) { player.sendSystemMessage(raceMessage.get()); } race.markDirty(); - race.addAttributeModifiers(owner); + if (instance != race) { + race.addAttributeModifiers(owner); + } owner.setHealth(owner.getMaxHealth()); race.onRaceSet(owner);Alternatively, ensure that
addAttributeModifiersis idempotent or that callers never invokesetRacewith the same race instance.
74-91: Wrap race ticking in error handling to prevent server crashes.If
instance.onTick(entity)(line 88) throws an exception, it could crash the server's tick loop. Race implementations from third-party mods or custom content might have bugs that shouldn't halt the entire server.Apply this diff to add error handling:
private static void tickRace(LivingEntity entity, Races storage) { Optional<ManasRaceInstance> optional = storage.getRace(); if (optional.isEmpty()) return; ManasRaceInstance instance = optional.get(); if (instance.isOnCooldown()) { if (!RaceEvents.RACE_UPDATE_COOLDOWN.invoker().cooldown(instance, entity, instance.getCooldown()).isFalse()) instance.setCooldown(instance.getCooldown() - 1); storage.checkAndMarkDirty(instance); } if (!instance.canActivateAbility(entity)) return; if (!instance.canTick(entity)) return; if (RaceEvents.RACE_PRE_TICK.invoker().tick(instance, entity).isFalse()) return; - instance.onTick(entity); - RaceEvents.RACE_POST_TICK.invoker().tick(instance, entity); - storage.checkAndMarkDirty(instance); + try { + instance.onTick(entity); + RaceEvents.RACE_POST_TICK.invoker().tick(instance, entity); + storage.checkAndMarkDirty(instance); + } catch (Exception e) { + log.error("Error ticking race {} for entity {}: {}", instance.getRaceId(), entity, e.getMessage(), e); + } }
128-128: Fix potential NPE when reading Changeable teleport.
Changeable.get()can returnnull(based on the Changeable implementation provided in context). Auto-unboxingteleport.get()in the boolean condition will throw aNullPointerExceptionif a listener sets it to null.Apply this diff to use null-safe comparison:
- if (teleport.get() && getOwner() instanceof ServerPlayer player) SpawnPointHelper.teleportToNewSpawn(player); + if (Boolean.TRUE.equals(teleport.get()) && getOwner() instanceof ServerPlayer player) { + SpawnPointHelper.teleportToNewSpawn(player); + }
139-143: Add error handling and attribute reapplication after loading.Two issues with the current implementation:
ManasRaceInstance.fromNBTcan throw aNullPointerExceptionif the race registry lookup fails (e.g., unknown race ID, mod removed). This would crash chunk loading.- After setting
this.raceInstance, attribute modifiers are not reapplied. When entities are loaded from NBT (chunk load, dimension change, server restart), they will have the race instance but miss the attribute modifiers, leading to incorrect stats until the next race change.Apply this diff to add error handling and attribute reapplication:
@Override public void load(CompoundTag data) { if (!data.contains(RACE_KEY)) return; - this.raceInstance = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); + try { + ManasRaceInstance loaded = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); + this.raceInstance = loaded; + LivingEntity owner = this.getOwner(); + if (!owner.level().isClientSide()) { + loaded.addAttributeModifiers(owner); + } + } catch (Exception e) { + log.error("Failed to load race instance for entity {}. Clearing race. Data: {}", this.getOwner(), data, e); + this.raceInstance = null; + } }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (3)
348-350: Mark dirty and defend against external mutationLine [349]: assigning the caller’s list directly creates aliasing and never flags the state change for sync, so later mutations can slip through undetected. Please copy the contents and call
markDirty().- this.cooldownList = list; + this.cooldownList = new ArrayList<>(list); + markDirty();
145-148: ValidateskillLocationbefore registry lookupLine [146]:
ResourceLocation.tryParsecan returnnullfor malformed data. Passing that through to the registry and later callingskill.createDefaultInstance()will still throw. Guard the parsed ID and fail fast with a descriptive exception before dereferencing it.- ResourceLocation skillLocation = ResourceLocation.tryParse(tag.getString("skill")); + ResourceLocation skillLocation = ResourceLocation.tryParse(tag.getString("skill")); + if (skillLocation == null) { + throw new IllegalArgumentException("Invalid skill ID in NBT data."); + }
67-77: Deep-copy sub-instances incopy()Line [75]: assigning
clone.subInstances = this.subInstances;makes the clone share mutable children with the original. Mutating a sub-skill through either instance will silently mutate the other copy. Please allocate a fresh map and clone each child instance before storing it.- clone.subInstances = this.subInstances; + clone.subInstances = new HashMap<>(); + for (Map.Entry<ManasSkill, ManasSkillInstance> entry : this.subInstances.entrySet()) { + clone.subInstances.put(entry.getKey(), entry.getValue().copy()); + }skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
45-45: This concern was already flagged in a previous review and remains unaddressed.
🧹 Nitpick comments (2)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (1)
40-40: Consider thread safety for the public static Multimap.
tickingRacesis a public static mutableMultimapthat is accessed and modified from multiple tick handlers. While Guava'sArrayListMultimapis not thread-safe by default, concurrent access from different entities ticking simultaneously could lead toConcurrentModificationExceptionor data races in multi-threaded server environments.Consider using a thread-safe alternative like
Multimaps.synchronizedMultimap()or document that access must be externally synchronized.- public static final Multimap<UUID, TickingRace> tickingRaces = ArrayListMultimap.create(); + public static final Multimap<UUID, TickingRace> tickingRaces = Multimaps.synchronizedMultimap(ArrayListMultimap.create());Note: You'll need to add the import:
import com.google.common.collect.Multimaps;skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
109-127: Consider removing redundantgetSkilllookup.Line 112 fetches
storage.getSkill(instance.getSkill())wheninstancewas already obtained fromstorage.getLearnedSkills()at line 111. This adds unnecessary overhead in the hot path (called every 5 seconds per entity).Apply this diff to simplify:
private static void tickSkills(LivingEntity entity, Skills storage) { List<ManasSkillInstance> tickingSkills = new ArrayList<>(); for (ManasSkillInstance instance : storage.getLearnedSkills()) { - Optional<ManasSkillInstance> optional = storage.getSkill(instance.getSkill()); - if (optional.isEmpty()) continue; - - ManasSkillInstance skillInstance = optional.get(); - if (!skillInstance.canInteractSkill(entity)) continue; - if (!skillInstance.canTick(entity)) continue; - if (SkillEvents.SKILL_PRE_TICK.invoker().tick(skillInstance, entity).isFalse()) continue; - tickingSkills.add(skillInstance); + if (!instance.canInteractSkill(entity)) continue; + if (!instance.canTick(entity)) continue; + if (SkillEvents.SKILL_PRE_TICK.invoker().tick(instance, entity).isFalse()) continue; + tickingSkills.add(instance); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
gradle.properties(2 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- gradle.properties
- skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java
🧰 Additional context used
🧬 Code graph analysis (7)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (2)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillActivationPacket.java (4)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-709)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java (1)
TickingSkill(21-85)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (5)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-709)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (2)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/InternalSkillPacketActions.java (1)
InternalSkillPacketActions(18-54)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (1)
SkillRegistry(20-142)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java (2)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
ManasSkill(56-535)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-709)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/network/c2s/RequestSkillReleasePacket.java (1)
storage-common/src/main/java/io/github/manasmods/manascore/storage/impl/StorageManager.java (1)
StorageManager(28-142)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (6)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
Log4j2(39-300)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
ManasRaceInstance(43-497)
🔇 Additional comments (8)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (5)
43-57: LGTM!The initialization logic correctly registers the storage, sets up tick handlers with appropriate throttling (INSTANCE_UPDATE cadence), and ensures cleanup on player quit. The event registration follows the module's patterns.
59-66: LGTM!The throttling logic correctly limits race ticking to once per second (every 20 ticks), reducing computational overhead. The null-check and early returns are appropriate.
99-101: LGTM!Clean and safe implementation using
Optionalto handle the nullable race instance.
133-137: LGTM!The save logic correctly handles the null case and delegates serialization to the race instance.
145-147: LGTM!The protected helper correctly casts the holder to
LivingEntity, which is safe given the registration predicate ininit()ensures onlyLivingEntityinstances use this storage.skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (3)
48-93: LGTM: Event handler setup is well-structured.The event handler chains are properly organized with appropriate callbacks. The PLAYER_QUIT cleanup (lines 82-92) correctly removes attribute modifiers before clearing ticking skills, preventing modifier leaks.
254-295: LGTM: Persistence implementation is well-designed.The persistence logic correctly handles:
- Full saves (lines 255-262)
- Incremental loads with reset capability (lines 265-278)
- Optimized dirty-tracking saves (lines 281-295) that distinguish between skill removals (requiring full reset) and updates (saving only dirty instances)
The error handling at lines 274-276 prevents corruption from individual skill load failures.
212-227: Critical bug: Cannot remove from Collection.values().Line 224 attempts
this.getLearnedSkills().remove(instance), butgetLearnedSkills()returnsskillInstances.values()(line 181), which is a Collection view that does not support removal operations. This will throwUnsupportedOperationExceptionat runtime.Apply this diff to fix:
if (forgetMessage.isPresent()) getOwner().sendSystemMessage(forgetMessage.get()); instance.onForgetSkill(this.getOwner()); instance.markDirty(); - this.getLearnedSkills().remove(instance); + this.skillInstances.remove(skillId); this.hasRemovedSkills = true; markDirty();Likely an incorrect or invalid review comment.
| private static void handleRaceHeldTick(Player player, Races storage) { | ||
| if (!tickingRaces.containsKey(player.getUUID())) return; | ||
| tickingRaces.get(player.getUUID()).removeIf(skill -> !skill.tick(storage, player)); | ||
| storage.markDirty(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Only mark dirty when races are actually removed.
Calling markDirty() every tick when tickingRaces contains the player's UUID causes unnecessary network synchronization, even when no races were removed. This generates excessive sync traffic.
Apply this diff to only mark dirty when the collection is modified:
private static void handleRaceHeldTick(Player player, Races storage) {
if (!tickingRaces.containsKey(player.getUUID())) return;
- tickingRaces.get(player.getUUID()).removeIf(skill -> !skill.tick(storage, player));
- storage.markDirty();
+ boolean removed = tickingRaces.get(player.getUUID()).removeIf(skill -> !skill.tick(storage, player));
+ if (removed) storage.markDirty();
}🤖 Prompt for AI Agents
In
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java
around lines 68 to 72, the method always calls storage.markDirty() every tick,
causing unnecessary syncs; change it to only markDirty when the collection was
actually modified by storing the set/list from
tickingRaces.get(player.getUUID()), call removeIf(...) and capture its boolean
result, then call storage.markDirty() only if that result is true (i.e.,
elements were removed).
| public void handleSkillRelease(ManasSkillInstance skillInstance, int heldTick, int keyNumber, int mode, boolean ignoreInteract) { | ||
| Changeable<ManasSkillInstance> changeable = Changeable.of(skillInstance); | ||
| if (SkillEvents.RELEASE_SKILL.invoker().releaseSkill(changeable, this.getOwner(), keyNumber, mode, heldTick).isFalse()) return; | ||
| ManasSkillInstance skill = changeable.get(); | ||
| if (skill == null) return; | ||
|
|
||
| if ((ignoreInteract || skill.canInteractSkill(getOwner())) && mode < skill.getModes()) { | ||
| if (!skill.onCoolDown(mode) || skill.canIgnoreCoolDown(getOwner(), mode)) { | ||
| skill.onRelease(getOwner(), heldTick, keyNumber, mode); | ||
| this.checkAndMarkDirty(skillInstance); | ||
| } | ||
| } | ||
|
|
||
| skill.removeAttributeModifiers(getOwner(), mode); | ||
| UUID ownerID = getOwner().getUUID(); | ||
| if (tickingSkills.containsKey(ownerID)) | ||
| tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.matches(skill.getSkill(), mode)); | ||
| this.checkAndMarkDirty(skillInstance); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
Verify TickingSkill.matches() implementation.
Line 250 uses tickingSkill.matches(skill.getSkill(), mode) to identify and remove the released skill from tickingSkills. Ensure the matches() method compares skill identifiers (ResourceLocation) using .equals() rather than object identity (==) to correctly match and remove entries.
A previous review flagged a type mismatch issue in this area. Please confirm the implementation avoids comparing incompatible types.
Run the following script to verify the matches method implementation:
🏁 Script executed:
#!/bin/bash
# Description: Verify TickingSkill.matches() implementation compares identifiers correctly.
# Search for TickingSkill class and its matches method
ast-grep --pattern $'class TickingSkill {
$$$
matches($_, $_) {
$$$
}
$$$
}'Length of output: 0
🏁 Script executed:
#!/bin/bash
# Locate TickingSkill.java
file=$(fd -t f TickingSkill.java | head -n1)
echo "Found file: $file"
# Show class declaration
rg -nP 'class\s+TickingSkill' -n "$file"
# Show matches method implementation
rg -nP 'matches\s*\(' -C3 -n "$file"Length of output: 1264
Use equals in TickingSkill.matches() instead of ==.
In TickingSkill.java (lines 55–61), both matches methods use this.skill == skill, which fails when different instances represent the same skill. Change these to compare by identifier (e.g. this.skill.equals(skill) or compare their registry names) to ensure reliable removal.
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java
lines 234-252 and in
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/TickingSkill.java
lines 55-61: the TickingSkill.matches(...) methods currently use reference
equality (==) to compare skills which fails when different instances represent
the same skill; change those comparisons to use a semantic equality check (e.g.,
this.skill.equals(skill) or compare their unique identifier/registry name) and
make the comparison null-safe (check for nulls before calling equals) so removal
in SkillStorage.removeIf reliably matches equivalent skills. Ensure both
overloads of matches are updated accordingly.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (3)
25-25: Use constant naming convention forregistryId.The variable
registryIdis a compile-time constant and should follow theUPPER_SNAKE_CASEnaming convention.Apply this diff:
- private static final ResourceLocation registryId = ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, "races"); - public static final Registrar<ManasRace> RACES = RegistrarManager.get(ModuleConstants.MOD_ID).<ManasRace>builder(registryId).syncToClients().build(); + private static final ResourceLocation REGISTRY_ID = ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, "races"); + public static final Registrar<ManasRace> RACES = RegistrarManager.get(ModuleConstants.MOD_ID).<ManasRace>builder(REGISTRY_ID).syncToClients().build();
37-40: Eliminate duplicatecheckAndMarkDirtycalls.The method
checkAndMarkDirtyis called in both the if and else branches. This pattern repeats in multiple handlers (lines 37-40, 55-58, 70-73, 84-87, 98-101).Apply this pattern to simplify:
- if (!instance.onEffectAdded(entity, source, changeableTarget)) { - storage.checkAndMarkDirty(instance); - return EventResult.interruptFalse(); - } else storage.checkAndMarkDirty(instance); + boolean result = instance.onEffectAdded(entity, source, changeableTarget); + storage.checkAndMarkDirty(instance); + if (!result) return EventResult.interruptFalse(); return EventResult.pass();Apply similar changes to the other handlers at lines 55-58, 70-73, 84-87, and 98-101.
45-47: Remove redundant null check afterOptional.isPresent().Line 45 checks
changeableTarget.isPresent(), which guarantees thatchangeableTarget.get()on line 46 will not returnnull(assuming correct Optional semantics). The null check on line 47 is redundant.Apply this diff:
if (!changeableTarget.isPresent()) return EventResult.pass(); LivingEntity owner = changeableTarget.get(); - if (owner == null) return EventResult.pass();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
gradle.properties(2 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- gradle.properties
🧰 Additional context used
🧬 Code graph analysis (1)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (3)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)
🔇 Additional comments (1)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
123-124: LGTM!The private constructor correctly prevents instantiation of this utility class.
| if (!conqueredEnd) { | ||
| instance.addAttributeModifiers(newPlayer); | ||
| newPlayer.setHealth(newPlayer.getMaxHealth()); | ||
| SpawnPointHelper.teleportToNewSpawn(newPlayer); | ||
| } | ||
|
|
||
| if (!instance.canActivateAbility(newPlayer)) return; | ||
| instance.onRespawn(newPlayer, conqueredEnd); | ||
| storage.checkAndMarkDirty(instance); |
There was a problem hiding this comment.
Inconsistent state tracking in respawn handler.
Lines 112-114 modify entity state (addAttributeModifiers, setHealth, teleport) regardless of whether canActivateAbility returns true. However, checkAndMarkDirty at line 119 is only reached when canActivateAbility is true (line 117). This means state modifications may not be persisted if the ability cannot be activated.
Consider one of these approaches:
- Move the
canActivateAbilitycheck before line 111 to gate all state modifications - Call
checkAndMarkDirtyafter line 115 to ensure state changes are persisted
Example fix (option 2):
ManasRaceInstance instance = optional.get();
if (!conqueredEnd) {
instance.addAttributeModifiers(newPlayer);
newPlayer.setHealth(newPlayer.getMaxHealth());
SpawnPointHelper.teleportToNewSpawn(newPlayer);
+ storage.checkAndMarkDirty(instance);
}
if (!instance.canActivateAbility(newPlayer)) return;
instance.onRespawn(newPlayer, conqueredEnd);
storage.checkAndMarkDirty(instance);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java
around lines 111 to 119, the respawn handler updates the player state
(addAttributeModifiers, setHealth, teleport) even when
instance.canActivateAbility(newPlayer) is false, but
storage.checkAndMarkDirty(instance) only runs when the ability can be activated;
move the canActivateAbility check to before line 111 to gate the state
modifications, or (preferred minimal change) add a call to
storage.checkAndMarkDirty(instance) immediately after line 115 so any state
changes are persisted regardless of the ability check.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
105-120: Inconsistent state tracking in respawn handler.The state modifications at lines 112-114 (
addAttributeModifiers,setHealth,teleportToNewSpawn) occur regardless of whethercanActivateAbilityreturns true. However,checkAndMarkDirtyat line 119 only executes when the ability can be activated (line 117), meaning state changes may not be persisted if the ability check fails.Consider moving the
canActivateAbilitycheck before line 111 to gate all state modifications, or add a call tostorage.checkAndMarkDirty(instance)after line 115 to ensure state changes are persisted.testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (2)
77-83: Fix duplicate AttributeModifier IDs causing collision.Both attribute modifiers use the same ResourceLocation ID
"test_critical_chance", which will cause them to collide. Each modifier must have a unique ID.Apply this diff to fix the duplicate IDs:
public static final RegistrySupplier<BlockItem> TEST_BLOCK_ITEM = ITEMS.register("test_block_item", () -> new BlockItem(RegistryTest.TEST_BLOCK.get(), new Item.Properties().arch$tab(TESTING_TAB).stacksTo(42) .attributes(ItemAttributeModifiers.builder() - .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), + .add(TestAttributeRegistry.TEST_ATTRIBUTE_PLAYER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_attr_player"), 69, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND) - .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_chance"), + .add(TestAttributeRegistry.TEST_ATTRIBUTE_ALL, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_attr_all"), 420, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND).build())));
177-192: Cap explosion radius to prevent excessive destruction during testing.Line 183 creates explosions with radius 10-12 blocks, which is extreme even for testing (TNT is 4, charged creeper is 6). This could cause performance issues and unintended map destruction.
Apply this diff to use a more reasonable radius:
public void onMobRemoved(LivingEntity entity, int i, Entity.RemovalReason removalReason) { if (removalReason == Entity.RemovalReason.KILLED) { if (entity.level() instanceof ServerLevel level) { double d = entity.getX(); double e = entity.getY() + (double)(entity.getBbHeight() / 2.0F); double f = entity.getZ(); - float g = 10.0F + entity.getRandom().nextFloat() * 2.0F; + float g = 3.0F + entity.getRandom().nextFloat() * 1.0F; if (this.equals(RegistryTest.TEST_MOB_EFFECT_PARTICLE.get())) level.explode(entity, null, AbstractWindCharge.EXPLOSION_DAMAGE_CALCULATOR, d, e, f, g, false, Level.ExplosionInteraction.TRIGGER, ParticleTypes.GUST_EMITTER_SMALL, ParticleTypes.GUST_EMITTER_LARGE, SoundEvents.BREEZE_WIND_CHARGE_BURST); else level.explode(entity, Explosion.getDefaultDamageSource(level, entity), null, d, e, f, g, false, Level.ExplosionInteraction.MOB, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE); } } }
🧹 Nitpick comments (3)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
29-103: Consider refactoring to reduce code duplication.The five event handlers (LIVING_EFFECT_ADDED, LIVING_CHANGE_TARGET, SKILL_DAMAGE_POST_CALCULATION, LIVING_DAMAGE, LIVING_DEATH) follow nearly identical patterns. Additionally, each handler calls
storage.checkAndMarkDirty(instance)in both the if and else branches, which can be simplified.Consider extracting a common helper method and simplifying the conditional logic. For example:
private static EventResult handleRaceEvent(LivingEntity entity, java.util.function.Predicate<ManasRaceInstance> handler) { Races storage = RaceAPI.getRaceFrom(entity); Optional<ManasRaceInstance> optional = storage.getRace(); if (optional.isEmpty()) return EventResult.pass(); ManasRaceInstance instance = optional.get(); if (!instance.canActivateAbility(entity)) return EventResult.pass(); boolean result = handler.test(instance); storage.checkAndMarkDirty(instance); return result ? EventResult.pass() : EventResult.interruptFalse(); }Then each handler can be simplified:
EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> handleRaceEvent(entity, instance -> instance.onEffectAdded(entity, source, changeableTarget)) );testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (2)
64-72: Consider more realistic test values for critical hit modifiers.The current values create extreme multipliers:
- 10 with
ADD_MULTIPLIED_TOTAL= 1000% critical damage multiplier- 50 with
ADD_MULTIPLIED_TOTAL= 5000% critical chanceWhile these may be intentional for testing, more moderate values would better exercise edge cases and ensure the system works correctly with realistic gameplay parameters.
133-152: Add null checks for storage objects and remove unused variables.The event handler has several minor issues:
- No null checks on
storageobjects returned bySkillAPI.getSkillsFrom()andRaceAPI.getRaceFrom(), which could lead to NPEs if the storage is unavailable.- Lines 137-138 declare unused variables
skillsandtestSkill.Consider applying these improvements:
PlayerEvent.DROP_ITEM.register((player, entity) -> { //Test giving Skills if (entity.getItem().is(Items.DIAMOND)) { Skills storage = SkillAPI.getSkillsFrom(player); - Registrar<ManasSkill> skills = SkillAPI.getSkillRegistry(); - RegistrySupplier<TestSkill> testSkill = TEST_SKILL; + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get skills storage for player"); + return EventResult.pass(); + } if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { ManasCoreTesting.LOG.info("Added Tested Skill!"); } } else if (entity.getItem().is(Items.EMERALD)) { Skills storage = SkillAPI.getSkillsFrom(player); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get skills storage for player"); + return EventResult.pass(); + } storage.forgetSkill(RegistryTest.TEST_SKILL.get(), Component.literal("Forgot Tested Skill!")); } else if (entity.getItem().is(Items.GOLD_INGOT)) { Races storage = RaceAPI.getRaceFrom(player); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get race storage for player"); + return EventResult.pass(); + } storage.setRace(RegistryTest.TEST_RACE.getId(), true, Component.literal("Set to Test Race!")); } return EventResult.pass(); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
gradle.properties(2 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- gradle.properties
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java
🧰 Additional context used
🧬 Code graph analysis (2)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (3)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (9)
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java (1)
ManasCoreAttributes(16-61)config-common/src/main/java/io/github/manasmods/manascore/config/ConfigRegistry.java (1)
ConfigRegistry(13-100)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)testing-common/src/main/java/io/github/manasmods/manascore/testing/ManasCoreTesting.java (1)
ManasCoreTesting(19-33)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestAttributeRegistry.java (1)
TestAttributeRegistry(13-21)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (1)
TestSkill(39-203)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRace.java (1)
TestRace(42-155)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java (1)
TestRaceEvolved(32-82)
🔇 Additional comments (2)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (2)
27-27: The unsafe cast concern from the previous review has been resolved.The current implementation uses
ResourceKey.createRegistryKey(RACES.key().location()), which is type-safe and avoids the unchecked cast warning that was previously flagged.
123-125: LGTM!The private constructor correctly prevents instantiation of this utility class.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (8)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
106-121: State modifications beforecanActivateAbilitycheck cause inconsistent dirty tracking.Lines 112-115 modify entity state (
addAttributeModifiers,setHealth, teleport) when!conqueredEnd, butcheckAndMarkDirtyat line 120 only runs ifcanActivateAbilityreturns true. This means attribute and health changes may not be persisted.This was previously flagged. Consider adding
storage.checkAndMarkDirty(instance)after line 115 to ensure state changes are persisted regardless of the ability check.🔎 Suggested fix:
ManasRaceInstance instance = optional.get(); if (!conqueredEnd) { instance.addAttributeModifiers(newPlayer); newPlayer.setHealth(newPlayer.getMaxHealth()); SpawnPointHelper.teleportToNewSpawn(newPlayer); + storage.checkAndMarkDirty(instance); } if (!instance.canActivateAbility(newPlayer)) return;race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (4)
69-73: UnconditionalmarkDirty()causes unnecessary sync traffic.
storage.markDirty()is called every tick when the player has entries intickingRaces, even if no races were actually removed. This generates excessive network synchronization.🔎 Only mark dirty when the collection is modified:
private static void handleRaceHeldTick(Player player, Races storage) { if (!tickingRaces.containsKey(player.getUUID())) return; - tickingRaces.get(player.getUUID()).removeIf(skill -> !skill.tick(storage, player)); - storage.markDirty(); + boolean removed = tickingRaces.get(player.getUUID()).removeIf(skill -> !skill.tick(storage, player)); + if (removed) storage.markDirty(); }
104-132: Multiple issues insetRacemethod need attention.
Line 120:
sendSystemMessageis called ongetOwner()which is aLivingEntity, but this method is primarily meaningful forPlayerentities.Line 129:
teleport.get()can returnnull, and auto-unboxing tobooleanwill throw NPE.🔎 Apply these fixes:
- if (raceMessage.isPresent()) getOwner().sendSystemMessage(raceMessage.get()); + if (raceMessage.isPresent() && owner instanceof Player p) { + p.sendSystemMessage(raceMessage.get()); + } race.markDirty(); race.addAttributeModifiers(owner); owner.setHealth(owner.getMaxHealth()); race.onRaceSet(owner); race.learnIntrinsicSkills(owner); this.raceInstance = race; - if (teleport.get() && getOwner() instanceof ServerPlayer player) SpawnPointHelper.teleportToNewSpawn(player); + if (Boolean.TRUE.equals(teleport.get()) && getOwner() instanceof ServerPlayer player) { + SpawnPointHelper.teleportToNewSpawn(player); + }
140-144: Missing error handling and attribute reapplication inload().
ManasRaceInstance.fromNBT()can throw on corrupted/invalid data, potentially crashing entity loading.- After loading, attribute modifiers are not reapplied, causing stale stats after chunk reload or dimension change.
🔎 Wrap in try-catch and reapply attributes:
@Override public void load(CompoundTag data) { if (!data.contains(RACE_KEY)) return; - this.raceInstance = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); + try { + ManasRaceInstance loaded = ManasRaceInstance.fromNBT(data.getCompound(RACE_KEY)); + this.raceInstance = loaded; + LivingEntity owner = this.getOwner(); + if (!owner.level().isClientSide()) { + loaded.addAttributeModifiers(owner); + } + } catch (Exception e) { + log.error("Failed to load race instance for {}: {}", this.getOwner(), e.getMessage(), e); + this.raceInstance = null; + } }
75-92: Consider adding error handling aroundonTick()to prevent server crashes.If
instance.onTick(entity)throws an exception, it could crash the server tick loop. Wrapping in try-catch with logging would improve resilience.🔎 Suggested error handling:
if (RaceEvents.RACE_PRE_TICK.invoker().tick(instance, entity).isFalse()) return; - instance.onTick(entity); + try { + instance.onTick(entity); + } catch (Exception e) { + log.error("Error ticking race for entity {}: {}", entity, e.getMessage(), e); + return; + } RaceEvents.RACE_POST_TICK.invoker().tick(instance, entity);skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (3)
225-245: Ensure TickingSkill.matches() uses semantic equality.Line 242 relies on
tickingSkill.matches(skill.getSkill(), mode)to remove released skills. A previous review identified thatmatches()uses reference equality (==) instead of semantic comparison, which can fail when different instances represent the same skill.Verify that the
matches()implementation inTickingSkill.javacompares skill identifiers using.equals()rather than==.Based on past review comments, this issue was flagged with a suggested fix to use
.equals()for identifier comparison.
45-45: Use a thread-safe multimap implementation.
tickingSkillsis accessed and modified from various event hooks that may execute concurrently.ArrayListMultimapis not thread-safe and can lead to data races. Wrap it withMultimaps.synchronizedMultimap()or use a concurrent collection.🔎 Apply this diff to make the multimap thread-safe:
+import com.google.common.collect.Multimaps; + -public static final Multimap<UUID, TickingSkill> tickingSkills = ArrayListMultimap.create(); +public static final Multimap<UUID, TickingSkill> tickingSkills = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
294-304: Ensure thread-safe access to tickingSkills.Lines 296-302 perform multiple operations on the
tickingSkillsmultimap without synchronization. SincetickingSkillsuses a non-thread-safeArrayListMultimap, concurrent access from different threads can cause race conditions.This is a consequence of the thread-safety issue at line 45. Once
tickingSkillsis wrapped withMultimaps.synchronizedMultimap(), ensure all access blocks are properly synchronized.
🧹 Nitpick comments (3)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (1)
45-61: Redundant null check afterisPresent()validation.On line 47,
owneris checked for null, but line 46 already returns early ifchangeableTarget.isPresent()is false. IfisPresent()returns true andget()still returns null, this indicates a bug in theChangeableimplementation rather than something to guard against here.🔎 Consider simplifying:
EntityEvents.LIVING_CHANGE_TARGET.register((entity, changeableTarget) -> { if (!changeableTarget.isPresent()) return EventResult.pass(); LivingEntity owner = changeableTarget.get(); - if (owner == null) return EventResult.pass(); Races storage = RaceAPI.getRaceFrom(owner);race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (1)
38-41: Consider restricting visibility of mutable static fields.
tickingRacesispublic staticand mutable, allowing external code to modify it directly. This could lead to unexpected behavior if external code adds/removes entries incorrectly.🔎 Consider making it package-private or providing controlled access:
- public static final Multimap<UUID, TickingRace> tickingRaces = ArrayListMultimap.create(); + static final Multimap<UUID, TickingRace> tickingRaces = ArrayListMultimap.create();skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
220-223: Consider conditional dirty marking in forEachSkill.Line 222 unconditionally marks the storage as dirty after iterating, even if the consumer made no modifications. For efficiency, consider marking dirty only when actual changes occur, or document that consumers must call markDirty() themselves.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
gradle.properties(2 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java(1 hunks)race-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java(1 hunks)skill-neoforge/src/main/resources/META-INF/neoforge.mods.toml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java
- skill-neoforge/src/main/resources/META-INF/neoforge.mods.toml
- race-neoforge/src/main/resources/META-INF/neoforge.mods.toml
🧰 Additional context used
🧬 Code graph analysis (2)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (2)
race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java (1)
RaceAPI(20-82)race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (1)
SpawnPointHelper(40-201)
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (6)
network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
Changeable(11-48)skill-common/src/main/java/io/github/manasmods/manascore/skill/ManasCoreSkill.java (1)
ManasCoreSkill(17-27)storage-common/src/main/java/io/github/manasmods/manascore/storage/api/Storage.java (1)
Storage(5-70)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
EntityEvents(21-75)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillAPI.java (1)
SkillAPI(20-82)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkillInstance.java (1)
ManasSkillInstance(32-709)
🔇 Additional comments (4)
gradle.properties (1)
21-21: Version bump to NeoForge 21.1.216 is valid.NeoForge 21.1.216 is the latest stable version for Minecraft 1.21.1 and is actively in use with no known compatibility issues. This is a patch release upgrade (from 21.1.28), not a downgrade.
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (2)
24-29: LGTM - Clean registry initialization pattern.The use of
createKey()helper method to derive theResourceKeyfrom theRegistraris a clean solution that avoids the unchecked cast warning previously flagged.
127-129: Clean helper for type-safe registry key creation.This helper method elegantly resolves the unchecked cast issue by reconstructing the
ResourceKeyfrom the registrar's location.race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (1)
43-58: Good initialization pattern with proper event cleanup.The storage registration and event handlers are well-structured. The cleanup on
PLAYER_QUITandCHANGE_DIMENSION(lines 56-57) properly removes stale entries fromtickingRaces, preventing memory leaks.
gradle.properties
Outdated
| org.gradle.parallel=true | ||
| # Mod properties | ||
| mod_version=4.0.0.0 | ||
| mod_version=3.0.3.0 |
There was a problem hiding this comment.
Version downgrade contradicts semantic versioning and new feature additions.
The mod_version has been downgraded from 4.0.0.0 to 3.0.3.0, which is unusual for a PR that adds new modules (Registry, Keybinding, Skill, Config). A major version downgrade could:
- Break existing deployments expecting version 4.x
- Confuse users about feature availability
- Cause issues with version-dependent integrations
Please clarify whether this downgrade is intentional or a typo.
🤖 Prompt for AI Agents
In gradle.properties at line 10 the mod_version was changed from 4.0.0.0 to
3.0.3.0; restore the correct semantic version for this PR by either reverting to
4.0.0.0 if the downgrade was accidental or bumping to an appropriate new 4.x.y.z
(e.g., 4.1.0.0) if these are new features, then update any other
files/changelogs that reference the version and add a note in the PR description
confirming the intended version choice.
| public void forgetSkill(@NotNull ResourceLocation skillId, @Nullable MutableComponent component) { | ||
| if (!this.skillInstances.containsKey(skillId)) return; | ||
| ManasSkillInstance instance = this.skillInstances.get(skillId); | ||
|
|
||
| Changeable<MutableComponent> forgetMessage = Changeable.of(component); | ||
| EventResult result = SkillEvents.REMOVE_SKILL.invoker().removeSkill(instance, getOwner(), forgetMessage); | ||
| if (result.isFalse()) return; | ||
|
|
||
| if (forgetMessage.isPresent()) getOwner().sendSystemMessage(forgetMessage.get()); | ||
| instance.onForgetSkill(this.getOwner()); | ||
| instance.markDirty(); | ||
|
|
||
| this.getLearnedSkills().remove(instance); | ||
| this.hasRemovedSkills = true; | ||
| markDirty(); | ||
| } |
There was a problem hiding this comment.
Fix incorrect removal from collection view.
Line 215 calls this.getLearnedSkills().remove(instance), but getLearnedSkills() returns skillInstances.values(), which is a view that doesn't support element removal by instance. This will fail to remove the skill from the underlying map.
🔎 Apply this diff to correctly remove the skill:
if (forgetMessage.isPresent()) getOwner().sendSystemMessage(forgetMessage.get());
instance.onForgetSkill(this.getOwner());
instance.markDirty();
- this.getLearnedSkills().remove(instance);
+ this.skillInstances.remove(skillId);
this.hasRemovedSkills = true;
markDirty();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public void forgetSkill(@NotNull ResourceLocation skillId, @Nullable MutableComponent component) { | |
| if (!this.skillInstances.containsKey(skillId)) return; | |
| ManasSkillInstance instance = this.skillInstances.get(skillId); | |
| Changeable<MutableComponent> forgetMessage = Changeable.of(component); | |
| EventResult result = SkillEvents.REMOVE_SKILL.invoker().removeSkill(instance, getOwner(), forgetMessage); | |
| if (result.isFalse()) return; | |
| if (forgetMessage.isPresent()) getOwner().sendSystemMessage(forgetMessage.get()); | |
| instance.onForgetSkill(this.getOwner()); | |
| instance.markDirty(); | |
| this.getLearnedSkills().remove(instance); | |
| this.hasRemovedSkills = true; | |
| markDirty(); | |
| } | |
| public void forgetSkill(@NotNull ResourceLocation skillId, @Nullable MutableComponent component) { | |
| if (!this.skillInstances.containsKey(skillId)) return; | |
| ManasSkillInstance instance = this.skillInstances.get(skillId); | |
| Changeable<MutableComponent> forgetMessage = Changeable.of(component); | |
| EventResult result = SkillEvents.REMOVE_SKILL.invoker().removeSkill(instance, getOwner(), forgetMessage); | |
| if (result.isFalse()) return; | |
| if (forgetMessage.isPresent()) getOwner().sendSystemMessage(forgetMessage.get()); | |
| instance.onForgetSkill(this.getOwner()); | |
| instance.markDirty(); | |
| this.skillInstances.remove(skillId); | |
| this.hasRemovedSkills = true; | |
| markDirty(); | |
| } |
🤖 Prompt for AI Agents
In
skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java
around lines 203 to 218, the code attempts to remove an instance via
this.getLearnedSkills().remove(instance) which operates on a values() view and
won't remove the entry from the underlying map; replace that call with
this.skillInstances.remove(skillId) so the map entry keyed by skillId is removed
(keep the surrounding logic: set hasRemovedSkills = true and call markDirty()).
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java`:
- Around line 179-184: Change the registration of the entity-type argument to
use Holder.Reference.class instead of Holder.class: in the registry.register
call that references EntityTypeArg.class, replace the registered key type from
Holder.class to Holder.Reference.class so the handler's addValueExtractor (which
uses ResourceArgument.getResource(commandContext, argumentName,
Registries.ENTITY_TYPE)) returns and matches Holder.Reference<EntityType> like
the other resource-backed args.
In
`@skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java`:
- Around line 231-251: The method handleSkillRelease uses the Changeable result
stored in local variable skill but still calls checkAndMarkDirty with the
original parameter skillInstance; update both calls to checkAndMarkDirty to pass
the potentially modified instance (skill) instead of skillInstance so the
correct instance is marked dirty after event listeners mutate it, keeping all
other logic (attribute removal and tickingSkills removal using skill.getSkill())
unchanged.
🧹 Nitpick comments (3)
config-common/src/main/java/io/github/manasmods/manascore/config/ManasCoreConfig.java (1)
9-15: Consider adding an idempotency guard toinit().If
init()is called more than once (e.g., due to a bug or future refactoring), thePLAYER_JOINevent handler would be registered multiple times, causing duplicate config syncs to clients on each join.♻️ Suggested defensive pattern
public class ManasCoreConfig { public static final Logger LOG = LoggerFactory.getLogger("ManasCore - Config"); + private static boolean initialized = false; + public static void init() { + if (initialized) return; + initialized = true; Config.setInsertionOrderPreserved(true); ManasConfigNetwork.init(); PlayerEvent.PLAYER_JOIN.register(ManasConfigNetwork::syncToClient); } }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (1)
21-40: Make this a non‑instantiable constants holder and prevent event reassignment.
EntityEventsis a static-only container; marking the classfinal, adding a private ctor, and making Event fieldsstatic finalprevents accidental reassignment or instantiation. (Line 21)♻️ Proposed refactor
-public class EntityEvents { - public static Event<LivingTickEvent> LIVING_PRE_TICK = EventFactory.createLoop(); - public static Event<LivingTickEvent> LIVING_POST_TICK = EventFactory.createLoop(); - public static Event<LivingEffectAddedEvent> LIVING_EFFECT_ADDED = EventFactory.createEventResult(); +public final class EntityEvents { + public static final Event<LivingTickEvent> LIVING_PRE_TICK = EventFactory.createLoop(); + public static final Event<LivingTickEvent> LIVING_POST_TICK = EventFactory.createLoop(); + public static final Event<LivingEffectAddedEvent> LIVING_EFFECT_ADDED = EventFactory.createEventResult(); - public static Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET = EventFactory.createEventResult(); - public static Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET_EARLY = EventFactory.createEventResult(); - public static Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET_LATE = EventFactory.createEventResult(); + public static final Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET = EventFactory.createEventResult(); + public static final Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET_EARLY = EventFactory.createEventResult(); + public static final Event<LivingChangeTargetEvent> LIVING_CHANGE_TARGET_LATE = EventFactory.createEventResult(); - public static Event<EntityEvent.LivingHurt> LIVING_PRE_DAMAGED = EventFactory.createEventResult(); - public static Event<EntityEvent.LivingHurt> LIVING_ON_BEING_DAMAGED = EventFactory.createEventResult(); - public static Event<LivingHurtEvent> LIVING_HURT = EventFactory.createEventResult(); - public static Event<LivingDamageEvent> LIVING_DAMAGE = EventFactory.createEventResult(); - public static Event<ProjectileHitEvent> PROJECTILE_HIT = EventFactory.createLoop(); + public static final Event<EntityEvent.LivingHurt> LIVING_PRE_DAMAGED = EventFactory.createEventResult(); + public static final Event<EntityEvent.LivingHurt> LIVING_ON_BEING_DAMAGED = EventFactory.createEventResult(); + public static final Event<LivingHurtEvent> LIVING_HURT = EventFactory.createEventResult(); + public static final Event<LivingDamageEvent> LIVING_DAMAGE = EventFactory.createEventResult(); + public static final Event<ProjectileHitEvent> PROJECTILE_HIT = EventFactory.createLoop(); - public static Event<EntityEvent.LivingDeath> DEATH_EVENT_FIRST = EventFactory.createEventResult(); - public static Event<EntityEvent.LivingDeath> DEATH_EVENT_HIGH = EventFactory.createEventResult(); - public static Event<EntityEvent.LivingDeath> DEATH_EVENT_NORMAL = EventFactory.createEventResult(); - public static Event<EntityEvent.LivingDeath> DEATH_EVENT_LOW = EventFactory.createEventResult(); - public static Event<EntityEvent.LivingDeath> DEATH_EVENT_LAST = EventFactory.createEventResult(); + public static final Event<EntityEvent.LivingDeath> DEATH_EVENT_FIRST = EventFactory.createEventResult(); + public static final Event<EntityEvent.LivingDeath> DEATH_EVENT_HIGH = EventFactory.createEventResult(); + public static final Event<EntityEvent.LivingDeath> DEATH_EVENT_NORMAL = EventFactory.createEventResult(); + public static final Event<EntityEvent.LivingDeath> DEATH_EVENT_LOW = EventFactory.createEventResult(); + public static final Event<EntityEvent.LivingDeath> DEATH_EVENT_LAST = EventFactory.createEventResult(); + + private EntityEvents() {}skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java (1)
226-229: UnconditionalmarkDirty()may cause unnecessary synchronization.
markDirty()is called regardless of whether the consumer actually modified any skill instances. This could trigger unnecessary network synchronization. Consider letting the consumer handle dirty marking, or only mark dirty if a modification occurred.Suggested approach
public void forEachSkill(BiConsumer<SkillStorage, ManasSkillInstance> skillInstanceConsumer) { List.copyOf(this.skillInstances.values()).forEach(skillInstance -> skillInstanceConsumer.accept(this, skillInstance)); - markDirty(); + // Callers should use checkAndMarkDirty() or markDirty() as needed }Alternatively, check if any instance became dirty:
public void forEachSkill(BiConsumer<SkillStorage, ManasSkillInstance> skillInstanceConsumer) { List.copyOf(this.skillInstances.values()).forEach(skillInstance -> { skillInstanceConsumer.accept(this, skillInstance); checkAndMarkDirty(skillInstance); }); }
| // Register Entity Type Argument | ||
| registry.register(Holder.class, EntityTypeArg.class, (annotation, handler) -> { | ||
| var argumentName = annotation.value().isBlank() ? handler.getAutoGeneratedArgumentName() : annotation.value(); | ||
| handler.addNode(Commands.argument(argumentName, ResourceArgument.resource(buildContext, Registries.ENTITY_TYPE))); | ||
| handler.addValueExtractor(commandContext -> ResourceArgument.getResource(commandContext, argumentName, Registries.ENTITY_TYPE)); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find the file and examine the context around lines 179-184
find . -name "ManasCoreCommand.java" -type fRepository: ManasMods/ManasCore
Length of output: 153
🏁 Script executed:
#!/bin/bash
# Let's also check what files are in the command-common directory
find . -path "*command-common*" -type f -name "*.java" | head -20Repository: ManasMods/ManasCore
Length of output: 2010
🏁 Script executed:
#!/bin/bash
# Examine the ManasCoreCommand.java file around lines 179-184
cat -n ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | sed -n '170,195p'Repository: ManasMods/ManasCore
Length of output: 2209
🏁 Script executed:
#!/bin/bash
# Let's see the full file to understand the structure and find all registry.register calls
wc -l ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.javaRepository: ManasMods/ManasCore
Length of output: 157
🏁 Script executed:
#!/bin/bash
# Let's see all registry.register calls in the file to understand the pattern
rg -n "registry\.register" ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.javaRepository: ManasMods/ManasCore
Length of output: 3567
🏁 Script executed:
#!/bin/bash
# Let's look at the imports to understand what ResourceArgument comes from
head -50 ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.javaRepository: ManasMods/ManasCore
Length of output: 2442
🏁 Script executed:
#!/bin/bash
# Find ResourceArgument to understand what getResource returns
find . -name "ResourceArgument*" -type fRepository: ManasMods/ManasCore
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Search for ResourceArgument in imports and usage
rg -n "ResourceArgument" ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | head -5Repository: ManasMods/ManasCore
Length of output: 740
🏁 Script executed:
#!/bin/bash
# Let's check other usages of ResourceArgument.getResource to see what they return
rg -n "ResourceArgument.getResource" --type=javaRepository: ManasMods/ManasCore
Length of output: 538
🏁 Script executed:
#!/bin/bash
# Let's look at lines 246-249 to see the DamageArg registration pattern
cat -n ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | sed -n '246,250p'Repository: ManasMods/ManasCore
Length of output: 614
🏁 Script executed:
#!/bin/bash
# Check the EntityTypeArg annotation to understand its intended usage
cat -n ./command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/EntityTypeArg.javaRepository: ManasMods/ManasCore
Length of output: 698
🏁 Script executed:
#!/bin/bash
# Let's look for imports related to ResourceArgument
rg -n "import.*ResourceArgument" ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.javaRepository: ManasMods/ManasCore
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Let's check the full imports section more thoroughly
head -60 ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | tail -20Repository: ManasMods/ManasCore
Length of output: 849
🏁 Script executed:
#!/bin/bash
# Search for usages of `@EntityTypeArg` to understand how it's used in practice
rg -n "@EntityTypeArg" --type=java -C5Repository: ManasMods/ManasCore
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Let's search for where ResourceArgument might be coming from
rg -n "class ResourceArgument" --type=javaRepository: ManasMods/ManasCore
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check all imports to see where ResourceArgument comes from
rg -n "^import" ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | grep -i resourceRepository: ManasMods/ManasCore
Length of output: 188
🏁 Script executed:
#!/bin/bash
# Let's look at the entire imports section more thoroughly
head -50 ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | grep "^import"Repository: ManasMods/ManasCore
Length of output: 2319
🏁 Script executed:
#!/bin/bash
# Let's find ResourceArgument in the resource parameter package
find . -path "*api/parameter/resource*" -name "*.java"Repository: ManasMods/ManasCore
Length of output: 719
🏁 Script executed:
#!/bin/bash
# Let's search for the actual ResourceArgument definition
rg -l "class ResourceArgument" --type=javaRepository: ManasMods/ManasCore
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Let's check what's imported from net.minecraft.commands.arguments
rg "net.minecraft.commands.arguments" ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.javaRepository: ManasMods/ManasCore
Length of output: 682
🏁 Script executed:
#!/bin/bash
# Let's look at one of the resource arg files to understand the pattern
cat -n ./command-common/src/main/java/io/github/manasmods/manascore/command/api/parameter/resource/DamageArg.javaRepository: ManasMods/ManasCore
Length of output: 703
🏁 Script executed:
#!/bin/bash
# Search for ResourceArgument usage across the codebase
rg "ResourceArgument" --type=java -A2 -B2Repository: ManasMods/ManasCore
Length of output: 6885
🏁 Script executed:
#!/bin/bash
# Let's check lines around all the Holder-based registrations to verify the pattern
cat -n ./command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java | sed -n '240,270p'Repository: ManasMods/ManasCore
Length of output: 3343
🏁 Script executed:
#!/bin/bash
# Let's confirm what the CommandArgumentRegistry.register method signature expects
rg -n "register.*Holder" ./command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandArgumentRegistry.java -A3 -B3Repository: ManasMods/ManasCore
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Let's check the CommandArgumentRegistry file to understand how registry works
cat -n ./command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandArgumentRegistry.java | head -50Repository: ManasMods/ManasCore
Length of output: 2405
🏁 Script executed:
#!/bin/bash
# Look for the register method definition
rg -n "public.*register" ./command-common/src/main/java/io/github/manasmods/manascore/command/internal/CommandArgumentRegistry.javaRepository: ManasMods/ManasCore
Length of output: 289
EntityTypeArg should register Holder.Reference.class instead of Holder.class. The current registration is inconsistent with all other resource-backed arguments (DamageArg, AttributeArg, EnchantmentArg, MobEffectArg, StructureArg, FeatureArg), which use Holder.Reference.class. Both EntityTypeArg and DamageArg use the same ResourceArgument.getResource() method, which returns Holder.Reference<T>, so they must register the same type. Command parameters typed as Holder.Reference<EntityType> will not be matched by the current Holder.class registration.
🔧 Proposed fix
- registry.register(Holder.class, EntityTypeArg.class, (annotation, handler) -> {
+ registry.register(Holder.Reference.class, EntityTypeArg.class, (annotation, handler) -> {
var argumentName = annotation.value().isBlank() ? handler.getAutoGeneratedArgumentName() : annotation.value();
handler.addNode(Commands.argument(argumentName, ResourceArgument.resource(buildContext, Registries.ENTITY_TYPE)));
handler.addValueExtractor(commandContext -> ResourceArgument.getResource(commandContext, argumentName, Registries.ENTITY_TYPE));
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Register Entity Type Argument | |
| registry.register(Holder.class, EntityTypeArg.class, (annotation, handler) -> { | |
| var argumentName = annotation.value().isBlank() ? handler.getAutoGeneratedArgumentName() : annotation.value(); | |
| handler.addNode(Commands.argument(argumentName, ResourceArgument.resource(buildContext, Registries.ENTITY_TYPE))); | |
| handler.addValueExtractor(commandContext -> ResourceArgument.getResource(commandContext, argumentName, Registries.ENTITY_TYPE)); | |
| }); | |
| // Register Entity Type Argument | |
| registry.register(Holder.Reference.class, EntityTypeArg.class, (annotation, handler) -> { | |
| var argumentName = annotation.value().isBlank() ? handler.getAutoGeneratedArgumentName() : annotation.value(); | |
| handler.addNode(Commands.argument(argumentName, ResourceArgument.resource(buildContext, Registries.ENTITY_TYPE))); | |
| handler.addValueExtractor(commandContext -> ResourceArgument.getResource(commandContext, argumentName, Registries.ENTITY_TYPE)); | |
| }); |
🤖 Prompt for AI Agents
In
`@command-common/src/main/java/io/github/manasmods/manascore/command/ManasCoreCommand.java`
around lines 179 - 184, Change the registration of the entity-type argument to
use Holder.Reference.class instead of Holder.class: in the registry.register
call that references EntityTypeArg.class, replace the registered key type from
Holder.class to Holder.Reference.class so the handler's addValueExtractor (which
uses ResourceArgument.getResource(commandContext, argumentName,
Registries.ENTITY_TYPE)) returns and matches Holder.Reference<EntityType> like
the other resource-backed args.
| public void handleSkillRelease(ManasSkillInstance skillInstance, int heldTick, int keyNumber, int mode, boolean heldInterrupt) { | ||
| Changeable<ManasSkillInstance> changeable = Changeable.of(skillInstance); | ||
| if (SkillEvents.RELEASE_SKILL.invoker().releaseSkill(changeable, this.getOwner(), keyNumber, mode, heldTick).isFalse()) return; | ||
| ManasSkillInstance skill = changeable.get(); | ||
| if (skill == null) return; | ||
|
|
||
| if ((heldInterrupt || skill.canInteractSkill(getOwner())) && mode < skill.getModes()) { | ||
| if (!skill.onCoolDown(mode) || skill.canIgnoreCoolDown(getOwner(), mode)) { | ||
| skill.onRelease(getOwner(), heldTick, keyNumber, mode); | ||
| this.checkAndMarkDirty(skillInstance); | ||
| } | ||
| } | ||
|
|
||
| skill.removeAttributeModifiers(getOwner(), mode); | ||
| if (!heldInterrupt) { | ||
| UUID ownerID = getOwner().getUUID(); | ||
| if (tickingSkills.containsKey(ownerID)) | ||
| tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.matches(skill.getSkill(), mode)); | ||
| } | ||
| this.checkAndMarkDirty(skillInstance); | ||
| } |
There was a problem hiding this comment.
Inconsistent usage of skill vs skillInstance after event modification.
Lines 239 and 244 correctly use skill (the potentially modified instance from changeable.get()), but lines 240 and 250 call checkAndMarkDirty(skillInstance) using the original parameter. If the event listener swaps the instance via Changeable, the wrong instance gets marked dirty.
Suggested fix
public void handleSkillRelease(ManasSkillInstance skillInstance, int heldTick, int keyNumber, int mode, boolean heldInterrupt) {
Changeable<ManasSkillInstance> changeable = Changeable.of(skillInstance);
if (SkillEvents.RELEASE_SKILL.invoker().releaseSkill(changeable, this.getOwner(), keyNumber, mode, heldTick).isFalse()) return;
ManasSkillInstance skill = changeable.get();
if (skill == null) return;
if ((heldInterrupt || skill.canInteractSkill(getOwner())) && mode < skill.getModes()) {
if (!skill.onCoolDown(mode) || skill.canIgnoreCoolDown(getOwner(), mode)) {
skill.onRelease(getOwner(), heldTick, keyNumber, mode);
- this.checkAndMarkDirty(skillInstance);
+ this.checkAndMarkDirty(skill);
}
}
skill.removeAttributeModifiers(getOwner(), mode);
if (!heldInterrupt) {
UUID ownerID = getOwner().getUUID();
if (tickingSkills.containsKey(ownerID))
tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.matches(skill.getSkill(), mode));
}
- this.checkAndMarkDirty(skillInstance);
+ this.checkAndMarkDirty(skill);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public void handleSkillRelease(ManasSkillInstance skillInstance, int heldTick, int keyNumber, int mode, boolean heldInterrupt) { | |
| Changeable<ManasSkillInstance> changeable = Changeable.of(skillInstance); | |
| if (SkillEvents.RELEASE_SKILL.invoker().releaseSkill(changeable, this.getOwner(), keyNumber, mode, heldTick).isFalse()) return; | |
| ManasSkillInstance skill = changeable.get(); | |
| if (skill == null) return; | |
| if ((heldInterrupt || skill.canInteractSkill(getOwner())) && mode < skill.getModes()) { | |
| if (!skill.onCoolDown(mode) || skill.canIgnoreCoolDown(getOwner(), mode)) { | |
| skill.onRelease(getOwner(), heldTick, keyNumber, mode); | |
| this.checkAndMarkDirty(skillInstance); | |
| } | |
| } | |
| skill.removeAttributeModifiers(getOwner(), mode); | |
| if (!heldInterrupt) { | |
| UUID ownerID = getOwner().getUUID(); | |
| if (tickingSkills.containsKey(ownerID)) | |
| tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.matches(skill.getSkill(), mode)); | |
| } | |
| this.checkAndMarkDirty(skillInstance); | |
| } | |
| public void handleSkillRelease(ManasSkillInstance skillInstance, int heldTick, int keyNumber, int mode, boolean heldInterrupt) { | |
| Changeable<ManasSkillInstance> changeable = Changeable.of(skillInstance); | |
| if (SkillEvents.RELEASE_SKILL.invoker().releaseSkill(changeable, this.getOwner(), keyNumber, mode, heldTick).isFalse()) return; | |
| ManasSkillInstance skill = changeable.get(); | |
| if (skill == null) return; | |
| if ((heldInterrupt || skill.canInteractSkill(getOwner())) && mode < skill.getModes()) { | |
| if (!skill.onCoolDown(mode) || skill.canIgnoreCoolDown(getOwner(), mode)) { | |
| skill.onRelease(getOwner(), heldTick, keyNumber, mode); | |
| this.checkAndMarkDirty(skill); | |
| } | |
| } | |
| skill.removeAttributeModifiers(getOwner(), mode); | |
| if (!heldInterrupt) { | |
| UUID ownerID = getOwner().getUUID(); | |
| if (tickingSkills.containsKey(ownerID)) | |
| tickingSkills.get(ownerID).removeIf(tickingSkill -> tickingSkill.matches(skill.getSkill(), mode)); | |
| } | |
| this.checkAndMarkDirty(skill); | |
| } |
🤖 Prompt for AI Agents
In
`@skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java`
around lines 231 - 251, The method handleSkillRelease uses the Changeable result
stored in local variable skill but still calls checkAndMarkDirty with the
original parameter skillInstance; update both calls to checkAndMarkDirty to pass
the potentially modified instance (skill) instead of skillInstance so the
correct instance is marked dirty after event listeners mutate it, keeping all
other logic (attribute removal and tickingSkills removal using skill.getSkill())
unchanged.
Add `bundle` modules for publishing
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@settings.gradle`:
- Around line 17-24: The settings.gradle registers new modules "attribute",
"config", "keybind", "race", and "skill" (in addition to the existing ones) but
the bundle publication scripts (bundle-fabric and bundle-neoforge) only include
"network", "storage", "inventory", and "command", so the new modules won't be
published; update bundle-fabric/build.gradle and bundle-neoforge/build.gradle to
add the missing projects for each bundle—specifically include the "-common",
"-fabric" and/or "-neoforge" variants as appropriate for "attribute", "config",
"keybind", "race", and "skill" (leave "testing" out if intended) so the bundles
match the modules declared in settings.gradle.
🧹 Nitpick comments (2)
bundle-neoforge/build.gradle (1)
6-37: Consider extracting shared bundle configuration.This file is nearly identical to
bundle-fabric/build.gradle. The only differences are the archive name suffix and project suffix (-neoforgevs-fabric).If more modules are added to
includedProjectsin the future, both files would need to be updated in sync (already an issue as noted in the settings.gradle review).Consider extracting the shared
includedProjectslist to a parent build script orgradle.propertiesto ensure consistency.♻️ Example approach using gradle.properties or buildSrc
In
gradle.propertiesor a shared script:# Define once, use in both bundles bundled_modules=network,storage,inventory,command,attribute,config,keybind,race,skillThen in each bundle's
build.gradle:ext { includedProjects = bundled_modules.split(',').collect { ":$it" } }bundle-fabric/src/main/resources/fabric.mod.json (1)
10-14: Provide a valid homepage URL or remove the empty contact block.The
homepagefield in thecontactblock is an empty string. Either populate it with a valid URL (e.g., GitHub repository or mod page) or remove the entirecontactblock if not currently needed. The icon asset atassets/manascore/icon.pngexists and is properly referenced.
| ["attribute", "command", "config", "inventory", "keybind", "network", "race", "storage", "testing", "skill"].forEach { module -> | ||
| include ":$module-common" | ||
| include ":$module-fabric" | ||
| include ":$module-neoforge" | ||
| } | ||
|
|
||
| include ':bundle-fabric' | ||
| include ':bundle-neoforge' |
There was a problem hiding this comment.
Module list mismatch between settings.gradle and bundle configurations.
The settings.gradle includes 10 modules (attribute, command, config, inventory, keybind, network, race, storage, testing, skill), but the bundle configurations (bundle-fabric/build.gradle and bundle-neoforge/build.gradle) only include 4 projects: network, storage, inventory, command.
The new modules added in this PR (attribute, config, keybind, race, skill) are missing from the bundle configurations, which means they won't be included in the published bundles.
🔧 Suggested fix for bundle-fabric/build.gradle and bundle-neoforge/build.gradle
ext {
includedProjects = [
+ ":attribute",
+ ":command",
+ ":config",
+ ":inventory",
+ ":keybind",
":network",
+ ":race",
+ ":skill",
- ":storage",
- ":inventory",
- ":command"
+ ":storage"
]
}Note: testing module is likely intentionally excluded from bundles.
🤖 Prompt for AI Agents
In `@settings.gradle` around lines 17 - 24, The settings.gradle registers new
modules "attribute", "config", "keybind", "race", and "skill" (in addition to
the existing ones) but the bundle publication scripts (bundle-fabric and
bundle-neoforge) only include "network", "storage", "inventory", and "command",
so the new modules won't be published; update bundle-fabric/build.gradle and
bundle-neoforge/build.gradle to add the missing projects for each
bundle—specifically include the "-common", "-fabric" and/or "-neoforge" variants
as appropriate for "attribute", "config", "keybind", "race", and "skill" (leave
"testing" out if intended) so the bundles match the modules declared in
settings.gradle.
Description
List of changes
Type of Change
Summary by CodeRabbit
New Features
Chores