Ejemplo n.º 1
0
 def __init__(self, manager_id=0):
     super().__init__(manager_id=manager_id)
     self._get_next_session_id = UniqueIdGenerator(1)
     self._added_to_distributor = set()
     self._callbacks = {}
     self._departing_situation_seed = None
     self._arriving_situation_seed = None
     self._zone_seeds_for_zone_spinup = []
     self._open_street_seeds_for_zone_spinup = []
     self._debug_sims = set()
     self._leave_situation_id = 0
     self._player_greeted_situation_id = 0
     self._player_waiting_to_be_greeted_situation_id = 0
     self._sim_being_created = None
     self._sim_data = {}
     self._delay_situation_destruction_ref_count = 0
     self._situations_for_delayed_destruction = set()
     self._bouncer = None
     self._zone_spin_up_greeted_complete = False
     self._pre_bouncer_update = []
Ejemplo n.º 2
0
 def __init__(self, owner):
     super().__init__(owner)
     self._active_buffs = {}
     self._get_next_handle_id = UniqueIdGenerator()
     self._success_chance_modification = 0
     self._active_mood = self.DEFAULT_MOOD
     self._active_mood_intensity = 0
     self._active_mood_buff_handle = None
     self.on_mood_changed = CallableList()
     self.on_mood_changed.append(self._publish_mood_update)
     self.on_mood_changed.append(self._send_mood_changed_event)
     self.load_in_progress = False
     self.on_buff_added = CallableList()
     self.on_buff_removed = CallableList()
     self.buff_update_alarms = {}
     if self._active_mood is None:
         logger.error('No default mood tuned in buff_component.py')
     elif self._active_mood.buffs:
         initial_buff_ref = self._active_mood.buffs[0]
         if initial_buff_ref and initial_buff_ref.buff_type:
             self._active_mood_buff_handle = self.add_buff(initial_buff_ref.buff_type)
