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 = []
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)
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)
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
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
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 = []
def __init__(self, owner): super().__init__(owner) self._censor_grid_handles = collections.defaultdict(list) self._censor_state = CensorState.OFF self._get_next_handle = UniqueIdGenerator()
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
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()]
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)
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
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]
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 = []
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:
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())