NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

fsd_behavior_loader.py (7907B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\behaviors\loaders\fsd_behavior_loader.py
      2 from collections import namedtuple
      3 from behaviors import BehaviortreeStorage
      4 from behaviors.behaviortree import BehaviorTree
      5 from behaviors.loaders import BehaviorDoesNotExistError, IncompatibleScopeTypeError, TaskAttributeMissingError
      6 from behaviors.registry import get_task_registry
      7 from ccpProfile import TimedFunction
      8 from functoolsext import lru_cache
      9 import logging
     10 logger = logging.getLogger(__name__)
     11 EMPTY_LIST = []
     12 
     13 class CircularReferenceError(Exception):
     14     pass
     15 
     16 
     17 def get_parameter_name(attribute_name):
     18     return attribute_name.split('.')[1]
     19 
     20 
     21 def get_behavior_tree_data(behavior_id, scope_type):
     22     storage = BehaviortreeStorage()
     23     try:
     24         behavior_tree_data = storage.get(behavior_id)
     25     except ValueError:
     26         raise BehaviorDoesNotExistError()
     27 
     28     if behavior_tree_data is None:
     29         raise BehaviorDoesNotExistError()
     30     if scope_type and scope_type != behavior_tree_data['scopeType']:
     31         raise IncompatibleScopeTypeError()
     32     return behavior_tree_data
     33 
     34 
     35 def get_id_and_names_by_scope(scope_type):
     36     storage = BehaviortreeStorage()
     37     ids_and_names = []
     38     for behavior_id in storage:
     39         behavior_tree_data = storage[behavior_id]
     40         if behavior_tree_data.get('scopeType', None) != scope_type:
     41             continue
     42         ids_and_names.append((str(behavior_id), '[FSD] %s (%d)' % (behavior_tree_data.get('name', '<Unnamed FSD Behavior>'), behavior_id)))
     43 
     44     return ids_and_names
     45 
     46 
     47 def get_attributes_with_overrides(node, overrides_by_uuid):
     48     task_uuid = node.get('uuid', None)
     49     overrides = overrides_by_uuid.get(task_uuid, {})
     50     attributes = node['attributes'].copy()
     51     for attribute_name, attribute_value in overrides.iteritems():
     52         attributes[attribute_name] = attribute_value
     53 
     54     return attributes
     55 
     56 
     57 def get_attribute_overrides_by_uuid(parameter_overrides, parameters_values):
     58     overrides_by_uuid = {}
     59     for parameter in parameter_overrides:
     60         for override in parameter['attributeOverrideList']:
     61             task_uuid = override['taskUuid']
     62             if task_uuid not in overrides_by_uuid:
     63                 overrides_by_uuid[task_uuid] = {}
     64             attribute_name = override['attributeName']
     65             parameter_name = parameter['parameterName']
     66             if parameter_name in parameters_values:
     67                 overrides_by_uuid[task_uuid][attribute_name] = parameters_values[parameter_name]
     68 
     69     return overrides_by_uuid
     70 
     71 
     72 def get_parameter_values_with_overrides(parameters, overrides):
     73     if not overrides:
     74         return parameters
     75     updated_parameters = parameters.copy()
     76     logger.debug('applying overrides to %s with overrides %s', parameters, overrides)
     77     for parameter_name, parameter_value in overrides.iteritems():
     78         if '.' not in parameter_name:
     79             continue
     80         updated_parameters[get_parameter_name(parameter_name)] = parameter_value
     81 
     82     return updated_parameters
     83 
     84 
     85 class FsdBehaviorLoader(object):
     86 
     87     def __init__(self):
     88         self.registry = get_task_registry()
     89         self.attribute_tuple_by_task_class = {}
     90         self._subtree_stack = []
     91 
     92     @TimedFunction('behaviors::loaders::FsdBehaviorLoader::load')
     93     def load(self, behavior_id, scope_type = None):
     94         logger.debug('loading behavior tree %s with with scope type %s', behavior_id, scope_type)
     95         behavior_tree_data = get_behavior_tree_data(behavior_id, scope_type)
     96         root_node = behavior_tree_data['root']
     97         tree = BehaviorTree(behaviorId=behavior_id, scopeType=scope_type)
     98         self._subtree_stack = []
     99         root_task = self._instantiate_tree(root_node, {})
    100         tree.StartRootTask(root_task)
    101         return tree
    102 
    103     @TimedFunction('behaviors::loaders::FsdBehaviorLoader::load_reference_subtree')
    104     def load_reference_subtree(self, behavior_id, scope_type, parameters_values):
    105         if behavior_id in self._subtree_stack:
    106             raise CircularReferenceError('Detected circular references while loading subtree %s. Stack was %s' % (behavior_id, self._subtree_stack))
    107         self._subtree_stack.append(behavior_id)
    108         logger.debug('loading subtree %s with parameter values %s', behavior_id, parameters_values)
    109         behavior_tree_data = get_behavior_tree_data(behavior_id, scope_type)
    110         root_node = behavior_tree_data['root']
    111         parameter_overrides = behavior_tree_data.get('parameters', [])
    112         overrides_by_uuid = get_attribute_overrides_by_uuid(parameter_overrides, parameters_values)
    113         root_task = self._instantiate_tree(root_node, overrides_by_uuid)
    114         self._subtree_stack.pop()
    115         return root_task
    116 
    117     @TimedFunction('behaviors::loaders::FsdBehaviorLoader::_instantiate_tree')
    118     def _instantiate_tree(self, node, overrides_by_uuid):
    119         task_name = node['taskClass']
    120         task_class = self.get_task_class(task_name)
    121         attributes = self._get_task_attributes(node, task_name, overrides_by_uuid)
    122         task = task_class(attributes)
    123         if task_name.endswith('ReferenceSubtree'):
    124             subtree_id = attributes.behaviorTreeId
    125             parameter_values = get_parameter_values_with_overrides(attributes.parameters, overrides_by_uuid.get(node['uuid']))
    126             task = self.load_reference_subtree(subtree_id, None, parameter_values)
    127         else:
    128             self._instantiate_child_tasks(node, task, overrides_by_uuid)
    129         return task
    130 
    131     def _instantiate_child_tasks(self, node, task, overrides_by_uuid):
    132         for child_node in node.get('subTasks', EMPTY_LIST):
    133             child_task = self._instantiate_tree(child_node, overrides_by_uuid)
    134             task.AddSubTask(child_task)
    135 
    136     def _get_task_attributes(self, node, task_name, overrides_by_uuid):
    137         attribute_wrapper = self._get_attribute_wrapper(task_name)
    138         node_attributes = get_attributes_with_overrides(node, overrides_by_uuid)
    139         try:
    140             attributes = attribute_wrapper(**self._get_converted_attributes(task_name, attribute_wrapper, node_attributes))
    141         except:
    142             raise
    143 
    144         return attributes
    145 
    146     @lru_cache(maxsize=None)
    147     def get_task_class(self, task_name):
    148         task_class = self.registry.get_class_from_name(task_name)
    149         return task_class
    150 
    151     @lru_cache(maxsize=None)
    152     def _get_attribute_wrapper(self, task_name):
    153         task_attributes = self.registry.get_class_attributes(task_name)
    154         attribute_tuple = namedtuple('%s_AttributeTuple' % task_name.split('.')[-1], ['name'] + task_attributes.keys())
    155         return attribute_tuple
    156 
    157     def get_id_and_names_by_scope(self, scope_type):
    158         return get_id_and_names_by_scope(scope_type)
    159 
    160     def _get_converted_attributes(self, task_name, attribute_wrapper, node_attributes):
    161         attributes = {}
    162         for name in attribute_wrapper._fields:
    163             if name == 'name':
    164                 value = node_attributes.get(name, '')
    165             elif name in node_attributes:
    166                 value = self._convert_fsd_value(task_name, name, node_attributes[name])
    167             elif self._is_optional(task_name, name):
    168                 value = None
    169             else:
    170                 raise TaskAttributeMissingError()
    171             attributes[name] = value
    172 
    173         return attributes
    174 
    175     def _convert_fsd_value(self, task_name, name, value):
    176         task_attributes = self.registry.get_class_attributes(task_name)
    177         attribute = task_attributes[name]
    178         try:
    179             return attribute.convert_fsd_to_attribute(value)
    180         except:
    181             logger.exception('task %s failed to convert fsd attribute %s with value %s' % (task_name, name, value))
    182             raise
    183 
    184     def _is_optional(self, task_name, name):
    185         task_attributes = self.registry.get_class_attributes(task_name)
    186         attribute = task_attributes[name]
    187         return attribute.is_optional