Ejemplo n.º 3
0
class AnimationContext:
    __qualname__ = 'AnimationContext'
    _get_next_asm_event_uid = UniqueIdGenerator()
    _CENSOR_MAPPING = {
        native.animation.arb.CENSOREVENT_STATE_OFF: CensorState.OFF,
        native.animation.arb.CENSOREVENT_STATE_TORSO: CensorState.TORSO,
        native.animation.arb.CENSOREVENT_STATE_TORSOPELVIS:
        CensorState.TORSO_PELVIS,
        native.animation.arb.CENSOREVENT_STATE_PELVIS: CensorState.PELVIS,
        native.animation.arb.CENSOREVENT_STATE_FULLBODY: CensorState.FULLBODY,
        native.animation.arb.CENSOREVENT_STATE_RHAND: CensorState.RHAND,
        native.animation.arb.CENSOREVENT_STATE_LHAND: CensorState.LHAND
    }

    def __init__(self, *args, is_throwaway=False, **kwargs):
        super().__init__(*args, **kwargs)
        self._user_data = {}
        self._props = {}
        self._placeholders = {}
        self._posture_owners = None if is_throwaway else set()
        self._vfx_overrides = None
        self._sound_overrides = None
        self._custom_event_handlers = {}
        self._event_handlers = {}
        self._asms = []
        self.reset_for_new_interaction()
        self.apply_carry_interaction_mask = []
        self._ref_count = []

    def __repr__(self):
        kwargs = {}
        if self._props:
            kwargs['props'] = list(sorted(self._props))
        if self._placeholders:
            kwargs['placeholders'] = list(
                sorted(str(e) for e in self._placeholders.values()))
        if self._asms:
            kwargs['asms'] = list(sorted({e.name for e in self._asms}))
        if self._ref_count:
            kwargs['refs'] = self._ref_count
        return standard_angle_repr(self, request_id=self.request_id, **kwargs)

    def add_asm(self, asm):
        self._asms.append(asm)

    def remove_asm(self, asm):
        raise RuntimeError(
            '[bhill] This function is believed to be dead code and is scheduled for pruning. If this exception has been raised, the code is not dead and this exception should be removed.'
        )
        self._asms.remove(asm)

    def get_asms_gen(self):
        return iter(self._asms)

    def add_posture_owner(self, posture):
        if self._posture_owners is not None:
            self._posture_owners.add(posture)
            self.add_ref(posture)

    def remove_posture_owner(self, posture):
        if self._posture_owners is not None:
            self._posture_owners.discard(posture)
            self.release_ref(posture)
            if not self._posture_owners:
                if self._ref_count:
                    logger.error(
                        '{} release all the postures but still have {} ref count. This is invalid.',
                        self, self._ref_count)

    def reset_for_new_interaction(self):
        self._vfx_overrides = None
        self._sound_overrides = None
        self._custom_event_handlers = {}
        self._event_handlers = {}
        self.register_event_handler(self._event_handler_effect_start,
                                    ClipEventType.Effect)
        self.register_event_handler(self._event_handler_effect_stop,
                                    ClipEventType.StopEffect)
        self.register_event_handler(self._event_handler_sound_start,
                                    ClipEventType.ServerSoundStart)
        self.register_event_handler(self._event_handler_sound_stop,
                                    ClipEventType.ServerSoundStop)
        self.register_event_handler(self._event_handler_censor_grid,
                                    ClipEventType.Censor)
        self.register_event_handler(self._event_handler_material_state,
                                    ClipEventType.MaterialState)
        self.register_event_handler(self._event_handler_fade_object,
                                    ClipEventType.FadeObject)

    def _reset_throwaway_context(self):
        self._stop()
        self.reset_for_new_interaction()
        del self._asms[:]

    def add_ref(self, tag):
        self._ref_count.append(tag)

    def release_ref(self, tag):
        if tag in self._ref_count:
            self._ref_count.remove(tag)
        else:
            logger.error(
                'Unexpected tag in release_ref: {} (remaining refs: {})', tag,
                self._ref_count)
        if not self._ref_count:
            self._stop()

    def _all_props_gen(self, held_only):
        for (name, prop) in self._props.items():
            if prop.id not in prop.manager:
                pass
            if held_only:
                parent = prop.parent
                while not parent is None:
                    if not parent.is_sim:
                        pass
                    yield name
            yield name

    def destroy_all_props(self, held_only=False):
        names = []
        for name in self._all_props_gen(held_only):
            names.append(name)
        for name in names:
            self._props.pop(name).destroy(
                source=self, cause='Animation destroying all props.')

    def set_all_prop_visibility(self, visible, held_only=False):
        for name in self._all_props_gen(held_only):
            self._props[name].visibility = VisibilityState(visible)

    def _stop(self):
        for key in self._user_data:
            data = self._user_data[key]
            if hasattr(data, 'stop'):
                if key.event_type == ClipEventType.Effect:
                    data.stop(immediate=False)
                else:
                    data.stop()
            while key.event_type == ClipEventType.Censor:
                censor_object = get_animation_object_by_id(key.actor_id)
                if censor_object is not None:
                    censor_object.censorgrid_component.remove_censor(data)
        self._user_data.clear()
        self.destroy_all_props()
        self.clear_reserved_slots()

    @property
    def request_id(self):
        return self._context_uid

    def _override_prop_states(self, actor, prop, states):
        if actor is None or actor.state_component is None:
            return
        if prop is None or prop.state_component is None:
            return
        for state in states:
            state_value = actor.get_state(state)
            while state_value is not None:
                prop.set_state(state, state_value)

    def _get_prop(self, asm, prop_name, definition_id):
        props = self._props
        prop = props.get(prop_name)
        (from_actor,
         states_to_override) = asm.get_prop_state_override(prop_name)
        if not definition_id:
            self._override_prop_states(from_actor, prop, states_to_override)
            return prop
        if prop is not None and prop.definition.id != definition_id:
            asm.set_actor(prop_name, None)
            prop.destroy(source=self, cause='Replacing prop.')
            del props[prop_name]
            prop = None
        if prop is None:
            prop = create_prop(definition_id)
            if prop is not None:
                props[prop_name] = prop
            else:
                logger.error(
                    "{}: Failed to create prop '{}' with definition id {:#x}",
                    asm.name, prop_name, definition_id)
        if prop is not None:
            asm.set_prop_state_values(prop_name, prop)
            self._override_prop_states(from_actor, prop, states_to_override)
        return prop

    def clear_reserved_slots(self):
        for (slot_manifest_entry,
             placeholder_info) in list(self._placeholders.items()):
            (reserve_sim, placeholder_obj) = placeholder_info
            logger.debug('Slot Reservation: Release: {} for {}',
                         placeholder_obj, slot_manifest_entry)
            placeholder_obj.release(reserve_sim, self)
            placeholder_obj.destroy(source=self,
                                    cause='Clearing reserved slots')
        self._placeholders.clear()

    @staticmethod
    def init_placeholder_obj(obj):
        obj.visibility = VisibilityState(False)

    def update_reserved_slots(self,
                              slot_manifest_entry,
                              reserve_sim,
                              objects_to_ignore=DEFAULT):
        runtime_slot = slot_manifest_entry.runtime_slot
        if runtime_slot is None:
            raise RuntimeError(
                'Attempt to reserve slots without a valid runtime slot: {}'.
                format(slot_manifest_entry))
        if slot_manifest_entry.actor in runtime_slot.children or slot_manifest_entry in self._placeholders:
            return sims4.utils.Result.TRUE
        definition = slot_manifest_entry.actor.definition
        result = sims4.utils.Result.TRUE

        def post_add(obj):
            nonlocal result
            try:
                result = runtime_slot.is_valid_for_placement(
                    obj=obj, objects_to_ignore=objects_to_ignore)
                while result:
                    runtime_slot.add_child(obj)
                    if reserve_sim is not None:
                        obj.reserve(reserve_sim, self)
                    self._placeholders[slot_manifest_entry] = (reserve_sim,
                                                               obj)
                    logger.debug('Slot Reservation: Reserve: {} for {}', obj,
                                 slot_manifest_entry)
            except:
                logger.exception('Exception reserving slot: {} for {}', obj,
                                 slot_manifest_entry)
                result = sims4.utils.Result(False, 'Exception reserving slot.')
            finally:
                if not result:
                    logger.debug('Slot Reservation: Fail:    {} for {} - {}',
                                 obj, slot_manifest_entry, result)
                    obj.destroy(source=self, cause='updating reserved slots')

        create_prop_with_footprint(definition,
                                   init=self.init_placeholder_obj,
                                   post_add=post_add)
        return result

    def register_event_handler(self,
                               callback,
                               handler_type=ClipEventType.Script,
                               handler_id=None,
                               tag=None):
        handle = EventHandle(self, tag=tag)
        for existing_handle in list(self._event_handlers):
            while existing_handle == handle:
                existing_handle.release()
        uid = AnimationContext._get_next_asm_event_uid()
        self._event_handlers[handle] = (uid, callback, handler_type,
                                        handler_id)
        return handle

    def register_custom_event_handler(self,
                                      callback,
                                      actor,
                                      time,
                                      allow_stub_creation=False,
                                      optional=False):
        handler_id = services.current_zone(
        ).arb_accumulator_service.claim_xevt_id()
        handle = EventHandle(self)
        uid = AnimationContext._get_next_asm_event_uid()
        self._custom_event_handlers[handle] = (uid, callback, handler_id,
                                               actor.id if actor is not None
                                               else None, time,
                                               allow_stub_creation, optional)
        return handle

    def _release_handle(self, handle):
        if handle in self._event_handlers.keys():
            del self._event_handlers[handle]
        if handle in self._custom_event_handlers.keys():
            del self._custom_event_handlers[handle]

    def _pre_request(self, asm, arb, state):
        arb.add_request_info(self, asm, state)
        for (uid, callback, event_type,
             event_id) in self._event_handlers.values():
            if not hasattr(arb, '_context_uids'):
                arb._context_uids = set()
            while uid not in arb._context_uids:
                arb.register_event_handler(callback, event_type, event_id)
                arb._context_uids.add(uid)
        props = asm.get_props_in_traversal(asm.current_state, state)
        for (prop_name, definition_id) in props.items():
            prop = self._get_prop(asm, prop_name, definition_id)
            while prop is not None:
                if not asm.set_actor(prop_name, prop):
                    logger.warn('{}: Failed to set actor: {} to {}', asm,
                                prop_name, prop)
        self._vfx_overrides = asm.vfx_overrides
        self._sound_overrides = asm.sound_overrides
        for actor_name in self.apply_carry_interaction_mask:
            asm._set_actor_trackmask_override(actor_name, 50000,
                                              'Trackmask_CarryInteraction')

    def _post_request(self, asm, arb, state):
        for (uid, callback, event_id, actor_id, time, allow_stub_creation,
             optional) in self._custom_event_handlers.values():
            if actor_id is not None:
                actors = arb._actors()
                if actors:
                    if actor_id not in actors:
                        if optional:
                            pass
                        logger.error(
                            "Failed to schedule custom x-event {} from {} on {} which didn't have the requested actor: {}, callback: {}",
                            event_id, asm, arb, actor_id, callback)
            scheduled_event = False
            if actor_id is None:
                actors = arb._actors()
                if actors:
                    while True:
                        for arb_actor_id in actors:
                            while arb.add_custom_event(arb_actor_id, time,
                                                       event_id,
                                                       allow_stub_creation):
                                scheduled_event = True
                                break
            elif arb.add_custom_event(actor_id, time, event_id,
                                      allow_stub_creation):
                scheduled_event = True
            while scheduled_event:
                if not hasattr(arb, '_context_uids'):
                    arb._context_uids = set()
                if uid not in arb._context_uids:
                    arb.register_event_handler(callback, ClipEventType.Script,
                                               event_id)
                    arb._context_uids.add(uid)
        for actor_name in self.apply_carry_interaction_mask:
            asm._clear_actor_trackmask_override(actor_name)
        self._custom_event_handlers = {}

    def _event_handler_discard_prop(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        prop_id = event_data.event_data['prop_actor_id']
        props = self._props
        for (prop_name, prop) in props.values():
            while prop.id == prop_id:
                prop.destroy(source=self, cause='Discarding props.')
                del props[prop_name]
                return
        return 'Discard prop event attempted to discard an object not registered as a prop: {}'.format(
            prop_id)

    def _event_handler_effect_start(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        (early_out, effect_parent_obj) = get_animation_object_for_event(
            event_data, 'effect_parent_id', 'parent', asms=self._asms)
        if early_out is not None:
            return
        target_parent_id = event_data.event_data['effect_target_parent_id']
        if target_parent_id == 0:
            target_parent_obj = None
        else:
            (early_out, target_parent_obj) = get_animation_object_for_event(
                event_data,
                'effect_target_parent_id',
                'parent',
                asms=self._asms)
            if early_out is not None:
                return
        event_actor_id = event_data.event_data['event_actor_id']
        effect_actor_id = event_data.event_data['effect_actor_id']
        effect_name = None
        target_joint_name_hash = event_data.event_data[
            'effect_target_joint_name_hash']
        if self._vfx_overrides and effect_actor_id in self._vfx_overrides:
            effect_overrides = self._vfx_overrides[effect_actor_id]
            if effect_overrides.effect is not None:
                if not effect_overrides.effect:
                    return
                effect_name = effect_overrides.effect
            target_joint_name_hash = effect_overrides.target_joint
        else:
            effect_name = event_data.event_data['effect_name']
        effect_joint_name_hash = event_data.event_data[
            'effect_joint_name_hash']
        key = UserDataKey(ClipEventType.Effect, event_actor_id,
                          effect_actor_id)
        if key in self._user_data:
            self._user_data[key].stop()
            del self._user_data[key]
        if effect_name is None:
            return
        mirrored = event_data.event_data['clip_is_mirrored']
        if mirrored:
            try:
                if effect_parent_obj is not None:
                    effect_joint_name_hash = get_mirrored_joint_name_hash(
                        effect_parent_obj.rig, effect_joint_name_hash)
                while target_parent_obj is not None:
                    target_joint_name_hash = get_mirrored_joint_name_hash(
                        target_parent_obj.rig, target_joint_name_hash)
            except Exception as e:
                logger.error(
                    'Failed to look up mirrored joint name...\nException: {}\nEventData: {}',
                    e, event_data.event_data)
        effect = vfx.PlayEffect(effect_parent_obj,
                                effect_name,
                                effect_joint_name_hash,
                                target_parent_id,
                                target_joint_name_hash,
                                mirror_effect=mirrored)
        self._user_data[key] = effect
        effect.start()

    def _event_handler_effect_stop(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        event_actor_id = event_data.event_data['event_actor_id']
        effect_actor_id = event_data.event_data['effect_actor_id']
        key = UserDataKey(ClipEventType.Effect, event_actor_id,
                          effect_actor_id)
        if key in self._user_data:
            self._user_data[key].stop(
                immediate=event_data.event_data['effect_hard_stop'])
            del self._user_data[key]

    def _event_handler_sound_start(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        (early_out, obj) = get_animation_object_for_event(event_data,
                                                          'target_actor_id',
                                                          'parent',
                                                          asms=self._asms)
        if early_out is not None:
            return
        sound_name = event_data.event_data['sound_name']
        sound_id = sims4.hash_util.hash64(sound_name)
        if self._sound_overrides and sound_id in self._sound_overrides:
            sound_id = self._vfx_overrides[sound_id]
        key = UserDataKey(ClipEventType.ServerSoundStart, obj.id, sound_name)
        if key in self._user_data:
            self._user_data[key].stop()
            del self._user_data[key]
        if sound_id is None:
            return
        sound = audio.primitive.PlaySound(obj, sound_id)
        self._user_data[key] = sound
        sound.start()

    def _event_handler_sound_stop(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        sound_parent_id = event_data.event_data['target_actor_id']
        sound_name = event_data.event_data['sound_name']
        key = UserDataKey(ClipEventType.ServerSoundStart, sound_parent_id,
                          sound_name)
        if key in self._user_data:
            self._user_data[key].stop()
            del self._user_data[key]

    def _event_handler_censor_grid(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        event_actor_id = event_data.event_data['event_actor_id']
        censor_state = event_data.event_data['censor_state']
        key = UserDataKey(ClipEventType.Censor, event_actor_id, None)
        censor_state = self._CENSOR_MAPPING[censor_state]
        actor = get_animation_object_by_id(event_actor_id)
        if key in self._user_data:
            actor.censorgrid_component.remove_censor(self._user_data[key])
            del self._user_data[key]
        if censor_state != CensorState.OFF:
            self._user_data[key] = actor.censorgrid_component.add_censor(
                censor_state)

    def _event_handler_material_state(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        target_actor_id = event_data.event_data['target_actor_id']
        material_state = event_data.event_data['material_state_name']
        target = get_animation_object_by_id(target_actor_id)
        material_state = MaterialState(material_state)
        target.material_state = material_state

    def _event_handler_fade_object(self, event_data):
        request_id = event_data.event_data['request_id']
        if request_id != self.request_id:
            return
        target_actor_id = event_data.event_data['target_actor_id']
        opacity = event_data.event_data['target_opacity']
        duration = event_data.event_data['duration']
        target = get_animation_object_by_id(target_actor_id)
        target.fade_opacity(opacity, duration)
Ejemplo n.º 4
0
class MasterController(sims4.service_manager.Service):
    get_next_id = UniqueIdGenerator()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._enabled = True
        self._processing = False
        self._reset_in_progress = False
        self._last_work_timestamps = {}
        self._sims = set()
        self._active_work = {}
        self._denied_sims = OrderedDict()
        self._global_required_resources = WeakSet()
        self._gsi_entry = None
        self._gsi_log_entries = None

    def stop(self):
        self._remove_all_sims()
        if self._sims:
            logger.error('Sims {} should be empty.  MC logic error.',
                         self._sims,
                         owner='mduke')
            self._sims.clear()
        if self._active_work:
            logger.error('Active Work {} should be empty.  MC logic error.',
                         self._active_work,
                         owner='mduke')
            self._active_work.clear()
        if self._denied_sims:
            logger.error('Denied Sims {} should be empty.  MC logic error.',
                         self._denied_sims,
                         owner='mduke')
            self._denied_sims.clear()

    @property
    def timeline(self):
        return services.time_service().sim_timeline

    def remove_all_sims_and_disable_on_teardown(self):
        self._enabled = False
        self._remove_all_sims()

    def _remove_all_sims(self):
        for sim in tuple(self._sims):
            self.remove_sim(sim)

    def add_sim(self, sim):
        logger.assert_raise(
            self._enabled == True,
            'Attempting to add a sim to the master controller when it is not enabled.',
            owner='sscholl')
        self._sims.add(sim)
        self.set_timestamp_for_sim_to_now(sim)
        self._process(sim)

    def added_sims(self):
        return list(self._sims)

    def remove_sim(self, sim):
        self._last_work_timestamps.pop(sim, None)
        self._sims.discard(sim)
        del self._denied_sims[sim]
        if sim in self._denied_sims and sim.is_sim:
            sim.queue.on_head_changed.remove(self._process)

    def reset_timestamp_for_sim(self, sim):
        self._last_work_timestamps[sim] = 0

    def set_timestamp_for_sim_to_now(self, sim):
        self._last_work_timestamps[sim] = self.get_next_id()

    def on_reset_sim(self, sim, reset_reason):
        self._active_work.pop(sim, None)

    def on_reset_begin(self):
        self._reset_in_progress = True

    def on_reset_end(self, *sims):
        self._reset_in_progress = False
        self._process(*sims)

    def add_interdependent_reset_records(self, sim, records):
        work_entry = self._active_work.get(sim, None)
        if work_entry is None:
            return records
        for other_sim in work_entry.resources:
            if other_sim is not sim:
                records.append(
                    ResetRecord(other_sim, ResetReason.RESET_EXPECTED, sim,
                                'Work entry resource:{}'.format(work_entry)))

    def add_global_lock(self, resource):
        self._global_required_resources.add(resource)

    def remove_global_lock(self, resource):
        self._global_required_resources.discard(resource)

    def _process_work_entry(self, sim, work_entry, requested_sims,
                            requested_resources):
        all_free = True
        must_run = not work_entry.cancelable
        immediate_cancels = []
        if work_entry.additional_resources:
            for additional_resource in work_entry.additional_resources:
                if additional_resource in requested_resources:
                    all_free = False
                    break
            else:
                requested_resources.update(work_entry.additional_resources)
        for required_sim in work_entry.resources:
            self._gsi_add_log_entry(
                sim, 'PROCESS_WORK_ENTRY',
                'Sim Resource: {}: testing if valid resource', required_sim)
            if required_sim not in self._sims:
                logger.error(
                    'Attempting to require a resource ({}) that is not managed by the MasterController.',
                    required_sim)
                self._gsi_add_log_entry(
                    sim, 'PROCESS_WORK_ENTRY',
                    'Denied because requested Sim not managed by the MC: {}.',
                    required_sim)
                all_free = False
            if required_sim in requested_sims:
                all_free = False
                self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                        'Already Requested')
            else:
                if required_sim in self._active_work:
                    self._gsi_add_log_entry(
                        sim, 'PROCESS_WORK_ENTRY',
                        'Sim Resource has Active Work: {} - ',
                        str(self._active_work[required_sim]))
                    if not must_run:
                        all_free = False
                        self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                                'Work Entry is not must run')
                    else:
                        required_work_entry = self._active_work[required_sim]
                        if not required_work_entry.cancelable:
                            all_free = False
                            requested_sims.add(required_sim)
                            self._gsi_add_log_entry(
                                sim, 'PROCESS_WORK_ENTRY',
                                'Sim Resource has work entry and cannot be canceled immediately'
                            )
                            if not required_sim.is_sim:
                                required_sim.on_requested_as_resource(
                                    work_entry)
                                self._gsi_add_log_entry(
                                    sim, 'PROCESS_WORK_ENTRY',
                                    'Sim Resource has work entry that can be canceled added to immediate_cancels'
                                )
                                immediate_cancels.append(
                                    (required_sim, required_work_entry))
                                self._gsi_add_log_entry(
                                    sim, 'PROCESS_WORK_ENTRY',
                                    'Sim Resource is free')
                        else:
                            self._gsi_add_log_entry(
                                sim, 'PROCESS_WORK_ENTRY',
                                'Sim Resource has work entry that can be canceled added to immediate_cancels'
                            )
                            immediate_cancels.append(
                                (required_sim, required_work_entry))
                            self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                                    'Sim Resource is free')
                self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                        'Sim Resource is free')
        if all_free:
            for (required_sim, required_work_entry) in immediate_cancels:
                self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                        '{} work entry canceled called.',
                                        required_sim)
                required_work_entry.cancel()
                if required_sim in self._active_work:
                    del self._active_work[required_sim]
            for required_sim in work_entry.resources:
                self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                        'work entry added to sim{}.',
                                        required_sim)
                self._active_work[required_sim] = work_entry
                requested_sims.add(required_sim)
            return True
        if sim not in self._denied_sims:
            self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                    'Entry added to denied sims.')
            if sim.is_sim:
                sim.queue.on_head_changed.append(self._process)
            self._denied_sims[sim] = work_entry
        if must_run:
            requested_sims.update(work_entry.resources)
        self._gsi_add_log_entry(sim, 'PROCESS_WORK_ENTRY',
                                'work entry NOT added to sim.')
        return False

    def _sorted_sims(self, sims):
        return sorted(
            sims,
            key=lambda sim:
            (-sim.get_next_work_priority(), self._last_work_timestamps[sim]))

    def _process(self, *sims):
        if not self._enabled or self._processing or self._reset_in_progress:
            return
        self._processing = True
        sims_filtered = list(sims)
        try:
            requested_sims = set(self._global_required_resources)
            requested_resources = set()
            for work_entry in self._active_work.values():
                if work_entry.additional_resources:
                    requested_resources.update(work_entry.additional_resources)
            new_work_accepted = []
            self._gsi_entry_initialize(*sims)
            self._gsi_add_sim_time_line_for_sims(sims, 'Start',
                                                 'Begin processing')
            sims_filtered = [
                sim for sim in sims if sim not in self._denied_sims
                if sim in self._sims
            ]
            for sim in self._sorted_sims(
                    itertools.chain(self._denied_sims, sims_filtered)):
                self._gsi_add_log_entry(sim, 'PROCESS', '----- START -----')
                if sim not in self._sims:
                    continue
                if sim in requested_sims:
                    continue
                existing_entry = self._active_work.get(sim)
                if existing_entry is not None and not existing_entry.cancelable:
                    continue
                if sim in self._denied_sims and sim.is_sim:
                    sim.queue.on_head_changed.remove(self._process)
                try:
                    work_request = sim.get_next_work()
                finally:
                    if sim in self._denied_sims and sim.is_sim:
                        sim.queue.on_head_changed.append(self._process)
                if work_request.work_element is None:
                    self._gsi_add_log_entry(sim, 'PROCESS', 'No Work Element')
                else:
                    work_entry = WorkEntry(
                        work_element=work_request.work_element,
                        resources=work_request.required_sims,
                        additional_resources=work_request.additional_resources,
                        owner=sim,
                        master_controller=self,
                        on_accept=work_request.on_accept,
                        debug_name=work_request._debug_name)
                    self._gsi_add_sim_time_line_for_sim(
                        sim, 'Create', 'Work Entry Created')
                    self._gsi_add_log_entry(
                        sim, 'PROCESS', 'Work Entry Created: required_sims:{}',
                        str(work_request.required_sims))
                    if self._process_work_entry(sim, work_entry,
                                                requested_sims,
                                                requested_resources):
                        if sim in self._denied_sims:
                            if sim.is_sim:
                                sim.queue.on_head_changed.remove(self._process)
                            del self._denied_sims[sim]
                        new_work_accepted.append((sim, work_entry))
                        if work_request.set_work_timestamp:
                            self.set_timestamp_for_sim_to_now(sim)
            for (sim, work_entry) in new_work_accepted:
                self._gsi_add_log_entry(sim, 'PROCESS',
                                        'Work Entry Start Called: {}',
                                        work_entry)
                self._gsi_add_sim_time_line_for_sim(sim, 'Start',
                                                    'Work Entry Started')
                work_entry.start()
            for sim in self._sims:
                if sim not in self._active_work:
                    (work_element_idle,
                     cancel_callable) = sim.get_idle_element()
                    if work_element_idle is not None:
                        work_entry = WorkEntry(work_element=work_element_idle,
                                               cancel_callable=cancel_callable,
                                               resources=(sim, ),
                                               owner=sim,
                                               master_controller=self)
                        self._active_work[sim] = work_entry
                        self._gsi_add_log_entry(
                            sim, 'PROCESS',
                            'No active work - run idle behavior')
                        if sim not in self._denied_sims and sim.is_sim:
                            sim.queue.on_head_changed.append(self._process)
                        self._denied_sims[sim] = work_entry
                        work_entry.start()
            self._gsi_entry_finalize()
            self._processing = False
        except:
            logger.exception(
                'Exception while processing the Master Controller.')
        finally:
            if self._processing:
                self._processing = False
                services.get_reset_and_delete_service().trigger_batch_reset(
                    sims_filtered, ResetReason.RESET_ON_ERROR, None,
                    'Exception in _process in the MasterController.')

    def _gsi_create_active_work_entry(self):
        gsi_active_work = []
        for (sim, work_entry) in self._active_work.items():
            entry = {'sim': str(sim), 'work_entry': str(work_entry)}
            gsi_active_work.append(entry)
        return gsi_active_work

    def _gsi_entry_initialize(self, *sims_being_processed):
        if gsi_handlers.master_controller_handlers.archiver.enabled:
            self._gsi_entry = {
                'sims_with_active_work':
                str([str(sim) for sim in self._active_work.keys()]),
                'last_time_stamp':
                str(self._last_work_timestamps)
            }
            self._gsi_entry[
                'active_work_start'] = self._gsi_create_active_work_entry()
            self._gsi_log_entries = []

    def _gsi_add_log_entry(self, sim, tag, log_message, *log_message_args):
        if gsi_handlers.master_controller_handlers.archiver.enabled:
            entry = {
                'sim': str(sim) if sim is not None else '',
                'tag': tag,
                'log': log_message.format(*log_message_args)
            }
            self._gsi_log_entries.append(entry)

    def _gsi_add_sim_time_line_for_sim(self, sim, status, log_message):
        if gsi_handlers.sim_timeline_handlers.archiver.enabled:
            gsi_handlers.sim_timeline_handlers.archive_sim_timeline(
                sim, 'MasterController', status, log_message)

    def _gsi_add_sim_time_line_for_sims(self, sims, status, log_message):
        if gsi_handlers.sim_timeline_handlers.archiver.enabled:
            for sim in sims:
                gsi_handlers.sim_timeline_handlers.archive_sim_timeline(
                    sim, 'MasterController', status, log_message)

    def _gsi_add_sim_time_line_entry(self, work_entry, status, log_message):
        if gsi_handlers.sim_timeline_handlers.archiver.enabled:
            for resource in work_entry.resources:
                if not resource.is_sim:
                    continue
                if resource is work_entry.owner:
                    message_to_log = '{}: as owner: {}'.format(
                        log_message, resource)
                else:
                    message_to_log = '{} as resource: {}'.format(
                        log_message, resource, log_message)
                gsi_handlers.sim_timeline_handlers.archive_sim_timeline(
                    resource, 'MasterController', status, message_to_log)

    def _gsi_entry_finalize(self):
        if gsi_handlers.master_controller_handlers.archiver.enabled:
            self._gsi_entry['sims_with_active_work_after'] = str(
                [str(sim) for sim in self._active_work.keys()])
            self._gsi_entry['last_time_stamp_end'] = str(
                self._last_work_timestamps)
            self._gsi_entry[
                'active_work_end'] = self._gsi_create_active_work_entry()
            self._gsi_entry['Log'] = self._gsi_log_entries
            gsi_handlers.master_controller_handlers.archive_master_controller_entry(
                self._gsi_entry)
            self._gsi_entry = None
            self._gsi_log_entries = None
