Let the outlaw (player killer) state actually wear off again - #956
Open
nolt wants to merge 1 commit into
Open
Conversation
The remaining time of a hero/outlaw state was never counted down, so a player killer stayed a 2nd stage outlaw forever, until /pkclear was used. Two defects worked together in RegenerateHeroStateAsync: - The elapsed time was calculated as _lastRegenerate.Subtract(DateTime.UtcNow), which is negative, because _lastRegenerate is the previous tick. Subtracting it made StateRemainingSeconds grow at exactly the rate time passed. - The method only did anything while StateRemainingSeconds was above zero. Once killed monsters had pushed it to zero or below, the state change was skipped. The countdown now uses the elapsed time with the correct sign, and a remaining time which ran below zero steps the state down and carries the surplus over to the next step, so hunting monsters can drop more than one step at once. Additionally the timings follow the classic behavior again: an outlaw state lasts three hours instead of one, stepping up to the next state restarts that clock, and further kills as a 2nd stage outlaw stack on top of the remaining time. Returning to the normal state also resets the player kill count, which otherwise kept the /pkclear price of kills the player already atoned for. Reducing the remaining time by the level of a killed monster is unchanged and stays available on every map.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The remaining time of a hero or outlaw state was never counted down. A player killer therefore
stayed an outlaw forever — in practice a permanent 2nd stage outlaw that only
/pkclearcouldremove — no matter how long the player stayed online or how many monsters they hunted. This fixes
the countdown and restores the classic timings around it.
The symptom
becomes a 2nd stage outlaw.
the drop and experience penalties stay, and
Character.StateRemainingSecondsin the databasekeeps growing instead of shrinking.
What did work: escalating into the states, the
IUpdateCharacterHeroStatePlugInnotification tothe observers, and
/pkclear, which is why this stayed unnoticed for so long. Our servers arepopulated with offline/bot players which occasionally kill each other, and every single one of
them accumulated a permanent outlaw state.
Root cause
Two defects in
Player.RegenerateHeroStateAsync()(src/GameLogic/Player.cs), which is calledfrom
RegenerateAsync()on the recovery timer:this._lastRegenerate.Subtract(DateTime.UtcNow). The fieldholds the previous tick, so the result is negative — despite the variable being named
secondsSinceLastRegenerate. Subtracting it madeStateRemainingSecondsgrow by exactly theelapsed time, so the countdown stood still (in net terms) forever.
if (currentCharacter?.StateRemainingSeconds > 0). Killingmonsters reduces the remaining time by the monster's level
(
AttackableNpcBase.OnDeathAsync()), which can push it to zero or below between two ticks.From that moment on, the guard was false and the state change was never reached again.
Together they meant that the state could only ever escalate.
git blamedates both todb15dea508(2021-11-13); nothing else in the code base lowersCharacter.Stateexcept/pkclearand the GM command/pk.The fix
RegenerateHeroStateAsync()is rewritten with early returns:DateTime.UtcNow.Subtract(this._lastRegenerate).surplus over into the next step, so a hunting spree can drop more than one step at a time.
HeroState.New(a freshly created character, which has no running timer) is now excludedexplicitly. Previously the
> 0guard did that by accident, and without the guard such acharacter would have been promoted towards
HeroState.Hero.HeroState.Normalalso resetsPlayerKillCount, which otherwise kept charging the/pkclearprice for kills the player had already atoned for.The timings follow the classic (pre-Season 9) behavior again, where they were wrong before:
PlayerKillerStateDuration).which can no longer escalate the state (i.e. as a 2nd stage outlaw) stack on top of the
remaining time. Three kills in a row therefore take 3 h + 3 h + 3 h to wear off, and every
further kill adds another three hours.
Math.Maxmakes sure a kill can never shorten an already longer remaining time.HeroStateDuration).Reducing the remaining time by the level of a killed monster is left as it is, and deliberately
stays available on every map and in every outlaw state: that matches the classic rule, where the
penalty was shortened by the monster's level in seconds anywhere, long before later seasons
restricted it to Vulcanus. The only change in
AttackableNpcBaseis a clarifying comment.Since the timer is only advanced while the player is in the game —
_lastRegenerateis reset whenentering the world — offline time still does not count towards wearing the state off.
Testing
dotnet build src/GameLogic/MUnique.OpenMU.GameLogic.csproj -p:ci=truein themcr.microsoft.com/dotnet/sdk:10.0container: 0 errors, and no analyzer warning in either ofthe two touched files.
were reviewed by reading the code only.
related test is
PKClearChatCommandPlugInTest.Possible follow-ups (not in this change)
GameConfiguration) instead ofbeing constants in
Player.WarpActiondoes not check the hero state at all, so an outlaw can still use the warp list. Theclassic rule forbids warping as a 2nd stage outlaw (the original client protocol even has a
MAPMOVE_FAILED_MURDERERresult), and later seasons charge 50x the zen instead.