-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Registry - Keybinding - Skill - Config modules #48
base: 1.21.1/master
Are you sure you want to change the base?
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
WalkthroughThe pull request introduces a comprehensive set of changes across multiple modules of the ManasMods project, focusing on expanding the core functionality of the modding framework. The changes primarily involve adding new modules and enhancing existing systems for configuration, skills, races, attributes, keybindings, and testing. Key additions include robust event systems, network communication mechanisms, and platform-specific implementations for Fabric and NeoForge environments. Changes
Sequence DiagramsequenceDiagram
participant Client
participant NetworkManager
participant Server
participant RaceStorage
participant ManasRace
Client->>NetworkManager: Send Race Evolution Request
NetworkManager->>Server: Transmit Race Evolution Packet
Server->>RaceStorage: Retrieve Current Race
RaceStorage->>ManasRace: Check Evolution Conditions
ManasRace-->>RaceStorage: Validate Evolution
RaceStorage->>Server: Apply Race Evolution
Server->>NetworkManager: Confirm Evolution
NetworkManager->>Client: Update Race Instance
This sequence diagram illustrates the race evolution process, demonstrating the interaction between client, network, server, and race management components. ✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
ImmutableList
orList.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
heldTick
represents (duration of skill hold?)- Purpose of
keyNumber
(keybinding identifier?)- Format/requirements for
skillList
keybinding-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
skills
as 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
init
method lacks documentation explaining its purpose, contract with the parent class, and the significance of thebeforeRegistration
parameter.Add Javadoc explaining:
- Method's purpose
- When it's called
- What
beforeRegistration
does- 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 ofkeyNumber
andmode
for better readability.In the code,
keyNumber
is assigned tomode
, and both variables are used subsequently. This might cause confusion since they appear to represent the same concept.Consider renaming
keyNumber
tomode
if 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
onHit
currently has package-private visibility. Consider specifying an access modifier (e.g.,private
orpublic
) 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) this
can be simplified to(LivingEntity) this
sincePlayer
extendsLivingEntity
. The extra cast toObject
is 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
onHit
currently has package-private visibility. Consider specifying an access modifier (e.g.,private
orpublic
) 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 Befinal
if They Are Meant to Be ConstantsThe
key
field is static but not marked asfinal
. Ifkey
is intended to be a constant, it should be declared asstatic final
.Apply this diff to make
key
a constant:-private static StorageKey<SkillStorage> key = null; +private static final StorageKey<SkillStorage> key = null;
88-89
: Unnecessary Return After CheckThe method
checkPlayerOnlyEffects
returns immediately ifpassiveSkillActivate
is 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
learnSkill
method, 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
Exception
in 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 NameresetDirty
The method
resetDirty
might be better namedmarkClean
to 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
AttributeTemplate
record 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
onBeingDamaged
mentions it is executed beforeonDamageEntity
, but in practice,onBeingDamaged
is 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
final
to 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
final
since 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
registerNew
method 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
mode
parameter inSkillUpdateCooldownEvent
- Documentation for the return values of
EventResult
in 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.png
is excluded by!**/*.png
command-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
config-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
config-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
config-neoforge/src/main/resources/icon.png
is excluded by!**/*.png
inventory-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
inventory-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
keybinding-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
keybinding-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
keybinding-neoforge/src/main/resources/icon.png
is excluded by!**/*.png
network-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
network-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
registry-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
registry-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
registry-neoforge/src/main/resources/icon.png
is excluded by!**/*.png
skill-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
skill-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
skill-neoforge/src/main/resources/icon.png
is excluded by!**/*.png
storage-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
storage-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
testing-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
testing-fabric/src/main/resources/assets/manascore/logo.png
is 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.json
in keybinding-commonmanascore_keybinding-fabric.mixins.json
in 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
client
entrypoints 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.png
file exists in the correct resources directory.✅ Verification successful
Icon file verification successful
The referenced
icon.png
file 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
AFTER
while 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.png
exists 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.png
file 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 theactuallyHurt
method 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@Unique
and@Nullable
annotationsThe declaration of
onHitEventResult
with@Unique
and@Nullable
is correct. Using@Unique
ensures that the mixin introduces this field without causing conflicts, and@Nullable
accurately 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 incancelActuallyHurt
In the
cancelActuallyHurt
method, ensure that the parametersDamageSource damageSource
andfloat damageAmount
match those of theactuallyHurt
method inLivingEntity
. If they do not match, the injection may fail.Run the following script to verify the method signature:
✅ Verification successful
Parameters in
cancelActuallyHurt
are correctly definedThe parameter types in the Mixin injection match the target method signature as evidenced by the fully qualified method reference in the
@Inject
annotation. 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 inonImpulseSet
MethodThe
onImpulseSet
method setsthis.onHitEventResult
tonull
regardless of the condition. Ifthis.onHitEventResult
is notnull
, andoriginal.call(instance, value)
is not called, this might skip necessary logic.Please verify if the intended behavior is to always reset
onHitEventResult
tonull
. 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_TARGET
EventIn the
LIVING_CHANGE_TARGET
event 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_CALCULATION
EventThe code casts
source.getEntity()
toLivingEntity
without 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_CALCULATION
are 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.instances
is used to manage attribute system integration across platformsMobEffect.particleFactory
enables 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_id
is 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_id
is properly defined in build configurationThe
mod_id
property is dynamically constructed in the root build.gradle for all subprojects using the pattern${rootProject.archives_name}_$module_name
, wheremodule_name
is 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
api
instead ofimplementation
if 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.json
and 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 = false
indicates a switch to the modern mixin annotation processor- Commenting out
defaultRefmapName
might 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 = false
is 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
public RequestSkillTogglePacket(FriendlyByteBuf buf) { | ||
this(buf.readList(FriendlyByteBuf::readResourceLocation)); | ||
} | ||
|
||
public void encode(FriendlyByteBuf buf) { | ||
buf.writeCollection(this.skillList, FriendlyByteBuf::writeResourceLocation); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add size validation for network security.
To prevent potential DoS attacks, consider adding a maximum size limit for the skill list when reading from the network buffer.
public RequestSkillTogglePacket(FriendlyByteBuf buf) {
- this(buf.readList(FriendlyByteBuf::readResourceLocation));
+ this(validateList(buf.readList(FriendlyByteBuf::readResourceLocation)));
}
+
+ private static List<ResourceLocation> validateList(List<ResourceLocation> list) {
+ if (list.size() > 100) { // Adjust limit as needed
+ throw new IllegalArgumentException("Skill list exceeds maximum size");
+ }
+ return list;
+ }
📝 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 RequestSkillTogglePacket(FriendlyByteBuf buf) { | |
this(buf.readList(FriendlyByteBuf::readResourceLocation)); | |
} | |
public void encode(FriendlyByteBuf buf) { | |
buf.writeCollection(this.skillList, FriendlyByteBuf::writeResourceLocation); | |
} | |
public RequestSkillTogglePacket(FriendlyByteBuf buf) { | |
this(validateList(buf.readList(FriendlyByteBuf::readResourceLocation))); | |
} | |
private static List<ResourceLocation> validateList(List<ResourceLocation> list) { | |
if (list.size() > 100) { // Adjust limit as needed | |
throw new IllegalArgumentException("Skill list exceeds maximum size"); | |
} | |
return list; | |
} | |
public void encode(FriendlyByteBuf buf) { | |
buf.writeCollection(this.skillList, FriendlyByteBuf::writeResourceLocation); | |
} |
public void handle(NetworkManager.PacketContext context) { | ||
if (context.getEnvironment() != Env.SERVER) return; | ||
context.queue(() -> { | ||
Player player = context.getPlayer(); | ||
if(player == null) return; | ||
Skills storage = SkillAPI.getSkillsFrom(player); | ||
for (ResourceLocation id : this.skillList) { | ||
storage.getSkill(id).ifPresent(skill -> { | ||
if(!skill.canInteractSkill(player)) return; | ||
|
||
if(skill.isToggled()) { | ||
skill.setToggled(false); | ||
skill.onToggleOff(player); | ||
} else { | ||
skill.setToggled(true); | ||
skill.onToggleOn(player); | ||
} | ||
|
||
storage.markDirty(); | ||
}); | ||
} | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling and client feedback.
The current implementation has several areas for improvement:
- Add error logging for invalid skills
- Consider synchronization for concurrent modifications
- Provide feedback to the client about success/failure
public void handle(NetworkManager.PacketContext context) {
if (context.getEnvironment() != Env.SERVER) return;
context.queue(() -> {
Player player = context.getPlayer();
if(player == null) return;
Skills storage = SkillAPI.getSkillsFrom(player);
+ boolean hasChanges = false;
for (ResourceLocation id : this.skillList) {
- storage.getSkill(id).ifPresent(skill -> {
+ try {
+ storage.getSkill(id).ifPresentOrElse(skill -> {
if(!skill.canInteractSkill(player)) return;
if(skill.isToggled()) {
skill.setToggled(false);
skill.onToggleOff(player);
} else {
skill.setToggled(true);
skill.onToggleOn(player);
}
-
- storage.markDirty();
- });
+ hasChanges = true;
+ }, () -> LOGGER.warn("Player {} attempted to toggle invalid skill: {}", player.getName().getString(), id));
+ } catch (Exception e) {
+ LOGGER.error("Error toggling skill {} for player {}", id, player.getName().getString(), e);
+ }
}
+ if (hasChanges) {
+ storage.markDirty();
+ // TODO: Send confirmation packet to client
+ }
});
}
Committable suggestion skipped: line range outside the PR's diff.
public void handle(NetworkManager.PacketContext context) { | ||
if (context.getEnvironment() != Env.SERVER) return; | ||
context.queue(() -> { | ||
Player player = context.getPlayer(); | ||
if (player == null) return; | ||
StorageManager.getStorage(player, SkillStorage.getKey()).handleSkillRelease(skillList, heldTick, keyNumber, keyNumber); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Confirmed: handleSkillRelease requires distinct mode parameter
The method signature in SkillStorage.java
confirms this is a bug:
public void handleSkillRelease(List<ResourceLocation> skillList, int heldTick, int keyNumber, int mode)
The current implementation incorrectly passes keyNumber
twice instead of providing a distinct mode
parameter.
🔗 Analysis chain
Fix duplicate keyNumber parameter in handleSkillRelease call.
The handleSkillRelease
method is called with keyNumber
passed twice as the last two parameters. This appears to be a bug.
Verify the correct parameters and update the call:
-StorageManager.getStorage(player, SkillStorage.getKey()).handleSkillRelease(skillList, heldTick, keyNumber, keyNumber);
+StorageManager.getStorage(player, SkillStorage.getKey()).handleSkillRelease(skillList, heldTick, keyNumber);
Let's verify the method signature in SkillStorage:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for handleSkillRelease method definition
ast-grep --pattern 'handleSkillRelease($_) {
$$$
}'
Length of output: 55
Script:
#!/bin/bash
# Search for handleSkillRelease method using ripgrep
rg "handleSkillRelease" -A 2
Length of output: 992
"depends": { | ||
"fabricloader": ">=${fabric_loader_version}", | ||
"minecraft": "~${minecraft_version}", | ||
"java": ">=21", | ||
"architectury": ">=${architectury_version}", | ||
"fabric-api": "*" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Consider lowering Java version requirement to 17
The codebase only uses the record
feature which was stabilized in Java 16. Java 17 (current LTS) would be sufficient and provide better mod adoption while maintaining all functionality.
Note: The fabric-api versioning is properly managed through the build system, so using "*"
in the mod.json is acceptable.
🔗 Analysis chain
Review dependency constraints.
Two potential concerns in the dependencies:
- Java 21 requirement is quite recent and might limit mod adoption. Consider if Java 17 (the current LTS) would be sufficient.
- Using
"*"
for fabric-api version could lead to compatibility issues. Consider specifying a minimum version instead.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if any code actually requires Java 21 features
rg "sealed|permits|record|switch.*case.*when"
# Find fabric-api version references in other files
rg "fabric-api.*version" -g "*.{gradle,properties}"
Length of output: 1735
/** | ||
* Creates a new {@link SkillBuilder} for the given name. | ||
*/ | ||
public <T extends ManasSkill> SkillBuilder<R, T> skill(final String name, final Supplier<T> skillFactory) { | ||
if (this.skills == null) this.skills = DeferredRegister.create(this.modId, SkillAPI.getSkillRegistryKey()); | ||
return new SkillBuilder<>(self(), name, skillFactory); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance input validation and documentation.
The method lacks parameter validation and comprehensive documentation.
Add parameter validation and enhance documentation:
/**
* Creates a new {@link SkillBuilder} for the given name.
+ * @param name The unique identifier for the skill. Must not be null or empty.
+ * @param skillFactory A supplier that creates new instances of the skill.
+ * @return A new SkillBuilder instance for configuring the skill.
+ * @throws IllegalArgumentException if name is null or empty
*/
public <T extends ManasSkill> SkillBuilder<R, T> skill(final String name, final Supplier<T> skillFactory) {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Skill name cannot be null or empty");
+ }
+ if (skillFactory == null) {
+ throw new IllegalArgumentException("Skill factory cannot be null");
+ }
- if (this.skills == null) this.skills = DeferredRegister.create(this.modId, SkillAPI.getSkillRegistryKey());
return new SkillBuilder<>(self(), name, skillFactory);
}
📝 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.
/** | |
* Creates a new {@link SkillBuilder} for the given name. | |
*/ | |
public <T extends ManasSkill> SkillBuilder<R, T> skill(final String name, final Supplier<T> skillFactory) { | |
if (this.skills == null) this.skills = DeferredRegister.create(this.modId, SkillAPI.getSkillRegistryKey()); | |
return new SkillBuilder<>(self(), name, skillFactory); | |
} | |
/** | |
* Creates a new {@link SkillBuilder} for the given name. | |
* @param name The unique identifier for the skill. Must not be null or empty. | |
* @param skillFactory A supplier that creates new instances of the skill. | |
* @return A new SkillBuilder instance for configuring the skill. | |
* @throws IllegalArgumentException if name is null or empty | |
*/ | |
public <T extends ManasSkill> SkillBuilder<R, T> skill(final String name, final Supplier<T> skillFactory) { | |
if (name == null || name.isEmpty()) { | |
throw new IllegalArgumentException("Skill name cannot be null or empty"); | |
} | |
if (skillFactory == null) { | |
throw new IllegalArgumentException("Skill factory cannot be null"); | |
} | |
return new SkillBuilder<>(self(), name, skillFactory); | |
} |
public static void createConfigs() { | ||
registeredConfigs.forEach(config -> { | ||
String content = gson.toJson(config); | ||
Path startPath = Paths.get(config.getParentPath() + "/" + config.getClass().getSimpleName() + ".json"); | ||
try { | ||
if (!Files.exists(startPath)) { | ||
Files.createDirectories(startPath.getParent()); | ||
Files.write(startPath, content.getBytes()); | ||
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " created."); | ||
} else { | ||
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " already exists."); | ||
} | ||
} catch (IOException e) { | ||
ManasCoreConfig.LOG.error("Error creating config: " + config.getClass().getSimpleName()); | ||
e.printStackTrace(); | ||
} | ||
}); | ||
loadConfigs(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Apply consistent improvements to config creation.
The same improvements suggested for config loading should be applied here:
- Path sanitization
- Structured error logging
- Parent path validation
try {
+ String parentPath = config.getParentPath();
+ if (parentPath == null || parentPath.isEmpty()) {
+ ManasCoreConfig.LOG.error("Invalid parent path for config: " + config.getClass().getSimpleName());
+ return;
+ }
+ Path startPath = Paths.get(parentPath).normalize()
+ .resolve(config.getClass().getSimpleName() + ".json");
if (!Files.exists(startPath)) {
Files.createDirectories(startPath.getParent());
Files.write(startPath, content.getBytes());
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " created.");
} else {
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " already exists.");
}
} catch (IOException e) {
- ManasCoreConfig.LOG.error("Error creating config: " + config.getClass().getSimpleName());
- e.printStackTrace();
+ ManasCoreConfig.LOG.error("Error creating config: " + config.getClass().getSimpleName()
+ + " - " + e.getMessage());
}
📝 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 static void createConfigs() { | |
registeredConfigs.forEach(config -> { | |
String content = gson.toJson(config); | |
Path startPath = Paths.get(config.getParentPath() + "/" + config.getClass().getSimpleName() + ".json"); | |
try { | |
if (!Files.exists(startPath)) { | |
Files.createDirectories(startPath.getParent()); | |
Files.write(startPath, content.getBytes()); | |
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " created."); | |
} else { | |
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " already exists."); | |
} | |
} catch (IOException e) { | |
ManasCoreConfig.LOG.error("Error creating config: " + config.getClass().getSimpleName()); | |
e.printStackTrace(); | |
} | |
}); | |
loadConfigs(); | |
} | |
public static void createConfigs() { | |
registeredConfigs.forEach(config -> { | |
String content = gson.toJson(config); | |
try { | |
String parentPath = config.getParentPath(); | |
if (parentPath == null || parentPath.isEmpty()) { | |
ManasCoreConfig.LOG.error("Invalid parent path for config: " + config.getClass().getSimpleName()); | |
return; | |
} | |
Path startPath = Paths.get(parentPath).normalize() | |
.resolve(config.getClass().getSimpleName() + ".json"); | |
if (!Files.exists(startPath)) { | |
Files.createDirectories(startPath.getParent()); | |
Files.write(startPath, content.getBytes()); | |
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " created."); | |
} else { | |
ManasCoreConfig.LOG.info("Config: " + config.getClass().getSimpleName() + " already exists."); | |
} | |
} catch (IOException e) { | |
ManasCoreConfig.LOG.error("Error creating config: " + config.getClass().getSimpleName() | |
+ " - " + e.getMessage()); | |
} | |
}); | |
loadConfigs(); | |
} |
public static void loadConfigs() { | ||
registeredConfigs.forEach(config -> { | ||
Path startPath = Paths.get(config.getParentPath() + "/" + config.getClass().getSimpleName().toLowerCase() + ".json"); | ||
try { | ||
if (Files.exists(startPath)) { | ||
String string = Files.readString(startPath); | ||
ManasConfig loadedConfig = gson.fromJson(string, config.getClass()); | ||
loadedConfigs.add(loadedConfig); | ||
ManasCoreConfig.LOG.info( config.getClass().getSimpleName() + " loaded."); | ||
} else { | ||
ManasCoreConfig.LOG.info( config.getClass().getSimpleName() + " not found."); | ||
} | ||
} catch (IOException e) { | ||
ManasCoreConfig.LOG.error("Error loading config: " + config.getClass().getSimpleName()); | ||
e.printStackTrace(); | ||
} | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling and path construction.
Consider these improvements:
- Replace
e.printStackTrace()
with structured logging - Sanitize file paths to prevent directory traversal
- Add validation for
config.getParentPath()
try {
+ String parentPath = config.getParentPath();
+ if (parentPath == null || parentPath.isEmpty()) {
+ ManasCoreConfig.LOG.error("Invalid parent path for config: " + config.getClass().getSimpleName());
+ return;
+ }
+ Path startPath = Paths.get(parentPath).normalize()
+ .resolve(config.getClass().getSimpleName().toLowerCase() + ".json");
if (Files.exists(startPath)) {
String string = Files.readString(startPath);
ManasConfig loadedConfig = gson.fromJson(string, config.getClass());
loadedConfigs.add(loadedConfig);
ManasCoreConfig.LOG.info(config.getClass().getSimpleName() + " loaded.");
} else {
ManasCoreConfig.LOG.info(config.getClass().getSimpleName() + " not found.");
}
} catch (IOException e) {
- ManasCoreConfig.LOG.error("Error loading config: " + config.getClass().getSimpleName());
- e.printStackTrace();
+ ManasCoreConfig.LOG.error("Error loading config: " + config.getClass().getSimpleName()
+ + " - " + e.getMessage());
}
📝 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 static void loadConfigs() { | |
registeredConfigs.forEach(config -> { | |
Path startPath = Paths.get(config.getParentPath() + "/" + config.getClass().getSimpleName().toLowerCase() + ".json"); | |
try { | |
if (Files.exists(startPath)) { | |
String string = Files.readString(startPath); | |
ManasConfig loadedConfig = gson.fromJson(string, config.getClass()); | |
loadedConfigs.add(loadedConfig); | |
ManasCoreConfig.LOG.info( config.getClass().getSimpleName() + " loaded."); | |
} else { | |
ManasCoreConfig.LOG.info( config.getClass().getSimpleName() + " not found."); | |
} | |
} catch (IOException e) { | |
ManasCoreConfig.LOG.error("Error loading config: " + config.getClass().getSimpleName()); | |
e.printStackTrace(); | |
} | |
}); | |
} | |
public static void loadConfigs() { | |
registeredConfigs.forEach(config -> { | |
try { | |
String parentPath = config.getParentPath(); | |
if (parentPath == null || parentPath.isEmpty()) { | |
ManasCoreConfig.LOG.error("Invalid parent path for config: " + config.getClass().getSimpleName()); | |
return; | |
} | |
Path startPath = Paths.get(parentPath).normalize() | |
.resolve(config.getClass().getSimpleName().toLowerCase() + ".json"); | |
if (Files.exists(startPath)) { | |
String string = Files.readString(startPath); | |
ManasConfig loadedConfig = gson.fromJson(string, config.getClass()); | |
loadedConfigs.add(loadedConfig); | |
ManasCoreConfig.LOG.info(config.getClass().getSimpleName() + " loaded."); | |
} else { | |
ManasCoreConfig.LOG.info(config.getClass().getSimpleName() + " not found."); | |
} | |
} catch (IOException e) { | |
ManasCoreConfig.LOG.error("Error loading config: " + config.getClass().getSimpleName() | |
+ " - " + e.getMessage()); | |
} | |
}); | |
} |
import java.util.HashMap; | ||
|
||
public class ManasKeybinding extends KeyMapping { | ||
private static final HashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider thread safety for shared state
The static PRESSED_KEYBINDINGS
HashMap is accessed from multiple points but lacks synchronization. In a game environment with multiple input events, this could lead to race conditions.
Consider using a thread-safe collection:
- private static final HashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new HashMap<>();
+ private static final Map<ManasKeybinding, Long> PRESSED_KEYBINDINGS = Collections.synchronizedMap(new HashMap<>());
📝 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.
private static final HashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new HashMap<>(); | |
private static final Map<ManasKeybinding, Long> PRESSED_KEYBINDINGS = Collections.synchronizedMap(new HashMap<>()); |
"mixins": [ | ||
"${mod_id}.mixins.json", | ||
"${mod_id}-fabric.mixins.json" | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Remove redundant empty mixin configuration
The mod currently includes two identical empty mixin configurations targeting the same package. Since no mixins are defined in either file, having both configurations is unnecessary. Consider removing ${mod_id}-fabric.mixins.json
and keeping only the common mixins file for now. The platform-specific mixin file can be added back when needed.
🔗 Analysis chain
Verify the necessity of multiple mixin configurations
The mod includes two mixin configurations. Verify that both are necessary and contain different mixins:
${mod_id}.mixins.json
(common mixins)${mod_id}-fabric.mixins.json
(fabric-specific mixins)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check the contents of both mixin files
echo "Common mixins:"
find registry-common -name "*.mixins.json" -exec cat {} \;
echo "Fabric mixins:"
find registry-fabric -name "*-fabric.mixins.json" -exec cat {} \;
Length of output: 666
@@ -0,0 +1,38 @@ | |||
modLoader = "javafml" | |||
loaderVersion = "[2,)" | |||
issueTrackerURL = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Add issue tracker URL for better user support
Set issueTrackerURL = "https://github.com/ManasMods/ManasCore/issues"
since the repository has GitHub Issues enabled.
🔗 Analysis chain
Add issue tracker URL for better user support.
The empty issue tracker URL should be populated to help users report bugs and request features.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if issue tracker URL is defined in other platform configurations
rg -l 'issueTrackerURL.*=.*"[^"]'
Length of output: 35
Script:
#!/bin/bash
# Find all mod configuration files
fd -e toml -x cat {} \;
# Get repository information
gh repo view --json url,hasIssuesEnabled
Length of output: 7602
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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_KEYBINDINGS
HashMap 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
name
parameter 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@param
tagThe 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_ID
is 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
KeyMapping
but lacks@Override
annotations on inherited methods.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
87-89
: Use correct constructor forResourceLocation
The method
ResourceLocation.fromNamespaceAndPath
may not exist. Instead, you should use the constructornew ResourceLocation(namespace, path)
to create aResourceLocation
instance.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@ModifyVariable
and local variablesThe use of
@ModifyVariable
with@Local(argsOnly = true)
assumes thatdamageSource
is a local variable within the target method at the specified injection point. Verify thatdamageSource
is indeed available as a local variable at ordinal 6 in theactuallyHurt
method ofPlayer
class. If not, this mixin may fail at runtime.Please confirm the availability of
damageSource
at 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@WrapOperation
methodIn the mixin method
onSetTarget
, ensure that the parameters match the expected signature when using@WrapOperation
. TheOperation
parameter should be last, and@WrapOperation
methods 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
@WrapOperation
to 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_HIT
Event.The event handler needs null checking for the entity from hitResult.
this.action = () -> { | ||
if (!PRESSED_KEYBINDINGS.containsKey(this)) { | ||
PRESSED_KEYBINDINGS.put(this, System.currentTimeMillis()); | ||
action.onPress(); | ||
} | ||
}; | ||
this.release = () -> { | ||
if (PRESSED_KEYBINDINGS.containsKey(this)) { | ||
long start = PRESSED_KEYBINDINGS.remove(this); | ||
long end = System.currentTimeMillis(); | ||
release.onRelease(end - start); | ||
} | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add duration bounds checking and overflow protection
The key press duration calculation has two issues:
- No maximum duration limit could lead to resource waste
- Potential integer overflow in duration calculation
this.action = () -> {
if (!PRESSED_KEYBINDINGS.containsKey(this)) {
PRESSED_KEYBINDINGS.put(this, System.currentTimeMillis());
action.onPress();
}
};
this.release = () -> {
if (PRESSED_KEYBINDINGS.containsKey(this)) {
long start = PRESSED_KEYBINDINGS.remove(this);
long end = System.currentTimeMillis();
- release.onRelease(end - start);
+ long duration = Math.max(0, Math.min(end - start, Long.MAX_VALUE));
+ // Limit maximum duration to prevent resource waste
+ if (duration > TimeUnit.MINUTES.toMillis(5)) {
+ LOGGER.warn("Key {} was pressed for too long: {} ms", this, duration);
+ return;
+ }
+ release.onRelease(duration);
}
};
Committable suggestion skipped: line range outside the PR's diff.
} | ||
|
||
public AttributeModifier create(double i) { | ||
return new AttributeModifier(this.id, this.amount * i, this.operation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect use of AttributeModifier
constructor and identifiers
The AttributeModifier
constructor expects a UUID
and a String
as parameters, but a ResourceLocation
is being used instead. Additionally, methods like removeModifier(UUID id)
require a UUID
, but a ResourceLocation
is provided. This will cause runtime errors as the types do not match.
Apply this diff to fix the issue:
In AttributeTemplate
, modify the id
to be a UUID
and include a name
:
- public static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) {
+ public static record AttributeTemplate(UUID id, String name, double amount, AttributeModifier.Operation operation) {
Update the constructor accordingly:
- public AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) {
+ public AttributeTemplate(UUID id, String name, double amount, AttributeModifier.Operation operation) {
Adjust the create
method to use the correct parameters:
- return new AttributeModifier(this.id, this.amount * i, this.operation);
+ return new AttributeModifier(this.id, this.name, this.amount * i, this.operation);
Update the methods where AttributeTemplate
is used:
In addHeldAttributeModifier
:
- public void addHeldAttributeModifier(Holder<Attribute> holder, ResourceLocation resourceLocation, double amount, AttributeModifier.Operation operation) {
+ public void addHeldAttributeModifier(Holder<Attribute> holder, UUID id, String name, double amount, AttributeModifier.Operation operation) {
In addHeldAttributeModifiers
and removeAttributeModifiers
, ensure that the correct UUID
is used for adding and removing modifiers:
- attributeInstance.removeModifier(entry.getValue().id());
+ attributeInstance.removeModifier(entry.getValue().id());
Also applies to: 230-231, 244-244
@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(getRegistryName(), skill.getRegistryName()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Override hashCode()
when equals()
is overridden
Since equals()
is overridden, it's important to also override hashCode()
to maintain the general contract between equals()
and hashCode()
. This ensures consistent behavior when instances are used in hash-based collections.
Consider adding the following hashCode()
method:
+ @Override
+ public int hashCode() {
+ return Objects.hash(getRegistryName());
+ }
📝 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 boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
ManasSkill skill = (ManasSkill) o; | |
return Objects.equals(getRegistryName(), skill.getRegistryName()); | |
} | |
@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(getRegistryName(), skill.getRegistryName()); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(getRegistryName()); | |
} |
public static void skillActivationPacket(int keyNumber) { | ||
if (Platform.getEnvironment() == Env.CLIENT) { | ||
InternalSkillPacketActions.sendSkillActivationPacket(keyNumber); | ||
} | ||
} | ||
|
||
/** | ||
* Send {@link InternalSkillPacketActions#sendSkillReleasePacket} with a DistExecutor on client side. | ||
* Used when player release a skill activation key bind. | ||
* | ||
* @see InternalSkillPacketActions#sendSkillReleasePacket | ||
*/ | ||
public static void skillReleasePacket(int keyNumber, int heldTicks) { | ||
if (Platform.getEnvironment() == Env.CLIENT) { | ||
InternalSkillPacketActions.sendSkillReleasePacket(keyNumber, heldTicks); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation for packet methods.
The methods skillActivationPacket
and skillReleasePacket
should validate their input parameters:
keyNumber
should be non-negativeheldTicks
should be non-negative
public static void skillActivationPacket(int keyNumber) {
+ if (keyNumber < 0) {
+ throw new IllegalArgumentException("keyNumber must be non-negative");
+ }
if (Platform.getEnvironment() == Env.CLIENT) {
InternalSkillPacketActions.sendSkillActivationPacket(keyNumber);
}
}
public static void skillReleasePacket(int keyNumber, int heldTicks) {
+ if (keyNumber < 0 || heldTicks < 0) {
+ throw new IllegalArgumentException("keyNumber and heldTicks must be non-negative");
+ }
if (Platform.getEnvironment() == Env.CLIENT) {
InternalSkillPacketActions.sendSkillReleasePacket(keyNumber, heldTicks);
}
}
📝 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 static void skillActivationPacket(int keyNumber) { | |
if (Platform.getEnvironment() == Env.CLIENT) { | |
InternalSkillPacketActions.sendSkillActivationPacket(keyNumber); | |
} | |
} | |
/** | |
* Send {@link InternalSkillPacketActions#sendSkillReleasePacket} with a DistExecutor on client side. | |
* Used when player release a skill activation key bind. | |
* | |
* @see InternalSkillPacketActions#sendSkillReleasePacket | |
*/ | |
public static void skillReleasePacket(int keyNumber, int heldTicks) { | |
if (Platform.getEnvironment() == Env.CLIENT) { | |
InternalSkillPacketActions.sendSkillReleasePacket(keyNumber, heldTicks); | |
} | |
} | |
public static void skillActivationPacket(int keyNumber) { | |
if (keyNumber < 0) { | |
throw new IllegalArgumentException("keyNumber must be non-negative"); | |
} | |
if (Platform.getEnvironment() == Env.CLIENT) { | |
InternalSkillPacketActions.sendSkillActivationPacket(keyNumber); | |
} | |
} | |
/** | |
* Send {@link InternalSkillPacketActions#sendSkillReleasePacket} with a DistExecutor on client side. | |
* Used when player release a skill activation key bind. | |
* | |
* @see InternalSkillPacketActions#sendSkillReleasePacket | |
*/ | |
public static void skillReleasePacket(int keyNumber, int heldTicks) { | |
if (keyNumber < 0 || heldTicks < 0) { | |
throw new IllegalArgumentException("keyNumber and heldTicks must be non-negative"); | |
} | |
if (Platform.getEnvironment() == Env.CLIENT) { | |
InternalSkillPacketActions.sendSkillReleasePacket(keyNumber, heldTicks); | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@Override
annotations 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
onDamageEntity
modifies 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_KEYBINDINGS
map.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.png
is excluded by!**/*.png
keybind-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
keybind-neoforge/src/main/resources/icon.png
is 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.png
exists in the correct resources directory.✅ Verification successful
Logo file verification successful
The referenced
icon.png
exists 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_network
dependency 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_network
serving 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.withDefaultNamespace
In the constructor, you use
ResourceLocation.withDefaultNamespace("skill.speed")
. Please verify that this method correctly creates aResourceLocation
with the intended namespace. If not, consider usingnew ResourceLocation("skill.speed")
for clarity.
118-124
: Ensure damage cancellation works as expectedIn the
onBeingDamaged
method, returningfalse
when 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
onTakenDamage
method, 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
ManasCoreKeybindFabric
class correctly implements theModInitializer
interface 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:config
have been properly added to theincludedProjects
list, 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:
@SubscribeEvent | ||
static void onLivingChangeTarget(final LivingChangeTargetEvent e) { | ||
if (!e.getTargetType().equals(LivingChangeTargetEvent.LivingTargetType.MOB_TARGET)) return; | ||
Changeable<LivingEntity> changeableTarget = Changeable.of(e.getNewAboutToBeSetTarget()); | ||
if (EntityEvents.LIVING_CHANGE_TARGET.invoker().changeTarget(e.getEntity(), changeableTarget).isFalse()) { | ||
e.setCanceled(true); | ||
} else { | ||
e.setNewAboutToBeSetTarget(changeableTarget.get()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add null check for target entity.
The getNewAboutToBeSetTarget()
could return null, which might cause NPE when creating the Changeable instance.
- Changeable<LivingEntity> changeableTarget = Changeable.of(e.getNewAboutToBeSetTarget());
+ LivingEntity newTarget = e.getNewAboutToBeSetTarget();
+ Changeable<LivingEntity> changeableTarget = Changeable.of(newTarget);
+ if (newTarget == null) return;
Committable suggestion skipped: line range outside the PR's diff.
@SubscribeEvent | ||
static void onProjectileHit(final ProjectileImpactEvent e) { | ||
Changeable<EntityEvents.ProjectileHitResult> result = Changeable.of(EntityEvents.ProjectileHitResult.DEFAULT); | ||
Changeable<ProjectileDeflection> deflection = Changeable.of(ProjectileDeflection.NONE); | ||
EntityEvents.PROJECTILE_HIT.invoker().hit(e.getRayTraceResult(), e.getProjectile(), deflection, result); | ||
if (!Objects.equals(result.get(), EntityEvents.ProjectileHitResult.DEFAULT)) e.setCanceled(true); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Address unused deflection parameter and add documentation.
The deflection
parameter is created but its value is never used in the logic. Also, the method would benefit from documentation explaining the cancellation behavior.
+ /**
+ * Handles projectile impact events.
+ * The event is canceled if the hit result is different from the default,
+ * allowing skills to modify projectile behavior.
+ *
+ * @param e The projectile impact event
+ */
@SubscribeEvent
static void onProjectileHit(final ProjectileImpactEvent e) {
Changeable<EntityEvents.ProjectileHitResult> result = Changeable.of(EntityEvents.ProjectileHitResult.DEFAULT);
Changeable<ProjectileDeflection> deflection = Changeable.of(ProjectileDeflection.NONE);
EntityEvents.PROJECTILE_HIT.invoker().hit(e.getRayTraceResult(), e.getProjectile(), deflection, result);
- if (!Objects.equals(result.get(), EntityEvents.ProjectileHitResult.DEFAULT)) e.setCanceled(true);
+ if (!Objects.equals(result.get(), EntityEvents.ProjectileHitResult.DEFAULT)) {
+ e.setCanceled(true);
+ // Apply deflection if modified
+ if (!Objects.equals(deflection.get(), ProjectileDeflection.NONE)) {
+ e.getProjectile().setDeltaMovement(e.getProjectile().getDeltaMovement().multiply(deflection.get().getMultiplier()));
+ }
+ }
}
Committable suggestion skipped: line range outside the PR's diff.
[[mixins]] | ||
config = "${mod_id}.mixins.json" | ||
|
||
[[mixins]] | ||
config = "manascore_skill-neoforge.mixins.json" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Remove redundant mixin configuration
The second mixin configuration manascore_skill-neoforge.mixins.json
is redundant and inconsistent with the project's pattern. All other NeoForge modules use only the ${mod_id}.mixins.json
configuration. Remove the second entry and ensure all mixins are consolidated into the primary configuration file.
- Remove the second
[[mixins]]
block referencingmanascore_skill-neoforge.mixins.json
- Move any unique mixins from
manascore_skill-neoforge.mixins.json
to the primary configuration file
🔗 Analysis chain
Consider consolidating mixin configurations.
Having two separate mixin configurations is unusual. Consider consolidating them into a single configuration file. Also, verify that both mixin JSON files exist.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if both mixin configuration files exist
echo "Checking for mixin configuration files..."
fd -t f "mixins.json" skill-neoforge/src/main/resources/
# Check the content of mixin configurations to see if they can be consolidated
echo "Checking mixin configuration contents..."
fd -t f "mixins.json" skill-neoforge/src/main/resources/ -x cat
Length of output: 650
Script:
#!/bin/bash
# Search for any .mixins.json files and gradle/properties files
echo "Searching for mixin configurations and mod properties..."
fd -t f "\.mixins\.json$|gradle\.properties$"
# Search for mixin-related references in the codebase
echo "Searching for mixin references in code..."
rg -g "!*.mixins.json" "mixins?.json"
Length of output: 2519
import java.util.ArrayList; | ||
|
||
public class KeybindingManager { | ||
private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider thread-safe collection for keybindings.
Using a non-synchronized ArrayList could lead to concurrent modification issues. Consider using a thread-safe collection.
- private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>();
+ private static final List<ManasKeybinding> keybindings =
+ Collections.synchronizedList(new ArrayList<>());
📝 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.
private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>(); | |
private static final List<ManasKeybinding> keybindings = | |
Collections.synchronizedList(new ArrayList<>()); |
public class KeybindingManager { | ||
private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Make the utility class final and add a private constructor.
Utility classes should be final and have a private constructor to prevent instantiation.
-public class KeybindingManager {
+public final class KeybindingManager {
private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>();
+
+ private KeybindingManager() {
+ throw new UnsupportedOperationException("Utility class");
+ }
📝 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 KeybindingManager { | |
private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>(); | |
public final class KeybindingManager { | |
private static final ArrayList<ManasKeybinding> keybindings = new ArrayList<>(); | |
private KeybindingManager() { | |
throw new UnsupportedOperationException("Utility class"); | |
} |
ClientTickEvent.CLIENT_POST.register(instance -> { | ||
for (final ManasKeybinding keybinding : keybindings) { | ||
if (keybinding.isDown()) { | ||
keybinding.getAction().onPress(); | ||
} else if (keybinding.getRelease() != null) { | ||
keybinding.getRelease().run(); | ||
} | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for action execution.
The event handler should catch and handle exceptions to prevent crashes from failed keybinding actions.
ClientTickEvent.CLIENT_POST.register(instance -> {
- for (final ManasKeybinding keybinding : keybindings) {
- if (keybinding.isDown()) {
- keybinding.getAction().onPress();
- } else if (keybinding.getRelease() != null) {
- keybinding.getRelease().run();
+ synchronized (keybindings) {
+ for (final ManasKeybinding keybinding : keybindings) {
+ try {
+ if (keybinding.isDown()) {
+ keybinding.getAction().onPress();
+ } else if (keybinding.getRelease() != null) {
+ keybinding.getRelease().run();
+ }
+ } catch (Exception e) {
+ LOGGER.error("Failed to execute keybinding action", e);
+ }
}
}
});
Committable suggestion skipped: line range outside the PR's diff.
import java.util.HashMap; | ||
|
||
public class ManasKeybinding extends KeyMapping { | ||
private static final HashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider using ConcurrentHashMap for thread safety.
The static PRESSED_KEYBINDINGS
HashMap is accessed from key press/release events which might occur on different threads. Using ConcurrentHashMap
would prevent potential race conditions.
- private static final HashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new HashMap<>();
+ private static final ConcurrentHashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new ConcurrentHashMap<>();
📝 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.
private static final HashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new HashMap<>(); | |
private static final ConcurrentHashMap<ManasKeybinding, Long> PRESSED_KEYBINDINGS = new ConcurrentHashMap<>(); |
KeybindingManager.register( | ||
new ManasKeybinding("manascore.keybinding.test", | ||
category, () -> ManasCoreTesting.LOG.info("Pressing"), | ||
duration -> ManasCoreTesting.LOG.info("Released in {} Seconds", duration / 1000.0) | ||
), | ||
new ManasKeybinding("manascore.keybinding.test_press", category, () -> ManasCoreTesting.LOG.info("Pressed")), | ||
new ManasKeybinding("manascore.keybinding.skill", category, | ||
() -> SkillAPI.skillActivationPacket(0), | ||
duration -> SkillAPI.skillReleasePacket(0, (int) (duration / 50))), | ||
new ManasKeybinding("manascore.keybinding.skill_2", category, | ||
() -> SkillAPI.skillActivationPacket(1), | ||
duration -> SkillAPI.skillReleasePacket(1, (int) (duration / 50))), | ||
new ManasKeybinding("manascore.keybinding.skill_toggle", category, SkillAPI::skillTogglePacket) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for skill activation/release packets.
The test keybindings directly call SkillAPI
methods without any error handling. Consider wrapping these calls in try-catch blocks to handle potential network or API failures gracefully.
KeybindingManager.register(
new ManasKeybinding("manascore.keybinding.skill", category,
- () -> SkillAPI.skillActivationPacket(0),
- duration -> SkillAPI.skillReleasePacket(0, (int) (duration / 50))),
+ () -> {
+ try {
+ SkillAPI.skillActivationPacket(0);
+ } catch (Exception e) {
+ ManasCoreTesting.LOG.error("Failed to send skill activation packet", e);
+ }
+ },
+ duration -> {
+ try {
+ SkillAPI.skillReleasePacket(0, (int) (duration / 50));
+ } catch (Exception e) {
+ ManasCoreTesting.LOG.error("Failed to send skill release packet", e);
+ }
+ }),
📝 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.
KeybindingManager.register( | |
new ManasKeybinding("manascore.keybinding.test", | |
category, () -> ManasCoreTesting.LOG.info("Pressing"), | |
duration -> ManasCoreTesting.LOG.info("Released in {} Seconds", duration / 1000.0) | |
), | |
new ManasKeybinding("manascore.keybinding.test_press", category, () -> ManasCoreTesting.LOG.info("Pressed")), | |
new ManasKeybinding("manascore.keybinding.skill", category, | |
() -> SkillAPI.skillActivationPacket(0), | |
duration -> SkillAPI.skillReleasePacket(0, (int) (duration / 50))), | |
new ManasKeybinding("manascore.keybinding.skill_2", category, | |
() -> SkillAPI.skillActivationPacket(1), | |
duration -> SkillAPI.skillReleasePacket(1, (int) (duration / 50))), | |
new ManasKeybinding("manascore.keybinding.skill_toggle", category, SkillAPI::skillTogglePacket) | |
); | |
KeybindingManager.register( | |
new ManasKeybinding("manascore.keybinding.test", | |
category, () -> ManasCoreTesting.LOG.info("Pressing"), | |
duration -> ManasCoreTesting.LOG.info("Released in {} Seconds", duration / 1000.0) | |
), | |
new ManasKeybinding("manascore.keybinding.test_press", category, () -> ManasCoreTesting.LOG.info("Pressed")), | |
new ManasKeybinding("manascore.keybinding.skill", category, | |
() -> { | |
try { | |
SkillAPI.skillActivationPacket(0); | |
} catch (Exception e) { | |
ManasCoreTesting.LOG.error("Failed to send skill activation packet", e); | |
} | |
}, | |
duration -> { | |
try { | |
SkillAPI.skillReleasePacket(0, (int) (duration / 50)); | |
} catch (Exception e) { | |
ManasCoreTesting.LOG.error("Failed to send skill release packet", e); | |
} | |
}), | |
new ManasKeybinding("manascore.keybinding.skill_2", category, | |
() -> SkillAPI.skillActivationPacket(1), | |
duration -> SkillAPI.skillReleasePacket(1, (int) (duration / 50))), | |
new ManasKeybinding("manascore.keybinding.skill_toggle", category, SkillAPI::skillTogglePacket) | |
); |
if (sender.getPlayer() != null) { | ||
Entity entity = ((ServerLevel) sender.getPlayer().level()).getEntity(uuid); | ||
if (entity instanceof LivingEntity living) { | ||
Skills storage = SkillAPI.getSkillsFrom(living); | ||
if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) | ||
ManasCoreTesting.LOG.info("Added Tested Skill to Entity!"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling and user feedback in skill learning.
The skill learning logic lacks proper error handling and user feedback. Consider adding appropriate error messages and success/failure notifications to the command sender.
if (sender.getPlayer() != null) {
Entity entity = ((ServerLevel) sender.getPlayer().level()).getEntity(uuid);
if (entity instanceof LivingEntity living) {
Skills storage = SkillAPI.getSkillsFrom(living);
- if (storage.learnSkill(RegistryTest.TEST_SKILL.get()))
+ try {
+ if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) {
ManasCoreTesting.LOG.info("Added Tested Skill to Entity!");
+ 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()));
+ }
}
}
📝 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 (sender.getPlayer() != null) { | |
Entity entity = ((ServerLevel) sender.getPlayer().level()).getEntity(uuid); | |
if (entity instanceof LivingEntity living) { | |
Skills storage = SkillAPI.getSkillsFrom(living); | |
if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) | |
ManasCoreTesting.LOG.info("Added Tested Skill to Entity!"); | |
} | |
} | |
if (sender.getPlayer() != null) { | |
Entity entity = ((ServerLevel) sender.getPlayer().level()).getEntity(uuid); | |
if (entity instanceof LivingEntity living) { | |
Skills storage = SkillAPI.getSkillsFrom(living); | |
try { | |
if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { | |
ManasCoreTesting.LOG.info("Added Tested Skill to Entity!"); | |
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())); | |
} | |
} | |
} |
void onHurt(DamageSource source, float amount, CallbackInfo ci) { | ||
Changeable<Float> changeable = Changeable.of(amount); | ||
if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, source, changeable).isFalse()) ci.cancel(); | ||
else damageContainers.peek().setNewDamage(changeable.get()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null check and empty stack validation
The code has potential null pointer risks:
- Missing null check for
changeable.get()
- No validation for empty damageContainers stack
Apply these changes:
void onHurt(DamageSource source, float amount, CallbackInfo ci) {
Changeable<Float> changeable = Changeable.of(amount);
- if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, source, changeable).isFalse()) ci.cancel();
- else damageContainers.peek().setNewDamage(changeable.get());
+ if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, source, changeable).isFalse()) {
+ ci.cancel();
+ return;
+ }
+ Float newDamage = changeable.get();
+ if (newDamage != null && !damageContainers.isEmpty()) {
+ damageContainers.peek().setNewDamage(newDamage);
+ }
}
📝 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.
void onHurt(DamageSource source, float amount, CallbackInfo ci) { | |
Changeable<Float> changeable = Changeable.of(amount); | |
if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, source, changeable).isFalse()) ci.cancel(); | |
else damageContainers.peek().setNewDamage(changeable.get()); | |
} | |
void onHurt(DamageSource source, float amount, CallbackInfo ci) { | |
Changeable<Float> changeable = Changeable.of(amount); | |
if (EntityEvents.LIVING_HURT.invoker().hurt((LivingEntity) (Object) this, source, changeable).isFalse()) { | |
ci.cancel(); | |
return; | |
} | |
Float newDamage = changeable.get(); | |
if (newDamage != null && !damageContainers.isEmpty()) { | |
damageContainers.peek().setNewDamage(newDamage); | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 atHEAD
can cause conflicts with other mixins.
Injecting at the head ofhurt
is 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 signaturegetCritChanceDamage
also 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
final
for 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
volatile
orAtomicReference
instead 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
registerPlayerAttribute
andregisterGenericAttribute
methods 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
getLookTowardVec
method 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
applyEntityCrit
method 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.png
is excluded by!**/*.png
attribute-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
attribute-neoforge/src/main/resources/icon.png
is 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@Local
injection. Otherwise, this code may fail at runtime.
27-38
: Enhance coverage of non-playerLivingEntity
attacks.
- The code currently excludes players (
Player
instance 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 atHEAD
and 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 correctordinal
if 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.gradle
and 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.json
which 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.png
exists 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
public interface AttributeEvents { | ||
Event<CriticalAttackChanceEvent> CRITICAL_ATTACK_CHANCE_EVENT = EventFactory.createEventResult(); | ||
|
||
@FunctionalInterface | ||
interface CriticalAttackChanceEvent { | ||
EventResult applyCrit(LivingEntity attacker, Entity target, float originalMultiplier, Changeable<Float> multiplier, Changeable<Double> chance); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add comprehensive documentation for the event interface and its parameters.
The event interface handles critical attack mechanics but lacks documentation explaining the purpose and parameter usage. Consider:
- Adding interface-level documentation
- Adding parameter validation for null checks
- Documenting the expected ranges for numerical parameters
+/**
+ * Events related to attribute-based mechanics.
+ */
public interface AttributeEvents {
Event<CriticalAttackChanceEvent> CRITICAL_ATTACK_CHANCE_EVENT = EventFactory.createEventResult();
@FunctionalInterface
+ /**
+ * Event fired when calculating critical attack chance.
+ * @param attacker The entity performing the attack
+ * @param target The entity being attacked
+ * @param originalMultiplier The base damage multiplier [0.0, inf)
+ * @param multiplier Mutable damage multiplier
+ * @param chance Mutable critical hit chance [0.0, 1.0]
+ * @return EventResult indicating if the critical hit should be applied
+ * @throws NullPointerException if attacker, multiplier or chance is null
+ */
interface CriticalAttackChanceEvent {
- EventResult applyCrit(LivingEntity attacker, Entity target, float originalMultiplier, Changeable<Float> multiplier, Changeable<Double> chance);
+ default EventResult applyCrit(LivingEntity attacker, Entity target, float originalMultiplier, Changeable<Float> multiplier, Changeable<Double> chance) {
+ Objects.requireNonNull(attacker, "Attacker cannot be null");
+ Objects.requireNonNull(multiplier, "Multiplier cannot be null");
+ Objects.requireNonNull(chance, "Chance cannot be null");
+ if (originalMultiplier < 0) {
+ throw new IllegalArgumentException("Original multiplier must be non-negative");
+ }
+ return onCriticalAttack(attacker, target, originalMultiplier, multiplier, chance);
+ }
+
+ EventResult onCriticalAttack(LivingEntity attacker, Entity target, float originalMultiplier, Changeable<Float> multiplier, Changeable<Double> chance);
}
📝 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 interface AttributeEvents { | |
Event<CriticalAttackChanceEvent> CRITICAL_ATTACK_CHANCE_EVENT = EventFactory.createEventResult(); | |
@FunctionalInterface | |
interface CriticalAttackChanceEvent { | |
EventResult applyCrit(LivingEntity attacker, Entity target, float originalMultiplier, Changeable<Float> multiplier, Changeable<Double> chance); | |
} | |
} | |
/** | |
* Events related to attribute-based mechanics. | |
*/ | |
public interface AttributeEvents { | |
Event<CriticalAttackChanceEvent> CRITICAL_ATTACK_CHANCE_EVENT = EventFactory.createEventResult(); | |
@FunctionalInterface | |
/** | |
* Event fired when calculating critical attack chance. | |
* @param attacker The entity performing the attack | |
* @param target The entity being attacked | |
* @param originalMultiplier The base damage multiplier [0.0, inf) | |
* @param multiplier Mutable damage multiplier | |
* @param chance Mutable critical hit chance [0.0, 1.0] | |
* @return EventResult indicating if the critical hit should be applied | |
*/ | |
interface CriticalAttackChanceEvent { | |
EventResult applyCrit(LivingEntity attacker, Entity target, float originalMultiplier, Changeable<Float> multiplier, Changeable<Double> chance); | |
} | |
} |
public static final Holder<Attribute> TEST_ATTRIBUTE_PLAYER = ManasCoreAttributes.registerPlayerAttribute(ModuleConstants.MOD_ID, | ||
"test_attribute_player", "manascore.attribute.test_attribute_player",69, 0, 420, true, Attribute.Sentiment.NEUTRAL); | ||
public static final Holder<Attribute> TEST_ATTRIBUTE_ALL = ManasCoreAttributes.registerGenericAttribute(ModuleConstants.MOD_ID, | ||
"test_attribute_all", "manascore.attribute.test_attribute_all", 420, 69, 4200, true, Attribute.Sentiment.NEGATIVE); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve test attribute configuration maintainability.
The current implementation has several maintainability issues:
- Magic numbers for attribute ranges
- Hardcoded string identifiers
- No documentation explaining the test cases
+/**
+ * Registry for test attributes used in integration tests.
+ */
public class TestAttributeRegistry {
+ private static final String TEST_ATTRIBUTE_PLAYER_ID = "test_attribute_player";
+ private static final String TEST_ATTRIBUTE_ALL_ID = "test_attribute_all";
+
+ // Test ranges for player attributes
+ private static final float PLAYER_MIN = 0;
+ private static final float PLAYER_DEFAULT = 69;
+ private static final float PLAYER_MAX = 420;
+
+ // Test ranges for generic attributes
+ private static final float GENERIC_MIN = 69;
+ private static final float GENERIC_DEFAULT = 420;
+ private static final float GENERIC_MAX = 4200;
+
public static final Holder<Attribute> TEST_ATTRIBUTE_PLAYER = ManasCoreAttributes.registerPlayerAttribute(ModuleConstants.MOD_ID,
- "test_attribute_player", "manascore.attribute.test_attribute_player",69, 0, 420, true, Attribute.Sentiment.NEUTRAL);
+ TEST_ATTRIBUTE_PLAYER_ID,
+ "manascore.attribute." + TEST_ATTRIBUTE_PLAYER_ID,
+ PLAYER_DEFAULT, PLAYER_MIN, PLAYER_MAX,
+ true, Attribute.Sentiment.NEUTRAL);
+
public static final Holder<Attribute> TEST_ATTRIBUTE_ALL = ManasCoreAttributes.registerGenericAttribute(ModuleConstants.MOD_ID,
- "test_attribute_all", "manascore.attribute.test_attribute_all", 420, 69, 4200, true, Attribute.Sentiment.NEGATIVE);
+ TEST_ATTRIBUTE_ALL_ID,
+ "manascore.attribute." + TEST_ATTRIBUTE_ALL_ID,
+ GENERIC_DEFAULT, GENERIC_MIN, GENERIC_MAX,
+ true, Attribute.Sentiment.NEGATIVE);
📝 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 static final Holder<Attribute> TEST_ATTRIBUTE_PLAYER = ManasCoreAttributes.registerPlayerAttribute(ModuleConstants.MOD_ID, | |
"test_attribute_player", "manascore.attribute.test_attribute_player",69, 0, 420, true, Attribute.Sentiment.NEUTRAL); | |
public static final Holder<Attribute> TEST_ATTRIBUTE_ALL = ManasCoreAttributes.registerGenericAttribute(ModuleConstants.MOD_ID, | |
"test_attribute_all", "manascore.attribute.test_attribute_all", 420, 69, 4200, true, Attribute.Sentiment.NEGATIVE); | |
} | |
/** | |
* Registry for test attributes used in integration tests. | |
*/ | |
public class TestAttributeRegistry { | |
private static final String TEST_ATTRIBUTE_PLAYER_ID = "test_attribute_player"; | |
private static final String TEST_ATTRIBUTE_ALL_ID = "test_attribute_all"; | |
// Test ranges for player attributes | |
private static final float PLAYER_MIN = 0; | |
private static final float PLAYER_DEFAULT = 69; | |
private static final float PLAYER_MAX = 420; | |
// Test ranges for generic attributes | |
private static final float GENERIC_MIN = 69; | |
private static final float GENERIC_DEFAULT = 420; | |
private static final float GENERIC_MAX = 4200; | |
public static final Holder<Attribute> TEST_ATTRIBUTE_PLAYER = ManasCoreAttributes.registerPlayerAttribute(ModuleConstants.MOD_ID, | |
TEST_ATTRIBUTE_PLAYER_ID, | |
"manascore.attribute." + TEST_ATTRIBUTE_PLAYER_ID, | |
PLAYER_DEFAULT, PLAYER_MIN, PLAYER_MAX, | |
true, Attribute.Sentiment.NEUTRAL); | |
public static final Holder<Attribute> TEST_ATTRIBUTE_ALL = ManasCoreAttributes.registerGenericAttribute(ModuleConstants.MOD_ID, | |
TEST_ATTRIBUTE_ALL_ID, | |
"manascore.attribute." + TEST_ATTRIBUTE_ALL_ID, | |
GENERIC_DEFAULT, GENERIC_MIN, GENERIC_MAX, | |
true, Attribute.Sentiment.NEGATIVE); | |
} |
public static float getWeaponDamage(LivingEntity attacker, @Nullable Entity target, @Nullable DamageSource source) { | ||
AttributeInstance attack = attacker.getAttribute(Attributes.ATTACK_DAMAGE); | ||
if (attack == null) return 0; | ||
|
||
float damage = 1F; | ||
AttributeModifier modifier = attack.getModifier(Item.BASE_ATTACK_DAMAGE_ID); | ||
if (modifier != null) damage += (float) modifier.amount(); | ||
|
||
if (target != null && source != null && attacker.level() instanceof ServerLevel serverLevel) | ||
damage = EnchantmentHelper.modifyDamage(serverLevel, attacker.getWeaponItem(), target, source, damage); | ||
return damage; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add null checks for weapon item and improve error handling.
The getWeaponDamage
method could benefit from additional null checks and better error handling:
- Check if
attacker.getWeaponItem()
is null before using it - Consider logging when attack attribute is null
public static float getWeaponDamage(LivingEntity attacker, @Nullable Entity target, @Nullable DamageSource source) {
AttributeInstance attack = attacker.getAttribute(Attributes.ATTACK_DAMAGE);
- if (attack == null) return 0;
+ if (attack == null) {
+ LOGGER.warn("Attack attribute is null for entity: {}", attacker);
+ return 0;
+ }
float damage = 1F;
AttributeModifier modifier = attack.getModifier(Item.BASE_ATTACK_DAMAGE_ID);
if (modifier != null) damage += (float) modifier.amount();
- if (target != null && source != null && attacker.level() instanceof ServerLevel serverLevel)
- damage = EnchantmentHelper.modifyDamage(serverLevel, attacker.getWeaponItem(), target, source, damage);
+ if (target != null && source != null && attacker.level() instanceof ServerLevel serverLevel) {
+ var weaponItem = attacker.getWeaponItem();
+ if (!weaponItem.isEmpty()) {
+ damage = EnchantmentHelper.modifyDamage(serverLevel, weaponItem, target, source, damage);
+ }
+ }
return damage;
}
Committable suggestion skipped: line range outside the PR's diff.
static void registerAttributes(final EntityAttributeModificationEvent e) { | ||
e.getTypes().forEach(type -> { | ||
if (type.equals(EntityType.PLAYER)) PLAYER_REGISTRY.forEach(holder -> e.add(type, holder)); | ||
GENERIC_REGISTRY.forEach(holder -> e.add(type, holder)); | ||
}); | ||
|
||
// Clear the registry | ||
PLAYER_REGISTRY.clear(); | ||
GENERIC_REGISTRY.clear(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding error handling for attribute registration.
The registerAttributes
method should handle potential exceptions during attribute registration and provide appropriate logging.
static void registerAttributes(final EntityAttributeModificationEvent e) {
+ try {
e.getTypes().forEach(type -> {
if (type.equals(EntityType.PLAYER)) PLAYER_REGISTRY.forEach(holder -> e.add(type, holder));
GENERIC_REGISTRY.forEach(holder -> e.add(type, holder));
});
// Clear the registry
PLAYER_REGISTRY.clear();
GENERIC_REGISTRY.clear();
+ } catch (Exception ex) {
+ LOGGER.error("Failed to register attributes", ex);
+ throw ex; // Re-throw to ensure the error is propagated
+ }
}
Committable suggestion skipped: line range outside the PR's diff.
@SubscribeEvent(priority = EventPriority.HIGH) | ||
public static void onCriticalHit(final CriticalHitEvent e) { | ||
if (e.isVanillaCritical()) { | ||
float multiplier = (float) e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER); | ||
e.setDamageMultiplier(e.getDamageMultiplier() / e.getVanillaMultiplier() * multiplier); | ||
return; | ||
} | ||
|
||
float critMultiplier = (float) e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER); | ||
Changeable<Float> multiplier = Changeable.of(e.getDamageMultiplier() * critMultiplier); | ||
Changeable<Double> chance = Changeable.of(e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) / 100); | ||
if (AttributeEvents.CRITICAL_ATTACK_CHANCE_EVENT.invoker().applyCrit(e.getEntity(), e.getTarget(), | ||
e.getDamageMultiplier(), multiplier, chance).isFalse()) return; | ||
|
||
if (e.getEntity().getRandom().nextFloat() > chance.get()) return; | ||
e.setDamageMultiplier(multiplier.get()); | ||
e.setCriticalHit(true); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for damage multiplier calculations.
The onCriticalHit
method should validate the damage multiplier to prevent potential exploits or overflow.
@SubscribeEvent(priority = EventPriority.HIGH)
public static void onCriticalHit(final CriticalHitEvent e) {
if (e.isVanillaCritical()) {
float multiplier = (float) e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER);
+ // Validate multiplier to prevent exploits
+ multiplier = Math.max(0.0f, Math.min(multiplier, MAX_SAFE_MULTIPLIER));
e.setDamageMultiplier(e.getDamageMultiplier() / e.getVanillaMultiplier() * multiplier);
return;
}
float critMultiplier = (float) e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER);
+ // Validate critMultiplier to prevent exploits
+ critMultiplier = Math.max(0.0f, Math.min(critMultiplier, MAX_SAFE_MULTIPLIER));
Changeable<Float> multiplier = Changeable.of(e.getDamageMultiplier() * critMultiplier);
Changeable<Double> chance = Changeable.of(e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) / 100);
if (AttributeEvents.CRITICAL_ATTACK_CHANCE_EVENT.invoker().applyCrit(e.getEntity(), e.getTarget(),
e.getDamageMultiplier(), multiplier, chance).isFalse()) return;
if (e.getEntity().getRandom().nextFloat() > chance.get()) return;
+ // Validate final multiplier to prevent overflow
+ float finalMultiplier = Math.max(0.0f, Math.min(multiplier.get(), MAX_SAFE_MULTIPLIER));
- e.setDamageMultiplier(multiplier.get());
+ e.setDamageMultiplier(finalMultiplier);
e.setCriticalHit(true);
}
Committable suggestion skipped: line range outside the PR's diff.
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()); | ||
ManasCoreTesting.LOG.info("Forgot Tested Skill!"); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling in skill management.
The skill learning/forgetting logic lacks error handling:
- No null checks for
storage
ortestSkill
- No feedback when skill learning fails
- Logging only happens on success
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");
+ return EventResult.pass();
+ }
Registrar<ManasSkill> skills = SkillAPI.getSkillRegistry();
RegistrySupplier<TestSkill> testSkill = TEST_SKILL;
+ if (testSkill == null) {
+ ManasCoreTesting.LOG.error("Test skill is not registered");
+ return EventResult.pass();
+ }
if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) {
ManasCoreTesting.LOG.info("Added Tested Skill!");
+ } 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");
+ return EventResult.pass();
+ }
storage.forgetSkill(RegistryTest.TEST_SKILL.get());
ManasCoreTesting.LOG.info("Forgot Tested Skill!");
}
return EventResult.pass();
});
📝 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.
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()); | |
ManasCoreTesting.LOG.info("Forgot Tested Skill!"); | |
} | |
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"); | |
return EventResult.pass(); | |
} | |
Registrar<ManasSkill> skills = SkillAPI.getSkillRegistry(); | |
RegistrySupplier<TestSkill> testSkill = TEST_SKILL; | |
if (testSkill == null) { | |
ManasCoreTesting.LOG.error("Test skill is not registered"); | |
return EventResult.pass(); | |
} | |
if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { | |
ManasCoreTesting.LOG.info("Added Tested Skill!"); | |
} 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"); | |
return EventResult.pass(); | |
} | |
storage.forgetSkill(RegistryTest.TEST_SKILL.get()); | |
ManasCoreTesting.LOG.info("Forgot Tested Skill!"); | |
} | |
return EventResult.pass(); | |
}); |
"item.manascore_testing.test_item": "Test Item", | ||
"item.manascore_testing.test_block_item": "Test Block Item", | ||
"block.manascore_testing.test_block": "Test Block", | ||
"entity.manascore_testing.test_block": "Test Entity", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect translation key for test entity.
The translation key for the test entity uses test_block
instead of test_entity
, which doesn't match the entity ID registered in RegistryTest.java
.
- "entity.manascore_testing.test_block": "Test Entity",
+ "entity.manascore_testing.test_entity": "Test Entity",
📝 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.
"entity.manascore_testing.test_block": "Test Entity", | |
"entity.manascore_testing.test_entity": "Test Entity", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 17
♻️ Duplicate comments (2)
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRace.java (1)
106-111
:⚠️ Potential issueImprove error handling in skill management.
The skill learning logic lacks proper error handling and null checks.
public void learnIntrinsicSkills(ManasRaceInstance instance, LivingEntity entity) { Skills storage = SkillAPI.getSkillsFrom(entity); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get skills storage for entity"); + return; + } for (ManasSkill skill : instance.getIntrinsicSkills(entity)) { + if (skill == null) continue; if (storage.learnSkill(skill)) ManasCoreTesting.LOG.info("LEARNT SKILL FROM RACE?!?!"); + else ManasCoreTesting.LOG.warn("Failed to learn skill: {}", skill.getName()); } }testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (1)
129-142
:⚠️ Potential issueAdd error handling for skill management operations.
The skill learning/forgetting logic lacks proper error handling and null checks.
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"); + return EventResult.pass(); + } Registrar<ManasSkill> skills = SkillAPI.getSkillRegistry(); RegistrySupplier<TestSkill> testSkill = TEST_SKILL; + if (testSkill == null) { + ManasCoreTesting.LOG.error("Test skill is not registered"); + return EventResult.pass(); + } if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { ManasCoreTesting.LOG.info("Added Tested Skill!"); + } 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"); + return EventResult.pass(); + } storage.forgetSkill(RegistryTest.TEST_SKILL.get()); ManasCoreTesting.LOG.info("Forgot Tested Skill!"); }
🧹 Nitpick comments (28)
race-common/src/main/java/io/github/manasmods/manascore/race/impl/data/RaceTagProvider.java (2)
1-4
: Update copyright year to reflect current year.The copyright notice shows 2025, which extends into the future. Consider updating it to 2024 or using a range (e.g., 2024-2025) if the project started in 2024.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
17-23
: Enhance error handling and code organization.Consider these improvements:
- Add a descriptive error message to
orElseThrow()
to aid debugging- Extract the key lookup lambda to a named method for better readability
+ private static ResourceKey<ManasRace> getManasRaceKey(ManasRace manasSkill) { + return RaceRegistry.RACES.getKey(manasSkill) + .orElseThrow(() -> new IllegalStateException( + "Failed to find registry key for ManasRace: " + manasSkill)); + } + public RaceTagProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider) { - super(output, RaceRegistry.KEY, lookupProvider, manasSkill -> RaceRegistry.RACES.getKey(manasSkill).orElseThrow()); + super(output, RaceRegistry.KEY, lookupProvider, RaceTagProvider::getManasRaceKey); } public RaceTagProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, CompletableFuture<TagLookup<ManasRace>> parentProvider) { - super(output, RaceRegistry.KEY, lookupProvider, parentProvider, manasSkill -> RaceRegistry.RACES.getKey(manasSkill).orElseThrow()); + super(output, RaceRegistry.KEY, lookupProvider, parentProvider, RaceTagProvider::getManasRaceKey); }race-neoforge/src/main/resources/META-INF/neoforge.mods.toml (2)
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"
11-13
: Enhance the mod description.The current description is quite brief. Consider adding more details about:
- The specific utilities and core features provided
- Key functionalities (Registry, Keybinding, Skill, Config modules)
- Target audience (mod developers, players)
race-neoforge/build.gradle (1)
1-5
: Update copyright year to current year.The copyright notice shows 2025, but it's currently January 2025. It's standard practice to use the current year or a year range (e.g., 2024-2025) for copyright notices.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024-2025. ManasModstesting-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.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.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
43-46
: Optimize target handling logic.The current implementation could be more efficient by:
- Early return if the target is null
- Using a more descriptive variable name than 'mob'
-public boolean onBeingTargeted(ManasRaceInstance instance, Changeable<LivingEntity> target, LivingEntity mob) { - if (mob.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) target.set(null); +public boolean onBeingTargeted(ManasRaceInstance instance, Changeable<LivingEntity> target, LivingEntity attacker) { + if (target.get() == null) return true; + if (attacker.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) { + target.set(null); + } return true; }
48-55
: Enhance logging messages for better debugging.The current log messages are not very descriptive. Consider adding more context such as the entity name, position, or cause of death.
public boolean onDeath(ManasRaceInstance instance, LivingEntity owner, DamageSource source) { - ManasCoreTesting.LOG.info("AWWWWW MANNNNN x2"); + ManasCoreTesting.LOG.info("TestRaceEvolved: Entity {} died from {}", owner.getName().getString(), source.getMsgId()); return true; } public void onRespawn(ManasRaceInstance instance, ServerPlayer owner, boolean conqueredEnd) { - ManasCoreTesting.LOG.info("CREEPER x2"); + ManasCoreTesting.LOG.info("TestRaceEvolved: Player {} respawned. Conquered end: {}", owner.getName().getString(), conqueredEnd); }
57-61
: Optimize evolution list creation.Consider using a constant list to avoid creating a new ArrayList on each method call.
+ private static final List<ManasRace> PREVIOUS_EVOLUTIONS = List.of(RegistryTest.TEST_RACE.get()); + public List<ManasRace> getPreviousEvolutions(ManasRaceInstance instance, LivingEntity entity) { - List<ManasRace> list = new ArrayList<>(); - list.add(RegistryTest.TEST_RACE.get()); - return list; + return PREVIOUS_EVOLUTIONS; }race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceEvolutionPacket.java (1)
45-46
: Add logging for unregistered evolutions.Currently, if
race == null
, the code simply returns. Consider logging a warning to help with troubleshooting or debugging missing evolutions.if (race == null) { + LOGGER.warn("Received evolution request, but no corresponding race was found for: {}", evolution); return; }
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (3)
43-49
: Reduce frequent calls to markDirty().
markDirty()
is called on every server-side entity tick. This can be expensive if many entities exist. Consider marking the storage as dirty only when actual changes occur (e.g., a race updates progress), rather than once per tick.
91-110
: Handle the case of setting the same race.When setting a new race, the code unconditionally removes the old race's attributes and adds the new race's attributes, even if it's the same race instance. This could lead to redundant event triggers or potential double-application of skills if overlooked. Consider short-circuiting when the old and new races are identical.
public boolean setRace(@NonNull ManasRaceInstance newRace, boolean evolution) { ManasRaceInstance oldRace = this.raceInstance; + if (oldRace != null && oldRace.equals(newRace)) { + // If it's the same race, skip to avoid redundant steps. + return false; + } // ...existing code... }
112-122
: Consider storing additional state in save/load methods.If the
raceInstance
changes frequently (e.g., partial evolution progress or dirty flags), these might need to be persisted. If you eventually need partial or incremental synchronization, storing more fields here could simplify debugging.race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (2)
104-109
: Handle missing or invalid races gracefully.
Objects.requireNonNull(...)
throws aNullPointerException
if the race is missing in the registry. Consider providing a more descriptive error or a fallback to avoid hard crashes.- ManasRaceInstance instance = Objects.requireNonNull(RaceAPI.getRaceRegistry().get(skillLocation)) - .createDefaultInstance(); + ManasRace race = RaceAPI.getRaceRegistry().get(skillLocation); + if (race == null) { + throw new IllegalArgumentException("No registered race found for " + skillLocation); + } + ManasRaceInstance instance = race.createDefaultInstance();
377-380
: Skip race evolution if the instance is already the desired evolution.If a player tries to evolve into the same race, consider short-circuiting. Re-evolving into the same race might trigger unintended re-initialization events or ability resets.
public void onRaceEvolution(LivingEntity entity, ManasRaceInstance evolution) { + if (this.equals(evolution)) { + return; + } this.getRace().onRaceEvolution(this, entity, evolution); }race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRace.java (3)
33-34
: Avoid using AWT Color on dedicated servers.Relying on
java.awt.Color
may cause unexpected behavior in server environments. Recommend using Minecraft’sChatFormatting
or built-in text styling to ensure compatibility across different platforms.
55-60
: Consider referencing the difficulty in your logic.The constructor sets the
difficulty
, but it isn’t used elsewhere in this class. Incorporate logic that uses the difficulty level or remove it if it’s not needed.
341-363
: Use stable or unique identifiers for AttributeModifiers if needed.The record
AttributeTemplate
usesResourceLocation
as the ID for attribute modifiers. Ensure these IDs don’t conflict across the codebase. Otherwise, a stable UUID or unique ID approach might be more robust.race-common/src/main/java/io/github/manasmods/manascore/race/ManasCoreRace.java (1)
15-15
: Consider using a more specific logger name.While the current logger name is acceptable, consider making it more specific to better identify the source of log messages in a larger codebase.
- public static final Logger LOG = LoggerFactory.getLogger("ManasCore - Race"); + public static final Logger LOG = LoggerFactory.getLogger(ManasCoreRace.class);race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/ManasRaceNetwork.java (1)
12-19
: Consider making the class final and adding documentation.Since this class only contains static initialization logic, consider:
- Making it final to prevent inheritance
- Adding documentation to describe the purpose of each registered packet type
+/** + * Network registration handler for race-related packets. + * Registers the following packets: + * - RequestRaceAbilityActivationPacket: Handles race ability activation requests + * - RequestRaceEvolutionPacket: Handles race evolution requests + */ -public class ManasRaceNetwork { +public final class ManasRaceNetwork {race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java (1)
13-38
: Add comprehensive documentation for events.Consider adding Javadoc to document:
- The purpose and timing of each event
- The expected usage of EventResult values
- The threading context of these events
+/** + * Defines the core events for the race system. + * These events allow for modifying and monitoring race-related actions. + */ public interface RaceEvents { + /** + * Fired when attempting to set or change a race. + * Return PASS to allow the change, FAIL to prevent it. + */ Event<SetRaceEvent> SET_RACE = EventFactory.createEventResult();race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java (2)
32-95
: Refactor event handlers to reduce code duplication.The event handlers follow a similar pattern with repeated code blocks. Consider extracting common logic into helper methods.
Here's a suggested refactor:
+private static EventResult handleRaceEvent(LivingEntity entity, + BiFunction<ManasRaceInstance, LivingEntity, Boolean> action) { + Optional<ManasRaceInstance> optional = RaceAPI.getRaceFrom(entity).getRace(); + if (optional.isEmpty()) return EventResult.pass(); + + ManasRaceInstance instance = optional.get(); + if (!instance.canActivateAbility(entity)) return EventResult.pass(); + return action.apply(instance, entity) ? EventResult.pass() : EventResult.interruptFalse(); +} public static void init() { EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> { - Optional<ManasRaceInstance> optional = RaceAPI.getRaceFrom(entity).getRace(); - if (optional.isEmpty()) return EventResult.pass(); - - ManasRaceInstance instance = optional.get(); - if (!instance.canActivateAbility(entity)) return EventResult.pass(); - if (!instance.onEffectAdded(entity, source, changeableTarget)) return EventResult.interruptFalse(); - return EventResult.pass(); + return handleRaceEvent(entity, + (instance, e) -> instance.onEffectAdded(e, source, changeableTarget)); }); // Apply similar pattern to other event handlers }
26-30
: Add documentation for registry initialization.The registry initialization lacks documentation explaining the purpose of syncToClients and the significance of the registry key.
Add JavaDoc to explain the registry setup:
+/** + * Registry for ManasRace instances. + * The registry is synchronized to clients to ensure consistent race data across the network. + * The registry key is used for creating deferred registers and accessing the registry. + */ public class RaceRegistry { 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(); public static final ResourceKey<Registry<ManasRace>> KEY = (ResourceKey<Registry<ManasRace>>) RACES.key();race-fabric/src/main/resources/fabric.mod.json (4)
11-11
: Homepage URL should be provided.The homepage URL is currently empty. Consider adding the GitHub repository URL or documentation site to help users find more information about the mod.
- "homepage": "" + "homepage": "https://github.com/ManasMods/ManasCore"
20-20
: Empty client entrypoint array.The client entrypoint array is empty. If this mod has no client-side initialization needs, consider removing the empty "client" entry entirely.
- "client": []
31-31
: Specify Fabric API version requirement.The Fabric API dependency is set to "*" which accepts any version. This could lead to compatibility issues if breaking changes are introduced in future versions.
- "fabric-api": "*", + "fabric-api": ">=${fabric_api_version}",
34-35
: Consider adding suggested mods.The "suggests" section is empty. Consider adding complementary mods that work well with this one to help users discover related content.
"suggests": { + "manascore_skills": "*", + "manascore_attributes": "*" }testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (1)
115-150
: Consider using a configuration file for test values.Currently, many test values (attribute modifiers, explosion radii, etc.) are hardcoded. Consider moving these to a configuration file to make testing more flexible and maintainable.
This would allow:
- Easy adjustment of test values without code changes
- Different test configurations for different scenarios
- Better separation of test configuration from implementation
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
race-fabric/src/main/resources/assets/manascore/icon.png
is excluded by!**/*.png
race-fabric/src/main/resources/assets/manascore/logo.png
is excluded by!**/*.png
race-neoforge/src/main/resources/icon.png
is excluded by!**/*.png
📒 Files selected for processing (37)
race-common/build.gradle
(1 hunks)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/ManasRace.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceAPI.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java
(1 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/data/RaceTagProvider.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/InternalRacePacketActions.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/ManasRaceNetwork.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/RequestRaceEvolutionPacket.java
(1 hunks)race-common/src/main/resources/architectury.common.json
(1 hunks)race-common/src/main/resources/assets/manascore/lang/en_us.json
(1 hunks)race-common/src/main/resources/manascore_race.accesswidener
(1 hunks)race-common/src/main/resources/manascore_race.mixins.json
(1 hunks)race-fabric/build.gradle
(1 hunks)race-fabric/src/main/java/io/github/manasmods/manascore/race/fabric/ManasCoreRaceFabric.java
(1 hunks)race-fabric/src/main/resources/fabric.mod.json
(1 hunks)race-fabric/src/main/resources/manascore_race-fabric.mixins.json
(1 hunks)race-neoforge/build.gradle
(1 hunks)race-neoforge/gradle.properties
(1 hunks)race-neoforge/src/main/java/io/github/manasmods/manascore/race/neoforge/ManasCoreRaceNeoForge.java
(1 hunks)race-neoforge/src/main/resources/META-INF/neoforge.mods.toml
(1 hunks)race-neoforge/src/main/resources/manascore_race-neoforge.mixins.json
(1 hunks)settings.gradle
(2 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/impl/SkillStorage.java
(1 hunks)skill-common/src/main/resources/assets/manascore/lang/en_us.json
(1 hunks)testing-common/build.gradle
(1 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/registry/RegistryTest.java
(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRace.java
(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java
(1 hunks)testing-common/src/main/resources/assets/manascore/lang/en_us.json
(1 hunks)
✅ Files skipped from review due to trivial changes (8)
- race-neoforge/gradle.properties
- race-common/src/main/resources/architectury.common.json
- race-common/src/main/resources/manascore_race.accesswidener
- skill-common/src/main/resources/assets/manascore/lang/en_us.json
- race-common/src/main/resources/assets/manascore/lang/en_us.json
- race-common/src/main/resources/manascore_race.mixins.json
- race-neoforge/src/main/resources/manascore_race-neoforge.mixins.json
- race-fabric/src/main/resources/manascore_race-fabric.mixins.json
🚧 Files skipped from review as they are similar to previous changes (4)
- testing-common/build.gradle
- testing-common/src/main/java/io/github/manasmods/manascore/testing/client/KeybindingTest.java
- testing-common/src/main/resources/assets/manascore/lang/en_us.json
- skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillStorage.java
🔇 Additional comments (31)
settings.gradle (1)
17-17
: Verify the missing Registry module.The PR objectives mention adding a Registry module, but it's not included in the module list. Please clarify if this was intentional or if the Registry functionality is implemented within another module.
The multi-platform module structure (common, fabric, neoforge) follows best practices for cross-platform Minecraft modding.
race-common/src/main/java/io/github/manasmods/manascore/race/impl/data/RaceTagProvider.java (1)
8-16
: LGTM! Well-structured class definition.The class is properly designed as an abstract tag provider, following Minecraft's data generation patterns. The imports are clean and the inheritance hierarchy is appropriate.
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (4)
87-89
: Use the correct constructor forResourceLocation
This code callsResourceLocation.fromNamespaceAndPath
, which may not exist or be supported. Instead, construct aResourceLocation
using its standard(namespace, path)
constructor.- return ResourceLocation.fromNamespaceAndPath(id.getNamespace(), "icons/skills/" + id.getPath()); + return new ResourceLocation(id.getNamespace(), "icons/skills/" + id.getPath());
101-107
: MissinghashCode()
override whenequals()
is overridden
Sinceequals()
is overridden, you should also overridehashCode()
to maintain the contract between these two methods, especially for use in hash-based data structures.+ @Override + public int hashCode() { + ResourceLocation registryName = getRegistryName(); + return registryName != null ? registryName.hashCode() : 0; + }
229-231
: Invalid argument type for removing attribute modifiers
TheremoveModifier
method expects aUUID
or a valid name, butentry.getValue().id()
returns aResourceLocation
. This mismatch will lead to runtime issues.Also applies to: 244-244
436-436
: IncorrectAttributeModifier
constructor argument types
Using aResourceLocation
as the first parameter is invalid, since theAttributeModifier
constructor generally expects aUUID
and aString
name, not aResourceLocation
. This type mismatch will cause runtime errors.- return new AttributeModifier(this.id, this.amount * i, this.operation); + // Example fix: add a UUID field and a string name field to AttributeTemplate + return new AttributeModifier(UUID.fromString("00000000-0000-0000-0000-000000000000"), + this.id.toString(), + this.amount * i, + this.operation);race-fabric/build.gradle (2)
6-8
: LGTM! Access widener configuration looks good.The Loom configuration correctly references the access widener path from the race-common project, maintaining consistency across subprojects.
10-14
: Verify the dependency configurations and their impact.The dependencies are correctly configured to use 'transformProductionFabric', but let's verify:
- That all required dependencies are included
- That these dependencies don't introduce circular dependencies
- That the transformation configuration is consistently used across other Fabric modules
✅ Verification successful
Dependencies are correctly configured and consistent
The dependency configuration review shows:
- All Fabric modules consistently use 'transformProductionFabric'
- No circular dependencies detected
- Dependencies follow a logical hierarchy and are appropriate for each module's functionality
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify dependency configurations across Fabric modules # Check for other Fabric modules using similar configurations echo "Checking for other Fabric module configurations..." fd -e gradle --exec grep -l "transformProductionFabric" {} # Check for potential circular dependencies echo "Checking for potential circular dependencies..." fd -e gradle --exec grep -l "race-fabric" {}Length of output: 423
Script:
#!/bin/bash # Examine the content of Gradle files using transformProductionFabric echo "=== Root build.gradle ===" cat build.gradle echo -e "\n=== storage-fabric/build.gradle ===" cat storage-fabric/build.gradle echo -e "\n=== skill-fabric/build.gradle ===" cat skill-fabric/build.gradle # We already know race-fabric/build.gradle content from the reviewLength of output: 10972
race-common/build.gradle (2)
1-3
: Verify the access widener file existence and configuration.The access widener configuration looks correct, following the project's convention. However, we should verify that the referenced file exists and is properly configured.
✅ Verification successful
Access widener configuration is valid and follows project conventions
The access widener file exists and contains valid configuration, consistent with other modules in the project.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the access widener file exists and examine its content if [ -f "race-common/src/main/resources/manascore_race.accesswidener" ]; then echo "Access widener file exists" cat "race-common/src/main/resources/manascore_race.accesswidener" else echo "Warning: Access widener file not found" fi # Look for other access widener files in the project for consistency check echo -e "\nChecking other access widener files for consistency:" fd -e accesswidenerLength of output: 956
5-9
: Review the implications of disabled transitive dependencies.While disabling transitive dependencies can help avoid dependency conflicts and reduce build size, it may also exclude critical dependencies. Please verify that:
- All necessary dependencies are explicitly declared
- No critical transitive dependencies are being excluded
✅ Verification successful
Verified: Disabled transitive dependencies are appropriate
The dependency structure is well-designed with network-common having no dependencies to propagate, and all modules explicitly declaring their required dependencies. The consistent use of
transitive false
helps maintain a clean and predictable dependency graph.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for dependency declarations in the referenced modules echo "Checking dependencies in referenced modules:" for module in "skill-common" "network-common" "storage-common"; do echo -e "\nChecking $module/build.gradle:" if [ -f "$module/build.gradle" ]; then # Extract dependencies block awk '/dependencies {/,/}/' "$module/build.gradle" else echo "Warning: $module/build.gradle not found" fi done # Look for any shared dependencies across modules echo -e "\nChecking for potential shared dependencies across all build.gradle files:" fd build.gradle | xargs grep -l "implementation"Length of output: 1665
Script:
#!/bin/bash # Check network-common's dependencies and potential shared dependencies echo "Checking network-common's dependencies:" cat network-common/build.gradle echo -e "\nChecking for common implementation dependencies across modules:" for f in $(fd build.gradle); do echo -e "\n=== $f ===" awk '/dependencies {/,/}/' "$f" | grep "implementation" doneLength of output: 8300
race-neoforge/src/main/resources/META-INF/neoforge.mods.toml (5)
1-2
: Consider tightening the loader version constraint.The current constraint
"[2,)"
is very broad and might lead to compatibility issues with future versions. Consider setting an upper bound to ensure compatibility.
19-19
: Consider constraining the NeoForge version range.The current constraint
"[21,)"
is very broad. Consider setting an upper bound to ensure compatibility with tested versions.
14-14
: Verify the icon file existence.Ensure that "icon.png" exists in the correct resources directory.
✅ Verification successful
Icon file verification successful ✓
The icon.png file exists at the correct location in race-neoforge/src/main/resources/icon.png.
🏁 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" race-neoforge/src/main/resources/Length of output: 94
44-48
: Verify mixin configurations and maintain consistent naming.
The mixin configurations use different naming patterns:
${mod_id}.mixins.json
manascore_race-neoforge.mixins.json
Verify that both mixin configuration files exist.
✅ Verification successful
Mixin configurations verified and naming pattern is consistent
Both mixin configurations exist and follow the project's established naming pattern:
- Common module:
${mod_id}.mixins.json
(resolves tomanascore_race.mixins.json
)- NeoForge module:
manascore_race-neoforge.mixins.json
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for the existence of mixin configuration files fd -t f "mixins.json" . # Look for references to these mixin configurations rg "mixins.json" -t jsonLength of output: 1965
37-42
: Verify manascore_network dependency.Ensure that the manascore_network module exists and is properly referenced.
✅ Verification successful
The manascore_network dependency is correctly configured ✅
The module exists in the network-common directory and is properly referenced as a dependency across multiple other modules in the project, including storage-neoforge, skill-neoforge, and network-neoforge.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for the existence of manascore_network module fd -t f "manascore_network" . # Check for references to manascore_network in build files rg "manascore_network" -t gradleLength of output: 307
Script:
#!/bin/bash # Check mod configuration files for module ID declarations fd -e toml -e json "mods" . # Search for manascore_network in all files to find module ID declarations rg -g '!{*.md,*.txt}' -i "modid.*manascore_network|manascore_network.*modid"Length of output: 1143
race-neoforge/build.gradle (3)
6-8
: LGTM! Good practice sharing access widener configuration.Sharing the access widener path with the common module ensures consistency across the project.
16-18
: Verify mod_id property and access widener file existence.Please ensure that:
- The
mod_id
property is properly defined in the project- The referenced access widener file exists
✅ Verification successful
Access widener configuration is correct ✓
The setup is properly configured:
- mod_id property is defined in the root build.gradle
- Access widener file exists at race-common/src/main/resources/manascore_race.accesswidener
- race-neoforge correctly inherits the access widener path from race-common
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify mod_id property and access widener file # Check if mod_id is defined in gradle.properties or settings echo "Checking for mod_id property:" for file in "gradle.properties" "settings.gradle"; do if [ -f "$file" ] && grep -q "mod_id" "$file"; then echo "✓ mod_id found in $file" grep "mod_id" "$file" fi done # Check for access widener file echo -e "\nChecking for access widener file:" find "race-neoforge/src" -name "*.accesswidener"Length of output: 432
Script:
#!/bin/bash # Expanded search for mod_id and access widener files echo "Checking for mod_id in all Gradle files:" fd -e gradle -e properties -x grep -l "mod_id" {} \; fd -e gradle -e properties -x grep "mod_id" {} \; echo -e "\nChecking for any .accesswidener files in the repository:" fd ".accesswidener$" echo -e "\nChecking build.gradle content in race-neoforge:" cat race-neoforge/build.gradleLength of output: 2586
10-14
: Verify dependency requirements and availability.Please ensure that:
- These dependencies are actually required by the race-neoforge module
- All referenced projects exist and are properly configured
✅ Verification successful
Dependencies are correctly configured and follow project architecture
The dependencies are valid and properly configured:
- The project follows a modular architecture where platform-specific implementations (neoforge) depend on their common counterparts
- The
transformProductionNeoForge
configuration is automatically set up by the build system for all NeoForge modules- The dependency structure matches the project's established pattern for cross-platform module organization
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the existence of dependent projects and their configurations # Check if the dependent projects exist for project in "skill-common" "network-common" "storage-common"; do if [ -d "$project" ]; then echo "✓ Project $project exists" # Check if the project has the necessary Gradle configuration if grep -q "transformProductionNeoForge" "$project/build.gradle"; then echo "✓ Project $project has transformProductionNeoForge configuration" else echo "✗ Project $project missing transformProductionNeoForge configuration" fi else echo "✗ Project $project not found" fi done # Check for actual usage of these dependencies in the race-neoforge module echo -e "\nChecking dependency usage:" rg -l "import.*skill.*common|import.*network.*common|import.*storage.*common" "race-neoforge/src/"Length of output: 1331
Script:
#!/bin/bash # List all source files and check for any usage of the common modules echo "=== Source files in race-neoforge ===" fd -e java -e kt -e groovy . race-neoforge/src/ echo -e "\n=== Searching for any references to common modules ===" rg -l "skill|network|storage" race-neoforge/src/ echo -e "\n=== Checking for Gradle configuration files ===" fd build.gradle fd settings.gradle echo -e "\n=== Checking content of root build.gradle ===" cat build.gradleLength of output: 11652
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java (1)
26-32
: Review attribute modifier values for game balance.The current attribute modifications seem extremely high:
- Jump strength is doubled (2x multiplier)
- Health is increased by 100 points
These values might significantly impact game balance. Consider reducing them to more reasonable values unless these extreme values are intentionally set for testing purposes.
✅ Verification successful
High attribute values are intentional for testing purposes
The class is located in the testing package and is specifically designed for testing race evolution mechanics. Both
TestRace
andTestRaceEvolved
use intentionally extreme values to verify attribute modification system:
TestRace
(parent): +50 attack damage, +10 armorTestRaceEvolved
(evolution): 2x jump strength, +100 health🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other race implementations to compare attribute modifier values ast-grep --pattern 'class $_ extends ManasRace { $$$ addAttributeModifier($_, $_, $_, $_) $$$ }'Length of output: 104
Script:
#!/bin/bash # First, find all relevant race files fd -e java "Race" # Then examine their content for attribute modifiers echo "=== File contents ===" fd -e java "Race" -x cat {}Length of output: 61345
race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/c2s/RequestRaceEvolutionPacket.java (1)
35-36
: Server-side validation looks good.It's a good practice to ensure we're only processing this packet on the server side. This helps avoid unintended changes caused by client-side code.
race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
128-135
:equals()
ignores tag data.Currently, two instances with the same race but different internal tags are considered equal. This may lead to logical inconsistencies if tag-based data is critical to identifying a unique race instance. Evaluate whether the tag should factor into the equality check.
race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRace.java (2)
1-4
: License header looks good.The inclusion of the GPLv3 license header aligns with open source requirements.
134-135
: Question the default return value incanTick()
.
canTick()
returnsfalse
by default, disabling race-specific ticking unless explicitly overridden. Confirm if this is intentional, as it may confuse future contributors who expect a race to tick by default.race-fabric/src/main/java/io/github/manasmods/manascore/race/fabric/ManasCoreRaceFabric.java (2)
1-4
: Confirmed license header.GPL v3 license is properly declared.
13-15
: Initialization looks correct.
onInitialize()
simply delegates toManasCoreRace.init()
, which is concise and appropriate for Fabric mods. Confirm that no additional client-specific registrations are needed.race-neoforge/src/main/java/io/github/manasmods/manascore/race/neoforge/ManasCoreRaceNeoForge.java (2)
1-4
: License header is acceptable.GPL v3 license is consistently applied here.
13-17
: Validate event bus usage.Your constructor calls
ManasCoreRace.init()
directly. Consider whether this method should register events usingmodEventBus
to ensure NeoForge event hooks are fully integrated.race-common/src/main/java/io/github/manasmods/manascore/race/ManasCoreRace.java (1)
14-22
: LGTM! Well-structured initialization sequence.The class follows good practices with:
- Final class declaration preventing unwanted inheritance
- Clear initialization sequence for race-related components
- Proper separation of concerns across different modules
race-common/src/main/java/io/github/manasmods/manascore/race/impl/network/InternalRacePacketActions.java (2)
35-40
: Add environment check and error handling for network operations.Similar to the previous method, this one needs environment checks. Additionally, consider adding error handling for network operations.
Add environment check and error handling:
public static void sendRaceEvolutionPacket(ResourceLocation evolution) { + if (Platform.getEnvironment() != Env.CLIENT) { + return; + } var minecraft = Minecraft.getInstance(); Player player = minecraft.player; if (player == null) return; - NetworkManager.sendToServer(new RequestRaceEvolutionPacket(evolution)); + try { + NetworkManager.sendToServer(new RequestRaceEvolutionPacket(evolution)); + } catch (Exception e) { + LOGGER.error("Failed to send race evolution packet", e); + } }
24-29
: Add environment check before accessing Minecraft instance.The method directly calls
Minecraft.getInstance()
which should only be called from the client thread. While the JavaDoc mentions client-side execution, adding an explicit environment check would prevent potential crashes.Add this check at the start of the method:
public static void sendRaceAbilityActivationPacket() { + if (Platform.getEnvironment() != Env.CLIENT) { + return; + } var minecraft = Minecraft.getInstance(); Player player = minecraft.player; if (player == null) return; NetworkManager.sendToServer(new RequestRaceAbilityActivationPacket()); }race-fabric/src/main/resources/fabric.mod.json (1)
22-24
: Verify mixin configurations.Ensure that both mixin configuration files exist and are properly configured:
${mod_id}.mixins.json
${mod_id}-fabric.mixins.json
✅ Verification successful
✓ Mixin configurations verified successfully
Both required mixin configuration files exist and are properly configured with all necessary fields:
manascore_race.mixins.json
in race-commonmanascore_race-fabric.mixins.json
in race-fabric🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if mixin configuration files exist echo "Checking for mixin configuration files..." fd --type f --extension json . | grep -E "mixins\.json$" # Validate mixin configuration format echo "Validating mixin configurations..." for f in $(fd --type f --extension json . | grep -E "mixins\.json$"); do echo "Checking $f..." jq -e '.package and .mixins and .client' "$f" || echo "Invalid mixin configuration in $f" doneLength of output: 6123
@@ -1,5 +1,5 @@ | |||
/* | |||
* Copyright (c) 2024. ManasMods | |||
* Copyright (c) 2025. ManasMods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update copyright year to current year.
The copyright year is set to 2025, but it's currently January 2025. Copyright notices typically use the current year or the year of creation, not future dates.
- * Copyright (c) 2025. ManasMods
+ * Copyright (c) 2024. ManasMods
📝 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.
* Copyright (c) 2025. ManasMods | |
* Copyright (c) 2024. ManasMods |
/* | ||
* Copyright (c) 2025. ManasMods | ||
* GNU General Public License 3 | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update copyright year to current year (2024).
The copyright year is set to 2025, which is in the future. Please update it to the current year.
- * Copyright (c) 2025. ManasMods
+ * Copyright (c) 2024. ManasMods
Committable suggestion skipped: line range outside the PR's diff.
public void onActivateAbility(ManasRaceInstance instance, LivingEntity entity) { | ||
entity.level().explode(entity, entity.getX(), entity.getY(), entity.getZ(), 10F, Level.ExplosionInteraction.BLOCK); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add safety checks for explosion ability.
The explosion ability lacks safety checks that could prevent griefing or unintended damage. Consider adding:
- Cooldown period
- Permission checks
- Safe zone restrictions
- Configurable explosion power
public void onActivateAbility(ManasRaceInstance instance, LivingEntity entity) {
+ if (entity.level().isClientSide()) return;
+ if (instance.isOnCooldown()) return;
+ // Configure explosion power through race attributes or config
+ float explosionPower = 10F;
entity.level().explode(entity, entity.getX(), entity.getY(), entity.getZ(),
- 10F, Level.ExplosionInteraction.BLOCK);
+ explosionPower, Level.ExplosionInteraction.NONE);
+ instance.setCooldown(20 * 30); // 30 seconds cooldown
}
Committable suggestion skipped: line range outside the PR's diff.
default boolean setRace(@NotNull ResourceLocation raceId) { | ||
return setRace(RaceAPI.getRaceRegistry().get(raceId).createDefaultInstance(), false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix parameter naming and standardize null annotations.
- The parameter name
skill
insetRace
andevolveRace
methods should berace
to match the context. - Standardize the use of null annotations by using either JetBrains' @NotNull or Lombok's @nonnull consistently.
- default boolean setRace(@NotNull ResourceLocation raceId) {
+ default boolean setRace(@NonNull ResourceLocation raceId) {
- default boolean setRace(@NonNull ManasRace skill) {
+ default boolean setRace(@NonNull ManasRace race) {
- return setRace(skill.createDefaultInstance(), false);
+ return setRace(race.createDefaultInstance(), false);
- default boolean evolveRace(@NonNull ManasRace skill) {
+ default boolean evolveRace(@NonNull ManasRace race) {
- return setRace(skill.createDefaultInstance(), true);
+ return setRace(race.createDefaultInstance(), true);
Also applies to: 21-23, 31-33
Event<RaceTickEvent> SKILL_PRE_TICK = EventFactory.createEventResult(); | ||
Event<RacePostTickEvent> SKILL_POST_TICK = EventFactory.createLoop(); | ||
Event<RaceAbilityActivationEvent> ACTIVATE_ABILITY = EventFactory.createEventResult(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix inconsistent event naming.
The event names SKILL_PRE_TICK
and SKILL_POST_TICK
use "SKILL" prefix while being in a race-related class. Consider renaming for consistency:
- Event<RaceTickEvent> SKILL_PRE_TICK = EventFactory.createEventResult();
- Event<RacePostTickEvent> SKILL_POST_TICK = EventFactory.createLoop();
+ Event<RaceTickEvent> RACE_PRE_TICK = EventFactory.createEventResult();
+ Event<RacePostTickEvent> RACE_POST_TICK = EventFactory.createLoop();
📝 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.
Event<RaceTickEvent> SKILL_PRE_TICK = EventFactory.createEventResult(); | |
Event<RacePostTickEvent> SKILL_POST_TICK = EventFactory.createLoop(); | |
Event<RaceAbilityActivationEvent> ACTIVATE_ABILITY = EventFactory.createEventResult(); | |
Event<RaceTickEvent> RACE_PRE_TICK = EventFactory.createEventResult(); | |
Event<RacePostTickEvent> RACE_POST_TICK = EventFactory.createLoop(); | |
Event<RaceAbilityActivationEvent> ACTIVATE_ABILITY = EventFactory.createEventResult(); |
public void onActivateAbility(ManasRaceInstance instance, LivingEntity entity) { | ||
entity.level().explode(entity, entity.getX(), entity.getY(), entity.getZ(), 4F, Level.ExplosionInteraction.BLOCK); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add safety constraints to explosion ability.
The current implementation allows unlimited explosions without cooldown or resource cost, which could be exploited.
+ private long lastExplosionTime = 0;
+ private static final long EXPLOSION_COOLDOWN = 10000; // 10 seconds in milliseconds
+
public void onActivateAbility(ManasRaceInstance instance, LivingEntity entity) {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastExplosionTime < EXPLOSION_COOLDOWN) {
+ return;
+ }
entity.level().explode(entity, entity.getX(), entity.getY(), entity.getZ(), 4F, Level.ExplosionInteraction.BLOCK);
+ lastExplosionTime = currentTime;
}
Committable suggestion skipped: line range outside the PR's diff.
public boolean onAttackEntity(ManasRaceInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) { | ||
if (owner.isShiftKeyDown() && target instanceof Pillager) { | ||
amount.set(amount.get() * 100F); | ||
ManasCoreTesting.LOG.info("Dealt {} damage.", amount.get()); | ||
} | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prevent potential overflow from excessive damage multiplier.
The 100x damage multiplier could cause integer overflow with high base damage values. Consider using a more reasonable multiplier and adding an upper limit.
public boolean onAttackEntity(ManasRaceInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) {
if (owner.isShiftKeyDown() && target instanceof Pillager) {
- amount.set(amount.get() * 100F);
+ float newAmount = Math.min(amount.get() * 2.5F, 50F); // 2.5x damage with 50 damage cap
+ amount.set(newAmount);
ManasCoreTesting.LOG.info("Dealt {} damage.", amount.get());
}
return true;
}
📝 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 boolean onAttackEntity(ManasRaceInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) { | |
if (owner.isShiftKeyDown() && target instanceof Pillager) { | |
amount.set(amount.get() * 100F); | |
ManasCoreTesting.LOG.info("Dealt {} damage.", amount.get()); | |
} | |
return true; | |
} | |
public boolean onAttackEntity(ManasRaceInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) { | |
if (owner.isShiftKeyDown() && target instanceof Pillager) { | |
float newAmount = Math.min(amount.get() * 2.5F, 50F); // 2.5x damage with 50 damage cap | |
amount.set(newAmount); | |
ManasCoreTesting.LOG.info("Dealt {} damage.", amount.get()); | |
} | |
return true; | |
} |
() -> new Item(new Item.Properties().arch$tab(TESTING_TAB).stacksTo(69) | ||
.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()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Adjust attribute modifier values for testing.
The current critical hit modifiers are extremely high:
- 10x critical damage multiplier
- 50% critical chance
Consider using more reasonable values to ensure proper testing of edge cases.
.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())));
+ .add(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 1.5,
+ AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND)
+ .add(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 0.15,
+ AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND).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.
() -> new Item(new Item.Properties().arch$tab(TESTING_TAB).stacksTo(69) | |
.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()))); | |
() -> new Item(new Item.Properties().arch$tab(TESTING_TAB).stacksTo(69) | |
.attributes(ItemAttributeModifiers.builder() | |
.add(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 1.5, | |
AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND) | |
.add(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 0.15, | |
AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND).build()))); |
} else if (entity.getItem().is(Items.GOLD_INGOT)) { | ||
Races storage = RaceAPI.getRaceFrom(player); | ||
storage.setRace(RegistryTest.TEST_RACE.getId()); | ||
ManasCoreTesting.LOG.info("Set to Test Race!"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for race management.
The race setting logic lacks error handling and race ID validation.
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();
+ }
+ if (!RaceAPI.getRaceRegistry().containsKey(RegistryTest.TEST_RACE.getId())) {
+ ManasCoreTesting.LOG.error("Test race is not registered");
+ return EventResult.pass();
+ }
storage.setRace(RegistryTest.TEST_RACE.getId());
ManasCoreTesting.LOG.info("Set to Test Race!");
+ player.displayClientMessage(Component.literal("Race set to Test Race"), true);
}
📝 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.
} else if (entity.getItem().is(Items.GOLD_INGOT)) { | |
Races storage = RaceAPI.getRaceFrom(player); | |
storage.setRace(RegistryTest.TEST_RACE.getId()); | |
ManasCoreTesting.LOG.info("Set to Test Race!"); | |
} | |
} 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(); | |
} | |
if (!RaceAPI.getRaceRegistry().containsKey(RegistryTest.TEST_RACE.getId())) { | |
ManasCoreTesting.LOG.error("Test race is not registered"); | |
return EventResult.pass(); | |
} | |
storage.setRace(RegistryTest.TEST_RACE.getId()); | |
ManasCoreTesting.LOG.info("Set to Test Race!"); | |
player.displayClientMessage(Component.literal("Race set to Test Race"), true); | |
} |
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; | ||
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); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add safety limits to death explosion mechanics.
The current implementation uses an uncapped random explosion radius and could create unexpectedly large explosions.
- float g = 10.0F + entity.getRandom().nextFloat() * 2.0F;
+ float baseRadius = 3.0F;
+ float randomAddition = entity.getRandom().nextFloat() * 1.0F;
+ float g = Math.min(baseRadius + randomAddition, 4.0F);
📝 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 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; | |
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); | |
} | |
} | |
} | |
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 baseRadius = 3.0F; | |
float randomAddition = entity.getRandom().nextFloat() * 1.0F; | |
float g = Math.min(baseRadius + randomAddition, 4.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); | |
} | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (4)
skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (4)
88-90
:⚠️ Potential issueUse correct constructor for ResourceLocation.
UsingResourceLocation.fromNamespaceAndPath(...)
may cause compilation or runtime issues if that method doesn’t exist in the codebase. Instead, use the constructor that takes(namespace, path)
as arguments.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());
102-108
: 🛠️ Refactor suggestionOverride
hashCode()
whenequals()
is overridden.
To maintain the contract betweenequals()
andhashCode()
, ensure that both are overridden together. Otherwise, objects of this class can behave unpredictably in hashed collections.Consider adding:
+ @Override + public int hashCode() { + return Objects.hash(getRegistryName()); + }
442-442
:⚠️ Potential issueUse correct constructor for ResourceLocation.
Similar to the icon path creation, usingResourceLocation.fromNamespaceAndPath(...)
may lead to issues if that method does not exist in the environment. Switch to a constructor-based approach.Suggested fix:
- this(ResourceLocation.fromNamespaceAndPath(ModuleConstants.MOD_ID, id), amount, operation); + this(new ResourceLocation(ModuleConstants.MOD_ID, id), amount, operation);
445-447
:⚠️ Potential issueIncorrect use of
AttributeModifier
constructor and identifiers.
Minecraft’sAttributeModifier
typically expects aUUID
and a string for the name, or certain other supported signatures. Passing aResourceLocation
as the first argument will cause a type mismatch at runtime.Apply this diff to resolve the issue by switching to a proper
UUID
and name:- public static record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) { + public static record AttributeTemplate(UUID id, String name, double amount, AttributeModifier.Operation operation) { public AttributeTemplate(UUID id, String name, double amount, AttributeModifier.Operation operation) { this.id = id; this.name = name; this.amount = amount; this.operation = operation; } public AttributeModifier create(double i) { - return new AttributeModifier(this.id, this.amount * i, this.operation); + return new AttributeModifier(this.id, this.name, this.amount * i, this.operation); } }
🧹 Nitpick comments (13)
attribute-common/build.gradle (1)
1-4
: Update copyright year to reflect actual creation date.The copyright year is set to 2025, which is the current year. Consider using the year when the code was first created, or use a range (e.g., "2023-2025") if this is a modification of existing code.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2023-2025. ManasModsattribute-neoforge/build.gradle (1)
1-5
: Update the copyright year to 2024.The copyright year is set to 2025, which extends beyond the current date. Consider updating it to 2024 or using a range (e.g., 2023-2024) if applicable.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasModsattribute-common/src/main/resources/assets/manascore/lang/en_us.json (1)
1-7
: LGTM! Consider adding unit clarification for multipliers.The translations are clear and follow a consistent naming pattern. However, consider adding unit clarification for multiplier attributes (e.g., "Critical Damage Multiplier (x)" or "Swim Speed Multiplier (%)") to improve user understanding.
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java (3)
23-25
: Consider reducing the maximum range for critical damage multiplier.A maximum of 1024x critical damage seems excessive and could lead to balance issues. Consider reducing it to a more reasonable value (e.g., 5.0 or 10.0) to prevent potential exploits or balance issues.
34-41
: Clarify elytra interaction in documentation.The documentation needs more clarity about:
- How this attribute interacts with vanilla elytra mechanics
- Whether the multiplier stacks with elytra gliding
- What value enables gliding without elytra (is it any value > 0?)
58-72
: Reduce code duplication in registration methods.The
registerPlayerAttribute
andregisterGenericAttribute
methods share almost identical code. Consider refactoring to reduce 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>> registrationCallback) { + Holder<Attribute> attribute = Registry.registerForHolder(BuiltInRegistries.ATTRIBUTE, + getResourceKey(modID, id), + new RangedAttribute(name, amount, min, max) + .setSyncable(syncable) + .setSentiment(sentiment)); + registrationCallback.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; - } + public static Holder<Attribute> registerPlayerAttribute(String modID, String id, String name, + double amount, double min, double max, boolean syncable, Attribute.Sentiment sentiment) { + 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; - } + public static Holder<Attribute> registerGenericAttribute(String modID, String id, String name, + double amount, double min, double max, boolean syncable, Attribute.Sentiment sentiment) { + return registerAttribute(modID, id, name, amount, min, max, syncable, sentiment, + ManasCoreAttributeRegister::registerToGeneric); + }attribute-common/src/main/java/io/github/manasmods/manascore/attribute/mixin/MixinLivingEntity.java (1)
32-39
: Refactor Repetitive Attribute CheckingHere, you retrieve the attribute instance and check its nullity and value to modify glide speed. This same pattern (lines 41-48 and 50-57) is repeated for lava speed and swim speed. Consider refactoring into a common utility method to avoid code duplication:
- if (instance == null || instance.getValue() <= 0) return vec3; - return vec3.multiply(instance.getValue(), 1, instance.getValue()); + return multiplyIfValid(instance, vec3); ... private Vec3 multiplyIfValid(AttributeInstance instance, Vec3 original) { if (instance == null || instance.getValue() <= 0) { return original; } return original.multiply(instance.getValue(), 1, instance.getValue()); }attribute-common/src/main/java/io/github/manasmods/manascore/attribute/impl/network/ManasCoreAttributeNetwork.java (1)
6-15
: Single Responsibility & Error HandlingThe
init()
method cleanly registers the C2S payload. However, consider handling scenarios in which the registration might fail or if the packet type is already registered. Logging or a fallback mechanism could provide more robust error handling.attribute-common/src/main/java/io/github/manasmods/manascore/attribute/mixin/MixinLocalPlayer.java (2)
2-2
: Update copyright year.The copyright year is set to 2025, which is incorrect as it's currently 2024.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
24-31
: Add validation for network packet sending.While the null check is good, consider adding validation before sending the network packet to ensure the player is in a valid state for gliding.
public void canStartGliding(CallbackInfo cb) { LocalPlayer player = Minecraft.getInstance().player; if (player == null) return; if (ManasCoreAttributeUtils.canElytraGlide(player, !player.isFallFlying() && !player.isInLiquid())) { + if (!player.isSpectator() && !player.isCreative()) { + // Check if player has equipped elytra before starting glide + if (!player.getItemBySlot(EquipmentSlot.CHEST).isEmpty()) { player.startFallFlying(); NetworkManager.sendToServer(new RequestGlideStartPacket()); + } + } } }attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributeUtils.java (3)
2-2
: Update copyright year.The copyright year is set to 2025, which is incorrect as it's currently 2024.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
24-29
: Document the attack damage calculation and extract magic numbers.The damage calculation formula uses magic numbers without explanation. Consider adding documentation and extracting these values as named constants.
+ /** Base multiplier for minimum attack damage */ + private static final float BASE_DAMAGE_MULTIPLIER = 0.2F; + /** Scaling multiplier for attack strength */ + private static final float STRENGTH_SCALING_MULTIPLIER = 0.8F; + + /** + * Calculates the player's attack damage based on their base attribute and attack strength. + * The final damage is scaled using the formula: damage = damage * (BASE + strength² * SCALING) + * + * @param player The player entity + * @return The calculated attack damage + */ public static float getAttackDamage(Player player) { float f = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE); float h = player.getAttackStrengthScale(0.5F); - f *= 0.2F + h * h * 0.8F; + f *= BASE_DAMAGE_MULTIPLIER + h * h * STRENGTH_SCALING_MULTIPLIER; return f; }
44-49
: Add error handling for sound and animation effects.Consider adding error handling to gracefully handle cases where sound or animation effects fail.
public static void triggerCriticalAttackEffect(Entity target, Entity attacker) { + if (target == null || attacker == null) return; + + try { target.level().playSound(null, target.getX(), target.getY(), target.getZ(), SoundEvents.PLAYER_ATTACK_CRIT, attacker.getSoundSource(), 1.0F, 1.0F); if (target.level() instanceof ServerLevel level) level.getChunkSource().broadcastAndSend(target, new ClientboundAnimatePacket(target, 4)); + } catch (Exception e) { + LOGGER.error("Failed to trigger critical attack effect", e); + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
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/api/ManasCoreAttributeUtils.java
(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java
(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/impl/network/ManasCoreAttributeNetwork.java
(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/impl/network/c2s/RequestGlideStartPacket.java
(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/mixin/MixinLivingEntity.java
(1 hunks)attribute-common/src/main/java/io/github/manasmods/manascore/attribute/mixin/MixinLocalPlayer.java
(1 hunks)attribute-common/src/main/resources/assets/manascore/lang/en_us.json
(1 hunks)attribute-common/src/main/resources/manascore_attribute.mixins.json
(1 hunks)attribute-fabric/build.gradle
(1 hunks)attribute-neoforge/build.gradle
(1 hunks)skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.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 (2)
- attribute-common/src/main/java/io/github/manasmods/manascore/attribute/ManasCoreAttribute.java
- testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java
🔇 Additional comments (22)
attribute-fabric/build.gradle (2)
6-8
: LGTM! Well-structured Loom configuration.The Loom configuration correctly shares the access widener path from the common module, which is a good practice for maintaining consistency across subprojects.
10-12
: LGTM! Proper dependency configuration.The dependency configuration correctly references the network-common module using the transformProductionFabric configuration, which is appropriate for cross-platform mod development.
attribute-common/build.gradle (2)
6-8
: Verify the existence of the access widener file.The Loom configuration references an access widener file. Let's ensure this file exists and contains the necessary configurations.
✅ Verification successful
Access widener file exists and is properly configured ✅
The file contains valid access widener configuration to expose the
instances
field from Minecraft'sAttributeSupplier
class.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the access widener file exists if [ -f "attribute-common/src/main/resources/manascore_attribute.accesswidener" ]; then echo "Access widener file exists" cat "attribute-common/src/main/resources/manascore_attribute.accesswidener" else echo "Access widener file is missing" fiLength of output: 346
10-12
: Verify the network-common project dependency.The dependency configuration looks good. The use of
namedElements
configuration and disabled transitive dependencies is a good practice for maintaining clean dependency management.✅ Verification successful
Project dependency verified successfully
The network-common project exists and is properly configured as a Gradle project. The dependency declaration is valid and follows best practices.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the network-common project exists if [ -d "network-common" ]; then echo "network-common project exists" if [ -f "network-common/build.gradle" ]; then echo "build.gradle found in network-common" else echo "WARNING: build.gradle missing in network-common" fi else echo "WARNING: network-common project directory not found" fiLength of output: 220
attribute-neoforge/build.gradle (3)
10-12
: Verify the network-common project configuration.The dependency references a custom configuration 'transformProductionNeoForge'. Let's verify this setup.
✅ Verification successful
Dependency configuration is correct and follows project standards ✅
The
transformProductionNeoForge
configuration is a standard part of the build setup for NeoForge modules in this Architectury-based project, and the dependency declaration in attribute-neoforge's build.gradle follows the established pattern.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if network-common project exists and defines the custom configuration fd -t f "build.gradle" -x grep -l "transformProductionNeoForge"Length of output: 233
Script:
#!/bin/bash # Check if network-common project exists fd -t d "network-common$" # Look for network-common build file fd -t f "build.gradle" -p "network-common" # Check root build.gradle for configuration definition cat build.gradleLength of output: 10154
6-8
: Verify the attribute-common project configuration.The loom configuration references the accessWidenerPath from the attribute-common project. Let's verify this dependency.
✅ Verification successful
Access widener configuration is correct ✅
The configuration correctly references the access widener path from attribute-common, which points to an existing file at
src/main/resources/manascore_attribute.accesswidener
.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if attribute-common project exists and has the access widener configuration fd -t f "build.gradle" -x grep -l "accessWidenerPath"Length of output: 751
Script:
#!/bin/bash # Check the loom configuration in attribute-common/build.gradle cat attribute-common/build.gradleLength of output: 326
Script:
#!/bin/bash # Verify the access widener file exists ls -l attribute-common/src/main/resources/manascore_attribute.accesswidenerLength of output: 195
14-16
: Verify the mod_id property and access widener file.The remapJar configuration uses
project.mod_id
to reference the access widener file. Let's verify these components.✅ Verification successful
✓ Configuration verified: mod_id and accesswidener are properly set up
The
mod_id
property is correctly defined in the root build.gradle and the referenced accesswidener file exists atattribute-common/src/main/resources/manascore_attribute.accesswidener
. The configuration is valid and follows the project's conventions.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if mod_id is defined in gradle.properties or settings fd -t f "gradle.properties" -x grep -l "mod_id" # Check if the access widener file exists fd -e accesswidenerLength of output: 657
Script:
#!/bin/bash # Check build.gradle files for mod_id fd build.gradle -x grep -l "mod_id" # Check content of attribute-neoforge/build.gradle cat attribute-neoforge/build.gradleLength of output: 659
Script:
#!/bin/bash # Check content of root build.gradle cat build.gradleLength of output: 10041
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributes.java (2)
1-4
: Verify the copyright year.The copyright year is set to 2025. Please verify if this is intentional or if it should be set to the project's inception year or a year range (e.g., "2023-2025").
74-76
: LGTM!The
getResourceKey
method is well-implemented and follows Minecraft conventions.attribute-common/src/main/java/io/github/manasmods/manascore/attribute/mixin/MixinLivingEntity.java (5)
1-5
: Header Check – License and CopyrightEverything appears in order with the license header. No issues detected here.
20-24
: Mixin Class Definition – Clarity and ConsistencyDeclaring this class as
abstract
is consistent with the standard Mixin pattern. No concerns regarding class-level design.
26-30
: Conditional Logic in updateFallFlyingThe method overrides the boolean flag controlling Elytra flight with a custom attribute check from
ManasCoreAttributeUtils
. The approach is clear and appropriate.
41-48
: Potential Negative Value Edge CaseWhile you check for
<= 0
to handle invalid or zero multipliers, you might also want to log or handle the case if the attribute is unexpectedly negative, as it could indicate a data error or malicious manipulation.
50-57
: Maintain Consistent Behavior Across Movement MethodsThe approach for modifying swim speed is consistent with lava speed logic. Ensure that any future attribute-based movement modifications follow the same pattern to maintain predictability.
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/impl/network/ManasCoreAttributeNetwork.java (1)
1-5
: License DeclarationThe license header is present and consistent.
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/impl/network/c2s/RequestGlideStartPacket.java (3)
1-5
: License HeaderCorrect license header noted.
19-22
: Packet Type DefinitionDefining both
TYPE
andSTREAM_CODEC
in a straightforward manner is good practice for maintainability.
41-43
: Method Return AnnotationsUsing
@NotNull
for method return is helpful in clarifying the contract for consumers of thetype()
method.attribute-common/src/main/java/io/github/manasmods/manascore/attribute/mixin/MixinLocalPlayer.java (1)
33-37
: LGTM! Clean implementation of elytra activation control.The method effectively prevents default elytra activation when custom glide conditions are met, falling back to original behavior otherwise.
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/ManasCoreAttributeUtils.java (2)
31-42
: Add null checks for weapon item and improve error handling.The same issues identified in the past review are still present in this implementation.
51-54
: LGTM! Comprehensive glide condition checking.The method effectively combines all necessary conditions for elytra gliding, including ground state, passenger state, levitation effect, and the custom glide speed multiplier attribute.
attribute-common/src/main/resources/manascore_attribute.mixins.json (1)
1-15
: Verify Java 21 compatibility with target platforms.The mixin configuration specifies Java 21 compatibility. Please ensure this aligns with the minimum Java version requirements of all target platforms and dependencies.
✅ Verification successful
Java 21 compatibility is correctly configured
The Java 21 requirement in the mixin configuration aligns perfectly with:
- Minecraft 1.21.1 and NeoForge 21.1.28 platform requirements
- Project's build configuration
- All mod metadata specifications
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check build configuration and dependency requirements echo "Checking build configuration..." fd -e gradle -e properties | xargs cat | grep -i "java\|jdk\|sourceCompatibility" echo "Checking dependency requirements..." if [ -f "gradle/libs.versions.toml" ]; then cat gradle/libs.versions.toml fiLength of output: 679
Script:
#!/bin/bash # Check Minecraft and Forge versions echo "Checking Minecraft and Forge configurations..." rg -i "minecraft|forge" build.gradle rg -i "minecraft|forge" gradle.properties echo "Checking mod metadata..." fd -e toml -e json | grep -i "mods\|fabric\|forge" | xargs cat 2>/dev/null | grep -i "minecraft\|java\|forge"Length of output: 3702
/* | ||
* Copyright (c) 2025. ManasMods | ||
* GNU General Public License 3 | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the copyright year to the current year.
The copyright notice shows 2025, which is a future date. Please update it to the current year (2024).
- * Copyright (c) 2025. ManasMods
+ * Copyright (c) 2024. ManasMods
📝 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.
/* | |
* Copyright (c) 2025. ManasMods | |
* GNU General Public License 3 | |
*/ | |
/* | |
* Copyright (c) 2024. ManasMods | |
* GNU General Public License 3 | |
*/ | |
public static final Holder<Attribute> LAVA_SPEED_MULTIPLIER = registerGenericAttribute(ModuleConstants.MOD_ID, | ||
"lava_speed_multiplier", "manascore.attribute.lava_speed_multiplier", | ||
1, 0, 1024, true, Attribute.Sentiment.POSITIVE); | ||
|
||
/** | ||
* Determine how fast the player can swim in Water. | ||
* Similar to NeoForge/Forge's Swim speed instead of Vanilla's Water Movement Efficiency. | ||
*/ | ||
public static final Holder<Attribute> SWIM_SPEED_MULTIPLIER = registerGenericAttribute(ModuleConstants.MOD_ID, | ||
"swim_speed_multiplier", "manascore.attribute.swim_speed_multiplier", | ||
1, 0, 1024, true, Attribute.Sentiment.POSITIVE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Review speed multiplier ranges and performance impact.
Both LAVA_SPEED_MULTIPLIER and SWIM_SPEED_MULTIPLIER:
- Have an extremely wide range (0-1024x)
- Could potentially cause performance issues at high values due to increased chunk loading
- Might need server-side validation to prevent exploitation
Consider:
- Reducing the maximum range to a reasonable value (e.g., 5.0)
- Adding server-side speed caps for performance
public void handle(NetworkManager.PacketContext context) { | ||
if (context.getEnvironment() != Env.SERVER) return; | ||
context.queue(() -> { | ||
Player player = context.getPlayer(); | ||
if (player == null) return; | ||
player.stopFallFlying(); | ||
if (ManasCoreAttributeUtils.canElytraGlide(player, !player.isFallFlying() && !player.isInLiquid())) | ||
player.startFallFlying(); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Server-Side Execution and Edge Cases
While the code properly checks for server environment and a non-null player, consider:
- Logging attempts to initiate glide for debugging.
- Checking additional states (e.g., ensuring the player is not in creative mode, specifying conditions on dimension).
Adding such validations can prevent unexpected or exploitable behaviors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
♻️ Duplicate comments (11)
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.Apply this diff to correct the comparison:
- if (!Objects.equals(resultChangeable.get(), EntityEvents.ProjectileHitResult.DEFAULT)) return deflectionChangeable.get(); + if (resultChangeable.get() != EntityEvents.ProjectileHitResult.DEFAULT) return deflectionChangeable.get();attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java (1)
40-57
: 🛠️ Refactor suggestionAdd validation for damage multiplier calculations.
The validation concerns from the previous review still apply. Additionally:
- Add documentation explaining the different critical hit scenarios
- Consider simplifying the branching logic
+ /** + * Handles critical hits for players, with special handling for vanilla critical hits. + * @param e The critical hit event + */ @SubscribeEvent(priority = EventPriority.HIGH) public static void onCriticalHit(final CriticalHitEvent e) { + // Get and validate the critical damage multiplier + float critMultiplier = (float) Math.max(1.0, Math.min( + e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER), + 10.0 + )); + if (e.isVanillaCritical()) { - float multiplier = (float) e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER); - e.setDamageMultiplier(e.getDamageMultiplier() / e.getVanillaMultiplier() * multiplier); + e.setDamageMultiplier(e.getDamageMultiplier() / e.getVanillaMultiplier() * critMultiplier); return; } - float critMultiplier = (float) e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER); Changeable<Float> multiplier = Changeable.of(e.getDamageMultiplier() * critMultiplier); Changeable<Double> chance = Changeable.of(e.getEntity().getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) / 100);testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java (1)
41-43
:⚠️ Potential issueAdd safety checks for explosion ability.
The explosion ability lacks critical safety measures that could prevent griefing or unintended damage.
The previous review's concerns about adding safety checks remain valid:
- Add client-side check
- Implement cooldown mechanism
- Add safe zone restrictions
- Make explosion power configurable
race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java (1)
15-18
: 🛠️ Refactor suggestionFix inconsistent event naming.
The event names
SKILL_PRE_TICK
andSKILL_POST_TICK
use "SKILL" prefix while being in a race-related class.- Event<RaceTickEvent> SKILL_PRE_TICK = EventFactory.createEventResult(); - Event<RacePostTickEvent> SKILL_POST_TICK = EventFactory.createLoop(); + Event<RaceTickEvent> RACE_PRE_TICK = EventFactory.createEventResult(); + Event<RacePostTickEvent> RACE_POST_TICK = EventFactory.createLoop();testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRace.java (2)
43-49
: 🛠️ Refactor suggestionConsider reducing the armor attribute modifier value.
The armor modifier value of 10 is quite high for testing purposes and could mask potential issues during testing.
Apply this diff to use a more reasonable value:
this.addAttributeModifier(Attributes.STEP_HEIGHT, ResourceLocation.withDefaultNamespace("race.step"), 1, AttributeModifier.Operation.ADD_VALUE); this.addAttributeModifier(Attributes.ARMOR, ResourceLocation.withDefaultNamespace("race.armor"), - 10, AttributeModifier.Operation.ADD_VALUE); + 2, AttributeModifier.Operation.ADD_VALUE);
69-79
:⚠️ Potential issueFix effect handling inconsistencies.
The log message mentions "Poison" but the code handles confusion effect.
Apply this diff to fix the inconsistency:
public boolean onEffectAdded(ManasRaceInstance instance, LivingEntity entity, @Nullable Entity source, Changeable<MobEffectInstance> effect) { MobEffectInstance effectInstance = effect.get(); if (effectInstance == null) return false; - if (effectInstance.getEffect().equals(MobEffects.WEAKNESS)) return false; + MobEffect mobEffect = effectInstance.getEffect(); + if (mobEffect == null || mobEffect.equals(MobEffects.WEAKNESS)) return false; - if (effectInstance.getEffect().equals(MobEffects.CONFUSION)) { - ManasCoreTesting.LOG.info("Poison is bad!"); + if (mobEffect.equals(MobEffects.CONFUSION)) { + ManasCoreTesting.LOG.info("Confusion converted to damage boost!"); effect.set(new MobEffectInstance(MobEffects.DAMAGE_BOOST, effectInstance.getDuration(), effectInstance.getAmplifier())); } return true; }testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (1)
142-150
:⚠️ Potential issuePrevent potential overflow from excessive damage multiplier.
The 100x damage multiplier could cause integer overflow with high base damage values.
Apply this diff to use a more reasonable multiplier:
public boolean onTouchEntity(ManasSkillInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) { if (owner.isShiftKeyDown() && target instanceof Villager) { instance.setMastery(instance.getMastery() + 1); ManasCoreTesting.LOG.info("My mastery is {}", instance.getMastery()); - amount.set(amount.get() * 100F); + float newAmount = Math.min(amount.get() * 2.5F, 50F); // 2.5x damage with 50 damage cap + amount.set(newAmount); } else if (target instanceof Pig) amount.set(0F); ManasCoreTesting.LOG.info("Dealt {} damage.", amount.get()); return true; }testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/RegistryTest.java (4)
67-71
: 🛠️ Refactor suggestionAdjust attribute modifier values for testing.
The current critical hit modifiers are extremely high:
- 10x critical damage multiplier
- 50% critical chance
Apply this diff to use more reasonable values:
.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()))); + .add(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 1.5, + AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND) + .add(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE, new AttributeModifier(ResourceLocation.withDefaultNamespace("test_critical_multiplier"), 0.15, + AttributeModifier.Operation.MULTIPLY_BASE), EquipmentSlotGroup.OFFHAND).build())));
131-137
:⚠️ Potential issueImprove error handling in skill management.
The skill learning logic lacks error handling:
- No null checks for
storage
ortestSkill
- No feedback when skill learning fails
Apply this diff to add proper error handling:
if (entity.getItem().is(Items.DIAMOND)) { Skills storage = SkillAPI.getSkillsFrom(player); + if (storage == null) { + ManasCoreTesting.LOG.error("Failed to get skills storage for player"); + return EventResult.pass(); + } Registrar<ManasSkill> skills = SkillAPI.getSkillRegistry(); RegistrySupplier<TestSkill> testSkill = TEST_SKILL; + if (testSkill == null) { + ManasCoreTesting.LOG.error("Test skill is not registered"); + return EventResult.pass(); + } if (storage.learnSkill(RegistryTest.TEST_SKILL.get())) { ManasCoreTesting.LOG.info("Added Tested Skill!"); + } else { + ManasCoreTesting.LOG.warn("Failed to learn test skill"); }
142-146
:⚠️ Potential issueAdd error handling for race management.
The race setting logic lacks error handling and race ID validation.
Apply this diff to add proper error handling:
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(); + } + if (!RaceAPI.getRaceRegistry().containsKey(RegistryTest.TEST_RACE.getId())) { + ManasCoreTesting.LOG.error("Test race is not registered"); + return EventResult.pass(); + } storage.setRace(RegistryTest.TEST_RACE.getId(), true); ManasCoreTesting.LOG.info("Set to Test Race!"); + player.displayClientMessage(Component.literal("Race set to Test Race"), true); }
174-189
:⚠️ Potential issueAdd safety limits to death explosion mechanics.
The current implementation uses an uncapped random explosion radius and could create unexpectedly large explosions.
Apply this diff to add safety limits:
- float g = 10.0F + entity.getRandom().nextFloat() * 2.0F; + float baseRadius = 3.0F; + float randomAddition = entity.getRandom().nextFloat() * 1.0F; + float g = Math.min(baseRadius + randomAddition, 4.0F);
🧹 Nitpick comments (39)
race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (5)
44-52
: Ensure safe fallback for unexpected dimension inputs.
While this method correctly distinguishes betweenServerPlayer
and other entities, consider adding logs in case the dimension is not valid or available, to help with debugging.
54-60
: Optimize repeated server-level retrievals.
This overload correctly fetches the dimension and delegates, but you might explore caching or centralizing dimension lookups if you're performing them frequently in other areas of the codebase.
65-70
: Add fallback behavior or logging on null spawn.
Whenspawn == null
, the method silently returns. Consider adding a debug log or user-friendly message so admins can diagnose teleport issues.
81-111
: Consider a custom spawn for non-Overworld & non-End scenarios.
Currently, if the dimension is neither End nor Overworld, you default to the Overworld’s shared spawn. Depending on your design, you may want to define an explicit fallback or unique spawn logic for other dimensions.
114-179
: Remove duplicated comment lines and clarify spiral search logic.
Lines 119 and 120 repeat the same explanation. Additionally, consider adding more context about how and why the spiral approach is used, to improve readability and maintenance.- // (di, dj) is a vector - direction in which we move right now - // (di, dj) is a vector - direction in which we move right now + // (dx, dz) represent the direction in which we move currentlyrace-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRace.java (9)
60-65
: Leverage composition or interfaces for advanced race features.
The current design leads you to extendManasRace
for new races. If advanced race logic expands, consider using an interface-based approach or composition to avoid a large inheritance chain.
72-74
: Use a specialized factory for instance creation.
If you expect more specializedManasRaceInstance
classes, consider introducing a factory or builder pattern for clarity and extensibility.
139-141
: Allow optional ticking for advanced race logic.
Currently returningfalse
might skip race logic entirely. Consider making it configurable or dynamic so un-ticked races can still run partial logic if needed.
148-150
: Centralize attribute modifiers for maintainability.
These methods effectively add/remove attribute modifiers. If you foresee more attribute logic or conditions, consider grouping them in a dedicated “AttributeManager” to keepManasRace
uncluttered.Also applies to: 158-167, 174-180
218-221
: Consider structured event-based design.
The override methods (onEffectAdded
,onBeingTargeted
,onAttackEntity
,onHurt
,onDeath
,onRespawn
) could be handled with a more unified or event-driven approach, especially if your codebase has a consistent event bus or observer pattern.Also applies to: 228-231, 238-241, 248-251, 258-261, 268-270
278-280
: Parameterize the default dimension and platform logic.
getRespawnDimension
always returns Overworld and air blocks. You might enable race-specific or config-driven dimension overrides that are more specialized.
300-305
: Log skill acquisitions for better debugging.
When the entity learns intrinsic skills, consider adding logs or debug statements to help modpack developers track skill assignments.
356-378
: Use dynamic IDs for attribute templates.
Relying on a staticResourceLocation
for each logic might become cumbersome if your mod or others add new attributes. Loading the ID from config or generating it at runtime could be beneficial.
381-392
: Use built-in color constants instead of AWT.
You rely onColor.ORANGE.getRGB()
for the INTERMEDIATE difficulty. Minecraft’sChatFormatting
or built-in color codes might offer more consistent cross-platform compatibility.-INTERMEDIATE(Component.translatable("manascore.race.difficulty.intermediate").withStyle(style -> style.withColor(Color.ORANGE.getRGB()))), +INTERMEDIATE(Component.translatable("manascore.race.difficulty.intermediate").withStyle(ChatFormatting.GOLD)),attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java (1)
1-4
: Update the copyright year.The copyright year is set to 2025, but it should reflect when the code was actually written (2024 or earlier) since we're only in January 2025.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasModsrace-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (3)
1-4
: Update the copyright year.The copyright year is set to 2025, which is the current year. Consider using a range (e.g., "2024-2025") to indicate when the file was first created.
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024-2025. ManasMods
35-40
: Consider using more Lombok annotations to reduce boilerplate code.The class could benefit from additional Lombok annotations to reduce boilerplate code and improve maintainability:
@ToString
for better debugging@EqualsAndHashCode(of = "raceRegistryObject")
to replace manual implementations
331-333
: Consider caching intrinsic skills for performance.The
getIntrinsicSkills
andlearnIntrinsicSkills
methods might be called frequently. Consider caching the results to improve performance, especially if the race's intrinsic skills don't change during runtime.+ private Map<LivingEntity, List<ManasSkill>> cachedIntrinsicSkills = new WeakHashMap<>(); + public List<ManasSkill> getIntrinsicSkills(LivingEntity entity) { - return this.getRace().getIntrinsicSkills(this, entity); + return cachedIntrinsicSkills.computeIfAbsent(entity, + e -> Collections.unmodifiableList(this.getRace().getIntrinsicSkills(this, e))); }Also applies to: 344-346
testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java (4)
45-48
: Improve sound configuration and feedback.The sound effect parameters should be configurable, and the message should be localized.
public void onRaceSet(ManasRaceInstance instance, LivingEntity living) { - ManasCoreTesting.LOG.info("You are a Test Race Evolved!"); - living.playSound(SoundEvents.ENCHANTMENT_TABLE_USE, 1, 1); + ManasCoreTesting.LOG.info(Component.translatable("race.evolved.set").getString()); + living.playSound(SoundEvents.ENCHANTMENT_TABLE_USE, + instance.getSoundVolume(), + instance.getSoundPitch()); }
50-53
: Improve target nullification feedback.Silently nullifying the target might confuse players. Consider providing feedback or particle effects.
public boolean onBeingTargeted(ManasRaceInstance instance, Changeable<LivingEntity> target, LivingEntity mob) { - if (mob.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) target.set(null); + if (mob.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) { + target.set(null); + // Add visual feedback + mob.level().addParticle(ParticleTypes.SMOKE, + mob.getX(), mob.getY() + 0.5, mob.getZ(), + 0.0D, 0.0D, 0.0D); + } return true; }
64-71
: Improve death and respawn feedback.The hardcoded messages should be localized and more informative.
public boolean onDeath(ManasRaceInstance instance, LivingEntity owner, DamageSource source) { - ManasCoreTesting.LOG.info("AWWWWW MANNNNN x2"); + ManasCoreTesting.LOG.info(Component.translatable("race.evolved.death", + owner.getName(), source.getMsgId()).getString()); return true; } public void onRespawn(ManasRaceInstance instance, ServerPlayer owner, boolean conqueredEnd) { - ManasCoreTesting.LOG.info("CREEPER x2"); + ManasCoreTesting.LOG.info(Component.translatable("race.evolved.respawn", + owner.getName()).getString()); }
79-81
: Make respawn dimension configurable.The respawn dimension and block state should be configurable rather than hardcoded.
public Pair<ResourceKey<Level>, BlockState> getRespawnDimension(ManasRaceInstance instance, LivingEntity owner) { - return Pair.of(Level.END, Blocks.END_STONE.defaultBlockState()); + return instance.getConfiguredRespawnPoint().orElse( + Pair.of(Level.END, Blocks.END_STONE.defaultBlockState())); }race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java (1)
1-4
: Update copyright year to current year.The copyright year is set to 2025, which is in the future. Please update it to the current year (2024).
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasModstesting-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestSkill.java (1)
38-42
: Use namespaced identifier for attribute modifier.The attribute modifier's identifier "test_speed" should be properly namespaced to avoid potential conflicts.
Apply this diff to add proper namespacing:
super(); ManasCoreTesting.LOG.info("Created skill!"); - this.addHeldAttributeModifier(Attributes.MOVEMENT_SPEED, "test_speed", 1, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL); + this.addHeldAttributeModifier(Attributes.MOVEMENT_SPEED, ResourceLocation.withDefaultNamespace("test_speed"), 1, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL);race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (3)
33-33
: Consider making this interval configurable instead of hardcoding 20.
Using a hardcoded tick interval of 20 might not be suitable for all situations, especially if the user wants to adjust balance in different modpacks or servers.- public static final int INSTANCE_UPDATE = 20; + public static int INSTANCE_UPDATE = 20; // or make it configurable via a config file
42-46
: Avoid unnecessary storage flagging to improve performance.
storage.markDirty()
is called unconditionally afterhandleSkillTick()
execution. Consider only marking it dirty when a change actually occurs, to reduce overhead in large-scale servers.- storage.markDirty(); + if (someDataChanged) { + storage.markDirty(); + }
71-77
: Review partial checks for ability activation and ticking.
Currently, the code stops further logic ifcanActivateAbility
orcanTick
is false. If you intend partial logic or fallback for either condition, consider splitting the checks or documenting why the entire logic is skipped.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (1)
203-209
: Clarify usage of ResourceLocation vs. String for attribute modifiers.You have two overloads for
addHeldAttributeModifier(Holder<Attribute>..., ResourceLocation/String, ...)
. Consider unifying them or clarifying their intended usage to avoid confusion when developers add new modifiers.network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
8-9
: Consider potential overhead of @synchronized.
@Synchronized
locks on the instance, which may be unnecessary if this class is only used on the main server/client thread. Consider removing or replacing with narrower synchronization if concurrency is truly required.skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinThrowableProjectile.java (1)
22-30
: Improve readability of the onHit method.The method logic is correct, but the code structure could be more readable.
Consider this refactoring for better readability:
@WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/ThrowableProjectile;hitTargetOrDeflectSelf(Lnet/minecraft/world/phys/HitResult;)Lnet/minecraft/world/entity/projectile/ProjectileDeflection;")) ProjectileDeflection onHit(ThrowableProjectile 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(); - original.call(instance, result); - return deflectionChangeable.get(); + if (!Objects.equals(resultChangeable.get(), EntityEvents.ProjectileHitResult.DEFAULT)) { + return deflectionChangeable.get(); + } + original.call(instance, result); + return deflectionChangeable.get(); }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/EntityEvents.java (2)
20-28
: Consider documenting the event creation pattern.The events are created using different patterns (
createLoop()
vscreateEventResult()
). This choice affects how the events behave and should be documented.Add class-level documentation explaining:
- When to use
createLoop()
vscreateEventResult()
- The implications of each event type
- The execution order of event handlers
31-58
: Add Javadoc to public API methods.The functional interfaces define the public API and would benefit from proper documentation explaining:
- Method parameters
- Expected behavior
- Return value implications
- Usage examples
Example for
LivingTickEvent
:/** * Event fired when a living entity ticks. */ @FunctionalInterface public interface LivingTickEvent { /** * Called when a living entity ticks. * * @param entity The entity that is ticking */ void tick(LivingEntity entity); }skill-common/src/main/java/io/github/manasmods/manascore/skill/api/SkillEvents.java (2)
26-28
: Document the damage calculation event chain.The presence of pre/post/main calculation events suggests a specific execution order that should be documented.
Add documentation explaining:
- The execution order of damage calculation events
- The purpose of each phase
- How modifications in each phase affect the final damage
32-79
: Standardize interface visibility and document parameters.The functional interfaces have inconsistent visibility modifiers and lack parameter documentation.
- Add
public
modifier to all interfaces- Document complex parameters, especially:
mode
in SkillUpdateCooldownEventkeyNumber
in SkillActivationEventdelta
in SkillScrollEventExample:
/** * Event fired when updating skill cooldown. */ @FunctionalInterface public interface SkillUpdateCooldownEvent { /** * Called when a skill's cooldown is being updated. * * @param skillInstance The skill instance being updated * @param owner The entity that owns the skill * @param currentCooldown The current cooldown value * @param mode The update mode (0: set, 1: add, 2: subtract) * @return EventResult indicating if the cooldown update should proceed */ EventResult cooldown(ManasSkillInstance skillInstance, LivingEntity owner, int currentCooldown, int mode); }skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinMob.java (1)
20-25
: Improve readability and add null check.The target setting logic could be more readable and should handle null targets.
Consider this refactoring:
@WrapOperation(method = "setTarget", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/Mob;target:Lnet/minecraft/world/entity/LivingEntity;")) private void onSetTarget(Mob instance, LivingEntity value, Operation<LivingEntity> original) { + // Allow clearing target + if (value == null) { + original.call(instance, null); + return; + } Changeable<LivingEntity> target = Changeable.of(value); - if (EntityEvents.LIVING_CHANGE_TARGET.invoker().changeTarget(instance, target).isFalse()) { - original.call(instance, value); - } else original.call(instance, target.get()); + boolean shouldChangeTarget = !EntityEvents.LIVING_CHANGE_TARGET.invoker() + .changeTarget(instance, target) + .isFalse(); + original.call(instance, shouldChangeTarget ? target.get() : value); }race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java (2)
2-2
: Update copyright year to current year.The copyright year is set to 2025, which is in the future. Update it to the current year (2024).
- * Copyright (c) 2025. ManasMods + * Copyright (c) 2024. ManasMods
39-39
: Add documentation for markDirty method.Consider adding Javadoc to explain when this method should be called and its implications for the race system.
+ /** + * Marks the race state as dirty, indicating that changes have occurred. + * This method should be called when the race instance is modified and needs to be synchronized. + */ void markDirty();skill-common/src/main/java/io/github/manasmods/manascore/skill/impl/SkillRegistry.java (2)
33-118
: Refactor repeated event handling pattern.The event handling code follows a similar pattern across all events. Consider extracting the common pattern into a reusable helper method.
+ private static EventResult handleSkillEvent(LivingEntity entity, SkillEventHandler handler) { + for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { + if (!instance.canInteractSkill(entity)) continue; + if (!handler.handle(instance)) return EventResult.interruptFalse(); + } + return EventResult.pass(); + } + + @FunctionalInterface + private interface SkillEventHandler { + boolean handle(ManasSkillInstance instance); + }This would simplify the event registrations, for example:
- EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> { - for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { - if (!instance.canInteractSkill(entity)) continue; - if (!instance.onEffectAdded(entity, source, changeableTarget)) return EventResult.interruptFalse(); - } - return EventResult.pass(); - }); + EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> + handleSkillEvent(entity, instance -> instance.onEffectAdded(entity, source, changeableTarget)) + );
121-122
: Add class documentation.Add Javadoc to explain that this is a utility class and why instantiation is prevented.
+ /** + * Utility class for managing skill registrations and event handlers. + * This class should not be instantiated as it only contains static members. + */ public class SkillRegistry {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (36)
attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/AttributeEvents.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-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java
(1 hunks)network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRace.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java
(1 hunks)race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java
(1 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/api/SpawnPointHelper.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)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/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/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/mixin/MixinLivingEntity.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)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinShulkerBullet.java
(1 hunks)skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinThrowableProjectile.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)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/TestRace.java
(1 hunks)testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.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 (16)
- attribute-common/src/main/java/io/github/manasmods/manascore/attribute/api/AttributeEvents.java
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/mixin/MixinPlayer.java
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/mixin/MixinLivingEntity.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/MixinAbstractArrow.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/MixinLlamaSpit.java
- attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinLivingEntity.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/ManasSkillInstance.java
- skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinFireworkRocketEntity.java
- race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceRegistry.java
- skill-neoforge/src/main/java/io/github/manasmods/manascore/skill/neoforge/NeoForgeCommonEventInvoker.java
- attribute-fabric/src/main/java/io/github/manasmods/manascore/attribute/fabric/mixin/MixinPlayer.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/SkillStorage.java
🔇 Additional comments (23)
race-common/src/main/java/io/github/manasmods/manascore/race/api/SpawnPointHelper.java (2)
72-79
: Confirm race dimension logic for re-teleportation.
This logic prevents re-teleporting if the player is already in the correct dimension. Ensure that the dimension check aligns with race evolution or dimension overrides for special scenarios.
181-196
: Evaluate block replacement side effects.
Dropping blocks (line 191) when replacing them might lead to item exploitation if triggered repeatedly. If this is intentional, consider clarifying this behavior in the documentation. Otherwise, remove or revise to avoid duping.race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRace.java (1)
114-120
: Prevent collisions between race IDs.
Ensuring unique race registry names is crucial. If collisions happen, equals/hashCode might produce unexpected issues. Verify that your race registry enforces uniqueness.attribute-neoforge/src/main/java/io/github/manasmods/manascore/attribute/neoforge/NeoForgeCommonEventInvoker.java (2)
20-24
: LGTM! Well-structured utility class.The class follows best practices with a private constructor and proper event bus subscription.
25-38
: 🛠️ Refactor suggestionAdd safety checks and documentation for critical hit mechanics.
Several improvements needed for robustness:
- Add validation for damage multiplier values
- Add null checks for attacker attributes
- Add documentation explaining the critical hit mechanics
+ /** + * Applies critical hit mechanics for non-player entities. + * @param e The incoming damage event + */ @SubscribeEvent(priority = EventPriority.HIGH) public static void applyEntityCrit(final LivingIncomingDamageEvent e) { if (!(e.getSource().getDirectEntity() instanceof LivingEntity attacker)) return; // Direct attack if (attacker instanceof Player) return; // Players have their own Critical Event LivingEntity target = e.getEntity(); + // Safely get attributes with fallback values + double critDamageMultiplier = attacker.getAttributes().hasAttribute(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER) + ? attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER) + : 1.0; + double critChance = attacker.getAttributes().hasAttribute(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) + ? attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) + : 0.0; - Changeable<Float> multiplier = Changeable.of((float) attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_DAMAGE_MULTIPLIER)); - Changeable<Double> chance = Changeable.of(attacker.getAttributeValue(ManasCoreAttributes.CRITICAL_ATTACK_CHANCE) / 100); + // Validate and cap multiplier to prevent excessive damage + float safeMultiplier = (float) Math.max(1.0, Math.min(critDamageMultiplier, 10.0)); + Changeable<Float> multiplier = Changeable.of(safeMultiplier); + Changeable<Double> chance = Changeable.of(critChance / 100);Likely invalid or redundant comment.
race-common/src/main/java/io/github/manasmods/manascore/race/api/ManasRaceInstance.java (1)
35-395
: Overall implementation looks solid!The
ManasRaceInstance
class provides a robust and well-documented implementation for managing race instances. The delegation pattern toManasRace
is well-implemented, and the event system is comprehensive. While there are some suggested improvements, the core functionality is sound.testing-common/src/main/java/io/github/manasmods/manascore/testing/registry/TestRaceEvolved.java (1)
73-77
: Add null check for race evolution.The
TEST_RACE.get()
call could return null if the registry is not properly initialized.public List<ManasRace> getPreviousEvolutions(ManasRaceInstance instance, LivingEntity entity) { List<ManasRace> list = new ArrayList<>(); - list.add(RegistryTest.TEST_RACE.get()); + ManasRace testRace = RegistryTest.TEST_RACE.get(); + if (testRace != null) { + list.add(testRace); + } return list; }Let's verify the registry initialization:
race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java (3)
8-12
: LGTM!The imports are well-organized and all are being used within the interface.
20-38
: LGTM! Well-structured event interfaces.The event interfaces are well-designed with:
- Proper @FunctionalInterface annotations
- Clear method signatures
- Logical parameter naming
- Appropriate use of EventResult for cancellable events
15-18
: Verify event usage across the codebase.Let's ensure these events are being used consistently across the codebase.
✅ Verification successful
Events are properly used across the codebase ✓
All events are being used consistently with proper result checking for result events and loop handling for post-tick events. The implementation follows a clear pattern that's also reflected in the skill system.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for event usage echo "Searching for event usage..." rg -A 2 "SKILL_PRE_TICK|SKILL_POST_TICK|SET_RACE|ACTIVATE_ABILITY" --type javaLength of output: 3980
race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java (2)
1-4
: Check license usage and clarity.
The license notice seems fine at first glance. However, ensure that distributing this file under the GNU GPLv3 is compatible with any third-party libraries or frameworks you rely on.Please validate that no conflicting licenses are in use by examining dependencies and other modules:
✅ Verification successful
License usage is consistent and appropriate ✅
The codebase consistently uses GNU GPLv3 throughout all source files, with proper headers and complete license text. The only exception is the standard Gradle wrapper scripts which use Apache 2.0, which is compatible with GPLv3.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check dependencies' licenses for potential conflicts with GNU GPLv3. rg -A 2 "License" .Length of output: 60832
31-32
: Confirm static storage-key assignment safety.
Becauseinit()
assignskey
at runtime, ensure thatinit()
is only called once. Multiple calls could overwrite the static key and cause undefined behavior if multiple assignments conflict.To confirm usage, you can search for all calls to
RaceStorage.init()
:✅ Verification successful
Static storage-key assignment is safe ✅
The implementation is secure asRaceStorage.init()
is called only once during mod initialization inManasCoreRace.java
, and the key assignment happens through the event registration system which guarantees single execution.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify only a single call to RaceStorage.init(). rg -A 2 "RaceStorage.init("Length of output: 114
Script:
#!/bin/bash # Search for RaceStorage.init calls and implementation rg "RaceStorage\.init" -A 3 echo "---" # Find the init method implementation rg "void init\(" race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java -A 5Length of output: 945
skill-fabric/src/main/java/io/github/manasmods/manascore/skill/fabric/mixin/MixinShulkerBullet.java (2)
22-23
: Ensure correctness of the Mixin target.Please verify that your
@WrapOperation
target (hitTargetOrDeflectSelf
) matches the method signature exactly in the mapped environment. Any mismatch can cause crashes or unexpected behavior if the method signature in the target Minecraft version changes.
24-30
: Validate event parameter usage and fallback logic.The logic in this method defers to
original.call(instance, result)
if the event’s result remainsDEFAULT
. This order looks correct, but make sure that event subscribers correctly set bothresultChangeable
anddeflectionChangeable
so that skipping the fallback is an intentional override rather than an accidental effect.skill-common/src/main/java/io/github/manasmods/manascore/skill/api/ManasSkill.java (3)
87-89
: Use the standard ResourceLocation constructor.
ResourceLocation.fromNamespaceAndPath(...)
may not exist in all mod loaders or may differ in behavior. Consider using the standard constructor for broader compatibility:-return ResourceLocation.fromNamespaceAndPath(id.getNamespace(), "icons/skills/" + id.getPath()); +return new ResourceLocation(id.getNamespace(), "icons/skills/" + id.getPath());
101-107
: Also overridehashCode()
to match theequals()
contract.When you override
equals()
, overridinghashCode()
is important for maintaining consistent behavior in hashed data structures.
434-459
: AttributeModifier may require a UUID and a descriptive name rather than a ResourceLocation.The
AttributeModifier
constructor typically takes a UUID and a string as the name. Using aResourceLocation
might cause runtime issues on certain platforms.network-common/src/main/java/io/github/manasmods/manascore/network/api/util/Changeable.java (1)
44-47
: Validate equality checks for null-safety.Your
hasChanged()
method handlesnull
by checkingoriginal == null
. This is correct for most use cases, but ensure any downstream code expecting deeper comparisons is aware thatnull
comparisons skip object fields insideT
.race-common/src/main/java/io/github/manasmods/manascore/race/api/Races.java (3)
14-15
: LGTM! Good use of Optional for null safety.The
getRace
method correctly uses Optional to handle the potential absence of a race instance.
21-23
: Fix parameter naming and standardize null annotations.The parameter name
skill
insetRace
andevolveRace
methods should berace
to match the context.Also applies to: 31-33
17-37
: Verify teleport functionality implementation.The methods include a
teleportToSpawn
parameter, but its implementation and testing are not visible in the current context.Run the following script to check for teleport-related tests and implementation:
✅ Verification successful
Teleport functionality is properly implemented ✓
The teleport functionality is implemented through:
SpawnPointHelper.teleportToNewSpawn
handles the actual teleportation with cross-dimensional supportRaceStorage
integrates with the event system allowing customization of teleport behavior- Proper null safety and error handling is in place
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for teleport implementation and tests echo "Searching for teleport implementation..." rg -l "teleportToSpawn" --type java echo "\nSearching for related tests..." ast-grep --pattern 'class $_ extends $_Test { $$$ test$_teleport$_($$$) { $$$ } $$$ }'Length of output: 542
Script:
#!/bin/bash # Check implementation details echo "=== RaceStorage.java implementation ===" rg -A 10 "setRace.*teleportToSpawn" race-common/src/main/java/io/github/manasmods/manascore/race/impl/RaceStorage.java echo -e "\n=== RaceEvents.java handling ===" rg -A 10 "teleportToSpawn" race-common/src/main/java/io/github/manasmods/manascore/race/api/RaceEvents.java echo -e "\n=== Looking for test files ===" fd "Test.*\.java" race-common/src/testLength of output: 1652
Script:
#!/bin/bash # Search for teleport implementation and event listeners echo "=== Searching for teleport implementation ===" rg -A 5 "teleport.*spawn|spawn.*teleport" --type java echo -e "\n=== Searching for SET_RACE event listeners ===" rg -A 10 "SET_RACE.*Event" --type java echo -e "\n=== Checking for spawn-related code ===" rg -l "getRespawnPosition|getSpawnPoint" --type javaLength of output: 3637
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 needs correction in the Registrar builder.
110-118
: Potential Null Pointer Exception in PROJECTILE_HIT Event.The event handler needs null checking for entity access.
original.call(instance, result); | ||
return deflectionChangeable.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Store and use the return value from original.call
The return value from original.call
is being ignored, which could lead to unexpected behavior. The original method's result should be considered when determining the final deflection.
Apply this diff to handle the return value:
- original.call(instance, result);
- return deflectionChangeable.get();
+ ProjectileDeflection originalDeflection = original.call(instance, result);
+ return deflectionChangeable.get() != ProjectileDeflection.NONE ? deflectionChangeable.get() : originalDeflection;
📝 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.
original.call(instance, result); | |
return deflectionChangeable.get(); | |
ProjectileDeflection originalDeflection = original.call(instance, result); | |
return deflectionChangeable.get() != ProjectileDeflection.NONE ? deflectionChangeable.get() : originalDeflection; |
/* | ||
* Copyright (c) 2025. ManasMods | ||
* GNU General Public License 3 | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the copyright year to the current year.
The copyright notice shows the year 2025, which is in the future. This should be updated to the current year (2024) to maintain legal accuracy.
Apply this diff to correct the year:
- * Copyright (c) 2025. ManasMods
+ * Copyright (c) 2024. ManasMods
Committable suggestion skipped: line range outside the PR's diff.
public boolean onEffectAdded(LivingEntity entity, @Nullable Entity source, Changeable<MobEffectInstance> instance) { | ||
return this.getRace().onEffectAdded(this, entity, source, instance); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add parameter validation in critical event handlers.
Critical event handlers like onEffectAdded
, onAttackEntity
, and onHurt
should validate their parameters to prevent null pointer exceptions and ensure data integrity.
public boolean onEffectAdded(LivingEntity entity, @Nullable Entity source, Changeable<MobEffectInstance> instance) {
+ Objects.requireNonNull(entity, "entity cannot be null");
+ Objects.requireNonNull(instance, "effect instance cannot be null");
return this.getRace().onEffectAdded(this, entity, source, instance);
}
public boolean onAttackEntity(LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) {
+ Objects.requireNonNull(owner, "owner cannot be null");
+ Objects.requireNonNull(target, "target cannot be null");
+ Objects.requireNonNull(source, "damage source cannot be null");
+ Objects.requireNonNull(amount, "amount cannot be null");
return this.getRace().onAttackEntity(this, owner, target, source, amount);
}
public boolean onHurt(LivingEntity owner, DamageSource source, Changeable<Float> amount) {
+ Objects.requireNonNull(owner, "owner cannot be null");
+ Objects.requireNonNull(source, "damage source cannot be null");
+ Objects.requireNonNull(amount, "amount cannot be null");
return this.getRace().onHurt(this, owner, source, amount);
}
Also applies to: 285-287, 296-298
public float getEvolutionProgress(LivingEntity entity, ManasRace evolution) { | ||
return this.getRace().getEvolutionProgress(this, entity, evolution); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for evolution progress value.
The getEvolutionProgress
method documentation states that acceptable values are between 0 and 1.0, but there's no validation to ensure this constraint.
public float getEvolutionProgress(LivingEntity entity, ManasRace evolution) {
- return this.getRace().getEvolutionProgress(this, entity, evolution);
+ float progress = this.getRace().getEvolutionProgress(this, entity, evolution);
+ if (progress < 0.0f || progress > 1.0f) {
+ throw new IllegalStateException("Evolution progress must be between 0 and 1.0, got: " + progress);
+ }
+ return progress;
}
📝 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 float getEvolutionProgress(LivingEntity entity, ManasRace evolution) { | |
return this.getRace().getEvolutionProgress(this, entity, evolution); | |
} | |
public float getEvolutionProgress(LivingEntity entity, ManasRace evolution) { | |
float progress = this.getRace().getEvolutionProgress(this, entity, evolution); | |
if (progress < 0.0f || progress > 1.0f) { | |
throw new IllegalStateException("Evolution progress must be between 0 and 1.0, got: " + progress); | |
} | |
return progress; | |
} |
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
ManasRaceInstance instance = (ManasRaceInstance) o; | ||
return this.getRaceId().equals(instance.getRaceId()) && | ||
raceRegistryObject.getRegistryKey().equals(instance.raceRegistryObject.getRegistryKey()); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(raceRegistryObject); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix potential issues in equals and hashCode implementation.
The current implementation:
- Might break symmetry with subclasses
- Doesn't use all fields that define object identity
- Inconsistent use of fields between equals and hashCode
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (this == o) return true;
+ if (!(o instanceof ManasRaceInstance)) return false;
ManasRaceInstance instance = (ManasRaceInstance) o;
return this.getRaceId().equals(instance.getRaceId()) &&
raceRegistryObject.getRegistryKey().equals(instance.raceRegistryObject.getRegistryKey());
}
@Override
public int hashCode() {
- return Objects.hash(raceRegistryObject);
+ return Objects.hash(getRaceId(), raceRegistryObject.getRegistryKey());
}
📝 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 boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
ManasRaceInstance instance = (ManasRaceInstance) o; | |
return this.getRaceId().equals(instance.getRaceId()) && | |
raceRegistryObject.getRegistryKey().equals(instance.raceRegistryObject.getRegistryKey()); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(raceRegistryObject); | |
} | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (!(o instanceof ManasRaceInstance)) return false; | |
ManasRaceInstance instance = (ManasRaceInstance) o; | |
return this.getRaceId().equals(instance.getRaceId()) && | |
raceRegistryObject.getRegistryKey().equals(instance.raceRegistryObject.getRegistryKey()); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(getRaceId(), raceRegistryObject.getRegistryKey()); | |
} |
public boolean onAttackEntity(ManasRaceInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) { | ||
if (owner.isShiftKeyDown() && target instanceof Pillager) { | ||
BlockPos pos = ServerLevel.END_SPAWN_POINT; | ||
SpawnPointHelper.teleportToAcrossDimensions(target, | ||
this.getRespawnDimension(instance, owner).getFirst(), pos.getX(), pos.getY(), pos.getZ(), 0, 0); | ||
} | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add safety checks for teleportation.
The function uses a hardcoded spawn point without validating if it's safe or accessible. This could lead to entities being teleported into unsafe locations.
Apply this diff to add safety checks:
public boolean onAttackEntity(ManasRaceInstance instance, LivingEntity owner, LivingEntity target, DamageSource source, Changeable<Float> amount) {
if (owner.isShiftKeyDown() && target instanceof Pillager) {
- BlockPos pos = ServerLevel.END_SPAWN_POINT;
+ BlockPos pos = findSafeSpawnPoint(target, this.getRespawnDimension(instance, owner).getFirst());
+ if (pos == null) {
+ ManasCoreTesting.LOG.warn("Could not find safe spawn point for teleportation");
+ return true;
+ }
SpawnPointHelper.teleportToAcrossDimensions(target,
this.getRespawnDimension(instance, owner).getFirst(), pos.getX(), pos.getY(), pos.getZ(), 0, 0);
}
return true;
}
+
+ private @Nullable BlockPos findSafeSpawnPoint(LivingEntity entity, ResourceKey<Level> dimension) {
+ if (!(entity.level() instanceof ServerLevel serverLevel)) return null;
+ BlockPos pos = ServerLevel.END_SPAWN_POINT;
+ return SpawnPlacements.getPlacementType(entity.getType()).canSpawnAt(serverLevel, pos, entity.getType()) ? pos : null;
+ }
Committable suggestion skipped: line range outside the PR's diff.
private static void handleSkillTick(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); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Wrap race ticking in error handling or logging.
If raceInstance.onTick()
throws an exception, the server might crash. Consider adding try/catch or logging to avoid halting the entire server loop.
- tickRace(entity, storage);
+ try {
+ tickRace(entity, storage);
+ } catch (Exception e) {
+ log.error("Error ticking race for entity {}: {}", entity, e.getMessage(), e);
+ }
📝 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.
private static void handleSkillTick(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); | |
} | |
private static void handleSkillTick(LivingEntity entity, Level level, Races storage) { | |
MinecraftServer server = level.getServer(); | |
if (server == null) return; | |
boolean shouldTickRace = server.getTickCount() % INSTANCE_UPDATE == 0; | |
if (!shouldTickRace) return; | |
try { | |
tickRace(entity, storage); | |
} catch (Exception e) { | |
log.error("Error ticking race for entity {}: {}", entity, e.getMessage(), e); | |
} | |
} |
public boolean setRace(@NonNull ManasRaceInstance race, boolean evolution, boolean teleportToSpawn) { | ||
ManasRaceInstance instance = this.raceInstance; | ||
Changeable<Boolean> teleport = Changeable.of(teleportToSpawn); | ||
EventResult result = RaceEvents.SET_RACE.invoker().set(instance, getOwner(), race, evolution, teleport); | ||
if (result.isFalse()) return false; | ||
|
||
LivingEntity owner = this.getOwner(); | ||
if (instance != null) { | ||
instance.removeAttributeModifiers(owner); | ||
if (evolution) instance.onRaceEvolution(owner, race); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Skip re-application of race attributes if the old and new race are the same instance.
Re-removing and re-adding the same race’s attributes can cause flickers or repeated triggers. Add a check to see if race == instance
early.
- if (instance != null) {
+ if (instance != null && instance != race) {
instance.removeAttributeModifiers(owner);
if (evolution) instance.onRaceEvolution(owner, race);
}
📝 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 boolean setRace(@NonNull ManasRaceInstance race, boolean evolution, boolean teleportToSpawn) { | |
ManasRaceInstance instance = this.raceInstance; | |
Changeable<Boolean> teleport = Changeable.of(teleportToSpawn); | |
EventResult result = RaceEvents.SET_RACE.invoker().set(instance, getOwner(), race, evolution, teleport); | |
if (result.isFalse()) return false; | |
LivingEntity owner = this.getOwner(); | |
if (instance != null) { | |
instance.removeAttributeModifiers(owner); | |
if (evolution) instance.onRaceEvolution(owner, race); | |
} | |
public boolean setRace(@NonNull ManasRaceInstance race, boolean evolution, boolean teleportToSpawn) { | |
ManasRaceInstance instance = this.raceInstance; | |
Changeable<Boolean> teleport = Changeable.of(teleportToSpawn); | |
EventResult result = RaceEvents.SET_RACE.invoker().set(instance, getOwner(), race, evolution, teleport); | |
if (result.isFalse()) return false; | |
LivingEntity owner = this.getOwner(); | |
if (instance != null && instance != race) { | |
instance.removeAttributeModifiers(owner); | |
if (evolution) instance.onRaceEvolution(owner, race); | |
} |
@Override | ||
public void save(CompoundTag data) { | ||
if (this.raceInstance == null) return; | ||
data.put(RACE_KEY, this.raceInstance.toNBT()); | ||
} | ||
|
||
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
The review comment is correct - attributes need to be reapplied after loading
The code shows that while setRace()
properly handles attribute reapplication, the load()
method only deserializes and sets the race instance without reapplying attributes. This could lead to stale attributes when loading entities from NBT data.
- Consider calling
this.raceInstance.addAttributeModifiers(entity)
after setting the instance in theload()
method to ensure attributes are properly reapplied.
🔗 Analysis chain
Reapply race to entity after loading to prevent stale attributes.
When load()
sets this.raceInstance
, you might need to add back attribute modifiers if the entity is live at that time. Otherwise, on chunk reload or dimension change, the entity could lose race buffs.
Consider verifying whether corresponding events or mechanism reapply the race attributes after load()
. If not, reapply them manually here or in an appropriate event callback.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Search for attribute reapplication logic after load.
rg -A 5 "load("
Length of output: 78
Script:
#!/bin/bash
# Search for attribute-related methods and their usage
ast-grep --pattern 'getAttribute($$$)'
ast-grep --pattern 'addAttribute($$$)'
ast-grep --pattern 'removeAttribute($$$)'
# Search for event handlers related to entity loading/joining
rg -l "EntityJoin|EntityLoad|PlayerLoad|PlayerJoin"
# Search for race instance attribute application
rg -l "raceInstance.*attribute"
Length of output: 213
Script:
#!/bin/bash
# Search for attribute-related code with broader patterns
rg -i "attribute" -A 5
rg -i "modif.*attribute" -A 5
# Find race instance implementation and usage
fd -e java -e kt -x cat {} | rg -A 10 "class.*Race(Instance|Storage|Handler)"
# Search for common Minecraft mod lifecycle events
rg -i "(onentity|onevent|setup|init|register).*event" -A 5
Length of output: 71844
EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> { | ||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(entity)) continue; | ||
if (!instance.onEffectAdded(entity, source, changeableTarget)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
EntityEvents.LIVING_CHANGE_TARGET.register((entity, changeableTarget) -> { | ||
if (!changeableTarget.isPresent()) return EventResult.pass(); | ||
|
||
LivingEntity owner = changeableTarget.get(); | ||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(owner).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(owner)) continue; | ||
if (!instance.onBeingTargeted(changeableTarget, entity)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
EntityEvent.LIVING_HURT.register((entity, source, amount) -> { | ||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(entity)) continue; | ||
if (!instance.onBeingDamaged(entity, source, amount)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
SkillEvents.SKILL_DAMAGE_PRE_CALCULATION.register((storage, target, source, amount) -> { | ||
if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | ||
|
||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(owner).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(owner)) continue; | ||
if (!instance.onDamageEntity(owner, target, source, amount)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
SkillEvents.SKILL_DAMAGE_POST_CALCULATION.register((storage, target, source, amount) -> { | ||
if (!(source.getEntity() instanceof LivingEntity owner)) return EventResult.pass(); | ||
|
||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(owner).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(owner)) continue; | ||
if (!instance.onTouchEntity(owner, target, source, amount)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
EntityEvents.LIVING_DAMAGE.register((entity, source, amount) -> { | ||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(entity)) continue; | ||
if (!instance.onTakenDamage(entity, source, amount)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
EntityEvent.LIVING_DEATH.register((entity, source) -> { | ||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(entity)) continue; | ||
if (!instance.onDeath(entity, source)) return EventResult.interruptFalse(); | ||
} | ||
|
||
return EventResult.pass(); | ||
}); | ||
|
||
PlayerEvent.PLAYER_RESPAWN.register((newPlayer, conqueredEnd, removalReason) -> { | ||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(newPlayer).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(newPlayer)) continue; | ||
instance.onRespawn(newPlayer, conqueredEnd); | ||
} | ||
}); | ||
|
||
EntityEvents.PROJECTILE_HIT.register((result, projectile, deflectionChangeable, hitResultChangeable) -> { | ||
if (!(result instanceof EntityHitResult hitResult)) return; | ||
if (!(hitResult.getEntity() instanceof LivingEntity hitEntity)) return; | ||
|
||
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(hitEntity).getLearnedSkills()) { | ||
if (!instance.canInteractSkill(hitEntity)) continue; | ||
instance.onProjectileHit(hitEntity, hitResult, projectile, deflectionChangeable, hitResultChangeable); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for skill interactions.
The event handlers should catch and handle potential exceptions from skill interactions to prevent event processing from being interrupted unexpectedly.
EntityEvents.LIVING_EFFECT_ADDED.register((entity, source, changeableTarget) -> {
for (ManasSkillInstance instance : SkillAPI.getSkillsFrom(entity).getLearnedSkills()) {
if (!instance.canInteractSkill(entity)) continue;
- if (!instance.onEffectAdded(entity, source, changeableTarget)) return EventResult.interruptFalse();
+ try {
+ if (!instance.onEffectAdded(entity, source, changeableTarget)) return EventResult.interruptFalse();
+ } catch (Exception e) {
+ LOGGER.error("Error processing effect added event for skill: " + instance.getSkill().getId(), e);
+ }
}
return EventResult.pass();
});
Committable suggestion skipped: line range outside the PR's diff.
Description
List of changes
Type of Change
Summary by CodeRabbit
I'll craft concise release notes focusing on the key additions and improvements across the project.
Release Notes for ManasCore v0.1.0
New Features
Improvements
Technical Enhancements
Platforms Supported