Ejemplo n.º 5
0
import json
import time
import zlib
from sims4.gsi.schema import GsiSchema
from uid import UniqueIdGenerator
import sims4.gsi.dispatcher
import sims4.log
import sims4.reload
import sims4.zone_utils
logger = sims4.log.Logger('GSI')
with sims4.reload.protected(globals()):
    archive_data = {}
    archive_schemas = {}
    all_archivers = {}
    archive_id = UniqueIdGenerator()
ARCHIVE_DEFAULT_RECORDS = 50
ARCHIVE_MAX_RECORDS = ARCHIVE_DEFAULT_RECORDS


def set_max_archive_records(max_records):
    global ARCHIVE_MAX_RECORDS
    ARCHIVE_MAX_RECORDS = max_records


def set_max_archive_records_default():
    set_max_archive_records(ARCHIVE_DEFAULT_RECORDS)


def set_archive_enabled(archive_type, enable=True):
    if archive_type in all_archivers:
        all_archivers[archive_type].archive_enable_fn(enableLog=enable)
import re
import weakref
from gsi_handlers.gameplay_archiver import GameplayArchiver
from sims4.gsi.schema import GsiGridSchema, GsiFieldVisualizers
from uid import UniqueIdGenerator
import algos
import postures
import sims4.log
from sims4.utils import setdefault_callable

