BLSE0.6

Python mods

Saved data, settings and translations

On this page 29 sections

Where a mod keeps its data, and how it talks to players in their language. Pick the place by who needs the data and how long:

You want to keep... Use Kept in
Settings the player edits (keys, speeds, on/off) Settings files (ini_get, ini_set) Bin\loader\blse\config\<mod>.ini, for every save
Text in the player's language Translated strings (translate) Bin\loader\blse\translations\<mod>_<language>.txt
A mark on one NPC or object ("already fed on") Values on entities (entity_set, entity_get) the save
Structured data other mods or plugins should read The shared database (db_get, db_set) the save
Data shared by every save (statistics, unlocks) JSON files (json_read, json_write) Bin\loader\blse\data\<file>.json
Your own mod's state in the save Saving data (attributes of G) the save

A first example: a setting the player can change, a count kept in the save, and a message in the player's language.

Python
import blse

# Bin\loader\blse\config\mymod.ini: [General] bonus = 5   (written with the default the first time)
bonus = blse.ini_get('mymod', 'General', 'bonus', 5)
if blse.ini_get('mymod', 'General', 'bonus') is None:
    blse.ini_set('mymod', 'General', 'bonus', bonus)

def count_kill():
    kills = blse.db_get('mymod.kills', 0) + 1           # saved with the game
    blse.db_set('mymod.kills', kills)
    # Bin\loader\blse\translations\mymod_english.txt:  KILLS<tab>{} kills so far.
    blse.console_print(blse.translate('mymod', '$KILLS{%d}' % kills) + '\n')

Read this first

  • Values on entities, the shared database and G are saved with the game; settings, translations and JSON files are plain files shared by every save.
  • Values on entities only work while a level runs (after blse.level_init, before blse.level_shutdown), from Python's normal code (the main thread). The shared database also needs the main thread.
  • Start every name with your mod's name ('mymod.kills', mymod.ini): every mod shares the same database, entity values and folders.
  • Bad names raise ValueError; a missing file or value returns the default (or None).
  • Python 2.1 has no booleans: store 1 and 0. JSON true/false read as 1 and 0.

