Native logging and profiling

Log functions

The engine exposes severity-specific log functions:

logTrace, logDebug, logInfo, logNotice, logWarning, logError, logCritical, and logHack.

Current logging adapters commonly call them as logFunction(prefix, message, metadata), with None for unused metadata. logExceptionFrame(exceptionType, exception, frame) records an exception from a specific Python frame. flushPythonLog() forces buffered Python log output to be written; periodic flushing is infrastructure behavior, not something each mod should schedule independently.

Use the standard Python logging layer for mods. It already maps levels to the native functions and avoids depending on their raw signature. Keep high-rate hooks quiet and never include secrets, authentication data, or private endpoints.

Profiling helpers

  • uniprofSceneStart() marks a new rendered scene after an arena, hangar, or offline space becomes ready.
  • uniprofRegionEnter(label, color) returns a region handle; uniprofRegionExit(handle) closes that exact region.
  • PFbeginFrame(logID) and PFendFrame() bracket a measured Python operation.
  • enableBattleStatisticCollector, wg_getClientStatistics, wg_getHangarStatistics, getFPS, getBattleFPS, statPing, and statLagDetected expose diagnostics or telemetry state.

Diagnostics and JSON bridges must serialize the four numeric components explicitly or publish a deliberately selected component. Do not stringify the native object and call that a stable schema; verify the component meanings at the consuming call site.

Profiling pairs require try/finally ownership so an exception cannot leave a region open. They are diagnostic infrastructure, not gameplay timers.

Preferences, paths, and native persistence

Preference path

wg_getPreferencesFilePath() returns the path of the active preferences file. Python 2 callers convert the returned UTF-8 byte string before using it with Unicode filesystem paths. The containing directory is the client-owned persistent data root used for account caches and selected mod data.

Some compatibility layers fall back to getPreferencesFilePath(). Treat the wg_ spelling as current and the unprefixed spelling as a guarded fallback.

Use the directory, not the preferences XML itself, for separate application or mod files. Normalize joined paths, create only owned subdirectories, and do not overwrite client caches.

Preference callbacks

  • wg_subscribeToReadPreferences(callback) registers a callback receiving the preferences root when native preferences are read.
  • wg_subscribeToSavePreferences(callback) registers a callback receiving the preferences root during save.
  • wg_preferencesExistedAtStartup() distinguishes a first/default startup from a client that loaded an existing preferences file.
  • savePreferences() asks the engine to save video, graphics, and script preferences.

These callbacks are intended for settings infrastructure. A mod with its own JSON configuration should normally own that file explicitly instead of injecting arbitrary state into the client preferences tree.

Owned preferences directory

The reliable primitive to verify in current client builds is the preferences file path helper, commonly exposed as wg_getPreferencesFilePath() with older compatibility fallbacks in some builds. Do not assume that a getPreferencesDir()-style helper exists unless the target build actually exposes it.

For separate mod data, derive an owned directory from the preferences file path and create a subdirectory with a stable generic name. Never write arbitrary data into the client preferences XML or overwrite client-owned caches.

import os
import BigWorld
import logging

LOG = logging.getLogger('mod.example.storage')


def _preferences_file_path():
    getter = getattr(BigWorld, 'wg_getPreferencesFilePath', None)
    if getter is None:
        getter = getattr(BigWorld, 'getPreferencesFilePath', None)
    if getter is None:
        LOG.warning('Preferences path helper is unavailable in this build')
        return None
    return getter()


def _owned_storage_dir():
    prefs = _preferences_file_path()
    if not prefs:
        return None
    root = os.path.dirname(os.path.abspath(prefs))
    path = os.path.join(root, 'mods', 'example')
    if not os.path.isdir(path):
        os.makedirs(path)
    return path

Logging through named loggers

The native debug functions exist for engine infrastructure, but normal mod diagnostics should usually go through Python's logging module. A named logger gives the log a stable owner and keeps high-rate hooks filterable.

  • Use one module-level logger per single-file mod or per broad subsystem section.
  • Use logger.exception only inside exception handlers.
  • Keep per-frame logging disabled or behind explicit diagnostics.
  • Do not log tokens, private endpoints, filesystem dumps, or full account data.