#include #include #include #include #include #include #include #include #include #include #include #include #include // start autoduck documentation // @DOC objtrack // @module objtrack | None // @subindex Scripting Database // @index script | bails namespace Obj { CEventLog::CEventLog() { m_next_entry = 0; m_wrapped = false; mp_event_type_hash_table = NULL; m_event_depth = 0; m_num_new_entries = 0; } CEventLog::~CEventLog() { Dbg_Assert(mp_event_type_hash_table); delete mp_event_type_hash_table; } void CEventLog::AddEntry(uint32 type, uint32 target, uint32 source, uint32 script, uint32 receiverID, EOccurenceType occurenceType) { if (!mp_event_type_hash_table) { mp_event_type_hash_table = new Lst::HashTable(8); Script::CArray *p_event_type_array = Script::GetArray(CRCD(0x8114f90,"event_type_array"), Script::ASSERT); for (int i = 0; i < (int) p_event_type_array->GetSize(); i++) { strcpy(m_event_type_table[i].mName, p_event_type_array->GetString(i)); uint32 name_crc = Script::GenerateCRC(p_event_type_array->GetString(i)); mp_event_type_hash_table->PutItem(name_crc, &m_event_type_table[i]); } } int last_entry = m_next_entry - 1; if (last_entry < 0) last_entry = MAX_LOG_ENTRIES - 1; if (occurenceType == vUPDATE && m_table[last_entry].m_occurence_type == vUPDATE) { // no need to keep piling on update entries, just increase tick count m_table[last_entry].m_tick_count++; } else { if (occurenceType == vHANDLED) m_event_depth--; Entry *p_entry = m_table + m_next_entry; if (occurenceType == vUPDATE) p_entry->m_tick_count = 0; else p_entry->m_type = type; p_entry->m_target = target; p_entry->m_source = source; p_entry->m_script = script; p_entry->m_receiver_id = receiverID; p_entry->m_occurence_type = occurenceType; p_entry->m_depth = m_event_depth; m_next_entry++; if (m_next_entry == MAX_LOG_ENTRIES) { m_next_entry = 0; m_wrapped = true; } if (occurenceType == vLAUNCHED) m_event_depth++; m_num_new_entries++; //PrintLatestEntry(); } } void CEventLog::Print(bool onlyPrintNewEntries, int maxEntriesToPrint) { int index = 0; int count = m_next_entry; if (m_wrapped) { index = m_next_entry; count = MAX_LOG_ENTRIES; } if (onlyPrintNewEntries) { maxEntriesToPrint = m_num_new_entries; } printf("========================================================\n"); if (onlyPrintNewEntries) printf("PRINTING EVENT LOG (ONLY NEW ENTRIES)\n\n"); else printf("PRINTING EVENT LOG\n\n"); while(count > 0) { if (count <= maxEntriesToPrint) print_entry(index); index++; if (index == MAX_LOG_ENTRIES) index = 0; count--; } printf("========================================================\n"); m_num_new_entries = 0; } void CEventLog::PrintLatestEntry() { int last_entry = m_next_entry - 1; if (last_entry < 0) last_entry = MAX_LOG_ENTRIES - 1; print_entry(last_entry); m_num_new_entries = 0; } void CEventLog::print_entry(int index) { #ifdef __NOPT_ASSERT__ Entry *p_entry = m_table + index; //printf("%d ", p_entry->m_depth); for (int i = 0; i < p_entry->m_depth; i++) printf(" "); // entry format: (<...> denotes optional) // "ACTION: type=TYPE " // or: "ACTION: type=TYPE " if (p_entry->m_occurence_type == vLAUNCHED || p_entry->m_occurence_type == vHANDLED || p_entry->m_occurence_type == vREAD) { if (p_entry->m_occurence_type == vLAUNCHED) printf("Launched event: type=%s ", get_type_name(p_entry->m_type)); else if (p_entry->m_occurence_type == vHANDLED) { if (p_entry->m_receiver_id == CTracker::vID_OBJECT_TRACKER) printf("Event expired: type=%s ", get_type_name(p_entry->m_type)); else printf("Handled event: type=%s ", get_type_name(p_entry->m_type)); } else if (p_entry->m_occurence_type == vREAD) printf("Read event: type=%s ", get_type_name(p_entry->m_type)); bool reciever_equals_target = (p_entry->m_target == p_entry->m_receiver_id); if (p_entry->m_target != CEvent::vSYSTEM_EVENT) { const char *p_target_name; p_target_name = Script::FindChecksumName(p_entry->m_target); if (reciever_equals_target) printf("target/receiver=%s ", p_target_name); else printf("target=%s ", p_target_name); } if (!reciever_equals_target && p_entry->m_receiver_id != CTracker::vID_UNSPECIFIED_RECEIVER) { const char *p_receiver_name; if (p_entry->m_receiver_id == CTracker::vID_SCREEN_ELEMENT_MANAGER) p_receiver_name = "Screen Element Manager"; else if (p_entry->m_receiver_id == CTracker::vID_SUSPENDED_SCRIPT) p_receiver_name = "Suspended Script"; else if (p_entry->m_receiver_id == CTracker::vID_OBJECT_TRACKER) p_receiver_name = "(Killed By) Object Tracker"; else p_receiver_name = Script::FindChecksumName(p_entry->m_target); printf("receiver=%s ", p_receiver_name); } if (p_entry->m_source != CEvent::vSYSTEM_EVENT) printf("source=%s ", Script::FindChecksumName(p_entry->m_source)); if (p_entry->m_script) printf("script=%s ", Script::FindChecksumName(p_entry->m_script)); printf("\n"); } else if (p_entry->m_occurence_type == vOBJECT_ADD || p_entry->m_occurence_type == vOBJECT_REMOVE) { if (p_entry->m_occurence_type == vOBJECT_ADD) printf("Added object: %s", Script::FindChecksumName(p_entry->m_target)); else printf("Removed object: %s", Script::FindChecksumName(p_entry->m_target)); printf("\n"); } else if (p_entry->m_occurence_type == vUPDATE) { printf("Tick, counts = %d\n", p_entry->m_tick_count); } #endif } const char *CEventLog::get_type_name(uint32 type) { EventType *p_type_entry = mp_event_type_hash_table->GetItem(type); if (p_type_entry) { return p_type_entry->mName; } else return Script::FindChecksumName(type); } DefineSingletonClass( CTracker, "CObject Tracker" ); /* Every CObject that is registered with a CBaseManager should also be registered with CTracker. */ void CTracker::addObject(CObject *pObject) { Dbg_MsgAssert(pObject->GetID() != 0xFFFFFFFF, ("CObject has no ID")); Dbg_MsgAssert(!mp_hash_table->GetItem(pObject->GetID()), ("CObject with ID %s already in tracking system", Script::FindChecksumName(pObject->GetID()))); // if object ID already being used as alias, remove the alias CObject *p_alias_obj = mp_alias_table->GetItem(pObject->GetID()); if (p_alias_obj) { p_alias_obj->RemoveReference(); mp_alias_table->FlushItem(pObject->GetID()); } mp_hash_table->PutItem(pObject->GetID(), pObject); #ifdef __DEBUG_OBJ_MAN__ printf("*** Added object %s to global tracker\n", Script::FindChecksumName(pObject->GetID())); #endif m_event_log.AddEntry(0, pObject->GetID(), CEvent::vSYSTEM_EVENT, 0, 0, CEventLog::vOBJECT_ADD); } /* Ryan Old Comment: The 'newIdOfObjectBeingMomentarilyRemoved' parameter is set if we are just changing the ID of the object, which requires removing it, then adding it again. Otherwise, this parameter will be zero (the id of the skater, fool!) Mick New comment: now we have an additional boolean passed top indicate if the value in newIdOfObjectBeingMomentarilyRemoved is valid, as it might be 0, if we are changing the id of a client skater back to 0 */ void CTracker::removeObject(CObject *pObject, uint32 newIdOfObjectBeingMomentarilyRemoved, bool momentary_removal) { mp_hash_table->FlushItem(pObject->GetID()); if (momentary_removal) { // go through all the scripts waiting on object, change the ID for (int i = 0; i < vMAX_SCRIPT_ENTRIES; i++) { if (m_waiting_script_tab[i].mObjectId == pObject->GetID()) { m_waiting_script_tab[i].mObjectId = newIdOfObjectBeingMomentarilyRemoved; } } } else { // only remove aliases to an object if it is going away "permanently" remove_aliases(pObject); } #ifdef __DEBUG_OBJ_MAN__ printf("*** Removed object %s from global tracker\n", Script::FindChecksumName(pObject->GetID())); #endif m_event_log.AddEntry(0, pObject->GetID(), CEvent::vSYSTEM_EVENT, 0, 0, CEventLog::vOBJECT_REMOVE); } /* Forwards the event to listeners associated with the object. Handy for forwarding global events to specific recipients. If no object specified, event goes to all listeners. */ void CTracker::forward_event_to_listeners(CEvent *pEvent, CObject *pObject) { Dbg_Assert(pEvent); // Try event on all registered listeners // The ref counting stuff will fire an assert if an event listener gets deleted CEventListener *p_listener = mp_event_listener_list; while (p_listener) { // prevents listener from being deleted (without an assert) p_listener->m_ref_count++; if (!pObject || p_listener->mp_object == pObject) p_listener->event_filter(pEvent); p_listener->m_ref_count--; p_listener = p_listener->mp_next_listener; } } /* When a CObject is removed from tracking, all aliases that point to it must be removed. */ void CTracker::remove_aliases(CObject *pObject) { while(1) { //if (mp_alias_table->GetSize() == 0) // break; mp_alias_table->IterateStart(); uint32 entry_key; CObject *p_entry = mp_alias_table->IterateNext(&entry_key); while(p_entry) { if (p_entry->GetID() == pObject->GetID()) { // Found match. Remove it and repeat outer loop p_entry->RemoveReference(); mp_alias_table->FlushItem(entry_key); break; } p_entry = mp_alias_table->IterateNext(&entry_key); } if (!p_entry) // no more matches left, we're done break; } } CTracker::CTracker() { mp_hash_table = new Lst::HashTable(8); mp_alias_table = new Lst::HashTable(4); mp_event_receiver_table = new Lst::HashTable(8); m_id_seed = 0; mp_event_listener_list = NULL; m_block_event_launching = false; m_event_recurse_depth = 0; m_next_event_script = 0; for (int i = 0; i < vMAX_SCRIPT_ENTRIES; i++) m_waiting_script_tab[i].mEventType = vDEAD_SCRIPT_ENTRY; // XXX m_debug = false; } CTracker::~CTracker() { Dbg_MsgAssert(mp_hash_table->GetSize(), ("entries still in tracker")); delete mp_hash_table; } /* Returns a pointer to the CObject whose ID matches the one given, returns NULL if not found. Will return the object if it has an alias, which is used in the front end for stuff like "MainMenu" But is also now used for "Skater" and "Skater2" */ CObject *CTracker::GetObject(uint32 id) { CObject *p_obj = mp_hash_table->GetItem(id); if (!p_obj) { p_obj = mp_alias_table->GetItem(id); } return p_obj; } CObject *CTracker::GetObjectByAlias(uint32 aliasId) { return mp_alias_table->GetItem(aliasId); } void CTracker::AddAlias(uint32 alias, CObject *pObject) { // make sure alias not already being used for object ID Dbg_MsgAssert(!mp_hash_table->GetItem(alias), ("CObject with ID %s already in tracking system", Script::FindChecksumName(alias))); // if desired alias already being used as alias, remove old one CObject *p_alias_obj = mp_alias_table->GetItem(alias); if (p_alias_obj) { p_alias_obj->RemoveReference(); mp_alias_table->FlushItem(alias); } pObject->AddReference(); mp_alias_table->PutItem(alias, pObject); } /* Returns an ID that is not currently being used. Pretty high probability of not colliding with a user-created ID. */ uint32 CTracker::GetFreshId() { while(1) { char name_string[64]; sprintf(name_string, "autoid%d", m_id_seed++); if (m_id_seed >= 1000000) m_id_seed = 0; uint32 id = Script::GenerateCRC(name_string); if (!mp_hash_table->GetItem(id)) { return id; } } } #ifdef __NOPT_ASSERT__ // Debug function to track down corruption in scripts event handler tables // we iterate over all the event receivers, and check that the tables that // they point to are valid void CTracker::ValidateReceivers() { mp_event_receiver_table->IterateStart(); uint32 entry_key; CEventReceiverList *p_entry = mp_event_receiver_table->IterateNext(&entry_key); while(p_entry) { p_entry = mp_event_receiver_table->IterateNext(&entry_key); if (p_entry) { Lst::Node< Script::CScript >*p_node = p_entry->FirstItem(); while (p_node) { Script::CScript * p_script = p_node->GetData(); // Got a pointer to a node, so first validate that // then validate the event handler table #ifndef __PLAT_WN32__ // These script functions are not necessary from PC tools Obj::CEventHandlerTable * p_event_handler_table = p_script->GetEventHandlerTable(); // Validate the table object Dbg_MsgAssert(Mem::Valid(p_event_handler_table),("Corrupt Event handler table object for event %s\n",Script::FindChecksumName(entry_key))); // and the table it contains Dbg_MsgAssert(!p_event_handler_table->m_num_entries || Mem::Valid(p_event_handler_table->mp_tab),("Corrupt Event handler table actual table for event %s\n",Script::FindChecksumName(entry_key))); #endif p_node = p_node->GetNext(); } } } } #endif // returns true if event was handled bool CTracker::LaunchEvent(uint32 type, uint32 target, uint32 source, Script::CStruct *pData, bool broadcast) { // printf("launch event, type=%s, target=0x%x, source= 0x%x, pData = %p\n", Script::FindChecksumName(type),target,source, pData); // Dbg_MsgAssert(!broadcast,("Don't use the broadcast flag yet!!!")); Dbg_MsgAssert(!m_block_event_launching, ("event launches are blocked")); Dbg_MsgAssert(m_event_recurse_depth < vMAX_EVENT_RECURSE_DEPTH, ("too many nested LaunchEvents -- check your scripts")); //printf("### launch event depth %d\n", m_event_recurse_depth); m_event_recurse_depth++; // log event m_event_log.AddEntry(type, target, source, m_next_event_script, 0, CEventLog::vLAUNCHED); m_next_event_script = 0; CEvent event; event.m_type = type; event.m_target_id = target; event.m_source_id = source; event.mp_data = pData; if (broadcast) { #ifdef __SCRIPT_EVENT_TABLE__ // broadcast to each script that has an event handler table CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type); if (p_event_list) { int scripts = p_event_list->CountItems(); Dbg_MsgAssert(scripts, ("Empty list for event %s",Script::FindChecksumName(type))); if (scripts == 1) { // Just one object, so do a quicker sending of the event Lst::Node< Script::CScript >*p_node = p_event_list->FirstItem(); Script::CScript *p_script = p_node->GetData(); p_script->PassTargetedEvent(&event, broadcast); } else { // For multiple scripts, we need to make a copy of the the list of scripts // that will receive this. Otherwise chaos ensues, as scripts change their event handlers based on events. // We also use a list of smart pointers to the mp_reference in a script, so if one event handler deletes // another object (and hence its script) // then we can safely ignore that object (since it no longer exists) // as the pointer will be set to NULL. Script::CScript ** pp_scripts = new Script::CScript*[scripts]; CSmtPtr * pp_refs = new CSmtPtr[scripts]; Lst::Node< Script::CScript >*p_node = p_event_list->FirstItem(); int n=0; while (p_node) { pp_scripts[n] = p_node->GetData(); CRefCounted * p_ref = &(p_node->GetData()->m_reference_counter); pp_refs[n] = p_ref; n++; p_node = p_node->GetNext(); } for (n = 0; nPassTargetedEvent(&event, broadcast); } } delete [] pp_refs; delete [] pp_scripts; } } #else // broadcast to each object that has an event handler table CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type); if (p_event_list) { int objects = p_event_list->CountItems(); Dbg_MsgAssert(objects, ("Empty list for event %s",Script::FindChecksumName(type))); if (objects == 1) { // Just one object, so do a quicker sending of the event Lst::Node< Obj::CObject >*p_node = p_event_list->FirstItem(); Obj::CObject *p_obj = p_node->GetData(); p_obj->PassTargetedEvent(&event, broadcast); } else { // For multiple objects, we need to make a copy of the the list of objects // that will receive this. Otherwise chaos ensues, as objects change their event handlers based on events. // We use a list of smart pointers, so if one object handler deletes another object // then we can safely ignore that object (since it no longer exists) // as the pointer will be set to NULL. CObjectPtr * pp_objects = new CObjectPtr[objects]; Lst::Node< Obj::CObject >*p_node = p_event_list->FirstItem(); int n=0; while (p_node) { pp_objects[n++] = p_node->GetData(); p_node = p_node->GetNext(); } for (n = 0; nPassTargetedEvent(&event, broadcast); } } delete [] pp_objects; } } #endif } else { // try event on all registered listeners forward_event_to_listeners(&event, NULL); // if event has a specific target, then send to that CObject if (event.m_target_id != CEvent::vSYSTEM_EVENT && !event.WasHandled()) { // look for a object with the target id CObject *p_object = GetObject(event.m_target_id); if (!p_object) { // or look for a script with the target id Script::CScript *p_script = Script::FindSpawnedScriptWithID(event.m_target_id); if (!p_script) { m_event_log.Print(256); #ifdef __NOPT_ASSERT__ mp_hash_table->PrintContents(); #endif } Dbg_MsgAssert(p_script, ("couldn't find object or script id %s", Script::FindChecksumName(event.m_target_id))); p_script->PassTargetedEvent(&event); } else { p_object->PassTargetedEvent(&event); } // p_object may have been deleted } } /* See if any scripts were waiting on the event. Unblock script if type matches AND: -target matches -OR script not associated with object -OR a system event */ for (int i = 0; i < vMAX_SCRIPT_ENTRIES; i++) { //if (m_waiting_script_tab[i].mEventType == Script::GenerateCRC("showed_wait_message")) // Ryan(" hoorah: 0x%x 0x%x\n", m_waiting_script_tab[i].mObjectId, target); if (m_waiting_script_tab[i].mEventType == type && (m_waiting_script_tab[i].mObjectId == target || m_waiting_script_tab[i].mObjectId == 0 || target == CEvent::vSYSTEM_EVENT || broadcast)) { // We found a suspended script of the correct type and target. // Can remove entry. m_waiting_script_tab[i].mEventType = vDEAD_SCRIPT_ENTRY; // does the script actually exist right now? Script::CScript *p_script = Script::GetScriptWithUniqueId(m_waiting_script_tab[i].mScriptId); if (p_script) { p_script->UnBlock(); event.MarkHandled(vID_SUSPENDED_SCRIPT, p_script->mScriptChecksum); //printf("unblocking script %s (id=%d)\n", Script::FindChecksumName(p_script->mScriptChecksum), m_waiting_script_tab[i].mScriptId); // alright, the event is handled, so leave loop break; } else { //printf("script to unblock doesn't exist (id=%d)\n", m_waiting_script_tab[i].mScriptId); } // the event is NOT handled } } if (!event.WasHandled()) // no one handled event, mark as killed by object tracker LogEventHandled(&event, vID_OBJECT_TRACKER); m_event_recurse_depth--; return event.WasHandled(); } // Call right before calling LaunchEvent() void CTracker::LogEventScript(uint32 script) { m_next_event_script = script; } void CTracker::LogEventHandled(CEvent *pEvent, uint32 receiverID, uint32 script) { // log event m_event_log.AddEntry(pEvent->GetType(), pEvent->GetTarget(), pEvent->GetSource(), script, receiverID, CEventLog::vHANDLED); } void CTracker::LogEventRead(CEvent *pEvent, uint32 receiverID, uint32 script) { // log event m_event_log.AddEntry(pEvent->GetType(), pEvent->GetTarget(), pEvent->GetSource(), script, receiverID, CEventLog::vREAD); } void CTracker::LogTick() { m_event_log.AddEntry(0, 0, 0, 0, 0, CEventLog::vUPDATE); } void CTracker::PrintEventLog(bool mostRecentOnly, int maxToPrint) { m_event_log.Print(mostRecentOnly, maxToPrint); } void CTracker::RegisterEventListener(CEventListener *pListener) { Dbg_MsgAssert(!pListener->m_registered, ("listener already registered")); pListener->mp_next_listener = mp_event_listener_list; mp_event_listener_list = pListener; pListener->m_registered = true; } void CTracker::UnregisterEventListener(CEventListener *pListener) { Dbg_MsgAssert(pListener->m_registered, ("listener not registered")); CEventListener *p_prev = NULL; CEventListener *p_current = mp_event_listener_list; while(p_current) { if (p_current == pListener) { if (p_prev) p_prev->mp_next_listener = pListener->mp_next_listener; else mp_event_listener_list = pListener->mp_next_listener; break; } p_prev = p_current; p_current = p_current->mp_next_listener; } pListener->m_registered = false; } // Add this object to the list of objects that are listening for // events of this "type", so any broadcast event of this type will go directly there #ifdef __SCRIPT_EVENT_TABLE__ void CTracker::RegisterEventReceiver(uint32 type, Script::CScript *p_obj) #else void CTracker::RegisterEventReceiver(uint32 type, CObject *p_obj) #endif { Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap()); // printf ("Registering Event %s from Object %s\n",Script::FindChecksumName(type), Script::FindChecksumName(p_obj->GetID())); // If there is no CEventList in the hash table for "type" then create one CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type); if (!p_event_list) { p_event_list = new CEventReceiverList(); mp_event_receiver_table->PutItem(type,p_event_list); } // Add this object to the that list, if it is not already added p_event_list->RegisterEventReceiverObject(p_obj); Mem::Manager::sHandle().PopContext(); } // Remove this object from the list of objects that are listening for // events of this "type" #ifdef __SCRIPT_EVENT_TABLE__ void CTracker::UnregisterEventReceiver(uint32 type, Script::CScript *p_obj) #else void CTracker::UnregisterEventReceiver(uint32 type, CObject *p_obj) #endif { // printf ("Unregistering Event %s from Object %s\n",Script::FindChecksumName(type), Script::FindChecksumName(p_obj->GetID())); // If there is a reciever list for this "type" CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type); if (p_event_list) { // then remove the object from the CEventList p_event_list->UnregisterEventReceiverObject(p_obj); // If the list is empty if (p_event_list->IsEmpty()) { // Then remove it from the hash table mp_event_receiver_table->FlushItem(type); // and delete it delete p_event_list; } } } CEventReceiverList::CEventReceiverList() { } // Register an object with this event receiver list // all this does is add it to the list, so events of this "type" are sent to it // it will not be added to the same list twice (just ignores additional registers) #ifdef __SCRIPT_EVENT_TABLE__ void CEventReceiverList::RegisterEventReceiverObject(Script::CScript *p_script) { // See if the object is already in the list, and return if it is Lst::Node * p_search = m_objects.FirstItem(); while (p_search) { if (p_search->GetData() == p_script) return; p_search = p_search->GetNext(); } // Create a new node for adding to the list Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap()); Lst::Node* p_node = new Lst::Node(p_script); Mem::Manager::sHandle().PopContext(); // Just add it to the tail of the list m_objects.AddToTail(p_node); } #else void CEventReceiverList::RegisterEventReceiverObject(CObject *p_obj) { // See if the object is already in the list, and return if it is Lst::Node * p_search = m_objects.FirstItem(); while (p_search) { if (p_search->GetData() == p_obj) return; p_search = p_search->GetNext(); } // Create a new node for adding to the list Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap()); Lst::Node* p_node = new Lst::Node(p_obj); Mem::Manager::sHandle().PopContext(); // Just add it to the tail of the list m_objects.AddToTail(p_node); } #endif // remove an object from this list (if it is in it) // if it is not in the list, it will just be ignored #ifdef __SCRIPT_EVENT_TABLE__ void CEventReceiverList::UnregisterEventReceiverObject(Script::CScript *p_script) { // Get the node from the list Lst::Node* p_node = NULL; Lst::Node * p_search = m_objects.FirstItem(); while (p_search) { if (p_search->GetData() == p_script) { p_node = p_search; break; } p_search = p_search->GetNext(); } if (p_node) { // Just remove it from the list p_node->Remove(); // and delete the node (might be left with an empty list, but the tracker is responsible for cleaning it up) delete p_node; } } #else void CEventReceiverList::UnregisterEventReceiverObject(CObject *p_obj) { // Get the node from the list Lst::Node* p_node = NULL; Lst::Node * p_search = m_objects.FirstItem(); while (p_search) { if (p_search->GetData() == p_obj) { p_node = p_search; break; } p_search = p_search->GetNext(); } if (p_node) { // Just remove it from the list p_node->Remove(); // and delete the node (might be left with an empty list, but the tracker is responsible for cleaning it up) delete p_node; } } #endif /* Causes a script to be blocked until the (future) arrival of an event of the designated type. If the script is associated with an object AND the event has a target, then the target must be that object. */ void CTracker::SuspendScriptUntilEvent(Script::CScript *pScript, uint32 event_type) { // find an unused entry int unused_entry_index = -1; int i = 0; for (; i < vMAX_SCRIPT_ENTRIES; i++) { if (m_waiting_script_tab[i].mEventType == vDEAD_SCRIPT_ENTRY) { unused_entry_index = i; break; } } if (unused_entry_index < 0) { // hmm, no unused entries, so find entry whose script is dead for (i = 0; i < vMAX_SCRIPT_ENTRIES; i++) { Script::CScript *p_script = Script::GetScriptWithUniqueId(m_waiting_script_tab[i].mScriptId); if (!p_script) { unused_entry_index = i; break; } } } if (unused_entry_index >= 0) { //printf("suspending script %s (id=%d)\n", Script::FindChecksumName(pScript->mScriptChecksum), pScript->GetUniqueId()); m_waiting_script_tab[unused_entry_index].mScriptId = pScript->GetUniqueId(); if (pScript->mpObject) m_waiting_script_tab[unused_entry_index].mObjectId = pScript->mpObject->GetID(); else m_waiting_script_tab[unused_entry_index].mObjectId = 0; m_waiting_script_tab[unused_entry_index].mEventType = event_type; #ifdef __NOPT_ASSERT__ m_waiting_script_tab[unused_entry_index].mScriptName = pScript->mScriptChecksum; #endif // suspend the script until later notice pScript->Block(); } else { #ifdef __NOPT_ASSERT__ printf("out of script entries, scripts still waiting for events:\n"); for (i = 0; i < vMAX_SCRIPT_ENTRIES; i++) { printf(" %s\n", Script::FindChecksumName(m_waiting_script_tab[i].mScriptName)); } Dbg_MsgAssert(0, ("%s\nout of script entries (%d)in WaitForEvent",pScript->GetScriptInfo(),vMAX_SCRIPT_ENTRIES)); #endif } } #ifndef __PLAT_WN32__ // These script functions are not necessary from PC tools // @script | LaunchEvent | // @parm name | type | event type // @parm structure | data | bool ScriptLaunchEvent(Script::CStruct *pParams, Script::CScript *pScript) { // Although events aren't necessarily tied to the Screen Element system, it is // convenient to enable this function to support complex ID's, for when we // are dealing with Screen Elements Front::CScreenElementManager* pManager = Front::CScreenElementManager::Instance(); uint32 target = pManager->ResolveComplexID(pParams, CRCD(0xb990d003,"target")); if (target) { if (target == CRCD(0x36b2ee74, "system")) target = CEvent::vSYSTEM_EVENT; } else { if (pScript->mpObject) target = pScript->mpObject->GetID(); else target = CEvent::vSYSTEM_EVENT; } uint32 source = pManager->ResolveComplexID(pParams, CRCD(0xa075808c,"source")); if (source) { if (source == CRCD(0x36b2ee74, "system")) source = CEvent::vSYSTEM_EVENT; } else { if (pScript->mpObject) source = pScript->mpObject->GetID(); else source = CEvent::vSYSTEM_EVENT; } Script::CStruct *pData = NULL; pParams->GetStructure(CRCD(0x520c0c9c,"data"), &pData); bool broadcast = pParams->ContainsFlag(CRCD(0x640e830a,"broadcast")); CTracker* p_tracker = CTracker::Instance(); uint32 type; if (pParams->GetChecksum(CRCD(0x7321a8d6,"type"), &type)) { p_tracker->LogEventScript(pScript->mScriptChecksum); p_tracker->LaunchEvent(type, target, source, pData, broadcast); } else { Script::CArray* pTypes; if (pParams->GetArray(CRCD(0x7321a8d6,"type"), &pTypes)) { Dbg_Assert(pTypes->GetType() == ESYMBOLTYPE_NAME) unsigned num_events = pTypes->GetSize(); for (unsigned n = 0; n < num_events; n++) { p_tracker->LogEventScript(pScript->mScriptChecksum); p_tracker->LaunchEvent(pTypes->GetChecksum(n), target, source, pData, broadcast); } } else { Script::CStruct* pTypes; if (pParams->GetStructure(CRCD(0x7321a8d6,"type"), &pTypes)) { for (Script::CComponent* pComp = pTypes->GetNextComponent(); pComp; pComp = pTypes->GetNextComponent(pComp)) { Dbg_Assert(pComp->mType == ESYMBOLTYPE_NAME); p_tracker->LogEventScript(pScript->mScriptChecksum); p_tracker->LaunchEvent(pComp->mChecksum, target, source, pData, broadcast); } } else { Dbg_MsgAssert(0, ("can't launch event without type")); } } } return true; } bool ScriptWaitForEvent(Script::CStruct *pParams, Script::CScript *pScript) { uint32 type; if (!pParams->GetChecksum(CRCD(0x7321a8d6,"type"), &type)) Dbg_MsgAssert(0, ("no event type specified"));; CTracker* p_tracker = CTracker::Instance(); p_tracker->SuspendScriptUntilEvent(pScript, type); return true; } bool ScriptObjectExists(Script::CStruct *pParams, Script::CScript *pScript) { // Although events aren't necessarily tied to the Screen Element system, it is // convenient to enable this function to support complex ID's, for when we // are dealing with Screen Elements Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance(); uint32 id = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id")); CTracker* p_tracker = CTracker::Instance(); return (p_tracker->GetObject(id) != NULL); } bool ScriptTerminateObjectsScripts(Script::CStruct *pParams, Script::CScript *pScript) { Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance(); uint32 id = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id")); CTracker* p_tracker = CTracker::Instance(); CObject *p_object = p_tracker->GetObject(id); // Brad - the use_proper_version flag is a last minute fix at the end of THPS4. // StopScriptsUsingThisObject is broken, but fixing it may break other things. The // proper version works as it always should have. It is only used when the user // specifically requests it. bool use_proper = pParams->ContainsFlag( CRCD(0xc89f3564,"use_proper_version") ); // see if array of script names Script::CArray *p_array; if (pParams->GetArray(CRCD(0x22e168c1,"scripts"), &p_array)) { for (uint i = 0; i < p_array->GetSize(); i++) { if ( use_proper ) Script::StopScriptsUsingThisObject_Proper(p_object, p_array->GetChecksum(i)); else Script::StopScriptsUsingThisObject(p_object, p_array->GetChecksum(i)); } } else { uint32 script_to_stop = 0; // means 'stop all' pParams->GetChecksum(CRCD(0x6166f3ad,"script_name"), &script_to_stop); if ( use_proper ) Script::StopScriptsUsingThisObject_Proper(p_object, script_to_stop); else Script::StopScriptsUsingThisObject(p_object, script_to_stop); } return true; } bool ScriptAssignAlias(Script::CStruct *pParams, Script::CScript *pScript) { Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance(); uint32 id_of_original = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id")); CTracker* p_tracker = CTracker::Instance(); CObject *p_object_to_alias = p_tracker->GetObject(id_of_original); Dbg_Assert(p_object_to_alias); uint32 alias; pParams->GetChecksum(CRCD(0x1e93946b,"alias"), &alias, Script::ASSERT); p_tracker->AddAlias(alias, p_object_to_alias); return true; } bool ScriptSetObjectProperties(Script::CStruct *pParams, Script::CScript *pScript) { Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance(); uint32 id_of_original = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id")); CTracker* p_tracker = CTracker::Instance(); CObject *p_object = p_tracker->GetObject(id_of_original); Dbg_Assert(p_object); p_object->SetProperties(pParams); return true; } bool ScriptPrintEventLog(Script::CStruct *pParams, Script::CScript *pScript) { bool only_print_new = true; int num_to_print = CEventLog::MAX_LOG_ENTRIES; if (pParams->GetInteger(NONAME, &num_to_print)) only_print_new = false; CTracker* p_tracker = CTracker::Instance(); p_tracker->PrintEventLog(only_print_new, num_to_print); return true; } #endif }