net.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 "platform/platform.h"
  23. #include "core/dnet.h"
  24. #include "core/idGenerator.h"
  25. #include "core/stream/bitStream.h"
  26. #include "console/simBase.h"
  27. #include "console/console.h"
  28. #include "console/consoleTypes.h"
  29. #include "sim/netConnection.h"
  30. #include "sim/netObject.h"
  31. #include "app/net/serverQuery.h"
  32. #include "console/engineAPI.h"
  33. #include <vector>
  34. #include "net.h"
  35. //----------------------------------------------------------------
  36. // remote procedure call console functions
  37. //----------------------------------------------------------------
  38. RemoteCommandEvent::RemoteCommandEvent(S32 argc, const char **argv, NetConnection *conn)
  39. {
  40. mArgc = argc;
  41. for(S32 i = 0; i < argc; i++)
  42. {
  43. if(argv[i][0] == StringTagPrefixByte)
  44. {
  45. char buffer[256];
  46. mTagv[i+1] = NetStringHandle(dAtoi(argv[i]+1));
  47. if(conn)
  48. {
  49. dSprintf(buffer + 1, sizeof(buffer) - 1, "%d", conn->getNetSendId(mTagv[i+1]));
  50. buffer[0] = StringTagPrefixByte;
  51. mArgv[i+1] = dStrdup(buffer);
  52. }
  53. }
  54. else
  55. mArgv[i+1] = dStrdup(argv[i]);
  56. }
  57. }
  58. #ifdef TORQUE_DEBUG_NET
  59. const char *RemoteCommandEvent::getDebugName()
  60. {
  61. static char buffer[256];
  62. dSprintf(buffer, sizeof(buffer), "%s [%s]", getClassName(), mTagv[1].isValidString() ? mTagv[1].getString() : "--unknown--" );
  63. return buffer;
  64. }
  65. #endif
  66. RemoteCommandEvent::~RemoteCommandEvent()
  67. {
  68. for(S32 i = 0; i < mArgc; i++)
  69. dFree(mArgv[i+1]);
  70. }
  71. void RemoteCommandEvent::pack(NetConnection* conn, BitStream *bstream)
  72. {
  73. bstream->writeInt(mArgc, CommandArgsBits);
  74. // write it out reversed... why?
  75. // automatic string substitution with later arguments -
  76. // handled automatically by the system.
  77. for(S32 i = 0; i < mArgc; i++)
  78. conn->packString(bstream, mArgv[i+1]);
  79. }
  80. void RemoteCommandEvent::write(NetConnection* conn, BitStream *bstream)
  81. {
  82. pack(conn, bstream);
  83. }
  84. void RemoteCommandEvent::unpack(NetConnection* conn, BitStream *bstream)
  85. {
  86. mArgc = bstream->readInt(CommandArgsBits);
  87. // read it out backwards
  88. for(S32 i = 0; i < mArgc; i++)
  89. {
  90. conn->unpackString(bstream, mBuf);
  91. mArgv[i+1] = dStrdup(mBuf);
  92. }
  93. }
  94. void RemoteCommandEvent::process(NetConnection *conn)
  95. {
  96. static char idBuf[10];
  97. // de-tag the command name
  98. if (mArgc < 1 || mArgv[1][0] != StringTagPrefixByte)
  99. return;
  100. for(S32 i = mArgc - 1; i >= 0; i--)
  101. {
  102. char *arg = mArgv[i+1];
  103. if(*arg == StringTagPrefixByte)
  104. {
  105. // it's a tag:
  106. U32 localTag = dAtoi(arg + 1);
  107. NetStringHandle tag = conn->translateRemoteStringId(localTag);
  108. NetStringTable::expandString( tag,
  109. mBuf,
  110. sizeof(mBuf),
  111. (mArgc - 1) - i,
  112. (const char**)(mArgv + i + 2) );
  113. dFree(mArgv[i+1]);
  114. mArgv[i+1] = dStrdup(mBuf);
  115. }
  116. }
  117. const char *rmtCommandName = dStrchr(mArgv[1], ' ') + 1;
  118. if(conn->isConnectionToServer())
  119. {
  120. dStrcpy(mBuf, "clientCmd", 1024);
  121. dStrcat(mBuf, rmtCommandName, 1024);
  122. char *temp = mArgv[1];
  123. mArgv[1] = mBuf;
  124. Con::execute(mArgc, (const char **) mArgv+1);
  125. mArgv[1] = temp;
  126. }
  127. else
  128. {
  129. dStrcpy(mBuf, "serverCmd", 1024);
  130. dStrcat(mBuf, rmtCommandName, 1024);
  131. char *temp = mArgv[1];
  132. dSprintf(idBuf, sizeof(idBuf), "%d", conn->getId());
  133. mArgv[0] = mBuf;
  134. mArgv[1] = idBuf;
  135. Con::execute(mArgc+1, (const char **) mArgv);
  136. mArgv[1] = temp;
  137. }
  138. }
  139. void RemoteCommandEvent::sendRemoteCommand(NetConnection *conn, S32 argc, const char **argv)
  140. {
  141. if(U8(argv[0][0]) != StringTagPrefixByte)
  142. {
  143. Con::errorf(ConsoleLogEntry::Script, "Remote Command Error - command must be a tag.");
  144. return;
  145. }
  146. S32 i;
  147. for(i = argc - 1; i >= 0; i--)
  148. {
  149. if(argv[i][0] != 0)
  150. break;
  151. argc = i;
  152. }
  153. for(i = 0; i < argc; i++)
  154. conn->validateSendString(argv[i]);
  155. RemoteCommandEvent *cevt = new RemoteCommandEvent(argc, argv, conn);
  156. conn->postNetEvent(cevt);
  157. }
  158. const char* RemoteCommandEvent::getTaggedString(const char* tag)
  159. {
  160. const char *indexPtr = tag;
  161. if (*indexPtr == StringTagPrefixByte)
  162. indexPtr++;
  163. return gNetStringTable->lookupString(dAtoi(indexPtr));
  164. }
  165. void RemoteCommandEvent::removeTaggedString(S32 tag)
  166. {
  167. if (tag)
  168. gNetStringTable->removeString(tag, true);
  169. }
  170. const char* RemoteCommandEvent::addTaggedString(const char* str)
  171. {
  172. NetStringHandle s(str);
  173. gNetStringTable->incStringRefScript(s.getIndex());
  174. char *ret = Con::getReturnBuffer(10);
  175. ret[0] = StringTagPrefixByte;
  176. dSprintf(ret + 1, 9, "%d", s.getIndex());
  177. return ret;
  178. }
  179. char RemoteCommandEvent::mBuf[1024];
  180. IMPLEMENT_CO_NETEVENT_V1(RemoteCommandEvent);
  181. ConsoleDocClass( RemoteCommandEvent,
  182. "@brief Object used for remote procedure calls.\n\n"
  183. "Not intended for game development, for exposing ConsoleFunctions (such as commandToClient) only.\n\n"
  184. "@internal");
  185. ConsoleFunctionGroupBegin( Net, "Functions for use with the network; tagged strings and remote commands.");
  186. DefineEngineStringlyVariadicFunction( commandToServer, void, 2, RemoteCommandEvent::MaxRemoteCommandArgs + 1, "(string func, ...)"
  187. "@brief Send a command to the server.\n\n"
  188. "@param func Name of the server command being called\n"
  189. "@param ... Various parameters being passed to server command\n\n"
  190. "@tsexample\n"
  191. "// Create a standard function. Needs to be executed on the client, such \n"
  192. "// as within scripts/client/default.bind." TORQUE_SCRIPT_EXTENSION "\n"
  193. "function toggleCamera(%val)\n"
  194. "{\n"
  195. " // If key was down, call a server command named 'ToggleCamera'\n"
  196. " if (%val)\n"
  197. " commandToServer('ToggleCamera');\n"
  198. "}\n\n"
  199. "// Server command being called from above. Needs to be executed on the \n"
  200. "// server, such as within scripts/server/commands." TORQUE_SCRIPT_EXTENSION "\n"
  201. "function serverCmdToggleCamera(%client)\n"
  202. "{\n"
  203. " if (%client.getControlObject() == %client.player)\n"
  204. " {\n"
  205. " %client.camera.setVelocity(\"0 0 0\");\n"
  206. " %control = %client.camera;\n"
  207. " }\n"
  208. " else\n"
  209. " {\n"
  210. " %client.player.setVelocity(\"0 0 0\");\n"
  211. " %control = %client.player;\n"
  212. " }\n"
  213. " %client.setControlObject(%control);\n"
  214. " clientCmdSyncEditorGui();\n"
  215. "}\n"
  216. "@endtsexample\n\n"
  217. "@ingroup Networking")
  218. {
  219. NetConnection *conn = NetConnection::getConnectionToServer();
  220. if(!conn)
  221. return;
  222. ConsoleValueToStringArrayWrapper args(argc - 1, argv + 1);
  223. RemoteCommandEvent::sendRemoteCommand(conn, args.count(), args);
  224. }
  225. DefineEngineStringlyVariadicFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(NetConnection client, string func, ...)"
  226. "@brief Send a command from the server to the client\n\n"
  227. "@param client The numeric ID of a client GameConnection\n"
  228. "@param func Name of the client function being called\n"
  229. "@param ... Various parameters being passed to client command\n\n"
  230. "@tsexample\n"
  231. "// Set up the client command. Needs to be executed on the client, such as\n"
  232. "// within scripts/client/client." TORQUE_SCRIPT_EXTENSION "\n"
  233. "// Update the Ammo Counter with current ammo, if not any then hide the counter.\n"
  234. "function clientCmdSetAmmoAmountHud(%amount)\n"
  235. "{\n"
  236. " if (!%amount)\n"
  237. " AmmoAmount.setVisible(false);\n"
  238. " else\n"
  239. " {\n"
  240. " AmmoAmount.setVisible(true);\n"
  241. " AmmoAmount.setText(\"Ammo: \"@%amount);\n"
  242. " }\n"
  243. "}\n\n"
  244. "// Call it from a server function. Needs to be executed on the server, \n"
  245. "//such as within scripts/server/game." TORQUE_SCRIPT_EXTENSION "\n"
  246. "function GameConnection::setAmmoAmountHud(%client, %amount)\n"
  247. "{\n"
  248. " commandToClient(%client, 'SetAmmoAmountHud', %amount);\n"
  249. "}\n"
  250. "@endtsexample\n\n"
  251. "@ingroup Networking\n")
  252. {
  253. NetConnection *conn;
  254. if(!Sim::findObject(argv[1], conn))
  255. return;
  256. ConsoleValueToStringArrayWrapper args(argc - 2, argv + 2);
  257. RemoteCommandEvent::sendRemoteCommand(conn, args.count(), args);
  258. }
  259. DefineEngineFunction(removeTaggedString, void, (S32 tag), (-1),
  260. "@brief Remove a tagged string from the Net String Table\n\n"
  261. "@param tag The tag associated with the string\n\n"
  262. "@see \\ref syntaxDataTypes under Tagged %Strings\n"
  263. "@see addTaggedString()\n"
  264. "@see getTaggedString()\n"
  265. "@ingroup Networking\n")
  266. {
  267. RemoteCommandEvent::removeTaggedString(tag);
  268. }
  269. DefineEngineFunction(addTaggedString, const char* , (const char* str), (""),
  270. "@brief Use the addTaggedString function to tag a new string and add it to the NetStringTable\n\n"
  271. "@param str The string to be tagged and placed in the NetStringTable. Tagging ignores case, "
  272. "so tagging the same string (excluding case differences) will be ignored as a duplicated tag.\n\n"
  273. "@return Returns a string( containing a numeric value) equivalent to the string ID for the newly tagged string"
  274. "@see \\ref syntaxDataTypes under Tagged %Strings\n"
  275. "@see removeTaggedString()\n"
  276. "@see getTaggedString()\n"
  277. "@ingroup Networking\n")
  278. {
  279. return RemoteCommandEvent::addTaggedString(str);
  280. }
  281. DefineEngineFunction(getTaggedString, const char* , (const char *tag), (""),
  282. "@brief Use the getTaggedString function to convert a tag to a string.\n\n"
  283. "This is not the same as detag() which can only be used within the context "
  284. "of a function that receives a tag. This function can be used any time and "
  285. "anywhere to convert a tag to a string.\n\n"
  286. "@param tag A numeric tag ID.\n"
  287. "@returns The string as found in the Net String table.\n"
  288. "@see \\ref syntaxDataTypes under Tagged %Strings\n"
  289. "@see addTaggedString()\n"
  290. "@see removeTaggedString()\n"
  291. "@ingroup Networking\n")
  292. {
  293. return RemoteCommandEvent::getTaggedString(tag);
  294. }
  295. DefineEngineStringlyVariadicFunction( buildTaggedString, const char*, 2, 11, "(string format, ...)"
  296. "@brief Build a string using the specified tagged string format.\n\n"
  297. "This function takes an already tagged string (passed in as a tagged string ID) and one "
  298. "or more additional strings. If the tagged string contains argument tags that range from "
  299. "%%1 through %%9, then each additional string will be substituted into the tagged string. "
  300. "The final (non-tagged) combined string will be returned. The maximum length of the tagged "
  301. "string plus any inserted additional strings is 511 characters.\n\n"
  302. "@param format A tagged string ID that contains zero or more argument tags, in the form of "
  303. "%%1 through %%9.\n"
  304. "@param ... A variable number of arguments that are insterted into the tagged string "
  305. "based on the argument tags within the format string.\n"
  306. "@returns An ordinary string that is a combination of the original tagged string with any additional "
  307. "strings passed in inserted in place of each argument tag.\n"
  308. "@tsexample\n"
  309. "// Create a tagged string with argument tags\n"
  310. "%taggedStringID = addTaggedString(\"Welcome %1 to the game!\");\n\n"
  311. "// Some point later, combine the tagged string with some other string\n"
  312. "%string = buildTaggedString(%taggedStringID, %playerName);\n"
  313. "echo(%string);\n"
  314. "@endtsexample\n\n"
  315. "@see \\ref syntaxDataTypes under Tagged %Strings\n"
  316. "@see addTaggedString()\n"
  317. "@see getTaggedString()\n"
  318. "@ingroup Networking\n")
  319. {
  320. const char *indexPtr = argv[1];
  321. if (*indexPtr == StringTagPrefixByte)
  322. indexPtr++;
  323. const char *fmtString = gNetStringTable->lookupString(dAtoi(indexPtr));
  324. static const U32 bufSize = 512;
  325. char *strBuffer = Con::getReturnBuffer(bufSize);
  326. const char *fmtStrPtr = fmtString;
  327. char *strBufPtr = strBuffer;
  328. S32 strMaxLength = bufSize - 1;
  329. if (!fmtString)
  330. goto done;
  331. //build the string
  332. while (*fmtStrPtr)
  333. {
  334. //look for an argument tag
  335. if (*fmtStrPtr == '%')
  336. {
  337. if (fmtStrPtr[1] >= '1' && fmtStrPtr[1] <= '9')
  338. {
  339. S32 argIndex = S32(fmtStrPtr[1] - '0') + 1;
  340. if (argIndex >= argc)
  341. goto done;
  342. const char *argStr = argv[argIndex];
  343. if (!argStr)
  344. goto done;
  345. S32 strLength = dStrlen(argStr);
  346. if (strLength > strMaxLength)
  347. goto done;
  348. dStrcpy(strBufPtr, argStr, strMaxLength);
  349. strBufPtr += strLength;
  350. strMaxLength -= strLength;
  351. fmtStrPtr += 2;
  352. continue;
  353. }
  354. }
  355. //if we don't continue, just copy the character
  356. if (strMaxLength <= 0)
  357. goto done;
  358. *strBufPtr++ = *fmtStrPtr++;
  359. strMaxLength--;
  360. }
  361. done:
  362. *strBufPtr = '\0';
  363. return strBuffer;
  364. }
  365. ConsoleFunctionGroupEnd( Net );