logger = sims4.log.Logger('GSI')
with sims4.reload.protected(globals()):
    gsi_path_id = UniqueIdGenerator()
    posture_transition_logs = weakref.WeakKeyDictionary()
    current_transition_interactions = weakref.WeakKeyDictionary()


class PostureTransitionGSILog:
    __qualname__ = 'PostureTransitionGSILog'

    def __init__(self):
        self.clear_log()

    def clear_log(self):
        self.current_pass = 0
        self.path = None
        self.path_success = False
        self.path_cost = 0
        self.dest_spec = None
        self.all_goals = []
        self.path_progress = 0
Ejemplo n.º 7
0
from protocolbuffers.Consts_pb2 import MSG_OBJECTS_VIEW_UPDATE, MGR_UNMANAGED
from sims4.callback_utils import consume_exceptions
from sims4.repr_utils import standard_repr
from uid import UniqueIdGenerator
import elements
import gsi_handlers
import protocolbuffers.DistributorOps_pb2
import reset
import services
import sims4.reload
__all__ = ['Distributor']
__unittest__ = 'test.distributor.system_test'
with sims4.reload.protected(globals()):
    _distributor_instance = None
    _current_tag_set = set()
    get_next_tag_id = UniqueIdGenerator()
    _distributor_log_enabled = False
    _send_index = 0
DEFAULT_MASK = protocolbuffers.Consts_pb2.OperationChannel.DESCRIPTOR.fields_by_name['mask'].default_value

def get_current_tag_set():
    return _current_tag_set

class Journal:
    __qualname__ = 'Journal'
    JournalEntry = namedtuple('JournalEntry', ('object', 'protocol_buffer', 'manager_id', 'debug_object_name'))
    JournalEntry.__repr__ = lambda self: standard_repr(self, self.object, self.protocol_buffer, self.manager_id, self.debug_object_name)

    def __init__(self):
        self.entries = []
        self.deferred_ops = []
Ejemplo n.º 8
0
 def __init__(self, owner):
     super().__init__(owner)
     self._censor_grid_handles = collections.defaultdict(list)
     self._censor_state = CensorState.OFF
     self._get_next_handle = UniqueIdGenerator()
