ModiBuff
Buff/Debuff/Modifier library focused on feature set and performance, while maintaining 0 GC. Fully pooled
Install / Use
/learn @Chillu1/ModiBuffREADME
- What is this?
- Features
- RoadMap
- Benchmarks
- Requirements
- Installation
- Usage
- FAQ
- Examples
- Differences to ModiBuffEcs and Old
What is this?
This zero dependency, engine-agnostic library was made to make a standardized powerful system that allows for manipulation of effects on entities.
It focuses on Feature Set, Performance and Ease of use, in that order.
Library goals:
- Capable of creating very sophisticated modifiers
- Modifiers being fast
- Creating them easily
The library is split into two core parts:
ModiBuff is the core backend part that handles all the modifier logic and is mostly unopinionated when it comes to the game logic.
Meanwhile ModiBuff.Units is a fully featured implementation of the library, that showcases how to tie the library into a game.
Note: The library is currently in development, and it will most likely encounter breaking API changes.
Why do I need this?
The vast majority of games make their own buff/debuff systems, to fit their game logic. Examples of this are: Dota 2, League of Legends, Path of Exile, Diablo, World of Warcraft, etc.
While this is a great approach, it's also very time consuming, and requires a fair amount of research to design well.
This library solves that, but also allows for more complex and deeper modifiers than the aforementioned games.
Features
- No GC/runtime heap allocations (fully pooled with state reset)
- Low memory usage (2-5 MB for 10_000 modifiers)
- Fast effects 10_000 damage modifiers in 0.24ms
- Fast iteration 10_000 interval modifiers & 10_000 units in 1.37ms
- Easy high level API recipes
- Instance Stackable modifiers (multiple instances of the same modifier type)
- Effects on actions
- Init
- Interval
- Duration
- Stack
- Callbacks (any user logic, support mutable and serializable state inside)
- Unit callbacks/events (ex. When Attacked/Cast/Killed/Healed/Stunned/Silenced, On Attack/Cast/Kill/Heal)
- Effect callbacks
- Custom signature callbacks
- Effect implementation examples
- Damage (& self damage)
- Heal
- Status effects (stun, silence, disarm, etc.) & immunity
- Multi instance Status effects impl (2 same stuns, 2 different sources, unique timers)
- Dispel status effect(s)
- Add stat (Damage, Heal)
- Actions (Attack, Heal, Cast, etc.)
- Centralized Effect
- And more, see the rest
- Internal Effects
- Special Applier (another Modifier)
- Applying Applier Modifiers as Applier Modifiers
- Modifier Action (refresh, reset stacks, custom stack)
- Remove (remove modifier)
- Revert Action
- Special Applier (another Modifier)
- Modifierless-Effects
- Meta & Post effect manipulation (ex. lifesteal)
- Stack Logic
- Custom (callback/interval/duration triggers)
- Stack timer
- Independent stack timers
- Revertable independent stacks
- Condition implementations (checks)
- Chance 0-100%
- Cooldown
- Charges (multiple use with cooldown)
- Health/Mana cost, flat & percent
- General:
- Stat (health/mana/damage) >/=/< than X
- Stat is full/empty
- Has LegalAction (can attack, cast spell, move, etc.)
- Has StatusEffect (stunned, silenced, disarmed, etc.)
- Has Modifier
- Applier Modifiers
- OnAttack
- Cast
- Fully revertible effects
- Manual modifier generation (for full control)
- Open generic serialization (of all mutable state and id's)
- System.Text.Json
Short showcase
We'll start off super basic, this is a modifier that deals 5 damage on target unit when added. Recipe explanations are down in the Usage section.
Add("InitDamage")
.Effect(new DamageEffect(5), EffectOn.Init);
Let's bring out the heavy stuff. These will be very complex, but will show the capabilities of the library. So don't be scared off by the code complexity, this is (almost) as complex as it gets. Only limited by imagination.
Add("DamageOnStun_HealOnAnyNotStunStatusEffectRemoved_StackDamageWhenLongStunned")
.Tag(TagType.CustomStack | TagType.CustomRefresh)
.Interval(2f).Refresh()
.ModifierAction(ModifierAction.ResetStacks, EffectOn.Interval)
.Stack(WhenStackEffect.Always)
.ModifierAction(ModifierAction.Stack | ModifierAction.Refresh, EffectOn.CallbackEffect)
.CallbackEffect(CallbackType.DamageChanged, effect => new DamageChangedEvent(
(source, newDamage, deltaDamage) =>
{
if (deltaDamage >= 1f)
effect.Effect(source, source);
}))
.Effect(new DamageEffect(5, true, StackEffectType.Add, 2), EffectOn.Stack | EffectOn.CallbackEffect2)
.CallbackEffect(CallbackType.StatusEffectAdded, effect => new AddStatusEffectEvent(
(target, source, duration, statusEffect, oldLegalAction, newLegalAction) =>
{
if (statusEffect.HasStatusEffect(StatusEffectType.Stun))
effect.Effect(target, source);
}))
.Effect(new HealEffect(5, HealEffect.EffectState.ValueIsRevertible, StackEffectType.Add, 2),
EffectOn.Stack | EffectOn.CallbackEffect3)
.CallbackEffect(CallbackType.StatusEffectRemoved, effect => new RemoveStatusEffectEvent(
(target, source, statusEffect, oldLegalAction, newLegalAction) =>
{
if (!statusEffect.HasStatusEffect(StatusEffectType.Stun))
effect.Effect(target, source);
}));
It's a mouthful, but if we split up the effects it's easy to understand.
- Every time a stun status effect gets added, we deal 5 damage to the unit.
- Every time a non-stun status effect (ex. freeze, root) gets removed (either by dispel, or duration), we heal the unit for 5.
- Every time the source unit changes its damage value, we add 2 damage and heal value stacks to effects above, each time.
- Every 2 seconds, we reset the stacks on above damage and heal effects
- If the unit gets extra damage, it refreshes the 2 seconds stack timer (so the stacks don't get removed)
RoadMap
| V0.4.0-V0.?.0 | V1.0.0 | |-------------------------------------------------|----------------------------------------------------------------| | GenId based<br/>modifier stacking | Fully released open source<br/>game using ModiBuff at its core | | Proper game samples<br/>(Godot and maybe Unity) | 98% of game mechanics<br/>implementable | | Improved Appliers API? | ... |
Benchmarks
TL;DR: It's fast. Takes 1ms± to add & update 10000 modifiers every frame.
BenchmarkDotNet v0.13.6, EndeavourOS
Intel Core i7-4790 CPU 3.60GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET SDK 6.0.120
.NET 6.0.20 (6.0.2023.36801), X64 RyuJIT AVX2
N: 10_000
Delta: 0.0167 * N
Add/Apply/Update Modifier table
Pre-allocated Pools
| Library | NoOp*<br/>(1 unit) | Apply<br/>InitDmg<br/>(1 unit) | Apply<br/>InitStackDmg<br/>(1 unit) | Apply Multi<br/>instance DoT | |-------------------------------------------------------|--------------------|--------------------------------|-------------------------------------|------------------------------| | ModiBuff (this) | 0.18ms, 0 B | 0.26ms, 0 B | 0.44ms, 0 B | 1.01ms, 0 B | | ModiBuffEcs | ? | 1.02ms, 0 GC | ? | X | | Old | ? | 21.4ms, 24 GC | ? | X |
| Library | Update DoT**<br/>(10_000 units, N:1) | Update Instance<br/>Stackable DoT | |-------------------------------------------------------|--------------------------------------|-----------------------------------| | ModiBuff (this) | 1.96ms, 0 B | 0.13ms, 0 B | | ModiBuffEcs | 0.44ms, 0 B | X | | Old | ? | X |
New Modifier/Pool table
| Library | DoT pool rent | DoT pool<br/>reset return | |-------------------------------------------------------|---------------|---------------------------| | ModiBuff (this) | 0.04ms, 0 B | 0.18ms, 0 B | | ModiBuffEcs
Related Skills
node-connect
352.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
