Skip to main content

Crate bevy_society

Crate bevy_society 

Source
Expand description

§bevy_society

Living societies for Bevy: personalities, moods, needs, memories, relationships, opinions, rumors, reputation and factions; utility-AI daily lives; Rhai-scripted triggers; and dialogue and barks through bevy_speech_bubbles.

Every model is distilled from social-psychology and game-AI literature (see docs/research/) and every behaviour is deterministic for a given seed.

§Quick start

use bevy::prelude::*;
use bevy_society::{
    Autonomous, Home, Person, Place, PlaceTags, SocietyClock, SocietyDefs, SocietyPlugins,
    SocietySettings,
};

let defs = SocietyDefs::starter().expect("the starter pack is valid");
// A headless app has no frame clock, so `Time<Fixed>` never advances on its own. Without this the
// society takes zero ticks however many updates you run.
let step = std::time::Duration::from_secs_f64(1.0 / 64.0);

let mut app = App::new();
app.add_plugins((MinimalPlugins, TransformPlugin))
    .insert_resource(bevy::time::TimeUpdateStrategy::ManualDuration(step))
    .insert_resource(SocietySettings::default())
    .insert_resource(defs);
// `DialoguePlugin` needs a speech-bubbles plugin over a camera, which this snippet has not.
let plugins = SocietyPlugins.build();
#[cfg(feature = "dialogue")]
let plugins = plugins.disable::<bevy_society::DialoguePlugin>();
app.add_plugins(plugins);

let well = app
    .world_mut()
    .spawn((Name::new("well"), Place { capacity: 6 }, PlaceTags(vec!["gathering".into()]), Transform::default()))
    .id();
for name in ["Bram", "Alys", "Corin"] {
    app.world_mut()
        .spawn((Person, Autonomous, Name::new(name), Home(well), Transform::default()));
}

for _ in 0..600 {
    app.update();
}
// The society advanced: this line is what proves the fixed step was pinned.
assert!(app.world().resource::<SocietyClock>().tick > 0);

The same code is examples/quickstart.rs, and README.md carries it too — tests/readme.rs checks the README’s text so the three cannot drift.

Spawn people with Person, give them somewhere to go with Place, hand in the definitions and read what happens. The example needs no renderer; with the default dialogue feature it also needs SpeechBubblesPlugin (see the note at the end of the example):

use bevy::prelude::*;
use bevy_society::{
    Autonomous, Home, Person, Place, PlaceTags, SocietyDefs, SocietyPlugins, SocietySettings,
};

fn main() {
    let defs = SocietyDefs::starter().expect("the starter pack is valid");
    let mut app = App::new();
    app.add_plugins(MinimalPlugins)
        .insert_resource(SocietySettings::default())
        .insert_resource(defs)
        .add_plugins(SocietyPlugins);

    let well = app
        .world_mut()
        .spawn((Name::new("well"), Place { capacity: 4 }, PlaceTags(vec!["gathering".into()])))
        .id();
    for name in ["Bram", "Alys"] {
        app.world_mut().spawn((
            Person,
            Autonomous,
            Name::new(name),
            Home(well),
            Transform::default(),
        ));
    }

    for _ in 0..600 {
        app.update();
    }
    // With the default `dialogue` feature the host must also add
    // `bevy_speech_bubbles::SpeechBubblesPlugin::<YourCamera>`, or `finish()` panics.
    // `no_run`: a headless `App::update` never calls `finish()`, so this only compiles.
}

§Plugins

SocietyPlugins installs the whole society. Each member also works alone after SocietyCorePlugin: the mind plugin for emotions, mood, needs and memories, the relationships plugin for ties, BeliefsPlugin for opinions and rumors, GroupsPlugin for factions and status, the behaviour plugin for daily life and social exchanges, RulesPlugin for Rhai triggers, then the feature-gated social-field, steering and dialogue plugins.

§Ticks, snapshots and scripts