Words used on this page#

  • Mod name (mod): your mod's short name, used in file names: 1 to 63 characters of A-Z a-z 0-9 _ - ., not starting with ., and not a Windows device name (con, nul, aux, prn, com1-com9, lpt1-lpt9).
  • Section, key, value (.ini files): a file is split into [Sections]; each holds key = value lines.
  • Entity, index, handle: see the world page. entity is an index (1 = the player) or a handle.
  • Save store: the part of the game's save where BLSE keeps its data (inside the game's script globals, G).
  • G: the game's own object for saved script data (__main__.G). Everything set on it is written into each save.
  • Path (database and JSON): where a value sits inside nested dicts and lists: keys separated by ., list positions in [ ], counted from 0 ('mymod.names[1]').
  • JSON: a common text format for dicts ({}), lists ([]), strings, numbers, true/false and null (Python's None).
  • Pickle, picklable: Python's way of writing objects into a file; the game pickles G into each save. Numbers, strings, lists, tuples and dicts of them are picklable.
  • UTF-8, UTF-16, BOM: ways to store text with accents and other alphabets. A BOM is a short mark at the start of a file that tells the encoding; Notepad writes one.
  • Payload: the text an event hands to your handler.

Settings files#

Mods' settings in .ini files: Bin\loader\blse\config\<mod>.ini, which players edit with any text editor.

ini
; a comment (also # at the start of a line)
[Sprint]
key = ALT
speed_factor = 1.6
Function
blse.ini_get(mod, section, key[, default]) The value as a string, or default (None) if the file or key is missing. With a number as default, the value comes as that type (ini_get('sprint', 'Sprint', 'speed_factor', 1.6) -> 1.6), or the default if it is not a number
blse.ini_set(mod, section, key, value) Writes the value (a string or a number; None removes the key, every copy of it in the section) and keeps the rest of the file as it was, comments included; makes the folder and file if needed. Returns 1 or 0
blse.ini_sections(mod) / blse.ini_keys(mod, section) The sections, or a section's keys, in the file's order; None if there is no such file

mod is 1-63 of A-Z a-z 0-9 _ - .. Sections and keys are matched without regard to case; spaces around keys and values are ignored, and a value in double quotes keeps its own. Nothing after a value is a comment (color = #FF0000 is a color). Files are UTF-8, up to 1 MB. Ship the file with comments explaining each setting (examples/config/sprint.ini), and write missing keys with their defaults so players can find them.

Names, in detail: a section is up to 127 characters without ] ('' means the keys before the first [section]); a key is 1 to 127 characters without = or [, not starting with ; or #. Neither may start or end with a space or tab, and neither may hold a line break. A bad name raises ValueError. The settings files work at any time, also at the main menu.

blse.ini_get#

Python
blse.ini_get(mod, section, key[, default])   # -> str, int, float, or default

Reads one setting. Use it at the top of your script to load your mod's options.

  • mod (str): the file's name without .ini ('sprint' reads config\sprint.ini).
  • section, key (str): where the value is; case is ignored.
  • default (any, optional): what you get when the file or key is missing (None if left out). Its type also decides the result's type:
    • an int: the value is read as a whole number ('010' is 10); if it is not one ('1.6', 'fast'), you get default;
    • a float: the value is read as a number; if it is not one, you get default;
    • anything else: the value as text.
  • Returns the value, or default. Raises ValueError for a bad name, RuntimeError if memory runs out.
Python
speed = blse.ini_get('sprint', 'Sprint', 'speed_factor', 1.6)   # 1.6 if missing or not a number
key = blse.ini_get('sprint', 'Sprint', 'key', 'ALT')            # text

blse.ini_set#

Python
blse.ini_set(mod, section, key, value)   # -> 1 or 0

Writes one setting and keeps everything else in the file as it was (comments, order, the other keys). Use it to write the defaults the first time, or to save an option the player changed in a menu.

  • mod, section, key: as for ini_get. A missing folder, file or section is created.
  • value: a string (one line, up to 4095 characters), an int or a float (written with up to 15 digits), or None to remove the key (every copy of it in the section). A value of another type, with a line break, too long, or a float that is not finite raises ValueError.
  • Returns 1 when written (also when nothing changed: the file is then not rewritten), 0 if the file exists but cannot be read, or cannot be written.
Python
blse.ini_set('sprint', 'Sprint', 'speed_factor', 1.8)
blse.ini_set('sprint', 'Sprint', 'old_option', None)    # removes it

blse.ini_sections and blse.ini_keys#

Python
blse.ini_sections(mod)         # -> [section, ...] or None
blse.ini_keys(mod, section)    # -> [key, ...] or None

List what a settings file holds, in the file's order. Use them for files with a section per item (one per weapon, one per NPC).

  • mod, section: as for ini_get.
  • Return the names as written in the file, [] for a section that does not exist, or None if there is no such file. Raise ValueError for a bad name.
Python
for section in blse.ini_sections('mymod_shops') or []:
    price = blse.ini_get('mymod_shops', section, 'price', 100)

Translated strings#

Text for each language in files next to the mod: Bin\loader\blse\translations\<mod>_<language>.txt, one string a line, a key, a tab (or =), the text:

text
; sprint_english.txt
$TIRED	You are out of breath.
$READY = Ready to sprint\nagain
Function
blse.translate(mod, key) The text for key (with or without $; case is ignored) from the language's file, else from <mod>_english.txt, else the key itself without $. UTF-8
blse.translation_keys(mod) The keys of the mod's file for the current language (or English)
blse.language() The language: 'english', 'german', 'russian', ...
blse.translate_text(text) A '$KEY' text translated from any mod's files (b0.5.43), or the text as given if no file has the key

Files are UTF-8 (a BOM is fine) or UTF-16 LE with a BOM. Lines starting with ;, # or // are comments; \n, \t, \\ in the text are a line break, a tab and a backslash. Bloodlines has no language setting (each translation of the game replaces its files), so BLSE uses Windows' display language; players choose another one with language = german in [Translations] of config\blse.ini (BLSE writes language = auto there the first time). Files and the setting are read again when the scripts run again (an engine restart). The game's fonts lack some characters: in the HUD, an em dash (—) shows as a box, while letters like é à ô show.

Keys, in detail: 1 to 255 characters after the optional $, without spaces, tabs or =; lines with another key are skipped. When a line has both a tab and =, the first of them separates the key from the text. Files may be up to 4 MB.

Since b0.5.43:

  • Placeholders: $FOUND{3}{$GOLD} looks up FOUND (You found {} {}) and fills each {} with the next value in braces, in order; a value starting with $ is translated too. Works in translate and everywhere below.
  • Interface text is translated: a text starting with $ given to hud_set (and hints), show_text, window_create (the title), menu_add_label, menu_add_button, menu_set_text and menu_set_hint is looked up in every mod's files for the language (then English; files in name order, the first with the key wins). Text without a translation is shown as given, so '$5 a night' stays as it is.
Python
blse.hud_set('greet', '$MYMOD_HELLO{Kindred}', 0.5, 0.15, '#FFFFFF', 26, 'center')   # MYMOD_HELLO\tGood evening, {}.

blse.translate#

Python
blse.translate(mod, key)   # -> str

Gives the text for a key in the player's language, from your mod's files. Use it for every message your mod shows, so translators only need to add a file.

  • mod (str): your mod's name; the files are translations\<mod>_<language>.txt.
  • key (str): the key, with or without $, case ignored, optionally followed by placeholder values in braces ('$FOUND{3}{$GOLD}'). Each {} in the text is filled with the next value; a value starting with $ is translated too (up to 4 levels deep). Extra {} without a value stay as they are.
  • Returns the text: from the player's language file, else from <mod>_english.txt, else the key itself without $ (and without its placeholder values). Raises ValueError for a bad mod name or key, or for braces that are not whole {...} groups after the key; RuntimeError if memory runs out.
Python
# mymod_english.txt:  FOUND<tab>You found {} {}.     GOLD<tab>gold coins
blse.translate('mymod', '$FOUND{3}{$GOLD}')   # 'You found 3 gold coins.'

blse.translation_keys#

Python
blse.translation_keys(mod)   # -> [key, ...]

Lists the keys of your mod's file for the current language (or the English file if there is none), in file order, each once. Keys come in lower case and without $. Use it to check a translation for missing keys.

  • mod (str): your mod's name.
  • Returns the list ([] if neither file exists). Raises ValueError for a bad mod name.
Python
english_only = blse.translation_keys('mymod')

blse.language#

Python
blse.language()   # -> str

The language BLSE uses: the language setting in [Translations] of config\blse.ini if it is set (letters, digits, _ and -, up to 32 characters, in lower case), else Windows' display language. Windows' languages BLSE knows are english, german, french, spanish, italian, russian, ukrainian, polish, czech, hungarian, portuguese, dutch, swedish, turkish, japanese, korean and chinese; any other is english.

Python
if blse.language() == 'russian':
    blse.log('using the Russian files')

blse.translate_text#

Python
blse.translate_text(text)   # -> str

(b0.5.43) Translates a '$KEY' text from any mod's files, the same way the interface functions do. Use it when you build a text yourself and want the same behaviour as hud_set.

  • text (str): a text starting with $ (placeholders allowed). Any other text is returned unchanged.
  • Returns the translation (the player's language, then English; every mod's files in name order, the first file with the key wins), or text unchanged if no file has the key. Raises RuntimeError if memory runs out.
Python
blse.translate_text('$MYMOD_HELLO{Kindred}')   # 'Good evening, Kindred.'
blse.translate_text('$5 a night')              # '$5 a night' (no such key)

Values on entities#

Named values on any entity (an NPC, the player, a prop), saved with the game and shared by every mod: one mod marks an NPC, another reads the mark.

Function
blse.entity_set(entity, key, value) Stores a string, a number, or a list or dict (b0.5.42) (None removes it). Returns 1 or 0
blse.entity_get(entity, key[, default]) The value (a string, an int or a float, as it was set), or default (None)
blse.entity_keys(entity) The keys stored on the entity ([] if none), or None if there is no such entity
blse.entity_clear(entity[, prefix]) Removes the entity's values whose key starts with prefix (all without one); how many were removed
blse.entity_find(key) The handles of this level's entities (and the player) that have key

entity is an index (1 = the player) or a handle. Keys: 1-63 of A-Z a-z 0-9 _ - ., compared without regard to case; start them with your mod's name ('mymod.fed_on'). Values up to 4096 characters, 256 keys an entity. While a level runs.

Values follow the entity: the player's everywhere; another entity's while it exists, also after a load and when its level is entered again. A loaded save gives entities new indexes but puts them back where they were saved, so BLSE notes each entity's position whenever the game saves (a save, or the level's state at a transition) and finds it again by class, name and that position at the level's first frame. An entity that is removed (killed and cleaned up, picked up, deleted by a script) loses its values. Plugins use the same values as text (entity_data_get/entity_data_set).

Python
def on_fed(target):
    blse.entity_set(target, 'mymod.fed', blse.entity_get(target, 'mymod.fed', 0) + 1)

blse.entity_set#

Python
blse.entity_set(entity, key, value)   # -> 1 or 0

Stores a value on an entity, in the save. Use it to remember something about one NPC or object: that the player fed on it, paid it, or that your mod spawned it.

  • entity: index or handle.
  • key (str): 1 to 63 characters of A-Z a-z 0-9 _ - ., case ignored (stored in lower case). Start it with your mod's name. A bad key raises ValueError.
  • value: a string (no NUL characters), an int, a float (finite), a list, tuple or dict (b0.5.42; stored as JSON, so a tuple reads back as a list), or None to remove the key. At most 4096 characters once stored. Another type raises TypeError; a too long value or a NUL character ValueError.
  • Returns 1 when stored or removed (also when the key was not there), 0 otherwise: no level runs, the entity does not exist, it already has 256 keys, or the save store refused the change. Raises RuntimeError if memory runs out.
Python
blse.entity_set(npc, 'mymod.paid', 50)
blse.entity_set(npc, 'mymod.info', {'met': 1, 'mood': 'angry'})
blse.entity_set(npc, 'mymod.paid', None)     # removed

blse.entity_get#

Python
blse.entity_get(entity, key[, default])   # -> the value, or default

Reads a value stored with entity_set (by any mod).

  • entity: index or handle. key (str): as for entity_set (a bad key raises ValueError).
  • default (optional): returned when there is no such value, no such entity, or no level runs. None if left out.
  • Returns the value with the type it was stored with: a string, an int, a float, or a list or dict.
Python
if blse.entity_get(npc, 'mymod.paid', 0) >= 50:
    blse.console_print('Already paid.\n')

blse.entity_keys#

Python
blse.entity_keys(entity)   # -> [key, ...] or None

Lists the keys stored on an entity (by every mod), in lower case. Use it while debugging, or to copy all values.

  • Returns the keys ([] if the entity has none), or None if there is no such entity or no level runs.
Python
blse.log(str(blse.entity_keys(1)))   # the player's values

blse.entity_clear#

Python
blse.entity_clear(entity[, prefix])   # -> int

Removes several values at once: all your mod's values with prefix set to your mod's name.

  • entity: index or handle.
  • prefix (str, optional): only keys starting with it (case ignored). Left out or '': all the entity's values, including other mods' ones.
  • Returns how many values were removed (0 also when the entity does not exist or no level runs).
Python
blse.entity_clear(npc, 'mymod.')

blse.entity_find#

Python
blse.entity_find(key)   # -> [handle, ...]

Finds every entity on this level that has a value with this key. Use it to find all NPCs your mod marked, for example after a load.

  • key (str): as for entity_set (a bad key raises ValueError).
  • Returns the handles, in index order (the player first if it has the key); [] if none or no level runs.
Python
for npc in blse.entity_find('mymod.paid'):
    blse.log('paid: %d' % npc)

The shared database#

One JSON document saved with the game and shared by every mod, Python and plugins alike. G holds a Python mod's own data; this is where mods leave structured data for each other (and for plugins, which cannot read G).

Function
blse.db_get(path[, default]) The value at path (dicts, lists, strings, numbers), or default (None). '' gives the whole database
blse.db_set(path, value) Sets it, making the dicts on the way (None removes it). Returns 1 or 0
blse.db_keys([path]) The keys of the dict at path (the top without one), or None
blse.version_at_least(version) 1 if this BLSE is that version or newer ('b0.5.42')

Paths as for JSON files: 'mymod.stats.kills', 'mymod.names[1]'; start them with your mod's name. Saved, loaded and cleared on a new game with the game's script globals; up to 1 MB. Plugins: db_get/db_set with JSON text.

Python
blse.db_set('mymod.stats', {'kills': 0, 'names': []})
blse.db_set('mymod.stats.kills', blse.db_get('mymod.stats.kills', 0) + 1)

Values on entities (entity_set) may also be lists and dicts since b0.5.42: blse.entity_set(npc, 'mymod.info', {'met': 1, 'mood': 'angry'}).

blse.db_get#

Python
blse.db_get(path[, default])   # -> the value, or default

(b0.5.42) Reads a value from the shared database.

  • path (str): where the value is ('mymod.stats.kills', 'mymod.names[1]'); '' gives the whole database as a dict. A malformed path raises ValueError.
  • default (optional): returned when nothing is at path (or the database cannot be used now, for example off the main thread). None if left out.
  • Returns dicts, lists, strings, ints, floats or None (JSON true/false as 1/0). Raises RuntimeError if memory runs out.
Python
names = blse.db_get('mymod.names', [])

blse.db_set#

Python
blse.db_set(path, value)   # -> 1 or 0

(b0.5.42) Stores a value in the shared database, in the save.

  • path (str): where to put it; not '' (the top stays a dict). Missing dicts on the way are made; a list index one past the end appends. A malformed or empty path raises ValueError.
  • value: dicts (string keys), lists, tuples, strings, numbers or None to remove the value. Nested at most 64 deep, 200000 values at most (else ValueError); another type raises TypeError.
  • Returns 1 when stored or removed (also when there was nothing to remove), 0 if the path crosses a value that is not a dict or list, the whole database would grow past 1 MB, or the database cannot be used now (off the main thread).
Python
blse.db_set('mymod.names[0]', 'Beckett')     # appends to an empty list
blse.db_set('mymod', None)                   # removes all of mymod's data

blse.db_keys#

Python
blse.db_keys([path])   # -> [key, ...] or None

(b0.5.42) Lists the keys of a dict in the shared database. Use blse.db_keys() to see which mods store data.

  • path (str, optional): the dict to look at; the top of the database if left out.
  • Returns the keys, or None if nothing is at path or it is not a dict. A malformed path raises ValueError.
Python
blse.db_keys()          # e.g. ['mymod', 'othermod']
blse.db_keys('mymod')   # ['stats', 'names']

blse.version_at_least#

Python
blse.version_at_least(version)   # -> 1 or 0

(b0.5.42) Tells whether the BLSE running is this version or newer. Use it before calling a function a newer BLSE added, to show a clear message instead of an error.

  • version (str): like 'b0.5.42': an optional prefix of letters, then numbers separated by dots. Anything else raises ValueError.
  • Returns 1 or 0.
Python
if not blse.version_at_least('b0.5.49'):
    blse.console_print('mymod needs BLSE b0.5.49 or newer.\n')

JSON files#

Structured data in Bin\loader\blse\data\<file>.json, shared by every save (statistics, unlocks, settings with lists). The game's Python (2.1) has no json module; BLSE reads and writes JSON itself.

Function
blse.json_read(file[, path]) The file's contents (or the value at path) as dicts, lists, strings, ints, floats and None; None if the file or value is missing. ValueError with the line for a broken file
blse.json_write(file, value[, pretty]) Writes dicts (string keys), lists, tuples, strings, numbers and None (pretty 1, the default, indents). Returns 1 or 0
blse.json_set(file, path, value) Sets one value and keeps the rest of the file (None removes it); makes the objects on the way. Returns 1 or 0
blse.json_files([folder]) The JSON files (without .json) in data or a folder of it

file: a name, with folders separated by / ('mymod/stats'), of A-Z a-z 0-9 _ - . (not starting or ending with ., and not a Windows device name such as con, nul, aux, com1). Values given to BLSE may be nested 64 deep (200000 values at most); files are read up to 160 deep. path: keys separated by ., list indexes in [ ] ('stats.kills', 'list[2].name', '' the whole file); setting the index one past the end appends. Python 2.1 has no booleans: JSON true/false read as 1 and 0 (and other values in the file stay as they were when json_set changes one). Whole numbers beyond a Python int come back as floats. Plugins: json_get/json_set with JSON text.

A file name is at most 200 characters; a file at most 4 MB. A bad file name or path raises ValueError. The JSON functions work at any time, also at the main menu.

blse.json_read#

Python
blse.json_read(file[, path])   # -> the value, or None

Reads a JSON file, or one value inside it.

  • file (str): the name without .json ('mymod/stats' reads data\mymod\stats.json).
  • path (str, optional): one value inside the file; '' (default) the whole file.
  • Returns dicts, lists, strings, ints, floats and None; None also when the file or the value is missing. Raises ValueError with the line number for a file that is not valid JSON (or cannot be read, or is over 4 MB).
Python
stats = blse.json_read('mymod/stats') or {}
kills = blse.json_read('mymod/stats', 'kills')

blse.json_write#

Python
blse.json_write(file, value[, pretty])   # -> 1 or 0

Writes a whole JSON file, replacing it. Folders on the way are made.

  • file (str): as for json_read.
  • value: dicts (string keys), lists, tuples, strings, numbers, None; nested at most 64 deep, 200000 values at most (else ValueError); another type raises TypeError.
  • pretty (int, optional): 1 (default) writes it indented over several lines, easy to read; 0 writes one line.
  • Returns 1 when written, 0 if the file would be over 4 MB or cannot be written.
Python
blse.json_write('mymod/stats', {'kills': 3, 'unlocked': ['knife', 'bat']})

blse.json_set#

Python
blse.json_set(file, path, value)   # -> 1 or 0

Changes one value in a JSON file and keeps the rest; makes the file if it is missing. The file is written indented.

  • file, path: as for json_read. Missing objects on the way are made; a list index one past the end appends.
  • value: as for json_write, or None to remove the value at path.
  • Returns 1 when written (or when there was nothing to remove), 0 if the path crosses a value that is not an object or list, or the file cannot be written. Raises ValueError if the existing file is not valid JSON.
Python
blse.json_set('mymod/stats', 'kills', (blse.json_read('mymod/stats', 'kills') or 0) + 1)
blse.json_set('mymod/stats', 'unlocked[2]', 'katana')     # appends

blse.json_files#

Python
blse.json_files([folder])   # -> [name, ...]

Lists the JSON files in data (or one of its folders), without .json. Use it for a folder with one file per item.

  • folder (str, optional): a folder inside data ('mymod'); '' (default) is data itself.
  • Returns the names ([] if the folder does not exist). Raises ValueError for a bad folder name.
Python
for name in blse.json_files('mymod/profiles'):
    profile = blse.json_read('mymod/profiles/' + name)

Saving data#

To store a mod's state exactly when the game saves, set it on G in a blse.before_save handler:

Python
import blse, __main__
state = {'kills': 0}
def on_save(topic, data):
    __main__.G.mymod_kills = state['kills']   # written into this save
blse.subscribe('blse.before_save', on_save)

Python mods do not need BLSE for this: attributes of the game's __main__.G object are saved with the game (the game pickles them into every save), so G.mymod_counter = 3 persists. Values must be picklable. Native plugins' save data lives in G.blse_data, a dict of strings (a value longer than about 100 characters is kept as a list of short strings, b0.5.40); scripts may read it but should not replace it.

A bug of the game, fixed by BLSE (b0.5.40): the game reads G back from a save (and at every level transition) with its own line reader, which keeps only the first 128 characters of a line and writes past its buffer beyond 256. Every string in G is one line, so a string longer than about 125 characters broke the next load or transition (cPickle.UnpicklingError, then a crash). BLSE replaces that reader with one without the limit, so long strings in G are safe while BLSE runs; a save that has one cannot be loaded without BLSE. Always on: it is part of BLSE's save system, not a switchable fix (b0.5.49). A mod that should also work without BLSE keeps its G strings under about 120 characters. BLSE's own saved data (plugins' save data, values on entities, the shared database, kept looks and scales, companions) is always stored within the game's limit, so a save made with BLSE loads in the game without it.

Python's error output (tracebacks the game prints to its console) is also written to blse.log since b0.5.40, as [python] python error output: ... lines.

blse.new_game (b0.5.25): the first level of a game state that was not loaded from a save (a new character's first map, or a console map command from the main menu); G is fresh, so reset your module's in-memory state. Data: the map name. Sent before blse.level_init reaches other subscribers. BLSE detects it with a marker in G.blse_data; saves made before b0.5.25 get the marker when loaded. It is not sent for loads or level transitions.

blse.before_save#

Fires right before the game writes a save: a save game, an autosave, or the current level's state at a level change. Messages still queued for Python are delivered first, then this one, at once, so whatever a handler sets on G is in the save (b0.5.9). The payload is the map's name. See also the events page.

Python
def on_save(topic, data):
    __main__.G.mymod_state = {'kills': state['kills']}   # data is the map name
blse.subscribe('blse.before_save', on_save)

blse.new_game#

(b0.5.25) Fires at the first level of a new game (see above). The payload is the map's name. Use it to reset your module's variables, which would otherwise still hold the last game's values.

Python
def on_new_game(topic, data):
    state['kills'] = 0
blse.subscribe('blse.new_game', on_new_game)

To read your state back after a load, read G in a blse.after_load or blse.level_init handler (see the events page):

Python
def on_load(topic, data):
    state['kills'] = getattr(__main__.G, 'mymod_kills', 0)
blse.subscribe('blse.after_load', on_load)