net.cpp 14 KB

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