safesandbox.py
上传用户:ghyvgy
上传日期:2009-05-26
资源大小:547k
文件大小:10k
源码类别:

其他游戏

开发平台:

Python

  1. # safesandbox.py
  2. #
  3. # Implements an environment in which protected code
  4. # can run. The code in this module is not restricted,
  5. # but the code in derived classes of SafeSandbox is
  6. # restricted.
  7. #
  8. # Author: Matthew Walker
  9. #         mwalker@softhome.net
  10. #
  11. import sandboxrexec
  12. import Bastion
  13. import string
  14. import gameserver
  15. import objmgr
  16. import deferred
  17. import gameobs
  18. import gameevents
  19. import gameeventkeys
  20. # a global instance of our restricted environment
  21. g_re = sandboxrexec.SandboxRExec()
  22. # a global dictionary of protected sandbox classes
  23. g_classDict = {}
  24. def _GetProtectedObject(object):
  25.   "Helper to return a protected object."
  26.   return Bastion.Bastion(object)
  27. def _GetProtectedModule(modulePath):
  28.   "Helper to return a protected module."
  29.   return g_re.r_import(modulePath)
  30. def _InitSandboxClasses(sandboxModules):
  31.   """
  32.   Build a dictionary of SafeSandbox derived class objects which is 
  33.   used by the CreateSafeSandbox() factory function. Called at server 
  34.   startup time.
  35.   Classes derived from SafeSandbox cannot directly import this module,
  36.   because this module indirectly imports modules that are not allowed
  37.   inside a SafeSandbox instance. Instead, we create the inheritance 
  38.   relationship at runtime, after importing the derived class's module 
  39.   via our SandboxRExec object.
  40.   The sandboxModules parameter is a list of tuples of sandbox module 
  41.   and class names to be loaded.
  42.   """
  43.   for module in sandboxModules:
  44.     # expand the row tuple into its elements
  45.     moduleName, className = module
  46.     # import the module via the restricted environment
  47.     try:
  48.       module = _GetProtectedModule(moduleName)
  49.     except ImportError:
  50.       import traceback
  51.       traceback.print_exc()
  52.       print 'SafeSandbox module [ %s ] cannot be imported.' % (moduleName,)
  53.       continue
  54.     # certain common modules may be needed within a sandbox,
  55.     # but may themselves import restricted objects; we insert
  56.     # them into our protected module from the 'outside' to avoid
  57.     # problems with trying to import them from within the sandbox.
  58.     import gameevents
  59.     setattr(module, 'gameevents', gameevents)
  60.     import gameeventkeys
  61.     setattr(module, 'gameeventkeys', gameeventkeys)
  62.     # get the class object from the sandbox module
  63.     safeSandboxClass = getattr(module, className)
  64.     # Because we manually create the inheritance relationship with
  65.     # the SafeSandbox base class, we cannot assume that implementors
  66.     # will write constructors that call the base constructor, so 
  67.     # we disallow it entirely.
  68.     assert not hasattr(safeSandboxClass, '__init__'), 
  69.        'Class [ %s ] must not define an __init__ method!' % (safeSandboxClass,)
  70.     # Insert the SafeSandbox base class into the list of bases,
  71.     # thereby creating inheritance without the derived class needing to 
  72.     # import this module.
  73.     bases = safeSandboxClass.__bases__
  74.     safeSandboxClass.__bases__ = bases + (SafeSandbox,)
  75.     # store the sandbox class object indexed by its name
  76.     g_classDict[string.upper(className)] = safeSandboxClass
  77.     print 'Created SafeSandbox class: %s' % (safeSandboxClass,)
  78. def CreateSafeSandbox(worldRegion, eventManager, className):
  79.   """
  80.   Factory function that returns an instance of the derived
  81.   class of SafeSandbox, as indicated by the className. The
  82.   worldRegion parameter is an object containing information
  83.   about the physical game world and the objects it contains. 
  84.   The eventManager parameter provides the ability to register
  85.   for and post asynchronous game events.
  86.   """
  87.   try:
  88.     sandbox = g_classDict[string.upper(className)](worldRegion, eventManager)
  89.     sandbox._FrameworkInit()
  90.     return sandbox
  91.   except KeyError:
  92.     raise RuntimeError('No sandbox class for id [ %s ]' % (className,))
  93. #
  94. # SafeSandbox base class.
  95. #
  96. class SafeSandbox:
  97.   """
  98.   Defines the logical space within which ad-hoc game code runs.
  99.   Provides controlled access to specific services via wrapper 
  100.   methods available to derived classes.
  101.   """
  102.   def __init__(self, worldRegion, eventManager):
  103.     """
  104.     Constructor. Derived classes are not permitted to override this.
  105.     The worldRegion contains the world geometry and game objects.
  106.     There is one event manager per sandbox, allowing for a clean 
  107.     partitioning of events in different sandboxes.
  108.     """
  109.     self.__worldRegion = worldRegion
  110.     self.__eventManager = eventManager
  111.   def _FrameworkInit(self):
  112.     """
  113.     Standard initalization. Calls derived class implementation if it exists.
  114.     This is in leiu of permitting derived classes to implement __init__().
  115.     """
  116.     if hasattr(self, 'Init'):
  117.       self.Init()
  118.     # common initialization
  119.     self.RegisterHandler(gameeventkeys.ByEvent(gameevents.PLAYER_ENTERS_ROOM), self._OnPlayerEnter)
  120.     self.RegisterHandler(gameeventkeys.ByEvent(gameevents.PLAYER_EXITS_ROOM), self._OnPlayerExit)
  121.     self.RegisterHandler(gameeventkeys.ByEvent(gameevents.ACTION_TAKE_OBJECT), self._OnTakeItem)
  122.     self.RegisterHandler(gameeventkeys.ByEvent(gameevents.ACTION_PLACE_OBJECT), self._OnPlaceItem)
  123.     self.RegisterHandler(gameeventkeys.ByEvent(gameevents.PLAYER_DIES), self._OnPlayerDies)
  124.   def __getattr__(self, name):
  125.     """
  126.     Simulate direct member access for protected members.
  127.     Posting of game events is not allowed within the sandbox, so 
  128.     we only expose registration and unregistration methods.
  129.     """
  130.     if name == 'RegisterHandler':
  131.       return self.__eventManager.RegisterHandler
  132.     elif name == 'UnregisterHandler':
  133.       return self.__eventManager.UnregisterHandler
  134.     elif name == 'playerids':
  135.       return self.__worldRegion.playerids[:]  # return a copy, to avoid mutating list
  136.     elif name == 'itemids':
  137.       return self.__worldRegion.itemids[:]  # return a copy, to avoid mutating list
  138.     else:
  139.       raise AttributeError(name)
  140.   def CreateItem(self, klassname, name):
  141.     """
  142.     Create an object of the given class with the name requested.
  143.     """
  144.     # resolve the class object from the class name
  145.     klass = getattr(gameobs, klassname)
  146.     assert(issubclass(klass, gameobs.Item))
  147.     # create a new instance and add to object mgr
  148.     id = objmgr.NextId()
  149.     ob = klass(name, id)
  150.     objmgr.AddObject(ob, id)
  151.     # keep track of it
  152.     self.__worldRegion.itemids.append(id)
  153.     # return a protected version
  154.     return self.GetGameObject(id)  
  155.   def GetGameObject(self, obId):
  156.     """
  157.     Return a protected game object from the world region, based
  158.     on its object id.
  159.     """
  160.     gameobject = objmgr.GetObject(obId)
  161.     return _GetProtectedObject(gameobject)
  162.   def ClientMessage(self, playerid, msg):
  163.     """
  164.     Sends a message to the player associates with the 
  165.     clientid using a deferred call.
  166.     """
  167.     import request
  168.     request.ClientMessage(playerid, msg)
  169.  
  170.   # 
  171.   # Default event handlers common to all sandboxes
  172.   #
  173.   def _OnPlayerEnter(self, key):
  174.     """
  175.     Private; always called in the base class; 
  176.     derived class can implement OnPlayerEnter()
  177.     (i.e. not virtual) to extend.
  178.     """
  179.     print '_OnPlayerEnter( %s )' % (key,)    
  180.     player = self.GetGameObject(key.subj)
  181.     seemsg = 'You see:n'
  182.     for playerid in self.playerids:
  183.       if playerid != key.subj:
  184.         # don't notify player who entered
  185.         self.ClientMessage(playerid, '%s has entered %s.' % (player.GetName(),self.__worldRegion.name))
  186.         # tell player who is around us
  187.         other = self.GetGameObject(playerid)
  188.         seemsg = seemsg + ('%sn' % (other.GetName(),))
  189.     # also tell of items you see
  190.     for itemid in self.itemids:
  191.       item = self.GetGameObject(itemid)
  192.       seemsg = seemsg + ('%sn' % (item.GetName(),))
  193.     
  194.     self.ClientMessage(key.subj, seemsg)
  195.     if hasattr(self, 'OnPlayerEnter'):
  196.       self.OnPlayerEnter(key)
  197.   def _OnPlayerExit(self, key):
  198.     """
  199.     Private; always called in the base class; 
  200.     derived class can implement OnPlayerExit()
  201.     (i.e. not virtual) to extend.
  202.     """
  203.     print 'OnPlayerExit( %s )' % (key,)    
  204.     player = self.GetGameObject(key.subj)
  205.     for playerid in self.playerids:
  206.       if playerid != key.subj:
  207.         # don't notify player who left
  208.         self.ClientMessage(playerid, '%s has left Town.' % (player.GetName(),))
  209.     if hasattr(self, 'OnPlayerExit'):
  210.       self.OnPlayerExit(key)
  211.   def _OnTakeItem(self, key):
  212.     """
  213.     Private; always called in the base class; 
  214.     derived class can implement OnTakeItem()
  215.     (i.e. not virtual) to extend.
  216.     """
  217.     print '_OnTakeItem( %s )' % (key,)    
  218.     player = objmgr.GetObject(key.src)
  219.     item = objmgr.GetObject(key.subj)
  220.     player._AddToInventory(item)
  221.     item._AssignOwner(player.id)
  222.     self.__worldRegion.itemids.remove(key.subj)
  223.     if hasattr(self, 'OnTakeItem'):
  224.       self.OnTakeItem(key)
  225.   def _OnPlaceItem(self, key):
  226.     """
  227.     Private; always called in the base class; 
  228.     derived class can implement OnPlaceItem()
  229.     (i.e. not virtual) to extend.
  230.     """
  231.     print '_OnPlaceItem( %s )' % (key,)    
  232.     player = objmgr.GetObject(key.src)
  233.     item = objmgr.GetObject(key.subj)
  234.     player._RemoveFromInventory(item)
  235.     item._AssignOwner(None)
  236.     self.__worldRegion.itemids.append(key.subj)
  237.     if hasattr(self, 'OnPlaceItem'):
  238.       self.OnPlaceItem(key)
  239.     
  240.   def _OnPlayerDies(self, key):
  241.     """
  242.     Notify the world when a player dies.
  243.     """
  244.     print '_OnPlaceItem( %s )' % (key,)    
  245.     player = objmgr.GetObject(key.src)
  246.     for playerid in self.playerids:
  247.       if playerid != key.subj:
  248.         # don't notify player who died - he already knows
  249.         self.ClientMessage(playerid, '%s has died.' % (player.GetName(),))