Ejemplo n.º 9
0
class AffordanceObjectPair:
    get_next_aop_id = UniqueIdGenerator(0, MAX_UINT16)

    def __init__(self,
                 affordance,
                 target,
                 sa,
                 si,
                 liabilities=None,
                 skip_safe_tests=False,
                 skip_test_on_execute=False,
                 **kwargs):
        self.affordance = affordance
        self.super_affordance = sa
        self.aop_id = self.get_next_aop_id()
        self._constraint_cache = None
        self.super_interaction = si
        self.target = target
        self.content_score = None
        self.autonomy_selection_time = 0
        self.lifetime_in_steps = 1
        self._liabilities = liabilities
        self.skip_safe_tests = skip_safe_tests
        self.skip_test_on_execute = skip_test_on_execute
        self.show_posture_incompatible_icon = False
        self._kwargs = kwargs

    def is_equivalent_to(self, other):
        return type(self) == type(other) and (
            self.affordance == other.affordance and
            (self.super_affordance == other.super_affordance and
             (self.super_interaction == other.super_interaction and
              (self.target == other.target and self.interaction_parameters
               == other.interaction_parameters))))

    @property
    def target(self):
        if self._target:
            return self._target()

    @target.setter
    def target(self, target):
        self._target = target.ref() if target is not None else None

    def constraint_intersection(self,
                                sim=DEFAULT,
                                target=DEFAULT,
                                posture_state=DEFAULT,
                                participant_type=ParticipantType.Actor,
                                include_super_affordance_constraint=False,
                                **kwargs):
        if self._constraint_cache is None:
            self._constraint_cache = WeakKeyDictionary()
        if sim in self._constraint_cache:
            return self._constraint_cache[sim]
        constraint = self.affordance.constraint_intersection(
            sim=sim,
            target=self.target,
            posture_state=posture_state,
            participant_type=participant_type,
            **kwargs)
        if include_super_affordance_constraint:
            si_constraint = self.super_affordance.constraint_intersection(
                sim=sim,
                target=self.target,
                posture_state=posture_state,
                participant_type=participant_type,
                **kwargs)
            constraint = constraint.intersect(si_constraint)
        self._constraint_cache[sim] = constraint
        return constraint

    @property
    def interaction_parameters(self):
        return self._kwargs

    def test(self, context, skip_safe_tests=DEFAULT, **kwargs) -> TestResult:
        if self.super_interaction is not None and not self.super_interaction.can_run_subinteraction(
                self, context=context):
            return TestResult(False, 'SuperInteraction is finishing.')
        combined_kwargs = dict(self._kwargs)
        combined_kwargs.update(kwargs)
        if skip_safe_tests is DEFAULT:
            skip_safe_tests = self.skip_safe_tests
        return self.affordance.test(target=self.target,
                                    context=context,
                                    liabilities=self._liabilities,
                                    super_interaction=self.super_interaction,
                                    skip_safe_tests=skip_safe_tests,
                                    **combined_kwargs)

    @staticmethod
    def execute_interaction(interaction) -> ExecuteResult:
        if interaction:
            if interaction.affordance.cheat:
                with telemetry_helper.begin_hook(
                        sims4.commands.cheats_writer,
                        sims4.commands.TELEMETRY_HOOK_INTERACTION) as hook:
                    hook.write_string(sims4.commands.TELEMETRY_FIELD_NAME,
                                      interaction.__str__())
                    hook.write_string(sims4.commands.TELEMETRY_FIELD_TARGET,
                                      str(interaction.target))
            if interaction.affordance.immediate:
                interaction._trigger_interaction_start_event()
                immediate_timeline = services.time_service(
                ).sim_timeline.get_sub_timeline()
                result_element = elements.ResultElement(
                    elements.GeneratorElement(interaction._run_gen))
                immediate_timeline.schedule(result_element)
                immediate_timeline.simulate(immediate_timeline.now)
                if immediate_timeline.heap:
                    logger.error(
                        'On immediate execute_interaction of {}, immediate timeline is not empty, heap:{}',
                        interaction, immediate_timeline.heap)
                    immediate_timeline.heap.clear()
                run_result = result_element.result
                interaction._trigger_interaction_complete_test_event()
                if run_result:
                    exit_behavior = interaction._exit(None, False)
                    try:
                        next(exit_behavior)
                        logger.error(
                            'Running immediate exit_behavior yielded despite allow_yield=False'
                        )
                    except StopIteration:
                        pass
                execute_result = ExecuteResult(run_result, interaction, None)
                log_interaction('Immediate', interaction,
                                '{}'.format(execute_result))
                return execute_result
            context = interaction.context
            return ExecuteResult(context.sim.queue.append(interaction),
                                 interaction, None)
        return ExecuteResult(False, None,
                             'Trying to execute a None interaction.')

    def execute(self, context) -> ExecuteResult:
        result = self.interaction_factory(context)
        if not result:
            return result
        return self.execute_interaction(result.interaction)

    def test_and_execute(self, context, **kwargs) -> EnqueueResult:
        test_result = self.test(context, **kwargs)
        execute_result = None
        if test_result:
            execute_result = self.execute(context)
        return EnqueueResult(test_result, execute_result)

    def is_equivalent_to_interaction(self, interaction):
        return interaction.is_equivalent(self.affordance, target=self.target)

    def is_linked_to(self, super_affordance):
        return self.super_affordance.is_linked_to(super_affordance)

    def interaction_factory(self, context) -> ExecuteResult:
        si = self.super_interaction
        if si is not None and not si.can_run_subinteraction(self, context):
            return ExecuteResult(
                False, None, 'SuperInteraction cannot run SubInteraction.')
        try:
            interaction = self.affordance(self,
                                          context,
                                          liabilities=self._liabilities,
                                          **self._kwargs)
        except Exception as exc:
            from interactions.base.interaction import logger
            logger.exception('{}: Error instantiating affordance:', self)
            return ExecuteResult(
                False, None, 'Error instantiating affordance: {}'.format(exc))
        if si is not None:
            interaction.super_interaction = si
        if context.continuation_id is not None:
            services.get_master_controller().reset_timestamp_for_sim(
                context.sim)
        return ExecuteResult(True, interaction, None)

    def name(self, context):
        return self.affordance.get_name(self.target, context)

    def __repr__(self):
        return '<AffordanceInstance; %s on %s>' % (self.affordance,
                                                   self.target)

    def __str__(self):
        if self.affordance is not None:
            affordance_name = self.affordance.__name__
        else:
            affordance_name = 'None'
        return '%s on %s' % (affordance_name, self.target)

    def get_provided_posture_change(self):
        return self.affordance.get_provided_posture_change(self)

    @property
    def basic_content(self):
        return self.affordance.basic_content

    @property
    def provided_posture_type(self):
        return self.affordance.provided_posture_type

    def compatible_with_current_posture_state(self, sim):
        sim_constraint = self.constraint_intersection(
            sim=sim,
            posture_state=None,
            include_super_affordance_constraint=True)
        sim_posture_state_constraint = sim.posture_state.posture_constraint
        if self.affordance.is_social and self.target is not None and self.target.is_sim:
            target_sim = self.target
            target_sim_constraint = self.constraint_intersection(
                sim=target_sim,
                posture_state=None,
                participant_type=ParticipantType.TargetSim,
                include_super_affordance_constraint=True)
            listener_constraint = self.constraint_intersection(
                sim=target_sim,
                posture_state=None,
                participant_type=ParticipantType.Listeners,
                include_super_affordance_constraint=True)
            target_constraint = target_sim_constraint.intersect(
                listener_constraint)
            target_posture_state_constraint = target_sim.posture_state.posture_constraint
            if not sim.posture.mobile:
                import socials.group
                sim_social_constraint = socials.group.create_social_circle_constraint_around_sim(
                    sim)
                if not (sim_social_constraint.valid
                        and sim_social_constraint.geometry.contains_point(
                            target_sim.position) and sim.can_see(target_sim)):
                    return False
            if sim_constraint.intersect(SIT_INTIMATE_CONSTRAINT).valid and (
                    self.target is not None and
                (self.target.is_sim and
                 (sim.posture.target is not None and
                  (self.target.posture.target is not None and
                   (sim.posture.target.is_part and
                    (target_sim.posture.target.is_part and
                     (sim.posture.target in list(
                         target_sim.posture.target.adjacent_parts_gen())
                      and sim_posture_state_constraint.intersect(
                          SIT_NO_CARRY_ANY_SURFACE_CONSTRAINT).valid))))))
            ) and target_posture_state_constraint.intersect(
                    SIT_NO_CARRY_ANY_SURFACE_CONSTRAINT).valid:
                return True
            if sim_constraint.intersect(
                    SIT_NO_CARRY_ANY_SURFACE_CONSTRAINT).valid and (
                        self.target is not None and
                        (self.target.is_sim and sim_posture_state_constraint.
                         intersect(SIT_INTIMATE_CONSTRAINT).valid)
                    ) and target_posture_state_constraint.intersect(
                        SIT_INTIMATE_CONSTRAINT).valid:
                return True
        if not sim.posture.mobile:
            sim_transform_constraint = interactions.constraints.Transform(
                sim.intended_transform,
                routing_surface=sim.intended_routing_surface)
            sim_constraint = sim_constraint.intersect(sim_transform_constraint)
            if not sim_constraint.valid:
                return False
        sim_intersection = sim_constraint.intersect(
            sim_posture_state_constraint)
        if not sim_intersection.valid:
            return False
        if self.target.is_sim:
            if not target_sim.posture.mobile:
                target_sim_transform_constraint = interactions.constraints.Transform(
                    target_sim.intended_transform,
                    routing_surface=sim.intended_routing_surface)
                target_constraint = target_constraint.intersect(
                    target_sim_transform_constraint)
                if not target_constraint.valid:
                    return False
                else:
                    target_intersection = target_constraint.intersect(
                        target_posture_state_constraint)
                    if not target_intersection.valid:
                        return False
            else:
                target_intersection = target_constraint.intersect(
                    target_posture_state_constraint)
                if not target_intersection.valid:
                    return False
        return True
Ejemplo n.º 10
0
class DynamicInteractionSpawnPoint(SpawnPoint):
    __qualname__ = 'DynamicInteractionSpawnPoint'
    get_next_id = UniqueIdGenerator()

    def __init__(self,
                 interaction,
                 participant_type,
                 distance_to_participant,
                 tag_set,
                 lot_id,
                 zone_id,
                 routing_surface=None):
        super().__init__(lot_id, zone_id, routing_surface=routing_surface)
        self.interaction = interaction
        self.participant_type = participant_type
        self.distance_to_participant = distance_to_participant
        self.obj_def_guid = 0
        self.spawn_point_id = DynamicInteractionSpawnPoint.get_next_id()
        self._tags = tag_set
        if routing_surface is None:
            self._routing_surface = routing.SurfaceIdentifier(
                services.current_zone().id, 0, routing.SURFACETYPE_WORLD)
        self.location = None
        self.spawn_index = 0
        self._valid_slots = 1

    @property
    def routing_surface(self):
        participant = self.get_participant()
        if participant is not None:
            self._routing_surface = participant.routing_surface
        return self._routing_surface

    def next_spawn_spot(self):
        trans = self.get_slot_pos()
        orient = self.get_orientation_to_participant(trans)
        return (trans, orient)

    def get_name(self):
        participant = self.interaction.get_participant(
            participant_type=self.participant_type)
        return 'Dynamic Spawn Point near {} in {}'.format(
            participant, self.interaction)

    def get_participant(self):
        if self.interaction is None:
            return
        return self.interaction.get_participant(self.participant_type)

    def reset_valid_slots(self):
        self._valid_slots = 1

    def get_slot_pos(self, index=None):
        participant = self.get_participant()
        trans = None
        if participant is not None:
            (trans, _) = placement.find_good_location(
                placement.FindGoodLocationContext(
                    starting_location=participant.location,
                    max_distance=FGLTuning.MAX_FGL_DISTANCE,
                    additional_avoid_sim_radius=routing.
                    get_default_agent_radius(),
                    object_id=participant.id,
                    max_steps=10,
                    offset_distance=self.distance_to_participant,
                    scoring_functions=(placement.ScoringFunctionRadial(
                        participant.location.transform.translation,
                        self.distance_to_participant, 0,
                        FGLTuning.MAX_FGL_DISTANCE), ),
                    search_flags=placement.FGLSearchFlag.
                    STAY_IN_CONNECTED_CONNECTIVITY_GROUP
                    | placement.FGLSearchFlag.SHOULD_TEST_BUILDBUY
                    | placement.FGLSearchFlag.USE_SIM_FOOTPRINT |
                    placement.FGLSearchFlag.CALCULATE_RESULT_TERRAIN_HEIGHTS))
        if trans is None:
            fallback_point = services.current_zone().get_spawn_point(
                lot_id=self.lot_id)
            (trans, _) = fallback_point.next_spawn_spot()
            return trans
        return trans

    def get_orientation_to_participant(self, position):
        participant = self.get_participant()
        if participant is None:
            return sims4.math.Quaternion.IDENTITY()
        target_location = participant.location
        vec_to_target = target_location.transform.translation - position
        theta = sims4.math.vector3_angle(vec_to_target)
        return sims4.math.angle_to_yaw_quaternion(theta)

    def get_position_constraints(self):
        trans = self.get_slot_pos()
        return [
            interactions.constraints.Position(
                trans,
                routing_surface=self.routing_surface,
                objects_to_ignore=set([self.spawn_point_id]))
        ]

    def get_footprint_polygon(self):
        pass

    def get_slot_positions(self):
        return [self.get_slot_pos()]
