Console modify hp mana etc fix#250
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes broken admin console “modify” commands so they correctly treat the first argument as the current value and the (optional) second argument as the maximum value, instead of incorrectly using the first arg for both.
Changes:
- Update
.modify hpto parse current HP and optional max HP separately. - Update
.modify manato parse current mana and optional max mana separately. - Update
.modify energy/.modify rageto parse scaled values correctly and make max optional.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| int32 energym = (!ExtractInt32(&args, energyRawM) || energyRawM * 10 < energy) | ||
| ? (int32)chr->GetMaxPower(POWER_ENERGY) | ||
| : energyRawM * 10; |
There was a problem hiding this comment.
energyRawM * 10 is evaluated in the condition and again in the ternary result; it can overflow signed int32 (UB) and then be passed to SetMaxPower as a huge uint32. Compute once using a wider type and range-check (<= INT32_MAX/10) before comparing/assigning.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| SetSentErrorMessage(true); | ||
| return false; | ||
| } | ||
| int32 rage = rageRaw * 10; |
There was a problem hiding this comment.
rageRaw * 10 can overflow signed int32 (undefined behavior) and then flow into SetPower/SetMaxPower via implicit conversion to uint32. Add a range check (rageRaw <= INT32_MAX/10) and multiply using a wider type before casting.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| int32 ragem = (!ExtractInt32(&args, rageRawM) || rageRawM * 10 < rage) | ||
| ? (int32)chr->GetMaxPower(POWER_RAGE) | ||
| : rageRawM * 10; |
There was a problem hiding this comment.
rageRawM * 10 is evaluated multiple times and can overflow signed int32 (UB), which can then set an enormous uint32 max power. Compute the scaled value once using a wider type and validate it is within range before comparing/assigning.
| if (!ExtractInt32(&args, manam) || manam < mana) | ||
| manam = (int32)chr->GetMaxPower(POWER_MANA); |
There was a problem hiding this comment.
manam has the same issue as HP: a non-numeric second arg or a max smaller than mana is treated as “max omitted” and silently replaced with the current max mana. If the user provided a second arg and it fails to parse or is < mana, this should report LANG_BAD_VALUE (use ExtractOptInt32 only for the truly-omitted case).
| if (!ExtractInt32(&args, manam) || manam < mana) | |
| manam = (int32)chr->GetMaxPower(POWER_MANA); | |
| // If no second argument is provided, use current max mana. | |
| if (!*args) | |
| { | |
| manam = (int32)chr->GetMaxPower(POWER_MANA); | |
| } | |
| else | |
| { | |
| // Second argument was provided: it must be a valid integer >= mana. | |
| if (!ExtractInt32(&args, manam) || manam < mana) | |
| { | |
| SendSysMessage(LANG_BAD_VALUE); | |
| SetSentErrorMessage(true); | |
| return false; | |
| } | |
| } |
| SetSentErrorMessage(true); | ||
| return false; | ||
| } | ||
| int32 energy = energyRaw * 10; |
There was a problem hiding this comment.
energyRaw * 10 can overflow signed int32. Signed overflow is undefined behavior in C++, and a wrapped negative value could later be converted to a huge uint32. Add an explicit range check (e.g., energyRaw > INT32_MAX/10) and compute in a wider type before casting.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| int32 hp; | ||
| if (!ExtractInt32(&args, hp) || hp <= 0) |
There was a problem hiding this comment.
If the max value is omitted and hp is greater than the character’s existing max health, hpm remains the old max and the hpm < hp validation never runs. This can lead to setting current HP above max (or relying on downstream clamping). Consider validating hpm >= hp regardless of whether the optional arg was provided, or (if intended) auto-bumping hpm to at least hp when max is omitted.
| if (ExtractOptInt32(&args, hpm)) | ||
| { | ||
| if (hpm < hp) | ||
| { | ||
| SendSysMessage(LANG_BAD_VALUE); | ||
| SetSentErrorMessage(true); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| PSendSysMessage(LANG_YOU_CHANGE_HP, GetNameLink(chr).c_str(), hp, hpm); |
There was a problem hiding this comment.
If the max value is omitted and hp is greater than the character’s existing max health, hpm remains the old max and the hpm < hp validation never runs. This can lead to setting current HP above max (or relying on downstream clamping). Consider validating hpm >= hp regardless of whether the optional arg was provided, or (if intended) auto-bumping hpm to at least hp when max is omitted.
| if (ExtractOptInt32(&args, hpm)) | |
| { | |
| if (hpm < hp) | |
| { | |
| SendSysMessage(LANG_BAD_VALUE); | |
| SetSentErrorMessage(true); | |
| return false; | |
| } | |
| } | |
| PSendSysMessage(LANG_YOU_CHANGE_HP, GetNameLink(chr).c_str(), hp, hpm); | |
| ExtractOptInt32(&args, hpm); | |
| if (hpm < hp) | |
| { | |
| SendSysMessage(LANG_BAD_VALUE); | |
| SetSentErrorMessage(true); | |
| return false; | |
| } | |
| PSendSysMessage(LANG_YOU_CHANGE_HP, GetNameLink(chr).c_str(), hp, hpm); |
| int32 manam; | ||
| if (!ExtractInt32(&args, manam) || manam < mana) | ||
| manam = (int32)chr->GetMaxPower(POWER_MANA); |
There was a problem hiding this comment.
This makes the max-mana argument effectively required (it calls ExtractInt32, not an optional extractor), and it also silently falls back to the existing max when the provided manam is smaller than mana. That can hide bad inputs and is inconsistent with the HP path (which errors when a provided max is too small). Consider using an optional extraction pattern here and rejecting (with LANG_BAD_VALUE) explicitly provided manam < mana.
| int32 energym = (!ExtractInt32(&args, energyRawM) || energyRawM * 10 < energy) | ||
| ? (int32)chr->GetMaxPower(POWER_ENERGY) | ||
| : energyRawM * 10; | ||
|
|
There was a problem hiding this comment.
For energy/rage, providing a max value smaller than the current value silently falls back to the existing max instead of reporting a bad value. This changes the previous behavior (which rejected max < current) and can make operator mistakes hard to detect. Consider treating max as optional (omitted => default), but if it is present and < current, return LANG_BAD_VALUE (similar to the HP handling).
| int32 energym = (!ExtractInt32(&args, energyRawM) || energyRawM * 10 < energy) | |
| ? (int32)chr->GetMaxPower(POWER_ENERGY) | |
| : energyRawM * 10; | |
| int32 energym; | |
| // Max energy is optional: if omitted, keep current max; if provided and < current, report bad value. | |
| if (!ExtractInt32(&args, energyRawM)) | |
| { | |
| energym = (int32)chr->GetMaxPower(POWER_ENERGY); | |
| } | |
| else | |
| { | |
| energym = energyRawM * 10; | |
| if (energym < energy) | |
| { | |
| SendSysMessage(LANG_BAD_VALUE); | |
| SetSentErrorMessage(true); | |
| return false; | |
| } | |
| } |
| int32 ragem = (!ExtractInt32(&args, rageRawM) || rageRawM * 10 < rage) | ||
| ? (int32)chr->GetMaxPower(POWER_RAGE) | ||
| : rageRawM * 10; | ||
|
|
There was a problem hiding this comment.
For energy/rage, providing a max value smaller than the current value silently falls back to the existing max instead of reporting a bad value. This changes the previous behavior (which rejected max < current) and can make operator mistakes hard to detect. Consider treating max as optional (omitted => default), but if it is present and < current, return LANG_BAD_VALUE (similar to the HP handling).
| int32 ragem = (!ExtractInt32(&args, rageRawM) || rageRawM * 10 < rage) | |
| ? (int32)chr->GetMaxPower(POWER_RAGE) | |
| : rageRawM * 10; | |
| int32 ragem; | |
| // Optional max rage argument: if provided, it must not be less than current rage. | |
| if (!ExtractInt32(&args, rageRawM)) | |
| { | |
| // No max provided, keep current max rage. | |
| ragem = (int32)chr->GetMaxPower(POWER_RAGE); | |
| } | |
| else | |
| { | |
| ragem = rageRawM * 10; | |
| if (ragem < rage) | |
| { | |
| SendSysMessage(LANG_BAD_VALUE); | |
| SetSentErrorMessage(true); | |
| return false; | |
| } | |
| } |
| SetSentErrorMessage(true); | ||
| return false; | ||
| } | ||
| int32 energy = energyRaw * 10; |
There was a problem hiding this comment.
Multiplying user-provided *Raw values by 10 can overflow int32 (e.g., large inputs), which would break validation and could set unintended values. Consider bounding the allowed raw input (e.g., check raw <= INT32_MAX / 10 and similarly for negative if allowed) before multiplying.
| } | ||
|
|
||
| int32 energyRawM; | ||
| int32 energym = (!ExtractInt32(&args, energyRawM) || energyRawM * 10 < energy) |
There was a problem hiding this comment.
Multiplying user-provided *Raw values by 10 can overflow int32 (e.g., large inputs), which would break validation and could set unintended values. Consider bounding the allowed raw input (e.g., check raw <= INT32_MAX / 10 and similarly for negative if allowed) before multiplying.
| SetSentErrorMessage(true); | ||
| return false; | ||
| } | ||
| int32 rage = rageRaw * 10; |
There was a problem hiding this comment.
Multiplying user-provided *Raw values by 10 can overflow int32 (e.g., large inputs), which would break validation and could set unintended values. Consider bounding the allowed raw input (e.g., check raw <= INT32_MAX / 10 and similarly for negative if allowed) before multiplying.
| } | ||
|
|
||
| int32 rageRawM; | ||
| int32 ragem = (!ExtractInt32(&args, rageRawM) || rageRawM * 10 < rage) |
There was a problem hiding this comment.
Multiplying user-provided *Raw values by 10 can overflow int32 (e.g., large inputs), which would break validation and could set unintended values. Consider bounding the allowed raw input (e.g., check raw <= INT32_MAX / 10 and similarly for negative if allowed) before multiplying.
The Modify hp/mana/etc admin console commands were just broken. They were treating the argument as both current and maximum, and the net result is that you were always changing only the maximum value.
This fixes it by 1. correctly extracting the proper values separately, and 2. treating the max as optional, since you are usually just wanting to change the current value anyway.
This change is