GameSpyChat.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: GameSpyChat.cpp //////////////////////////////////////////////////////
  24. // GameSpy chat handlers
  25. // Author: Matthew D. Campbell, February 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "GameClient/GameText.h"
  28. #include "GameClient/GadgetListBox.h"
  29. #include "GameClient/LanguageFilter.h"
  30. #include "GameNetwork/GameSpy.h"
  31. #include "GameNetwork/GameSpyChat.h"
  32. #include "Common/QuotedPrintable.h"
  33. typedef set<AsciiString>::const_iterator AsciiSetIter;
  34. /**
  35. * handleSlashCommands looks for slash ccommands and handles them,
  36. * returning true if it found one, false otherwise.
  37. * /i,/ignore list ignored players
  38. * /i,/ignore +name1 -name2 ignore name1, stop ignoring name2
  39. * /m,/me: shorthand for an action
  40. * /o,/on: find command to look up a user's location
  41. * /f,/find: find command to look up a user's location
  42. * /p,/page: page user(s)
  43. * /r,/reply: reply to last page
  44. * /raw: raw IRC command (only in debug & internal)
  45. * /oper: become an IRC op (only in debug & internal)
  46. * /quit: send the IRC quit command to exit WOL
  47. */
  48. static Bool handleSlashCommands( UnicodeString message, Bool isAction, GameWindow *playerListbox )
  49. {
  50. /*
  51. if (message.getCharAt(0) == L'/')
  52. {
  53. UnicodeString remainder = UnicodeString(message.str() + 1);
  54. UnicodeString token;
  55. switch (message.getCharAt(1))
  56. {
  57. case L'i':
  58. case L'I':
  59. remainder.nextToken(&token);
  60. if (token.compareNoCase(L"i") == 0 || token.compareNoCase(L"ignore") == 0)
  61. {
  62. if (remainder.isEmpty())
  63. {
  64. // List the people we're ignoring
  65. TheWOL->addText(TheGameText->fetch("WOL:BeginIgnoreList"));
  66. set<AsciiString> *ignoreList = getIgnoreList();
  67. if (ignoreList)
  68. {
  69. UnicodeString msg;
  70. UnicodeString uName;
  71. AsciiSetIter iter = ignoreList->begin();
  72. while (iter != ignoreList->end())
  73. {
  74. uName.translate(*iter);
  75. msg.format(TheGameText->fetch("WOL:IgnoredUser"), uName.str());
  76. TheWOL->addText(msg);
  77. iter++;
  78. }
  79. }
  80. TheWOL->addText(TheGameText->fetch("WOL:EndIgnoreList"));
  81. }
  82. while ( remainder.nextToken(&token) )
  83. {
  84. AsciiString name;
  85. int doIgnore = 0;
  86. if (token.getCharAt(0) == L'+')
  87. {
  88. // Ignore somebody
  89. token = UnicodeString(token.str() + 1);
  90. name.translate(token);
  91. doIgnore = 1;
  92. }
  93. else if (token.getCharAt(0) == L'-')
  94. {
  95. // Listen to someone again
  96. token = UnicodeString(token.str() + 1);
  97. name.translate(token);
  98. doIgnore = 0;
  99. }
  100. else
  101. {
  102. // Ignore somebody
  103. token = UnicodeString(token.str());
  104. name.translate(token);
  105. doIgnore = 1;
  106. }
  107. IChat *ichat = TheWOL->getIChat();
  108. User user;
  109. strncpy((char *)user.name, name.str(), 9);
  110. user.name[9] = 0;
  111. ichat->SetSquelch(&user, doIgnore);
  112. if (doIgnore)
  113. addIgnore(name);
  114. else
  115. removeIgnore(name);
  116. UnicodeString msg;
  117. UnicodeString uName;
  118. uName.translate(name);
  119. msg.format(TheGameText->fetch("WOL:IgnoredUser"), uName.str());
  120. TheWOL->addText(msg);
  121. }
  122. return true;
  123. }
  124. break;
  125. case L'r':
  126. case L'R':
  127. remainder.nextToken(&token);
  128. #if defined _DEBUG || defined _INTERNAL
  129. if (token.compareNoCase(L"raw") == 0)
  130. {
  131. // Send raw IRC commands (Ascii only)
  132. AsciiString str;
  133. str.translate(remainder);
  134. str.concat('\n');
  135. IChat *ichat = TheWOL->getIChat();
  136. ichat->RequestRawMessage(str.str());
  137. TheWOL->addText(remainder);
  138. return true; // show it anyway
  139. }
  140. #endif
  141. break;
  142. #if defined _DEBUG || defined _INTERNAL
  143. case L'k':
  144. case L'K':
  145. remainder.nextToken(&token);
  146. if (token.compareNoCase(L"kick") == 0)
  147. {
  148. while ( remainder.nextToken(&token) )
  149. {
  150. AsciiString name;
  151. name.translate(token);
  152. IChat *ichat = TheWOL->getIChat();
  153. User user;
  154. strncpy((char *)user.name, name.str(), 9);
  155. user.name[9] = 0;
  156. ichat->RequestUserKick(&user);
  157. }
  158. return true;
  159. }
  160. break;
  161. #endif
  162. case L'o':
  163. case L'O':
  164. remainder.nextToken(&token);
  165. if (token.compareNoCase(L"on") == 0 || token.compareNoCase(L"o") == 0)
  166. {
  167. remainder.nextToken(&token);
  168. AsciiString userName;
  169. userName.translate(token);
  170. User user;
  171. strncpy((char *)user.name, userName.str(), 10);
  172. user.name[9] = 0;
  173. if (user.name[0] == 0)
  174. {
  175. // didn't enter a name
  176. TheWOL->addText(message);
  177. }
  178. else
  179. {
  180. // Send find command
  181. IChat *ichat = TheWOL->getIChat();
  182. ichat->RequestGlobalFind(&user);
  183. }
  184. return true; // show it anyway
  185. }
  186. #if defined _DEBUG || defined _INTERNAL
  187. else if (token.compareNoCase(L"oper") == 0)
  188. {
  189. // Send raw IRC oper command
  190. AsciiString str;
  191. str.translate(message);
  192. str.concat('\n');
  193. IChat *ichat = TheWOL->getIChat();
  194. ichat->RequestRawMessage(str.str());
  195. TheWOL->addText(message);
  196. return true; // show it anyway
  197. }
  198. #endif
  199. break;
  200. case L'p':
  201. case L'P':
  202. remainder.nextToken(&token);
  203. if (token.compareNoCase(L"page") == 0 || token.compareNoCase(L"p") == 0)
  204. {
  205. remainder.nextToken(&token);
  206. AsciiString userName;
  207. userName.translate(token);
  208. User user;
  209. strncpy((char *)user.name, userName.str(), 10);
  210. user.name[9] = 0;
  211. remainder.trim();
  212. if (user.name[0] == 0 || remainder.isEmpty())
  213. {
  214. // didn't enter a name or message
  215. TheWOL->addText(message);
  216. }
  217. else
  218. {
  219. // Send page command
  220. IChat *ichat = TheWOL->getIChat();
  221. ichat->RequestGlobalUnicodePage(&user, remainder.str());
  222. }
  223. return true; // show it anyway
  224. }
  225. break;
  226. case L'q':
  227. case L'Q':
  228. remainder.nextToken(&token);
  229. if (token.compareNoCase(L"quit") == 0)
  230. {
  231. TheWOL->setState(WOLAPI_LOGIN);
  232. TheWOL->addCommand(WOLCOMMAND_LOGOUT);
  233. //TheWOL->setScreen(WOLAPI_MENU_WELCOME);
  234. return true; // show it anyway
  235. }
  236. break;
  237. #if defined _DEBUG || defined _INTERNAL
  238. case L'c':
  239. case L'C':
  240. remainder.nextToken(&token);
  241. if (token.compareNoCase(L"colortest") == 0)
  242. {
  243. addColorText(token, 0xDD, 0xE2, 0x0D, 0xff);
  244. addColorText(token, 0xFF, 0x19, 0x19, 0xff);
  245. addColorText(token, 0x2A, 0x74, 0xE2, 0xff);
  246. addColorText(token, 0x3E, 0xD1, 0x2E, 0xff);
  247. addColorText(token, 0xFF, 0xA0, 0x19, 0xff);
  248. addColorText(token, 0x32, 0xD7, 0xE6, 0xff);
  249. addColorText(token, 0x95, 0x28, 0xBD, 0xff);
  250. addColorText(token, 0xFF, 0x9A, 0xEB, 0xff);
  251. return true; // show it anyway
  252. }
  253. break;
  254. #endif // _DEBUG || defined _INTERNAL
  255. }
  256. }
  257. */
  258. return false;
  259. }
  260. static handleUnicodeMessage( const char *nick, UnicodeString msg, Bool isPublic, Bool isAction );
  261. Bool GameSpySendChat( UnicodeString message, Bool isAction, GameWindow *playerListbox )
  262. {
  263. RoomType roomType = StagingRoom;
  264. if (TheGameSpyChat->getCurrentGroupRoomID())
  265. roomType = GroupRoom;
  266. message.trim();
  267. // Echo the user's input to the chat window
  268. if (!message.isEmpty())
  269. {
  270. // Check for slash commands
  271. if (handleSlashCommands(message, isAction, playerListbox))
  272. {
  273. return false; // already handled
  274. }
  275. if (!playerListbox)
  276. {
  277. // Public message
  278. if (isAction)
  279. {
  280. peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), ActionMessage);
  281. //if (roomType == StagingRoom)
  282. //handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, true);
  283. }
  284. else
  285. {
  286. peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), NormalMessage);
  287. //if (roomType == StagingRoom)
  288. //handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, false);
  289. }
  290. return false;
  291. }
  292. // Get the selections (is this a private message?)
  293. Int maxSel = GadgetListBoxGetListLength(playerListbox);
  294. Int *selections;
  295. GadgetListBoxGetSelected(playerListbox, (Int *)&selections);
  296. if (selections[0] == -1)
  297. {
  298. // Public message
  299. if (isAction)
  300. {
  301. peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), ActionMessage);
  302. //if (roomType == StagingRoom)
  303. //handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, true);
  304. }
  305. else
  306. {
  307. peerMessageRoom(TheGameSpyChat->getPeer(), roomType, UnicodeStringToQuotedPrintable(message).str(), NormalMessage);
  308. //if (roomType == StagingRoom)
  309. //handleUnicodeMessage(TheGameSpyChat->getloginName().str(), message, true, false);
  310. }
  311. return false;
  312. }
  313. else
  314. {
  315. // Private message
  316. // Construct a list
  317. AsciiString names = AsciiString::TheEmptyString;
  318. AsciiString tmp = AsciiString::TheEmptyString;
  319. AsciiString aStr; // AsciiString buf for translating Unicode entries
  320. names.format("%s", TheGameSpyChat->getLoginName().str());
  321. for (int i=0; i<maxSel; i++)
  322. {
  323. if (selections[i] != -1)
  324. {
  325. aStr.translate(GadgetListBoxGetText(playerListbox, selections[i], 0));
  326. if (aStr.compareNoCase(TheGameSpyChat->getLoginName()))
  327. {
  328. tmp.format(",%s", aStr.str());
  329. names.concat(tmp);
  330. }
  331. }
  332. else
  333. {
  334. break;
  335. }
  336. }
  337. if (!names.isEmpty())
  338. {
  339. if (isAction)
  340. {
  341. peerMessagePlayer(TheGameSpyChat->getPeer(), names.str(), UnicodeStringToQuotedPrintable(message).str(), ActionMessage);
  342. }
  343. else
  344. {
  345. peerMessagePlayer(TheGameSpyChat->getPeer(), names.str(), UnicodeStringToQuotedPrintable(message).str(), NormalMessage);
  346. }
  347. }
  348. return true;
  349. }
  350. }
  351. return false;
  352. }
  353. void RoomMessageCallback(PEER peer, RoomType roomType,
  354. const char * nick, const char * message,
  355. MessageType messageType, void * param)
  356. {
  357. DEBUG_LOG(("RoomMessageCallback\n"));
  358. handleUnicodeMessage(nick, QuotedPrintableToUnicodeString(message), true, (messageType == ActionMessage));
  359. }
  360. void PlayerMessageCallback(PEER peer,
  361. const char * nick, const char * message,
  362. MessageType messageType, void * param)
  363. {
  364. DEBUG_LOG(("PlayerMessageCallback\n"));
  365. handleUnicodeMessage(nick, QuotedPrintableToUnicodeString(message), false, (messageType == ActionMessage));
  366. }
  367. static handleUnicodeMessage( const char *nick, UnicodeString msg, Bool isPublic, Bool isAction )
  368. {
  369. GameSpyColors style;
  370. Bool isOwner = false;
  371. Int flags = 0;
  372. if (TheGameSpyChat->getCurrentGroupRoomID())
  373. peerGetPlayerFlags(TheGameSpyChat->getPeer(), nick, GroupRoom, &flags);
  374. else
  375. peerGetPlayerFlags(TheGameSpyChat->getPeer(), nick, StagingRoom, &flags);
  376. isOwner = flags & PEER_FLAG_OP;
  377. if (isPublic && isAction)
  378. {
  379. style = (isOwner)?GSCOLOR_CHAT_OWNER_EMOTE:GSCOLOR_CHAT_EMOTE;
  380. }
  381. else if (isPublic)
  382. {
  383. style = (isOwner)?GSCOLOR_CHAT_OWNER:GSCOLOR_CHAT_NORMAL;
  384. }
  385. else if (isAction)
  386. {
  387. style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER_EMOTE:GSCOLOR_CHAT_PRIVATE_EMOTE;
  388. }
  389. else
  390. {
  391. style = (isOwner)?GSCOLOR_CHAT_PRIVATE_OWNER:GSCOLOR_CHAT_PRIVATE;
  392. }
  393. UnicodeString name;
  394. name.translate(nick);
  395. // filters language
  396. // if( TheGlobalData->m_languageFilterPref )
  397. // {
  398. TheLanguageFilter->filterLine(msg);
  399. // }
  400. UnicodeString fullMsg;
  401. if (isAction)
  402. {
  403. fullMsg.format( L"%ls %ls", name.str(), msg.str() );
  404. }
  405. else
  406. {
  407. fullMsg.format( L"[%ls] %ls", name.str(), msg.str() );
  408. }
  409. GameSpyAddText(fullMsg, style);
  410. }
  411. void GameSpyAddText( UnicodeString message, GameSpyColors color )
  412. {
  413. GameWindow *textWindow = NULL;
  414. if (!textWindow)
  415. textWindow = listboxLobbyChat;
  416. if (!textWindow)
  417. textWindow = listboxGameSetupChat;
  418. if (!textWindow)
  419. return;
  420. GadgetListBoxAddEntryText(textWindow, message, GameSpyColor[color], -1, -1);
  421. }