Libary Loader Tutorial

Abstract

As the size of your game increases, it becomes increasingly important to load files only when there needed. Blender shows us a way in witch this can be done. Keep track of the number of references to a file. If there is at least one reference, make sure the file is loaded. Otherwise make sure it is not loaded. We will store the files in a collections.Counter object based off the path to the file. We then extend the Counter object so that it loads and unloads the files for us.

Prerequisites

Object Orientated Programing
bge.logic.LibLoad

Code

import bge
# https://docs.python.org/3/library/collections.html#collections.Counter
from collections import Counter
# (A) Subclass Counter.
class LibaryLoader(Counter):
  """
  counts references to libLoad blend files and loads or unloads them as needed.
 
  add a reference: self[path] += 1
  subtract a reference: self[path] -= 1
  set reference to 0:  self[path] = 0
  must self.update() after manually changing references
 
  remove all non positive references:  self += collection.Counter()
  stop monitoring a reference without unloading it:  del self[path]
  """
  def refresh(self):
    """
    loads and unloads according to number of references
    """
    # Get the list of everything currently loaded.
    libList = bge.logic.LibList()
    # Iterate through all references.
    # Module is the path to the file.  Count is the number of references to that file
    for module, count in self.items():
      # If theres a reference to it, but its not loaded, then load it.
      if count > 0 and module not in libList:
        bge.logic.LibLoad(module, 'Scene', load_actions=True)
      # If all the references have been removed, unload the file.
      elif count == 0 and module in libList:
        bge.logic.LibFree(module)
      # In our case count should not be negative, but its not worth creating an exception.
      # Print a warning and unload the file.
      elif count < 0:
        if module in libList:  bge.logic.LibFree(module)
        print(str(module)+' has a '+str(count)+' count!')
    # This removes all 0 and negative count modules sense we just unloaded them.
    self += Counter()
    # Note that refresh dose not effect files in LibList that are not being counted.
 
  def flush(self):
    """
    removes all monitored blends and set references to 0
    """
    # Iterate over all monitored files and unload them
    for module, count in self.items():
      bge.logic.LibFree(module)
    # clear() sets all counts to 0
    self.clear()
    # Sense everything is now set to 0, this removes everything.
    self += Counter()
 
  def add(self, uId):
    # You don't have to have sheets.py unless you use add/remove.
    import sheets
    sheet = sheets[uId]
    paths = sheet['Path']['blendPaths']
    # Load all the .blend files in sheet['Path']['blendPaths'] by uId.
    for path in paths:
      self[path] += 1
    self.refresh()
 
  def remove(self, uId):
    import sheets
    sheet = sheets[uId]
    paths = sheet['Path']['blendPaths']
    # Subtract one reference for all the .blend files in sheet['Path']['blendPaths'] by uId.
    for path in paths:
      self[path] -= 1
    self.refresh()
 
# Override this module with our custom object.
# https://sohliloquies.blogspot.com/2017/07/how-to-make-subscriptable-module-in.html
import sys
sys.modules[__name__] = LibaryLoader()

Notes

(A) Subclass Counter

We are subclassing Counter. This means we get all the functionality of Counter plus whatever we add to it. We are subclassing rather than using composition because we're adding new functionality without in any way effecting the old behavior. Our new class is being put on top of the old. More abstractly, its sometimes said, that you generally only subclass when the new object is a specialized form of the old. For instance a Van is a particular type of Car. Otherwise you would generally use composition. The LibaryLoader is still a Counter, it just dose something extra with what is being counting.

Usage

import libaryLoader as loader
 
# The dictionary keys are the path to the blend file
path = '//stuff/thing.blend'
path = bge.logic.expandPath(path)
 
# This adds one reference, then loads the file, if it wasn't already loaded.
libLoad[path] += 1
libLoad.refresh()
 
# This removes one reference, then unloads the file, if theres no other references to it.
libLoad[path] -= 1
libLoad.refresh()
 
# This removes the file if its loaded.
libLoad[path] = 0
libLoad.refresh()
 
# This unloads all files that are being monitored and sets there references to zero.
libLoad.flush()

Questions

Edit this page to ask or answer questions.

Further Reading

https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)
https://en.wikipedia.org/wiki/Object_composition
https://en.wikipedia.org/wiki/Composition_over_inheritance
https://docs.python.org/3/library/collections.html#collections.Counter

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License