Skip to content
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

[Damage api] Custom damage types #284

Merged
merged 11 commits into from
May 11, 2021

Conversation

KingEnderBrine
Copy link
Contributor

First draft of custom damage types.

TL; DR;

Allows to use up to 1152 damage types for modding purposes while sending only 3 extra bytes in most cases.
Still need to add extensions for vanilla EntityStates that store DamageType to later use in DamageInfo.
There are 3 public methods:

  • ReserveDamageType - returns ModdedDamageType enum value that mod can use to add/check with extension methods.
  • AddModdedDamageType - adds reserved damage type to DamageInfo. Can add more than one ModdedDamageType to single DamageInfo. (need to do overloads for some vanilla EntityStates)
  • HasModdedDamageType - checks reserved damage type on DamageInfo (need to do overloads for some vanilla EntityStates)

Implementation details

Vanilla DamageInfos

Obviously, vanilla attacks won't use this system and for them, only 1 extra byte will be sent over the network that will indicate that there are no modded damage types.

Modded DamageInfos

The first thing necessary to understand is that while you can have a lot of unique damage types most of the time only 1-2 will be used per attack. This means you need to send 144 bytes with each attack (for 1152 unique flag values) even if it doesn't have any damage types enabled, or you can invent stupidly complicated compression that will allow you to send a lot less information in most cases, which I did.

There are 3 key pieces in this system:

  • Section
  • Block
  • Value

Section

Section contains up to 8 blocks. When written to NetworkWriter it's converted to a byte with each bit indicating if a corresponding block has a value, that allows to not write blocks that don't have any value. Each block stores info about ModdedDamageTypes within range: from block index * 144 to (block index + 1) * 144 - 1.

Block

Block contains up to 18 values. When written to NetworkWriter it's converted to 1-4 bytes depending on enabled values.
Block is the most complicated thing in this implementation, so it requires an image with a description:
BlockBytes

Value

Byte value storing 8 flags that indicate that corresponding DamageTypes are enabled or not.

What is sent over the network

  • 1 byte - section, indicating what blocks are enabled, if equal to 0 then there is no ModdedDamageTypes and no more info in NetworkReader.
  • 1-4 bytes - block parts, indicating what values are enabled.
  • 1-18 bytes - values, indicating what flags are enabled.
  • Next block bytes if there are any.

Examples

If I were to send DamageInfo with ModdedDamageType with index 12 enabled only 3 extra bytes would be used:

00000001 10010000 00001000

If I were to send 12, 13, and 14 I would still use only 3 bytes:

00000001 10010000 00001110

If I were to send 463, still only 3 bytes:

00001000 10000100 00000001

If I were to send 12 and 463, that would require 5 bytes:

00001001 10010000 00001000 10000100 00000001

It's pretty complicated, so, if you have any questions I will of course answer them.

@KingEnderBrine
Copy link
Contributor Author

Actually, having an array of 36 ints is more memory efficient than Dictionary of Dictionaries with one element. So, I will change how flags stored in the memory.

@KingEnderBrine
Copy link
Contributor Author

Switched to use byte[] to store flags. Seems like it also simplified logic somewhat.

@KingEnderBrine
Copy link
Contributor Author

KingEnderBrine commented May 3, 2021

Since there are quite a few vanilla classes that store damage type in a field and then pass it to the new DamageInfo instance, I've made a few overloads and IL hooks to address that. But there are still some cases that require special handling:

  • BlastAttack - has BlastAttackDamageInfo which is a struct and can't be used in ConditionalWeakTable. Don't really know how to deal with that.
  • DotController - has PendingDamage which is created in DotController.AddPendingDamageEntry() and DamageType is passed as a method parameter. I think I can IL the method that is calling AddPendingDamageEntry since it has a list of created PendingDamages.
  • OverlapAttack - DamageInfo is created in a static method PerformDamage and DamageType is passed as a method parameter. The issue like in DotController, but worse.

There also are a few more cases that I've not covered and they are questionable to do.

Somewhat useful:

  • GlobalEventManager.OnHitAll() - Behemoth item creates new BlastAttack with DamageType from DamageInfo. This looks like better be added.
  • DamageDealtMessage - it is sent from HealthComponent, nothing in vanilla really use DamageType from it, only checks if it's DamageType.Silent.

Things too specific to implement:

  • Some Acrid EntityStates
  • Some Rex EntityStates
  • BloodSiphonNearbyController - not sure what it is, seems like Pillar of Blood from the new Commencement.

@KingEnderBrine
Copy link
Contributor Author

So, I think I've covered everything I've wanted. The only leftover thing is testing, a lot of it. And maybe add commentaries for private methods.

@KingEnderBrine KingEnderBrine marked this pull request as ready for review May 9, 2021 09:20
@KingEnderBrine
Copy link
Contributor Author

I've finished with everything I've wanted to do. I think I've tested everything. There are some places where DamageTypes can be used that I've not covered because I think they are too specific and this API is already a monstrosity of IL hooks, though they can be added later if someone needs them.

@tristanmcpherson tristanmcpherson merged commit d2b71fb into risk-of-thunder:master May 11, 2021
@KingEnderBrine KingEnderBrine deleted the damage-api branch June 2, 2021 16:24
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