net.cpp 14 KB

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