Ejemplo n.º 11
0
                return selection.choice.make_test_pass(context)
            else:
                logger.warn(
                    'Attempt to select invalid interaction from a ChoiceMenu')
        if not self.make_pass:
            return EnqueueResult.NONE
        return TestResult.NONE

    def clear(self):
        self.menu_items.clear()
        self.objects = None
        self.context = None
        self.simref = None


choice_collection_id_gen = UniqueIdGenerator(1)


class ChoiceMenuCollection:
    __qualname__ = 'ChoiceMenuCollection'

    def __init__(self, callback=None):
        self.menus = weakref.WeakKeyDictionary()
        self.revision = choice_collection_id_gen()
        self._callback = callback
        self._visible_choice_ids = set()

    @property
    def has_visible_choices(self):
        return len(self._visible_choice_ids)
Ejemplo n.º 12
0
class BouncerRequest:
    TARDY_SIM_TIME = TunableSimMinute(
        description=
        'Amount of time until a sim coming to a situation is considered tardy',
        default=30,
        tuning_filter=FilterTag.EXPERT_MODE)
    _get_next_creation_id = UniqueIdGenerator(1)
    BOUNCER_PRIORITY_INDEX_MULTIPLIER = 2

    def __init__(self,
                 situation,
                 callback_data,
                 job_type,
                 request_priority,
                 user_facing,
                 exclusivity,
                 requested_sim_id=0,
                 accept_alternate_sim=False,
                 blacklist_sim_ids=None,
                 spawning_option=RequestSpawningOption.DONT_CARE,
                 requesting_sim_info=None,
                 expectation_preference=False,
                 common_blacklist_categories=0,
                 for_persisted_sim=False,
                 elevated_importance_override=False,
                 accept_looking_for_more_work=False,
                 specific_spawn_point=None,
                 specific_location=None):
        self._situation = situation
        self._callback_data = callback_data
        self._job_type = job_type
        self._sim_filter = job_type.filter
        self._spawner_tags = job_type.sim_spawner_tags
        self._spawn_at_lot = job_type.spawn_at_lot
        self._spawn_action = job_type.get_spawn_action(job_type)
        self._spawn_point_option = job_type.sim_spawner_leave_option
        self._saved_spawner_tags = job_type.sim_spawner_leave_saved_tags
        self._requested_sim_id = requested_sim_id
        self._constrained_sim_ids = {requested_sim_id
                                     } if requested_sim_id != 0 else None
        self._continue_if_constraints_fail = accept_alternate_sim
        self._blacklist_sim_ids = blacklist_sim_ids
        self._status = BouncerRequestStatus.INITIALIZED
        self._sim = None
        self._user_facing = user_facing
        self._request_priority = request_priority
        self._spawning_option = spawning_option
        self._requesting_sim_info = requesting_sim_info
        self._exclusivity = exclusivity
        self._creation_id = self._get_next_creation_id()
        self._expectation_preference = expectation_preference
        self._common_blacklist_categories = common_blacklist_categories
        self._for_persisted_sim = for_persisted_sim
        self.elevated_importance_override = elevated_importance_override
        if self._is_explicit:
            unfulfilled_index = 0
        else:
            unfulfilled_index = len(BouncerRequestPriority
                                    ) * self.BOUNCER_PRIORITY_INDEX_MULTIPLIER
        unfulfilled_index += self._request_priority * self.BOUNCER_PRIORITY_INDEX_MULTIPLIER
        if not self._job_type.elevated_importance:
            if not self.elevated_importance_override:
                unfulfilled_index += 1
        self._unfulfilled_index = unfulfilled_index
        self._sim_spawner_service_request = None
        self._accept_looking_for_more_work = accept_looking_for_more_work
        self._specific_spawn_point = specific_spawn_point
        self._specific_location = specific_location

    @property
    def situation(self):
        return self._situation

    def _destroy(self, reason=None):
        self._status = BouncerRequestStatus.DESTROYED
        self._sim_spawner_service_request = None
        self._sim = None
        if gsi_handlers.situation_handlers.bouncer_archiver.enabled:
            gsi_handlers.situation_handlers.archive_bouncer_request(
                self, 'DESTROYED', status_reason=reason)

    def __str__(self):
        return 'Request(situation: {}, sim id: {}, filter: {})'.format(
            self._situation, self._requested_sim_id, self._sim_filter)

    def _submit(self):
        self._status = BouncerRequestStatus.SUBMITTED
        self._reset_tardy()
        if gsi_handlers.situation_handlers.bouncer_archiver.enabled:
            gsi_handlers.situation_handlers.archive_bouncer_request(
                self, 'SUBMITTED')

    def _can_assign_sim_to_request(self, sim):
        return True

    def _assign_sim(self, sim, silently=False):
        assert not self._sim is not None
        self._status = BouncerRequestStatus.FULFILLED
        self._sim = sim
        if gsi_handlers.situation_handlers.bouncer_archiver.enabled:
            gsi_handlers.situation_handlers.archive_bouncer_request(
                self, 'SIM ASSIGNED')
        if silently == False:
            self._situation.on_sim_assigned_to_request(sim, self)

    def _unassign_sim(self, sim, silently=False):
        if self._status == BouncerRequestStatus.DESTROYED:
            return
        assert not self._is_sim_assigned_to_request(sim) == False
        self._sim = None
        if gsi_handlers.situation_handlers.bouncer_archiver.enabled:
            gsi_handlers.situation_handlers.archive_bouncer_request(
                self, 'SIM UNASSIGNED', sim_override=sim)
        if silently == False:
            self._situation.on_sim_unassigned_from_request(sim, self)

    def _change_assigned_sim(self, new_sim):
        if gsi_handlers.situation_handlers.bouncer_archiver.enabled:
            gsi_handlers.situation_handlers.archive_bouncer_request(
                self, 'ASSIGNEMENT CHANGED START')
        old_sim = self._sim
        self._unassign_sim(old_sim, silently=True)
        self._assign_sim(new_sim, silently=True)
        self._situation.on_sim_replaced_in_request(old_sim, new_sim, self)
        if gsi_handlers.situation_handlers.bouncer_archiver.enabled:
            gsi_handlers.situation_handlers.archive_bouncer_request(
                self, 'ASSIGNEMENT CHANGED END')

    def _is_sim_assigned_to_request(self, sim):
        return self._sim is sim

    @property
    def _assigned_sim(self):
        return self._sim

    @property
    def _allows_spawning(self):
        if self._spawning_option == RequestSpawningOption.CANNOT_SPAWN:
            return False
        if self._requested_sim_id == 0:
            return True
        sim_info = services.sim_info_manager().get(self._requested_sim_id)
        if sim_info is None:
            return True
        elif sim_info.get_sim_instance(
                allow_hidden_flags=ALL_HIDDEN_REASONS) is not None:
            return False
        return True

    def _can_spawn_now(self, during_zone_spin_up_service):
        if not self._allows_spawning:
            return False
        if not during_zone_spin_up_service:
            return True
        return self._situation.spawn_sims_during_zone_spin_up

    @property
    def _requires_spawning(self):
        return self._spawning_option == RequestSpawningOption.MUST_SPAWN

    @property
    def spawn_at_lot(self):
        return self._spawn_at_lot

    def spawner_tags(self, during_zone_spin_up=False):
        if during_zone_spin_up and self._situation.is_traveling_situation:
            return (world.spawn_point.SpawnPoint.ARRIVAL_SPAWN_POINT_TAG, )
        return self._spawner_tags

    def raw_spawner_tags(self):
        return self._spawner_tags

    @property
    def sim_spawn_reason(self):
        if self._situation.is_situation_of_elevated_importance:
            return sims.sim_spawner_service.SimSpawnReason.IMPORTANT_SITUATION
        if self._situation.situation_serialization_option == situations.situation_types.SituationSerializationOption.LOT:
            return sims.sim_spawner_service.SimSpawnReason.ZONE_SITUATION
        if self._situation.situation_serialization_option == situations.situation_types.SituationSerializationOption.OPEN_STREETS:
            return sims.sim_spawner_service.SimSpawnReason.OPEN_STREETS_SITUATION
        return sims.sim_spawner_service.SimSpawnReason.DEFAULT

    @property
    def should_preroll_during_zone_spin_up(self):
        return not self._situation.is_traveling_situation

    @property
    def elevated_importance(self):
        return self._job_type.elevated_importance or self.elevated_importance_override

    @property
    def spawn_point_option(self):
        return self._spawn_point_option

    @property
    def saved_spawner_tags(self):
        return self._saved_spawner_tags

    @property
    def _initiating_sim_info(self):
        return self._situation.initiating_sim_info

    @property
    def _should_use_auto_fill_blacklist(self):
        return self._request_priority == BouncerRequestPriority.EVENT_AUTO_FILL or (
            self._request_priority == BouncerRequestPriority.BACKGROUND_HIGH or
            (self._request_priority == BouncerRequestPriority.BACKGROUND_MEDIUM
             or self._request_priority
             == BouncerRequestPriority.BACKGROUND_LOW))

    def _get_blacklist(self):
        if self._blacklist_sim_ids:
            blacklist = set(self._blacklist_sim_ids)
        else:
            blacklist = set()
        if self._should_use_auto_fill_blacklist:
            blacklist = blacklist | services.get_zone_situation_manager(
            ).get_auto_fill_blacklist(self._job_type)
        return blacklist

    @property
    def common_blacklist_categories(self):
        return self._common_blacklist_categories

    @property
    def _is_obsolete(self):
        return self._status == BouncerRequestStatus.FULFILLED and self._sim is None

    @property
    def _is_tardy(self):
        if self._status == BouncerRequestStatus.FULFILLED or self._status == BouncerRequestStatus.DESTROYED:
            return False
        return self._tardy_time < services.time_service().sim_now

    def _reset_tardy(self):
        self._tardy_time = services.time_service(
        ).sim_now + clock.interval_in_sim_minutes(
            BouncerRequest.TARDY_SIM_TIME)

    @property
    def _is_fulfilled(self):
        return self._status == BouncerRequestStatus.FULFILLED

    @property
    def _is_explicit(self):
        return self._requested_sim_id != 0

    @property
    def _has_assigned_sims(self):
        return self._sim is not None

    def _exclusivity_compare(self, other):
        return exclusivity_compare(self, other)

    @property
    def _is_factory(self):
        return False

    def _get_request_klout(self):
        if self._situation.has_no_klout:
            return
        klout = self._request_priority * 4
        if not self.elevated_importance:
            klout += 1
        if not self._user_facing:
            klout += 2
        return klout

    def get_additional_filter_terms(self):
        return self._job_type.get_location_based_filter_terms()

    @property
    def callback_data(self):
        return self._callback_data

    def clone_for_replace(self, only_if_explicit=False):
        if only_if_explicit and not self._is_explicit:
            return
        request = BouncerRequest(
            self._situation,
            self._callback_data,
            self._job_type,
            self._request_priority,
            user_facing=self._user_facing,
            exclusivity=self._exclusivity,
            blacklist_sim_ids=self._blacklist_sim_ids,
            spawning_option=self._spawning_option,
            requesting_sim_info=self._requesting_sim_info,
            accept_looking_for_more_work=self._accept_looking_for_more_work)
        return request

    @property
    def assigned_sim(self):
        return self._assigned_sim

    @property
    def requested_sim_id(self):
        return self._requested_sim_id

    @property
    def is_factory(self):
        return self._is_factory

    @property
    def job_type(self):
        return self._job_type

    @property
    def spawning_option(self):
        return self._spawning_option

    @property
    def request_priority(self):
        return self._request_priority

    @property
    def expectation_preference(self):
        return self._expectation_preference

    @property
    def accept_alternate_sim(self):
        return self._continue_if_constraints_fail

    @property
    def specific_spawn_point(self):
        return self._specific_spawn_point

    @property
    def specific_location(self):
        return self._specific_location
Ejemplo n.º 13
0
class FocusInterestTuning:
    __qualname__ = 'FocusInterestTuning'
    FOCUS_INTEREST_LEVEL_TO_SCORE = TunableMapping(
        key_type=TunableEnumEntry(FocusInterestLevel, FocusInterestLevel.LOW),
        value_type=Tunable(
            int,
            1,
            description=
            '\n        This is a weighting relative to other focus values, chosen via weighted random from all objects\n        near the sim.'
        ),
        description='\n        Focus score per focus level.')


_normal_logger = sims4.log.LoggerClass('Focus')
logger = _normal_logger
id_gen = UniqueIdGenerator(1)
active_focus = []


def get_next_focus_id():
    return id_gen()


def log_to_cheat_console(_connection=None):
    pass


def FocusPrintAll(_connection=None):
    console = sims4.log.CheatLogger(_normal_logger.group, _connection)
    for l in active_focus:
        focus_id = l[0]
Ejemplo n.º 14
0
from gsi_handlers.distributor_handlers import archive_operation
from sims4.callback_utils import consume_exceptions
from sims4.repr_utils import standard_repr
from uid import UniqueIdGenerator
import elements
import gsi_handlers
import reset
import services
import sims4.reload

__all__ = ['Distributor']
__unittest__ = 'test.distributor.system_test'
with sims4.reload.protected(globals()):
    _distributor_instance = None
    _current_tag_set = set()
    get_next_tag_id = UniqueIdGenerator(min_uid=1)
    _distributor_log_enabled = False
    _send_index = 0
DEFAULT_MASK = protocolbuffers.Consts_pb2.OperationChannel.DESCRIPTOR.fields_by_name[
    'mask'].default_value


def get_current_tag_set():
    return _current_tag_set