The society advances on society ticks: one every SocietySettings::tick_secs of fixed-step time, with every rate expressed per game-day. The systems run in the order of SocietySystems — Clock, Perceive, Mind, Decide, Interact, Propagate, Relate, Rules — and only Clock runs on every fixed step.

Scripts see the society as it stood at the start of the tick, through a snapshot built in Perceive; a script’s own writes go to a queue. Those writes are applied once per tick, after Rules, by one exclusive system, so no script can change what another script in the same tick reads. Game-written input messages (PlantRumor, Interact, FireTrigger) are drained on every fixed step, not only on society ticks, so a message written between ticks is never dropped.

§Guides

docs/guide/getting-started.md, concepts.md, authoring.md, scripting.md and dialogue.md; docs/research/ for the papers behind every formula; docs/DEMOS.md for the capture and GIF.

Modules§

model
Engine-free model math: response curves, the personality-to-mood baseline, opinion dynamics, recall and forgetting, rumor spreading, structural balance and the scarcity price on a good. Nothing here names a Bevy type; every function is a pure function of its arguments, so each formula is tested against its paper’s worked values.

Structs§

AcceptDef
Terms of the acceptance probability (Deffuant et al. 2000 and Byrne 1961 inform the defaults; the plan records the numbers).
ActiveEmotion
One active emotion. toward names the person it is about, when there is one.
ActivityDef
Something a person can do, chosen by utility scoring.
ActivityEnded
A person stopped doing an activity (plan D7).
ActivityId
Index of an activity, in definition order. IDLE_ACTIVITY is always ActivityId(0).
ActivityStarted
A person began doing an activity (plan D7).
Appraise
Asks for an emotion to be felt by person: the game, a deed, an interaction or a rumor writes it and the mind plugin merges it into the person’s Emotions. Appraisals are applied on every fixed step, not only on society ticks, so a message is never dropped between ticks.
Autonomous
Marker: this person chooses its own activities. A player-controlled person is a Person without this marker.
Away
Marker: this person is out of the town and expected back — on an expedition, a voyage, a posting.
AwaySince
When an Away person left: the game-day, written by the crate when Away is inserted (unless the host or a restore already inserted it). Read it for how long someone has been gone; do not insert it without Away.
BehaviorPlugin
Runs daily life and social exchanges (plan D7): autonomous people choose activities, travel, arrive, satisfy needs, and exchange with each other.
BeliefsPlugin
Runs opinions, rumors and reputation: applies PlantRumor on every fixed step, then, on society ticks, pulls every person’s opinions toward the group mean it is in contact with. Requires crate::SocietyCorePlugin.
BoundPlaces
The places bound to one person by name, for ActivityTarget::Bound.
CellFrame
An axis-aligned world↔cell mapping: where the grid starts, how wide a cell is, and how many cells there are.
ChannelDef
Per-channel behaviour. The caller supplies one of these per channel, typically from its own config.
Chronotype
How far a person’s body clock runs behind the wall clock, in hours: positive is an owl, negative a lark, 0.0 (the default) a person whose body keeps the village’s time.
ClockSave
The society clock and the tick accumulator, as they stood at capture.
Commotion
Raise the commotion field at a point on the society plane.
Consideration
One scoring term: an input mapped through a curve.
ConversationRuleDef
Chooses the conversation for a TalkTo. The highest priority matching rule wins.
Deceased
Marker: this person is dead, and stays dead.
Decision
Why one person chose the activity it chose, at one tick.
DecisionTrace
The decision trace: a fixed-capacity ring of the most recent decisions.
DeedDef
A deed a person can do to another. Witnesses judge it by their opinion of topic.
DeedId
Index of a deed, in definition order.
DefsFile
Everything a host hands in, as one RON document. Every list is optional.
Destination
Where a travelling person is walking to, on the society plane.
DialoguePlugin
Runs society conversations and barks through bevy_speech_bubbles. Add it after SocietyPlugins’ core, and add SpeechBubblesPlugin::<YourCamera> to the app: this plugin checks for it in finish().
DialogueProblem
One definition that names a conversation or node the DialogueScript does not define.
Doing
The activity a person is currently performing.
DoingSave
The activity a person was performing at capture.
Dormant
Marker: this person is out of its daily life for now — posted on a wall, sheltering indoors, away on an expedition, fleeing, or in a conversation with the player.
EconomyPlugin
Goods, stock and prices.
EffectsDef
The effects of one interaction outcome.
EmotionSave
One active emotion, with the person it is about as a PersonKey.
Emotions
The emotions a person currently feels.
EndActivity
The host asks for a person’s current activity to end, without dormancy.
FactionDef
A faction.
FactionId
Index of a faction, in definition order.
FactionStat
Per-faction aggregates, indexed by FactionId (plan D6). Recomputed each tick by the groups system.
FactionStats
All factions’ aggregates (plan D6). Index i is FactionId(i).
FastForward
What a fast_forward did.
FieldsSave
Every social grid’s contents: the crowd grid’s channels, then one entry per faction grid.
FireTrigger
A named event the game fires at the society (plan D8): the Custom triggers with this name run, with actor and target bound when the game names them.
GoodDef
A tradeable quantity. Not a currency: there is no exchange rate and no Good is worth another.
GoodId
Index of a good, in definition order.
GroupsPlugin
Plugin for factions, standing and faction aggregates (plan D6). Runs in SocietySystems::Relate after the tie labels are refreshed, so standing reads settled ties. Requires SocietyCorePlugin.
Home
The home of a person, used by the Home activity target.
HostClock
The host’s game time, in days. Write it every frame; the crate never inserts it.
Interact
Request that actor performs the named interaction on target.
InteractionBarks
Bark lines for an interaction. Templates may use {self}, {target}, {subject} and {place}.
InteractionDef
A social exchange between two persons.
InteractionId
Index of an interaction, in definition order.
InteractionResolved
A social exchange finished (plan D7).
Knowledge
Everything a person knows about rumors, sorted by RumorId (plan D5).
Known
What one person knows about one rumor (plan D5).
Memberships
The factions a person belongs to, sorted by FactionId (plan D6).
Memories
A bounded list of memories, at most memory_capacity (default 64).
Memory
One remembered event.
MemorySave
One remembered event.
Mood
Pleasure, arousal and dominance, each in [-1, 1] (Russell 1980, DOI 10.1037/h0077714; the Mehrabian 1996 mapping this crate uses is DOI 10.1080/00049539608259510, the one model::pad implements).
MoodRates
Rates of the mood update, per game-day (plan D3 defaults).
NamesSave
The names of every definition list, in definition order.
NeedDef
A need. Level 1 is satisfied, 0 is desperate.
NeedId
Index of a need, in definition order.
Needs
Need levels, indexed by NeedId. Level 1 is satisfied, 0 is desperate.
NextPersonId
Next PersonId to hand out. Ids follow spawn order.
NodeEffectDef
Runs a script when a dialogue node is entered.
NormViolated
Written when a witness judges an actor’s deed harshly enough to count as a norm violation (plan D5): the witness’s approval is below −0.5.
Obstacle
Something round and solid that walkers must go around, in metres on the society plane.
Opinion
One opinion on one topic (plan D5). value is in [-1, 1], stubbornness in [0, 1].
Opinions
A person’s opinions, indexed by TopicId (plan D5). Sized to the defs by size_person_state.
Perception
Per-tick perception: positions and neighbourhoods, indexed by position in PersonIndex::sorted. Neighbour lists are ascending by index and exclude the person itself.
Person
Marker of a simulated person. Adding it gives the person every per-person component.
PersonDef
One authored person.
PersonDied
A person died: written once, when Deceased is inserted.
PersonId
Stable, deterministic id of a person, assigned in spawn order.
PersonIndex
Persons in spawn order, with lookup by id. Maintained by the add and remove observers only.
PersonKey
The host’s own stable identity for a person, written by the host before a save is taken.
PersonPreview
What a preview did to one person.
PersonReturned
A person came back: written once, when Away is removed.
PersonSave
Everything the crate keeps per person, as plain fields.
Personality
The five traits, each in [0, 1]; default 0.5 on every trait.
PersonalityDef
A person’s five traits, each in [0, 1]. A trait left out is 0.5.
Place
A place people can go to. capacity limits how many persons may be doing an activity there.
PlaceDef
A place: a named spot with a capacity and tags.
PlaceDependent
One person who depends on a place: it is their home or one of their bound places.
PlaceKey
The host’s own stable identity for a Place, written by the host before a save is taken.
PlaceTags
Tags that let activities find a place (ActivityTarget::Place(tag)).
Placed
What PlacementFile::spawn made, in file order.
PlacementFile
A list of places and persons, as one RON document. Both lists are optional.
PlantRumor
Game-facing request to start a rumor about subject’s deed, told by origin (plan D5). An unknown deed name is logged and ignored.
Preview
What preview_forward saw.
Prices
The price of each good, in definition order.
RelationshipsPlugin
Plugin for the directed-tie model (plan D4). Runs in SocietySystems::Relate, after SocietyCorePlugin.
ReportDeed
The host reports a deed the society could not see, naming the people who saw it.
RestoreReport
What restore did beyond the people it restored.
RngSave
The one RNG’s exact position: its seed, its stream and its offset in words.
RulesPlugin
Runs triggers (plan D8). Every other plugin that observes an event pushes a trigger event onto the trigger queue, and the game fires custom events with FireTrigger. Requires crate::SocietyCorePlugin.
Rumor
One rumor about one person’s deed (plan D5). valence is the deed’s harm: negative means damaging to the subject.
RumorBook
Every rumor in the society, in creation order (plan D5). RumorId(i) addresses rumors[i].
RumorId
Index of a rumor in the RumorBook (plan D5).
RumorLearned
Written when a person first hears a rumor by gossip (plan D5).
RumorSave
One rumor, with every person reference as a PersonKey.
ScoredOption
One option a decision weighed: the activity, the score it reached, and why it lost or won.
ScriptEngine
The Rhai engine every runtime script runs on, with the ctx / fx API registered (plan D8).
ScriptError
A script that failed: the authored path that names it and what went wrong (plan D8).
ScriptEvent
A named event a script emitted with fx.emit (plan D8).
ScriptId
Index of a compiled script inside SocietyDefs, in compile order.
ScriptSyntaxError
Where and why a script does not parse, from check_script.
SignalDef
A host signal. The crate never changes its value; the host sets it.
SignalId
Index of a host signal, in definition order.
Signals
Host-written signal values, indexed by SignalId. The crate only reads them; a value the host never wrote is 0.0.
SkillDef
A named capability. Level comes from personality, not from stored state.
SkillId
Index of a skill, in definition order.
SocialFieldPlugin
Installs the social fields: the crowd presence and commotion grids, plus one presence grid per faction. They are deposited into and evaporated every tick. Requires crate::SocietyCorePlugin and is included in SocietyPlugins when the fields feature is on.
SocialFieldSettings
Where the fields sit in the world and how they behave. Changing it rebuilds the fields empty.
SocialFields
The grids that daily-life options read. Built from SocialFieldSettings and the faction count, and rebuilt empty whenever the settings change or the faction count differs. Not Reflect, because bevy_stigmergy’s grids are plain data without it.
SocialMover
Opt-in walking for a person. Add it to a Person that should be steered toward its Destination; a Person without it is moved by the game.
SocialVelocity
The velocity last computed for a SocialMover, in metres per second on the plane.
SocietyBark
A line a person says aloud, or thinks, for the game to show.
SocietyClock
The society clock. now_days is the continuous game time in days; dt_days is the length of one society tick in days.
SocietyCorePlugin
Persons, ids, clock, RNG, sets and perception. Add it before every other society plugin, or use crate::SocietyPlugins, which does.
SocietyDefs
Compiled definitions: the resource the simulation reads. Default is the empty set plus the reserved idle activity.
SocietyPlugins
Every society plugin, in dependency order. Add it to an app with DefaultPlugins and the host’s own systems; disable a member with .disable::<…>() to replace it.
SocietyRng
The only randomness in the crate: a ChaCha8 stream seeded from SocietySettings::seed.
SocietySave
A whole society, as plain data.
SocietySettings
Global settings of the society. Insert before adding SocietyCorePlugin to override defaults.
SocietyTick
Accumulates fixed-step time and says whether a society tick is due this step.
Status
A person’s standing: the mean respect others hold for them (plan D6). Magee & Galinsky 2008, DOI 10.5465/19416520802211628, on status as conferred respect; Berger et al. 1972, DOI 10.2307/2093465.
SteeringPlugin
Adds ORCA steering for SocialMovers. Requires crate::SocietyCorePlugin (or crate::SocietyPlugins).
Stock
Per-person holdings, one level per good in definition order, each in [0, Tuning::stock_ceiling].
TalkTo
Asks the society to start a conversation between two people. The host writes it, or a script does through fx.converse (feature dialogue).
Tie
One directed edge from the owning person to target (plan D4).
TieChange
One tie that is not what it was.
TieDelta
Tie changes applied by an interaction outcome.
TieLabelChanged
Written once per tie whose label changed (plan D4). from owns the tie, to is its target.
TieSave
One directed tie, with its target as a PersonKey.
TieSpec
A directed tie from the owning person to another authored person.
Ties
A person’s outgoing ties, sorted by target.to_bits() with at most one entry per target.
TopicDef
An opinion topic.
TopicId
Index of a topic, in definition order.
TravelFailed
The host reports that it cannot walk a person to its Destination.
TriggerArea
An axis-aligned rectangle on the society plane, edges inclusive: where an area trigger is live.
TriggerDef
A rule that fires on a simulation event and runs a script.
TriggerFired
A trigger ran: its event matched, its area and if held and its then finished without error.
TriggerTest
What test_trigger found: whether one trigger would run for the given people, and what its then would write.
TriggersSave
The trigger bookkeeping: which triggers have fired once, which are cooling, what the clock’s previous instant was, which people have fired a need threshold and not yet recovered, and which (deed, rumor) reach triggers have already fired.
Tuning
Every model rate of the crate, one named field each. Rates are per game-day unless the name says otherwise.
UrgentDef
A rank an activity takes while one need is low, and only then.

