messageVector.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gui/utility/messageVector.h"
  23. #include "core/fileObject.h"
  24. #include "console/engineAPI.h"
  25. IMPLEMENT_CONOBJECT(MessageVector);
  26. ConsoleDocClass( MessageVector,
  27. "@brief Store a list of chat messages.\n\n"
  28. "This is responsible for managing messages which appear in the chat HUD, not the actual control rendered to the screen\n\n"
  29. "@tsexample\n"
  30. "// Declare ChatHud, which is what will display the actual chat from a MessageVector\n"
  31. "new GuiMessageVectorCtrl(ChatHud) {\n"
  32. " profile = \"ChatHudMessageProfile\";\n"
  33. " horizSizing = \"width\";\n"
  34. " vertSizing = \"height\";\n"
  35. " position = \"1 1\";\n"
  36. " extent = \"252 16\";\n"
  37. " minExtent = \"8 8\";\n"
  38. " visible = \"1\";\n"
  39. " helpTag = \"0\";\n"
  40. " lineSpacing = \"0\";\n"
  41. " lineContinuedIndex = \"10\";\n"
  42. " matchColor = \"0 0 255 255\";\n"
  43. " maxColorIndex = \"5\";\n"
  44. "};\n\n"
  45. "// All messages are stored in this HudMessageVector, the actual\n"
  46. "// MainChatHud only displays the contents of this vector.\n"
  47. "new MessageVector(HudMessageVector);\n\n"
  48. "// Attach the MessageVector to the chat control\n"
  49. "chatHud.attach(HudMessageVector);\n"
  50. "@endtsexample\n\n"
  51. "@see GuiMessageVectorCtrl for more details on how this is used."
  52. "@ingroup GuiUtil\n"
  53. );
  54. DefineEngineMethod( MessageVector, clear, void, (),,
  55. "Clear all messages in the vector\n\n"
  56. "@tsexample\n"
  57. "HudMessageVector.clear();\n"
  58. "@endtsexample\n\n")
  59. {
  60. object->clear();
  61. }
  62. //ConsoleMethod( MessageVector, clear, void, 2, 2, "Clear the message vector.")
  63. //{
  64. // object->clear();
  65. //}
  66. DefineEngineMethod( MessageVector, pushBackLine, void, ( const char* msg, S32 tag ),,
  67. "Push a line onto the back of the list.\n\n"
  68. "@param msg Text that makes up the message\n"
  69. "@param tag Numerical value associated with this message, useful for searching.\n\n"
  70. "@tsexample\n"
  71. "// Add the message...\n"
  72. "HudMessageVector.pushBackLine(\"Hello World\", 0);\n"
  73. "@endtsexample\n\n")
  74. {
  75. object->pushBackLine(msg, tag);
  76. }
  77. //ConsoleMethod( MessageVector, pushBackLine, void, 3, 4, "(string msg, int tag=0)"
  78. // "Push a line onto the back of the list.")
  79. //{
  80. // U32 tag = 0;
  81. // if (argc == 4)
  82. // tag = dAtoi(argv[3]);
  83. //
  84. // object->pushBackLine(argv[2], tag);
  85. //}
  86. DefineEngineMethod( MessageVector, popBackLine, bool, (),,
  87. "Pop a line from the back of the list; destroys the line.\n\n"
  88. "@tsexample\n"
  89. "HudMessageVector.popBackLine();\n"
  90. "@endtsexample\n\n"
  91. "@return False if there are no lines to pop (underflow), true otherwise")
  92. {
  93. if (object->getNumLines() == 0) {
  94. Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow");
  95. return false;
  96. }
  97. object->popBackLine();
  98. return true;
  99. }
  100. //ConsoleMethod( MessageVector, popBackLine, bool, 2, 2, "()"
  101. // "Pop a line from the back of the list; destroys the line.")
  102. //{
  103. // if (object->getNumLines() == 0) {
  104. // Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow");
  105. // return false;
  106. // }
  107. //
  108. // object->popBackLine();
  109. // return true;
  110. //}
  111. DefineEngineMethod( MessageVector, pushFrontLine, void, ( const char* msg, S32 tag ),,
  112. "Push a line onto the front of the vector.\n\n"
  113. "@param msg Text that makes up the message\n"
  114. "@param tag Numerical value associated with this message, useful for searching.\n\n"
  115. "@tsexample\n"
  116. "// Add the message...\n"
  117. "HudMessageVector.pushFrontLine(\"Hello World\", 0);\n"
  118. "@endtsexample\n\n")
  119. {
  120. object->pushFrontLine(msg, tag);
  121. }
  122. //ConsoleMethod( MessageVector, pushFrontLine, void, 3, 4, "(string msg, int tag=0)"
  123. // "Push a line onto the front of the vector.")
  124. //{
  125. // U32 tag = 0;
  126. // if (argc == 4)
  127. // tag = dAtoi(argv[3]);
  128. //
  129. // object->pushFrontLine(argv[2], tag);
  130. //}
  131. DefineEngineMethod( MessageVector, popFrontLine, bool, (),,
  132. "Pop a line from the front of the vector, destroying the line.\n\n"
  133. "@tsexample\n"
  134. "HudMessageVector.popFrontLine();\n"
  135. "@endtsexample\n\n"
  136. "@return False if there are no lines to pop (underflow), true otherwise")
  137. {
  138. if (object->getNumLines() == 0) {
  139. Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow");
  140. return false;
  141. }
  142. object->popFrontLine();
  143. return true;
  144. }
  145. //ConsoleMethod( MessageVector, popFrontLine, bool, 2, 2,
  146. // "Pop a line from the front of the vector, destroying the line.")
  147. //{
  148. // if (object->getNumLines() == 0) {
  149. // Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow");
  150. // return false;
  151. // }
  152. //
  153. // object->popFrontLine();
  154. // return true;
  155. //}
  156. DefineEngineMethod( MessageVector, insertLine, bool, ( S32 insertPos, const char* msg, S32 tag ),,
  157. "Push a line onto the back of the list.\n\n"
  158. "@param msg Text that makes up the message\n"
  159. "@param tag Numerical value associated with this message, useful for searching.\n\n"
  160. "@tsexample\n"
  161. "// Add the message...\n"
  162. "HudMessageVector.insertLine(1, \"Hello World\", 0);\n"
  163. "@endtsexample\n\n"
  164. "@return False if insertPos is greater than the number of lines in the current vector")
  165. {
  166. if (insertPos > object->getNumLines())
  167. return false;
  168. object->insertLine(insertPos, msg, tag);
  169. return true;
  170. }
  171. //ConsoleMethod( MessageVector, insertLine, bool, 4, 5, "(int insertPos, string msg, int tag=0)"
  172. // "Insert a new line into the vector at the specified position.")
  173. //{
  174. // U32 pos = U32(dAtoi(argv[2]));
  175. // if (pos > object->getNumLines())
  176. // return false;
  177. //
  178. // S32 tag = 0;
  179. // if (argc == 5)
  180. // tag = dAtoi(argv[4]);
  181. //
  182. // object->insertLine(pos, argv[3], tag);
  183. // return true;
  184. //}
  185. DefineEngineMethod( MessageVector, deleteLine, bool, ( S32 deletePos),,
  186. "Delete the line at the specified position.\n\n"
  187. "@param deletePos Position in the vector containing the line to be deleted\n"
  188. "@tsexample\n"
  189. "// Delete the first line (index 0) in the vector...\n"
  190. "HudMessageVector.deleteLine(0);\n"
  191. "@endtsexample\n\n"
  192. "@return False if deletePos is greater than the number of lines in the current vector")
  193. {
  194. if (deletePos >= object->getNumLines())
  195. return false;
  196. object->deleteLine(deletePos);
  197. return true;
  198. }
  199. //ConsoleMethod( MessageVector, deleteLine, bool, 3, 3, "(int deletePos)"
  200. // "Delete the line at the specified position.")
  201. //{
  202. // U32 pos = U32(dAtoi(argv[2]));
  203. // if (pos >= object->getNumLines())
  204. // return false;
  205. //
  206. // object->deleteLine(pos);
  207. // return true;
  208. //}
  209. static ConsoleDocFragment _MessageVectordump1(
  210. "@brief Dump the message vector to a file without a header.\n\n"
  211. "@param filename Name and path of file to dump text to.\n"
  212. "@tsexample\n"
  213. "// Dump the entire chat log to a text file\n"
  214. "HudMessageVector.dump(\"./chatLog.txt\");\n"
  215. "@endtsexample\n\n\n",
  216. "MessageVector",
  217. "void dump( string filename);");
  218. static ConsoleDocFragment _MessageVectordump2(
  219. "@brief Dump the message vector to a file with a header.\n\n"
  220. "@param filename Name and path of file to dump text to.\n"
  221. "@param header Prefix information for write out\n\n"
  222. "@tsexample\n"
  223. "// Arbitrary header data\n"
  224. "%headerInfo = \"Ars Moriendi Chat Log\";\n\n"
  225. "// Dump the entire chat log to a text file\n"
  226. "HudMessageVector.dump(\"./chatLog.txt\", %headerInfo);\n"
  227. "@endtsexample\n\n\n",
  228. "MessageVector",
  229. "void dump( string filename, string header);");
  230. DefineEngineMethod( MessageVector, dump, void, (const char * filename, const char * header), (""), "(string filename, string header=NULL)"
  231. "Dump the message vector to a file, optionally prefixing a header."
  232. "@hide")
  233. {
  234. object->dump( filename, header );
  235. }
  236. DefineEngineMethod( MessageVector, getNumLines, S32, (),,
  237. "Get the number of lines in the vector.\n\n"
  238. "@tsexample\n"
  239. "// Find out how many lines have been stored in HudMessageVector\n"
  240. "%chatLines = HudMessageVector.getNumLines();\n"
  241. "echo(%chatLines);\n"
  242. "@endtsexample\n\n")
  243. {
  244. return object->getNumLines();
  245. }
  246. //ConsoleMethod( MessageVector, getNumLines, S32, 2, 2, "Get the number of lines in the vector.")
  247. //{
  248. // return object->getNumLines();
  249. //}
  250. DefineEngineMethod( MessageVector, getLineTextByTag, const char*, ( S32 tag),,
  251. "Scan through the lines in the vector, returning the first line that has a matching tag.\n\n"
  252. "@param tag Numerical value assigned to a message when it was added or inserted\n"
  253. "@tsexample\n"
  254. "// Locate text in the vector tagged with the value \"1\", then print it\n"
  255. "%taggedText = HudMessageVector.getLineTextByTag(1);\n"
  256. "echo(%taggedText);\n"
  257. "@endtsexample\n\n"
  258. "@return Text from a line with matching tag, other wise \"\"")
  259. {
  260. for(U32 i = 0; i < object->getNumLines(); i++)
  261. if(object->getLine(i).messageTag == tag)
  262. return object->getLine(i).message;
  263. return "";
  264. }
  265. //ConsoleMethod( MessageVector, getLineTextByTag, const char*, 3, 3, "(int tag)"
  266. // "Scan through the lines in the vector, returning the first line that has a matching tag.")
  267. //{
  268. // U32 tag = dAtoi(argv[2]);
  269. //
  270. // for(U32 i = 0; i < object->getNumLines(); i++)
  271. // if(object->getLine(i).messageTag == tag)
  272. // return object->getLine(i).message;
  273. // return "";
  274. //}
  275. DefineEngineMethod( MessageVector, getLineIndexByTag, S32, ( S32 tag),,
  276. "Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found.\n\n"
  277. "@param tag Numerical value assigned to a message when it was added or inserted\n"
  278. "@tsexample\n"
  279. "// Locate a line of text tagged with the value \"1\", then delete it.\n"
  280. "%taggedLine = HudMessageVector.getLineIndexByTag(1);\n"
  281. "HudMessageVector.deleteLine(%taggedLine);\n"
  282. "@endtsexample\n\n"
  283. "@return Line with matching tag, other wise -1")
  284. {
  285. for(U32 i = 0; i < object->getNumLines(); i++)
  286. if(object->getLine(i).messageTag == tag)
  287. return i;
  288. return -1;
  289. }
  290. //ConsoleMethod( MessageVector, getLineIndexByTag, S32, 3, 3, "(int tag)"
  291. // "Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found.")
  292. //{
  293. // U32 tag = dAtoi(argv[2]);
  294. //
  295. // for(U32 i = 0; i < object->getNumLines(); i++)
  296. // if(object->getLine(i).messageTag == tag)
  297. // return i;
  298. // return -1;
  299. //}
  300. DefineEngineMethod( MessageVector, getLineText, const char*, ( S32 pos),,
  301. "Get the text at a specified line.\n\n"
  302. "@param pos Position in vector to grab text from\n"
  303. "@tsexample\n"
  304. "// Print a line of text at position 1.\n"
  305. "%text = HudMessageVector.getLineText(1);\n"
  306. "echo(%text);\n"
  307. "@endtsexample\n\n"
  308. "@return Text at specified line, if the position is greater than the number of lines return \"\"")
  309. {
  310. if (pos >= object->getNumLines()) {
  311. Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line");
  312. return "";
  313. }
  314. return object->getLine(pos).message;
  315. }
  316. //ConsoleMethod( MessageVector, getLineText, const char*, 3, 3, "(int line)"
  317. // "Get the text at a specified line.")
  318. //{
  319. // U32 pos = U32(dAtoi(argv[2]));
  320. // if (pos >= object->getNumLines()) {
  321. // Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line");
  322. // return "";
  323. // }
  324. //
  325. // return object->getLine(pos).message;
  326. //}
  327. DefineEngineMethod( MessageVector, getLineTag, S32, ( S32 pos),,
  328. "Get the tag of a specified line.\n\n"
  329. "@param pos Position in vector to grab tag from\n"
  330. "@tsexample\n"
  331. "// Remove all lines that do not have a tag value of 1.\n"
  332. "while( HudMessageVector.getNumLines())\n"
  333. "{\n"
  334. " %tag = HudMessageVector.getLineTag(1);\n"
  335. " if(%tag != 1)\n"
  336. " %tag.delete();\n"
  337. " HudMessageVector.popFrontLine();\n"
  338. "}\n"
  339. "@endtsexample\n\n"
  340. "@return Tag value of a given line, if the position is greater than the number of lines return 0")
  341. {
  342. if (pos >= object->getNumLines()) {
  343. Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line");
  344. return 0;
  345. }
  346. return object->getLine(pos).messageTag;
  347. }
  348. //ConsoleMethod( MessageVector, getLineTag, S32, 3, 3, "(int line)"
  349. // "Get the tag of a specified line.")
  350. //{
  351. // U32 pos = U32(dAtoi(argv[2]));
  352. // if (pos >= object->getNumLines()) {
  353. // Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line");
  354. // return 0;
  355. // }
  356. //
  357. // return object->getLine(pos).messageTag;
  358. //}
  359. //--------------------------------------------------------------------------
  360. MessageVector::MessageVector()
  361. {
  362. VECTOR_SET_ASSOCIATION(mMessageLines);
  363. VECTOR_SET_ASSOCIATION(mSpectators);
  364. }
  365. //--------------------------------------------------------------------------
  366. MessageVector::~MessageVector()
  367. {
  368. for (U32 i = 0; i < mMessageLines.size(); i++) {
  369. char* pDelete = const_cast<char*>(mMessageLines[i].message);
  370. delete [] pDelete;
  371. mMessageLines[i].message = 0;
  372. mMessageLines[i].messageTag = 0xFFFFFFFF;
  373. }
  374. mMessageLines.clear();
  375. }
  376. //--------------------------------------------------------------------------
  377. void MessageVector::initPersistFields()
  378. {
  379. docsURL;
  380. Parent::initPersistFields();
  381. }
  382. //--------------------------------------------------------------------------
  383. bool MessageVector::onAdd()
  384. {
  385. return Parent::onAdd();
  386. }
  387. //--------------------------------------------------------------------------
  388. void MessageVector::onRemove()
  389. {
  390. // Delete all the lines from the observers, and then forcibly detatch ourselves
  391. //
  392. for (S32 i = mMessageLines.size() - 1; i >= 0; i--)
  393. spectatorMessage(LineDeleted, i);
  394. spectatorMessage(VectorDeletion, 0);
  395. mSpectators.clear();
  396. Parent::onRemove();
  397. }
  398. //--------------------------------------------------------------------------
  399. void MessageVector::pushBackLine(const char* newMessage, const S32 newMessageTag)
  400. {
  401. insertLine(mMessageLines.size(), newMessage, newMessageTag);
  402. }
  403. void MessageVector::popBackLine()
  404. {
  405. AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!");
  406. if (mMessageLines.size() == 0)
  407. return;
  408. deleteLine(mMessageLines.size() - 1);
  409. }
  410. void MessageVector::clear()
  411. {
  412. while(mMessageLines.size())
  413. deleteLine(mMessageLines.size() - 1);
  414. }
  415. //--------------------------------------------------------------------------
  416. void MessageVector::pushFrontLine(const char* newMessage, const S32 newMessageTag)
  417. {
  418. insertLine(0, newMessage, newMessageTag);
  419. }
  420. void MessageVector::popFrontLine()
  421. {
  422. AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!");
  423. if (mMessageLines.size() == 0)
  424. return;
  425. deleteLine(0);
  426. }
  427. //--------------------------------------------------------------------------
  428. void MessageVector::insertLine(const U32 position,
  429. const char* newMessage,
  430. const S32 newMessageTag)
  431. {
  432. AssertFatal(position >= 0 && position <= mMessageLines.size(), "MessageVector::insertLine: out of range position!");
  433. AssertFatal(newMessage != NULL, "Error, no message to insert!");
  434. U32 len = dStrlen(newMessage) + 1;
  435. char* copy = new char[len];
  436. dStrcpy(copy, newMessage, len);
  437. mMessageLines.insert(position);
  438. mMessageLines[position].message = copy;
  439. mMessageLines[position].messageTag = newMessageTag;
  440. // Notify of insert
  441. spectatorMessage(LineInserted, position);
  442. }
  443. //--------------------------------------------------------------------------
  444. void MessageVector::deleteLine(const U32 position)
  445. {
  446. AssertFatal(position >= 0 && position < mMessageLines.size(), "MessageVector::deleteLine: out of range position!");
  447. char* pDelete = const_cast<char*>(mMessageLines[position].message);
  448. delete [] pDelete;
  449. mMessageLines[position].message = NULL;
  450. mMessageLines[position].messageTag = 0xFFFFFFFF;
  451. mMessageLines.erase(position);
  452. // Notify of delete
  453. spectatorMessage(LineDeleted, position);
  454. }
  455. //--------------------------------------------------------------------------
  456. bool MessageVector::dump( const char* filename, const char* header )
  457. {
  458. Con::printf( "Dumping message vector %s to %s...", getName(), filename );
  459. FileObject file;
  460. if ( !file.openForWrite( filename ) )
  461. return( false );
  462. // If passed a header line, write it out first:
  463. if ( header )
  464. file.writeLine( (const U8*) header );
  465. // First write out the record count:
  466. char* lineBuf = (char*) dMalloc( 10 );
  467. dSprintf( lineBuf, 10, "%d", mMessageLines.size() );
  468. file.writeLine( (const U8*) lineBuf );
  469. // Write all of the lines of the message vector:
  470. U32 len;
  471. for ( U32 i = 0; i < mMessageLines.size(); i++ )
  472. {
  473. len = ( dStrlen( mMessageLines[i].message ) * 2 ) + 10;
  474. lineBuf = (char*) dRealloc( lineBuf, len );
  475. dSprintf( lineBuf, len, "%d ", mMessageLines[i].messageTag );
  476. expandEscape( lineBuf + dStrlen( lineBuf ), mMessageLines[i].message );
  477. file.writeLine( (const U8*) lineBuf );
  478. }
  479. file.close();
  480. return( true );
  481. }
  482. //--------------------------------------------------------------------------
  483. void MessageVector::registerSpectator(SpectatorCallback callBack, void *spectatorKey)
  484. {
  485. AssertFatal(callBack != NULL, "Error, must have a callback!");
  486. // First, make sure that this hasn't been registered already...
  487. U32 i;
  488. for (i = 0; i < mSpectators.size(); i++) {
  489. AssertFatal(mSpectators[i].key != spectatorKey, "Error, spectator key already registered!");
  490. if (mSpectators[i].key == spectatorKey)
  491. return;
  492. }
  493. mSpectators.increment();
  494. SpectatorRef& lastSpectatorRef = mSpectators.last();
  495. lastSpectatorRef.callback = callBack;
  496. lastSpectatorRef.key = spectatorKey;
  497. // Need to message this spectator of all the lines currently inserted...
  498. for (i = 0; i < mMessageLines.size(); i++) {
  499. (*lastSpectatorRef.callback)(lastSpectatorRef.key,
  500. LineInserted, i);
  501. }
  502. }
  503. void MessageVector::unregisterSpectator(void * spectatorKey)
  504. {
  505. for (U32 i = 0; i < mSpectators.size(); i++) {
  506. if (mSpectators[i].key == spectatorKey) {
  507. // Need to message this spectator of all the lines currently inserted...
  508. for (S32 j = mMessageLines.size() - 1; j >= 0 ; j--) {
  509. (*mSpectators[i].callback)(mSpectators[i].key,
  510. LineDeleted, j);
  511. }
  512. mSpectators.erase(i);
  513. return;
  514. }
  515. }
  516. AssertFatal(false, "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!");
  517. Con::errorf(ConsoleLogEntry::General,
  518. "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!");
  519. }
  520. void MessageVector::spectatorMessage(MessageCode code, const U32 arg)
  521. {
  522. for (U32 i = 0; i < mSpectators.size(); i++) {
  523. (*mSpectators[i].callback)(mSpectators[i].key,
  524. code, arg);
  525. }
  526. }