EVEShip.fit's Dogma Engine
This library calculates accurately statistics of an EVE Online ship fit.
The input are several data-files provided by EVE Online, together with a ship fit. The output are all the Dogma attributes of the ship, its items and the character.
Implementation
This Dogma engine implements a multi-pass approach.
- pass 1: collect all the Dogma attributes of the hull and modules.
- pass 2: collect all the Dogma effects of the hull and modules.
- pass 3: apply all the Dogma effects to the hull/modules, calculating the actual Dogma attribute values.
- pass 4: augment the Dogma attributes with EVEShip.fit specific attributes, that are too complex for the Dogma itself to handle.
Input and output
calculate takes a fit and options, and returns a calculation.
All identifiers are those from the SDE.
Fit
name(optional): name of the fit.ship: the ship being fitted.type_id: its type.mode(optional): type ID of the active mode, for ships that have modes. Whether the mode belongs to the ship is not checked.
items: everything fitted or carried. Each item has:type_id: its type.slot: where the item is.type:high,medium,low,rig,subsystem,service,fighter_tube,fighter_bay,implant,booster,drone_bayorcargo.index: position within that slot type, starting at 0 (implantandboosterstarting at 1). Absent forfighter_bay,drone_bayandcargo.
quantity(optional, default 1): stack size for drones, fighters and cargo. For fighters in a tube, the squadron size.state: requested state;offline,online,activeoroverload.charge(optional): the loaded charge, astype_id.mutation(optional): for mutated items (Abyssal modules, mutated drones, ...).base: type ID of the item before it was mutated.attributes: the rolled value per attribute ID.
fighter_abilities(optional): the abilities a fighter uses, as effect IDs. Absent means the fighter's default abilities.booster_side_effects(optional): the side effects a booster rolled, as effect IDs. Absent means none.spool(optional): for modules whose bonus grows every cycle, how far it has spooled. Only per-second stats use it; volley is always unspooled. Absent means fully spooled.multiplier_bonus: the bonus reached so far; 0.0 is unspooled, 2.125 is +212.5%.
character(optional):skills: level (0 to 5) per skill type ID. A missing skill gives no bonuses.security_status(optional, default 0.0): the pilot's security status, -10.0 to 5.0.
environment(optional): where the fit is.damage_profile(optional, default 0.25 each): incoming damage for effective hitpoints, asem,explosive,kineticandthermal(relative to each other).security(optional, defaulthigh_sec):high_sec,low_sec,null_secorwormhole.reactive_armor(optional, defaultdo_not_adapt): what a Reactive Armor Hardener shifts its resistances towards.do_not_adaptleaves them where EVE shows them,damage_profileshifts towardsdamage_profile, and{"profile": {..}}shifts towards a profile of its own, written the same way asdamage_profile.
incoming(optional): what effects and buffs to apply that come from outside the ship. A calculation reports the same shape asoutgoing: feed one fit's result into another'sincominglinks them up.buffs(optional, default none): buffs to apply, like the ones a command burst hands out.id: which buff, asdbuffCollectionsin the SDE numbers them.value: how strong it is, in whatever the buff's operation reads.
effects(optional, default none): effects aimed at the fit, like a stasis webifier.type_id: the type the effect belongs to; its category decides the stacking penalty.effect_id: which effect, asdogmaEffectsin the SDE numbers them.attributes: the value per attribute ID the effect reads, worked out by the fit that aimed it.
Options
sources(optional, default false): report per attribute what its value was calculated from. Leave it off unless you show it; it makes the calculation several times bigger.
Calculation
ship: result for the ship.mode: result for the mode; absent when the fit has no mode.items: one result per item of the fit, in the same order.character: result for the character.buffs: the buffs that landed, ordered by id. Those ofincoming, plus the ones the fit's own bursts hand out: a fleet boost reaches the ship running it. What is missing lost to another source of the same buff, or the SDE has no such buff.id: which buff, asdbuffCollectionsin the SDE numbers them.value: how strong it is, in whatever the buff's operation reads.
outgoing: what the fit hands to other fits, in the shapeincomingtakes.
Each result has:
-
attributes: per attribute ID, itsbasevalue before effects and its finalvalue. With thesourcesoption, alsosources: every modifier on it, in the order they were applied. Each has:from: where it comes from;typeisship,mode,character,itemorcharge(with theindexintoitems),projected(with theindexintoincoming.effects),skill(with itstype_id) orbuff(with itsid).effect_id: the effect that holds the modifier;nullfor a buff, which has none.source_attribute_id: the attribute on the source that holdsvalue;nullfor a buff, which carries its own strength.operator:pre_assign,pre_mul,pre_div,mod_add,mod_sub,post_mul,post_div,post_percentorpost_assign.value: the value of the modifying attribute, or the strength of the buff.quantity: how many times it counts. A stacking penalised stack is listed once per item instead.penalty: the stacking penalty factor it got, ornullif not penalised.applied: false when the source's state is too low for the effect.
How much each source added is not reported: multiplications compound and stacking penalties depend on order, so there is no single answer.
-
state: the state the item reached, which can be lower than requested. -
max_state: the highest state the item can reach. -
charge: result for its charge, if it has one.
EVEShip.fit's specific attributes
Pass 4 create Dogma attributes that do not exist in-game, but are rather complicated to calculate.
To make rendering a fit easier, these are calculated by this library, and presented as new Dogma attributes.
Their identifier is always a negative value, to visually separate them. What additional attributes exist are defined in EVEShipFit/sde-patched repository.
Validation
validate() checks if the fit violates any rules that would prevent you from flying it in-game.
It returns the violations, grouped by the kind of rule and, within a kind, in the order of items.
An empty list means the fit breaks no rules.
Each violation has:
target: what the rule is about.ship, for what the ship carries as a whole.item, with theindexintoitems.charge, with theindexintoitemsof the item holding it.
rule: the rule, and the values that failed it.typesays which:resource:resourceran out,usedofavailable. One ofcpu,powergrid,calibration,drone_bay,drone_bandwidth,launched_drones,fighter_bay,fighter_tubes,light_fighter_tubes,support_fighter_tubes,heavy_fighter_tubes,cargo_bayorcharge_capacity.slots: more items in theslotrack than the ship has,usedofavailable. One ofhigh,medium,low,rig,subsystem,service,turretorlauncher; the last two are hardpoints a weapon takes on top of its slot.wrong_slot: the item belongs in theexpectedrack.slot_taken: another item of the fit is in this slot too.wrong_slot_index: an implant or booster outside the slot it occupies, which isexpected.subsystem_taken: another subsystem covers the same part of the ship.skill: the character is missingtype_id, or has it atlevelwhere the item asks forrequired.rig_size: a rig of sizeitemwhere theshiptakes another.ship_restricted: the item cannot go on this ship at all.capital_item: a capital item on a ship that is not a capital.max_group:usedofgroup_idarelimit(fitted,onlineoractive), whereallowedmay be. This is what keeps a second propulsion module from running: an afterburner and a microwarpdrive share a group.max_type:usedoftype_idare fitted, whereallowedmay be.charge_group: a charge of a group the module does not take.charge_size: a charge of sizechargewhere themoduletakes another.
Usage
The engine is published for Rust, Javascript and Python; all three calculate the same way.
Each hands over sde.dat once, and every lookup after that happens inside Rust.
An EFT import matches the English names in sde.dat; hand over names.dat too to also match the other languages EVE supports.
How each package is built is explained under Integration.
Rust
The crate is published on crates.io as esf-dogma-engine.
esf-data reads sde.dat.
use ;
use ;
let bytes = read?;
let sde = new?;
let info = new;
let fit: Fit = from_str?;
let calculation = calculate;
// Or if you want to know the source of the effects:
let with_sources = calculate;
// Or if you have a beacon in space (like wormhole effects):
let with_beacon = calculate;
// What EVE would not let you fly:
let violations = validate;
Javascript (WebAssembly)
The WebAssembly variant is published on npm as
@eveshipfit/dogma-engine.
@eveshipfit/sde ships sde.dat in its dist folder; serve or bundle that file.
import init from "@eveshipfit/dogma-engine";
await ;
;
const sde = await .;
const buildNumber = ;
const fit = ;
const calculation = ;
/* Or if you want to know the source of the effects: */
const withSources = ;
/* Or if you have a beacon in space (like wormhole effects): */
const withBeacon = ;
/* Or if you have an EFT, the text format EVE copies a fit to the clipboard in: */
const imported = ;
/* What EVE would not let you fly; it calculates the fit itself: */
const violations = ;
Python
The Python variant is published on PyPI as
eveshipfit-dogma-engine.
The sde extra brings in eveshipfit-sde, which ships sde.dat.
=
=
=
# Or if you want to know the source of the effects:
=
# Or if you have a beacon in space (like wormhole effects):
=
# Or if you have an EFT, the text format EVE copies a fit to the clipboard in:
=
# What EVE would not let you fly; it calculates the fit itself:
=
Fits and calculations are plain dicts, typed with TypedDict in esf_dogma_engine.types.
load_eft, calculate and beacon release the GIL while they work, so a thread pool calculates fits in parallel.
Development
Make sure you have Rust installed.
Next, we need the data-files.
They are Flatbuffers, built by sde-patched and published on npm as @eveshipfit/sde:
sde.datholds everything needed to calculate a fit.names.datholds the type names in the other seven languages EVE supports. It is optional.
English names live in sde.dat, so an EFT-fit written in English imports without it; names.dat is only consulted when a name does not match.
After that, we can run the application.
For example, some attributes of a fit with every skill at L0 except two:
|
It prints a table on a terminal and JSON otherwise; see --help for the rest.
The regression suite reads the same paths; set ESF_SDE and ESF_NAMES to point it elsewhere.
Regression
The engine is locked down by snapshot tests. A case calculates one fit with one set of skills, and compares the result against a stored snapshot in tests/snapshots.
If failures are expected differences, use insta to resolve them:
Integration
Every variant is built from the same Rust crates; the flatc step from Development comes first.
Rust
The engine itself is a plain crate; esf-cli is an example of using it.
Javascript (WebAssembly)
The primary goal of this library is to build a WebAssembly variant that can easily be used in the browser. This means that there is no need for a server-component, and everything can be calculated in the browser.
This is done with wasm-pack:
In the pkg folder is now a NPM module to use.
Python
This is done with maturin:
In the target/wheels folder is now a wheel to install.