Enums§

ActivityTarget
Where an activity happens.
AssessmentNorm
How witnesses judge a rule-breaking actor’s trust (see the reputation note).
ClockSource
Where the society clock’s time comes from.
DefsError
Why a definitions file was rejected.
DialogueReferenceKind
What kind of definition names a conversation the DialogueScript does not hold.
DoingTarget
The target of a Doing activity.
DoingTargetSave
What a Doing is directed at, with places and persons as host-stable keys.
EmotionKind
The OCC emotion subset (Ortony, Clore & Collins; via Gratch & Marsella 2004, DOI 10.1016/j.cogsys.2004.02.002, and Dias & Paiva 2005, DOI 10.1007/11595014_13).
FastForwardError
Why a fast_forward did not run.
Feeling
The feeling a bark carries, mapped from the speaker’s strongest emotion.
FieldInput
Social fields a consideration or gradient target can read.
Input
Signals a consideration can read.
Locomotion
Who moves a person’s Transform toward its Destination.
MemoryKind
What a memory is about.
MemoryKindSave
What a memory is about, with every person reference as a PersonKey.
PreviewError
Why a preview_forward did not run or could not reset.
RestoreError
Why a SocietySave could not be applied. The world is untouched when one of these returns.
ScriptKind
What a piece of authored script text must be, so check_script parses it the way the definitions compiler will.
ScriptValue
One value an fx.emit call carries. Any other Rhai type is a runtime error at emit time.
Signal
A compiled signal, with ids resolved.
SkillGroup
Which personality axis a skill draws on.
SocietyPlane
Which two world axes the society lives on. Positions are projected onto these axes.
SocietySystems
System sets of one society tick, chained in this order inside FixedUpdate.
TieLabel
The label a tie carries, recomputed each tick from affinity and familiarity (plan D4).
Trait
One of the five personality traits (McCrae & John 1992, DOI 10.1111/j.1467-6494.1992.tb00970.x).
TriggerOn
The events a trigger can listen to.
TriggerTestError
Why test_trigger could not answer.

Constants§

IDLE_ACTIVITY
Name of the reserved activity every compiled SocietyDefs contains. Persons with no valid option pick it for one game-hour.
STANDING_FAMILIARITY
Familiarity needed before a respect value counts toward standing (plan D6).
VERSION
Version of the SocietySave format this build writes and reads.

Functions§

belief_after_hearing
Belief a listener takes away from hearing a rumor: teller_belief · (0.5 + 0.5 · trust) (Sommerfeld et al. 2007, DOI 10.1073/pnas.0704598104), re-exported for callers of this module.
capture
Reads the whole society into a SocietySave.
check_script
Parses script text under the same sandbox limits as the definitions compiler and reports the first syntax error with its line and column, without needing a DefsFile or a running app. An editor calls this on every edit and refuses to save text that fails. Nothing is evaluated; a script that parses can still fail at run time (an unknown function is found only when it runs).
contest_win_chance
Probability that the actor wins a contest (plan D6): logistic of the difference in (dominance + standing) between actor and target, with steepness from Tuning::contest_steepness (Bonabeau et al. 1995, DOI 10.1016/0378-4371(95)00064-e; Hemelrijk 1999, DOI 10.1098/rspb.1999.0646).
dialogue_problems
Every conversation rule, node effect and node-entered trigger in defs that names a conversation or node script does not define, in definition order. Empty when the two agree.
fast_forward
Runs days game-days of society ticks on world and returns how many ticks ran.
persons_in_rect
Persons whose position on the society plane lies inside the rectangle min..=max, in ascending PersonId order: the selection an editor binds a trigger or a bulk edit to.
place_dependents
Everyone whose home or bound place is place, in ascending PersonId order, so an editor can refuse to delete a building that someone lives in or works at and name them. A person holding the place under several roles appears once per role.
preview_forward
Runs days game-days, reports what changed, and puts the society back as it was.
reload_defs
Applies an edited definitions file to a running society (see SocietyDefs::reload for what may change) and keeps the once and cooldown bookkeeping of every trigger that survives the edit, matched by its authored id. A trigger that was renamed or removed forgets its state; a new one starts clean. A refused file leaves the world untouched.
restore
Applies a SocietySave to a world the host has already spawned its persons and keyed places into.
size_person_state
Keeps every person’s Needs, Signals, Opinions and Stock the length of the defs (plan D2). A short vector grows with the defs’ starting values; a long one is an error and is truncated. Mut changes only when a length actually differs.
skill_level
A skill’s level for a person, in [0, 1]: the trait its group reads, as it stands.
society_tick_due
Run condition: true on fixed steps in which a society tick is due.
standing_from
Standing from (respect, familiarity) pairs, one per person who holds a tie toward this person. Only pairs with familiarity >= STANDING_FAMILIARITY count; 0 when none do (plan D6).
test_trigger
Dry-runs the trigger authored as trigger against the society as the last tick froze it, as if its event had just happened to actor and target, and reports what it would write without writing it.
value_of
What one unit of a good is worth to a person holding held of it: 1 / (1 + held).