class Journal:
    JournalEntry = namedtuple('JournalEntry',
                              ('object', 'protocol_buffer', 'payload_type',
                               'manager_id', 'debug_object_name'))
    JournalEntry.__repr__ = lambda self: standard_repr(
import collections
import sys
import traceback
import weakref
from gsi_handlers.gameplay_archiver import GameplayArchiver
from sims4.gsi.schema import GsiGridSchema
from sims4.utils import setdefault_callable
from uid import UniqueIdGenerator
import interactions
import services
import sims4.log
logger = sims4.log.Logger('GSI')
with sims4.reload.protected(globals()):
    gsi_log_id = UniqueIdGenerator()
    interaction_archive = weakref.WeakKeyDictionary()


class InteractionArchiveGSILog:
    def __init__(self):
        self.clear_log()

    def clear_log(self):
        self.status = None
        self.exit_reason = None
        self.asms_and_actors = collections.defaultdict(dict)
        self.participants = []
        self.animation_data = []
        self.constraints = []
        self.exit_reasons = []
        self.cancel_callstack = []
Ejemplo n.º 16
0
class BouncerRequest:
    __qualname__ = 'BouncerRequest'
    TARDY_SIM_TIME = TunableSimMinute(description='Amount of time until a sim coming to a situation is considered tardy', default=30, tuning_filter=FilterTag.EXPERT_MODE)
    _get_next_creation_id = UniqueIdGenerator(1)

    def __init__(self, situation, callback_data, job_type, request_priority, user_facing, exclusivity, requested_sim_id=0, accept_alternate_sim=False, blacklist_sim_ids=None, spawning_option=RequestSpawningOption.DONT_CARE, requesting_sim_info=None, expectation_preference=False, loaded=False, common_blacklist_categories=0, spawn_during_zone_spin_up=False):
        self._situation = situation
        self._callback_data = callback_data
        self._job_type = job_type
        self._sim_filter = job_type.filter
        self._spawner_tags = job_type.sim_spawner_tags
        self._spawn_action = job_type.sim_spawn_action
        self._spawn_point_option = job_type.sim_spawner_leave_option
        self._requested_sim_id = requested_sim_id
        self._constrained_sim_ids = {requested_sim_id} if requested_sim_id != 0 else None
        self._continue_if_constraints_fail = accept_alternate_sim
        self._blacklist_sim_ids = blacklist_sim_ids
        self._status = BouncerRequestStatus.INITIALIZED
        self._sim = None
        self._user_facing = user_facing
        self._request_priority = request_priority
        self._spawning_option = spawning_option
        self._requesting_sim_info = requesting_sim_info
        self._exclusivity = exclusivity
        self._creation_id = self._get_next_creation_id()
        self._expectation_preference = expectation_preference
        self._loaded = loaded
        self._common_blacklist_categories = common_blacklist_categories
        self._spawn_during_zone_spin_up = spawn_during_zone_spin_up
        if self._is_explicit:
            unfulfilled_index = 0
        else:
            unfulfilled_index = BouncerRequestPriority.COUNT*2
        unfulfilled_index += self._request_priority*2
        if not self._user_facing:
            unfulfilled_index += 1
        self._unfulfilled_index = unfulfilled_index

    def _destroy(self):
        self._status = BouncerRequestStatus.DESTROYED
        self._sim = None

    def __str__(self):
        return 'Request(situation: {}, sim id: {}, filter: {})'.format(self._situation, self._requested_sim_id, self._sim_filter)

    def _submit(self):
        self._status = BouncerRequestStatus.SUBMITTED
        self._reset_tardy()

    def _can_assign_sim_to_request(self, sim):
        return True

    def _assign_sim(self, sim, silently=False):
        if self._sim is not None:
            raise AssertionError('Attempting to assign sim: {} to a request: {} that already has a sim: {}'.format(sim, self, self._sim))
        self._status = BouncerRequestStatus.FULFILLED
        self._sim = sim
        if silently == False:
            self._situation.on_sim_assigned_to_request(sim, self)

    def _unassign_sim(self, sim, silently=False):
        if self._is_sim_assigned_to_request(sim) == False:
            raise AssertionError('Attempting to unassign sim {} from a request {} that it is not assigned to{}'.format(sim, self))
        self._sim = None
        if silently == False:
            self._situation.on_sim_unassigned_from_request(sim, self)

    def _change_assigned_sim(self, new_sim):
        old_sim = self._sim
        self._unassign_sim(old_sim, silently=True)
        self._assign_sim(new_sim, silently=True)
        self._situation.on_sim_replaced_in_request(old_sim, new_sim, self)

    def _is_sim_assigned_to_request(self, sim):
        return self._sim is sim

    @property
    def _assigned_sim(self):
        return self._sim

    @property
    def _allows_spawning(self):
        if self._spawning_option == RequestSpawningOption.CANNOT_SPAWN:
            return False
        if self._requested_sim_id == 0:
            return True
        sim_info = services.sim_info_manager().get(self._requested_sim_id)
        if sim_info is None:
            return True
        if sim_info.get_sim_instance(allow_hidden_flags=ALL_HIDDEN_REASONS) is not None:
            return False
        return True

    def _can_spawn_now(self, during_zone_spin_up_service):
        if not self._allows_spawning:
            return False
        if not during_zone_spin_up_service:
            return True
        return self._spawn_during_zone_spin_up or self._situation.spawn_sims_during_zone_spin_up

    @property
    def _requires_spawning(self):
        return self._spawning_option == RequestSpawningOption.MUST_SPAWN

    def spawner_tags(self, during_zone_spin_up=False):
        if during_zone_spin_up and self._situation.is_traveling_situation:
            return (world.spawn_point.SpawnPoint.ARRIVAL_SPAWN_POINT_TAG,)
        return self._spawner_tags

    def raw_spawner_tags(self):
        return self._spawner_tags

    @property
    def should_preroll_during_zone_spin_up(self):
        return not self._situation.is_traveling_situation

    @property
    def spawn_point_option(self):
        return self._spawn_point_option

    @property
    def _initiating_sim_info(self):
        return self._situation.initiating_sim_info

    def _get_blacklist(self):
        if self._blacklist_sim_ids:
            blacklist = set(self._blacklist_sim_ids)
        else:
            blacklist = set()
        if self._request_priority == BouncerRequestPriority.AUTO_FILL or self._request_priority == BouncerRequestPriority.AUTO_FILL_PLUS:
            blacklist = blacklist | services.get_zone_situation_manager().get_auto_fill_blacklist()
        return blacklist

    @property
    def common_blacklist_categories(self):
        return self._common_blacklist_categories

    @property
    def _is_obsolete(self):
        return self._status == BouncerRequestStatus.FULFILLED and self._sim is None

    @property
    def _is_tardy(self):
        if self._status != BouncerRequestStatus.SUBMITTED:
            return False
        return self._tardy_time < services.time_service().sim_now

    def _reset_tardy(self):
        self._tardy_time = services.time_service().sim_now + clock.interval_in_sim_minutes(BouncerRequest.TARDY_SIM_TIME)

    @property
    def _is_fulfilled(self):
        return self._status == BouncerRequestStatus.FULFILLED

    @property
    def _is_explicit(self):
        return self._requested_sim_id != 0

    @property
    def _has_assigned_sims(self):
        return self._sim is not None

    @property
    def _must_assign_on_load(self):
        return self._loaded

    def _exclusivity_compare(self, other):
        rule = situations.bouncer.bouncer.Bouncer.are_mutually_exclusive(self._exclusivity, other._exclusivity)
        if rule is None:
            return 0

        def determine_result(trumping_category):
            if self._exclusivity == trumping_category:
                return 1
            return -1

        option = rule[2]
        if option == BouncerExclusivityOption.EXPECTATION_PREFERENCE:
            if self._expectation_preference and not other._expectation_preference:
                return determine_result(self._exclusivity)
            if not self._expectation_preference and other._expectation_preference:
                return determine_result(other._exclusivity)
            if self._expectation_preference and other._expectation_preference:
                if self._creation_id >= other._creation_id:
                    return determine_result(self._exclusivity)
                return determine_result(other._exclusivity)
            return determine_result(rule[0])
        if option == BouncerExclusivityOption.NONE:
            return determine_result(rule[0])
        if option == BouncerExclusivityOption.ERROR:
            logger.error('Unexpected Bouncer exclusivity pairing Request:{}, Request:{}. Tell SScholl', self, other)
            return determine_result(rule[0])
        if option == BouncerExclusivityOption.ALREADY_ASSIGNED:
            return determine_result(self._exclusivity)

    @property
    def _is_factory(self):
        return False

    def _get_request_klout(self):
        klout = self._request_priority*2
        if not self._user_facing:
            klout += 1
        return klout

    @property
    def callback_data(self):
        return self._callback_data

    def clone_for_replace(self, only_if_explicit=False):
        if only_if_explicit and not self._is_explicit:
            return
        request = BouncerRequest(self._situation, self._callback_data, self._job_type, self._request_priority, user_facing=self._user_facing, exclusivity=self._exclusivity, blacklist_sim_ids=self._blacklist_sim_ids, spawning_option=self._spawning_option, requesting_sim_info=self._requesting_sim_info)
        return request

    @property
    def assigned_sim(self):
        return self._assigned_sim

    @property
    def requested_sim_id(self):
        return self._requested_sim_id

    @property
    def is_factory(self):
        return self._is_factory

    @property
    def job_type(self):
        return self._job_type

    @property
    def spawning_option(self):
        return self._spawning_option

    @property
    def request_priority(self):
        return self._request_priority

    @property
    def expectation_preference(self):
        return self._expectation_preference

    @property
    def accept_alternate_sim(self):
        return self._continue_if_constraints_fail
live_drag_schema.add_field('live_drag_target', label='Drop Target', width=1)
live_drag_schema.add_field('live_drag_stack_id',
                           label='Stack ID',
                           type=GsiFieldVisualizers.INT,
                           width=1,
                           hidden=True)
live_drag_schema.add_field('live_drag_stack_count',
                           label='Stack Count',
                           type=GsiFieldVisualizers.INT,
                           width=1)
live_drag_schema.add_field('live_drag_object_inventory',
                           label='Inventory',
                           width=1)
live_drag_archiver = GameplayArchiver('live_drag', live_drag_schema)
with sims4.reload.protected(globals()):
    _live_drag_index = UniqueIdGenerator()


def archive_live_drag(op_or_command,
                      message_type,
                      location_from,
                      location_to,
                      live_drag_object=None,
                      live_drag_object_id: int = 0,
                      live_drag_target=None):
    definition_id = 0
    stack_id = 0
    stack_count = 1
    can_live_drag = False
    current_inventory = None
    if live_drag_object is None:
Ejemplo n.º 18
0
from story_progression.story_progression_enums import CullingReasons
from tunable_time import TunableTimeOfDay
from uid import UniqueIdGenerator
import gsi_handlers
import services
import sims4.log
import telemetry_helper
logger = sims4.log.Logger('SimInfoCulling', default_owner='manus')
TELEMETRY_GROUP_STORY_PROGRESSION = 'STRY'
TELEMETRY_HOOK_CULL_SIMINFO_BEFORE = 'CSBE'
TELEMETRY_HOOK_CULL_SIMINFO_BEFORE2 = 'CSBT'
TELEMETRY_HOOK_CULL_SIMINFO_AFTER = 'CSAF'
TELEMETRY_CREATION_SOURCE_HOOK_COUNT = 10
TELEMETRY_CREATION_SOURCE_BUFFER_LENGTH = 100
with sims4.reload.protected(globals()):
    telemetry_id_generator = UniqueIdGenerator()
writer = sims4.telemetry.TelemetryWriter(TELEMETRY_GROUP_STORY_PROGRESSION)
SimInfoCullingScoreInfo = namedtuple('SimInfoCullingScoreInfo', ('score', 'rel_score', 'inst_score', 'importance_score'))
DEFAULT_CULLING_INFO = SimInfoCullingScoreInfo(0, 0, 0, 1.0)

class StoryProgressionActionMaxPopulation(_StoryProgressionAction):
    FACTORY_TUNABLES = {'sim_info_cap_per_lod': TunableMapping(description="\n            The mapping of SimInfoLODLevel value to an interval of sim info cap\n            integer values.\n            \n            NOTE: The ACTIVE lod can't be tuned here because it's being tracked\n            via the Maximum Size tuning in Household module tuning.\n            ", key_type=TunableEnumEntry(description='\n                The SimInfoLODLevel value.\n                ', tunable_type=SimInfoLODLevel, default=SimInfoLODLevel.FULL, invalid_enums=(SimInfoLODLevel.ACTIVE,)), value_type=TunableRange(description='\n                The number of sim infos allowed to be present before culling\n                is triggered for this SimInfoLODLevel.\n                ', tunable_type=int, default=210, minimum=0)), 'time_of_day': TunableTuple(description='\n            Only run this action when it is between a certain time of day.\n            ', start_time=TunableTimeOfDay(default_hour=2), end_time=TunableTimeOfDay(default_hour=6)), 'culling_buffer_percentage': TunablePercent(description='\n            When sim infos are culled due to the number of sim infos exceeding\n            the cap, this is how much below the cap the number of sim infos\n            will be (as a percentage of the cap) after the culling, roughly.\n            The margin of error is due to the fact that we cull at the household\n            level, so the number of sims culled can be a bit more than this value\n            if the last household culled contains more sims than needed to reach\n            the goal. (i.e. we never cull partial households)\n            ', default=20), 'homeless_played_demotion_time': OptionalTunable(description='\n            If enabled, played Sims that have been homeless for at least this\n            many days will be drops from FULL to BASE_SIMULATABLE lod.\n            ', tunable=TunableRange(tunable_type=int, default=10, minimum=0))}

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._played_family_tree_distances = {}
        self._precull_telemetry_data = Counter()
        self._precull_telemetry_lod_counts_str = ''
        self._telemetry_id = 0
        self._total_sim_cap = Household.MAXIMUM_SIZE
        self._total_sim_cap += sum(self.sim_info_cap_per_lod.values())