Skip to content

Conversation

@felipecastagnarodecarvalho
Copy link
Contributor

All Sadhu Skills:

  • Enira
  • Moksha
  • Patati
  • Possession
  • Prakriti
  • SpiritExpert
  • Tanoti

Also there was an implementation of the art Spirit Expert: Wandering Soul which creates a dummy that acts by himself.

Copy link
Member

@exectails exectails left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I took a quick glance and I noticed that some of it still seems a bit prototypical, and I also have some questions and concerns before we continue. Please refer to my comments.

if (!ZoneServer.Instance.Data.MapDb.TryFind(mapId, out var map))
throw new ArgumentException("Map '" + mapId + "' not found in data.");

if (this.IsOutOfBody())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is a weird place to check for out of body to prevent warps, because what if we do want to warp? This special behavior also isn't documented anywhere, so anyone who might try to warp someone will have to dig deep into the warp code to figure out why it's not working.

Additionally, creating explicit checks for very specific conditions within general purpose functions is not ideal, because it's not extensible. An alternative idea that I'm just going to throw out there might be to add a warp lock, which you can set for these buffs you're checking in IsOutOfBody, which will generalize the whole approach, make it somewhat self-documenting, save you from having to do that manual buff check, and have it be reusable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You marked this as resolved, but the issues are still there 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warping prevention removed!

{
LearnSkill(character, SkillId.Hammer_Attack);
LearnSkill(character, SkillId.Hammer_Attack_TH);
LearnSkill(character, SkillId.Sadhu_OutofBodyCancel);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you tell me why you removed this? It's part of the official data, which there probably is a reason for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skill is not being used anymore and has no purposes. Sadhu has been remake I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is based on official data though, and removing it creates a dissonance between server and client. Unless this causes issues, it should stay this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This WAS from the official data, but it got outdated, since the last changes on sadhu, the skill is not being used anymore.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That may be true, but it is in fact still part of the job data as of the last time I checked, which this script is based on.

Removing this makes updating the data more challenging, because you have to remember not to add this skill. Potential automated tools for this purpose would also have to be changed not to re-add it. And if it is actually still used for something without our knowledge, we would rather not remove it. Not to mention that any kind of backwards compatibility we might want to introduce would potentially require this line to be re-added.

For these reasons I asked whether there are any issues with leaving it as is, because that is the simplest solution for my concerns, unless we have to conditionally remove it for some reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is skill is not being used anywhere.
The class got reworked at some point and the skill got removed.

"Sadhu_OutofBodyCancel" => To cancel from being out of Body you now need to press the use the same skill you used first time.

image

This image shows the actual behavior.

if (!ZoneServer.Instance.Data.MapDb.TryFind(mapId, out var map))
throw new ArgumentException("Map '" + mapId + "' not found in data.");

if (this.IsOutOfBody())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You marked this as resolved, but the issues are still there 🤔

@felipecastagnarodecarvalho
Copy link
Contributor Author

@exectails this PR is ready to be reviewed again.
I have put some effort into improving the code.

Copy link
Member

@exectails exectails left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate your efforts for improving the code, though some concerns have not been cleared yet. Still, I might consider merging the PR soon, assuming you're still around and able to maintain the changes, fixing issues in a timely manner should they arise.

Given the nature of this class, the code is unfortunately not very straightforward to follow in its current form, and I suspect potential changes or improvements to it would be simpler to accomplish if one were to rewrite the skills. As their developer, I assume you would have a better overview of them and their behavior.

/// </summary>
/// <param name="character"></param>
/// <param name="targetCharacter"></param>
public static void ZC_UPDATED_PCAPPEARANCE(Character character, Character targetCharacter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've added a few overloads like this, where you differentiate between a character and target, but several of them don't seem to serve a real purpose. Like this one, where all the information is taken from the target, but the source of the broadcast is a different character. This could lead to confusion over what information the caller is expected to give. It could also lead to visual bugs, because if someone else is in range of the target character, but not the character, they might not receive the packets you're broadcasting. These senders should get reevaluated and cleaned up to only make this distinction if that is actually necessary and desired.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved.

/// <param name="propertyList"></param>
public static void ZC_OBJECT_PROPERTY(IZoneConnection conn, long objectId, PropertyList propertyList)
{
// TODO: Find a better way to check this - This was done to avoid sending packets for dummies (while receiving buffs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recall if I mentioned this before, but the idea behind a dummy anything is usually that you can use them like the real thing without thinking twice about it. As such, if you have a dummy character, would it not be easier to also have a dummy connection, to be able to "send" them packets without actually doing so? Or is there a reason I'm missing for why a null check might be preferable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we would need a fake connection for dummies.
A connection means that a client exists behind it, right?
For the other side, we can send packets to the dummy map:

dummy.Map.Broadcast(packet);

If in the future the dummy plays an animation (when the dummy is selling equipament improvements and it needs to play the refinement animation, for example) we can use this without problem.


I have move this check to InitEvents() method on CharacterProperties.cs:

		/// <summary>
		/// Sets up event subscriptions, to react to actions of the
		/// character with property updates.
		/// </summary>
		private void InitEvents()
		{
			// This was done to avoid sending packets for dummies (while receiving buffs)
			if (this.Character.Connection == null)
				return;
			
			[...]
		}

// [i373230] Maybe added earlier
// [i373230] Maybe added earlier [Updated at 28/07/24]
{
packet.PutByte(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were you able to confirm that this is actually a short and not two bytes? Changing data like this could easily lead to confusion at a later point when we try to figure out what these bytes do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes reverted.

/// </summary>
/// <param name="entity"></param>
/// <param name="target"></param>
public static void ZC_MSPD(ICombatEntity entity, ICombatEntity target)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible you're misunderstanding what this sender does? While this overload allows you to treat the two values as distinct entities, the only difference is who gets notified about the change. Generally speaking, you'd want the source entity to also be the target entity, so the other clients in range of the target get informed about the target's new speed--not the clients who are close to the source, who might not even be in visible range of the target.

/// <param name="creator"></param>
private async Task MovePad(Pad pad, ICombatEntity creator)
{
// Forward and back, hovering a moment in between.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says forward and back but that's not what's happening here. Could you clarify whether the error is in the code or the comment? If it's the comment that's incorrect, and the pad only moves forward, this comment could safely be removed, as the method appears clear-cut and is commented appropriately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong commentary. Fix applied.

/// <summary>
/// Returns true if the DummyCharacter has Owner
/// </summary>
public bool hasOwner => Owner != null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still some convention violations, such as curly braces on the same line as a foreach or this camelCase property. If you're using Visual Studio, auto-formatting should solve some of these issues and the style checker should inform you about the rest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

if (this.Entity is Character character)
if (this.Entity is Character character && character is not DummyCharacter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're mixing conventions of checking for dummy characters before calling senders and inside them, as well as mixing type and null checks. I do understand what led to this, but it would be better to stay consistent. Better yet would be to not need this distinction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed to:

if (this.Entity is Character character && this.Entity is not DummyCharacter)
	Send.ZC_COOLDOWN_CHANGED(character, cooldown);

But it's essentially the same. It's needed since the dummy casts a skill.


Send.ZC_MSPD(this.Entity);
}
else if (this.Entity is Character character && character is DummyCharacter dummyCharacter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would presume this check to be either incorrect or redundant. Am I right in that assessment? You're checking whether the entity is a character and a dummy character, which would mean the code applies only to dummy characters, as only they are both. Though in that case it would be enough to check for the dummy character type. And if you wanted it to apply to both, a check for character would be enough, as the dummy inherits character iirc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

{
foreach (var character in _characters.Values)
character.Connection.Send(packet);
character.Connection?.Send(packet);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a huge fan of this change, as it introduces a new type of character into the system that did not exist prior and might not be handled correctly in all cases: a character without connection. Before this, all characters could be presumed to have a connection during their entire session, from when they connect to when they disconnect. If that's no longer the case, we'd have to make sure that any access to the connection property, as well as information behind that, such as accounts, are properly checked before they're accessed.

While I recognize that this only applies to the Sadhu dummy characters right now, and that everything may very well work as intended for the moment, we will inevitably use the cloning methods you introduced for other purposes and other systems are going to have to work around these dummy characters, regardless of where they came from.

With that in mind, have you made sure that this radical change won't cause any issues in the existing code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit confused for me.
Last time we talked you said that it would be better to keep using the on going Characters instead of creating a new dictionary:

private readonly Dictionary<int, Character> _dummiesCharacter = new();

That what I did last time and I have refactored to keep using the current dictionary.
I am not sure if this approach will work still with vending dummies character but it should!
It would require a few changes but mainly it's the same.

{
LearnSkill(character, SkillId.Hammer_Attack);
LearnSkill(character, SkillId.Hammer_Attack_TH);
LearnSkill(character, SkillId.Sadhu_OutofBodyCancel);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That may be true, but it is in fact still part of the job data as of the last time I checked, which this script is based on.

Removing this makes updating the data more challenging, because you have to remember not to add this skill. Potential automated tools for this purpose would also have to be changed not to re-add it. And if it is actually still used for something without our knowledge, we would rather not remove it. Not to mention that any kind of backwards compatibility we might want to introduce would potentially require this line to be re-added.

For these reasons I asked whether there are any issues with leaving it as is, because that is the simplest solution for my concerns, unless we have to conditionally remove it for some reason.

@felipecastagnarodecarvalho
Copy link
Contributor Author

@exectails I have update this PR with:

  • Correct OOBE animation for the dummy.
  • Improvements on the AI for the cloned character (can be used if you have [Arts] Spirit Expert: Wandering Soul).
  • Fixed all mentioned commentaries.
  • Fixed a bug that the monster wouldn't be alerted by the cloned character when taking damage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants