lsl-bot-controller.tpl 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. {{ define "lsl-bot-controller" }}
  2. <p>This is the master bot controller script, which allows controlling all agents, including creating new ones.</p>
  3. <p>Copy the below code to a script called <code>bot controller.lsl</code> and put it inside a cube.</p>
  4. <pre><code class="language-javascript">
  5. // Handles registration with the external database
  6. // Send inventory and deal with external commands
  7. // This combines the old listen channel with the new HTTP-based command, since we need to be able to clone avatars
  8. // to notecards (which was only present on the old code)
  9. string registrationURL = "http://{{.Host}}{{.ServerPort}}{{.URLPathPrefix}}/register-position/";
  10. string externalURL; // this is what we'll get from SL to get incoming connections
  11. string webServerURLupdateInventory = "http://{{.Host}}{{.ServerPort}}{{.URLPathPrefix}}/update-inventory/";
  12. key registrationRequest; // used to track down the request for registration
  13. key updateRequest; // used to track down the request for registration
  14. key serverKey; // for inventory updates
  15. key httpRequestKey;
  16. list npcNames;
  17. integer howManyNPCs = 0;
  18. string deleteAvatarURL = "http://{{.Host}}{{.ServerPort}}{{.URLPathPrefix}}/register-position/";
  19. key deleteRequest;
  20. key npc; // used on chat commands
  21. integer LSLSignaturePIN = {{.LSLSignaturePIN}};
  22. integer listenChannel = 10;
  23. init()
  24. {
  25. llSetText("Registering bot controller...", &lt;1.0,0.0,0.0&gt;, 1.0);
  26. llOwnerSay("Registering bot controller...");
  27. llSetTimerEvent(0.0);
  28. // release URLs before requesting a new one
  29. llReleaseURL(externalURL);
  30. externalURL = "";
  31. llRequestURL();
  32. }
  33. default
  34. {
  35. state_entry()
  36. {
  37. //llOwnerSay("On state_entry");
  38. llListen(listenChannel,"",NULL_KEY,"");
  39. llSetText("Listening on " + listenChannel, &lt;0, 255, 0&gt;, 1);
  40. llOwnerSay("Say /" + (string)listenChannel + " help for commands");
  41. llSetTimerEvent(3600.0); // this will hopefully force an update every hour
  42. }
  43. on_rez(integer what)
  44. {
  45. //llOwnerSay("On on_rez");
  46. init();
  47. }
  48. touch_start(integer total_number)
  49. {
  50. // llOwnerSay("On touch_start");
  51. // just re-register
  52. if (llDetectedKey(0) == llGetOwner() || llDetectedGroup(0))
  53. {
  54. if (llDetectedTouchFace(0) != 0)
  55. init();
  56. }
  57. }
  58. listen(integer channel, string name, key id, string msg)
  59. {
  60. if (msg != "")
  61. {
  62. list commands = llParseString2List(msg, [ " " ], []);
  63. string msg0 = llList2String(commands, 0);
  64. string msg1 = llList2String(commands, 1);
  65. string msg2 = llList2String(commands, 2);
  66. string msg3 = llList2String(commands, 3);
  67. if (msg0 == "create")
  68. {
  69. if (msg1 != "")
  70. {
  71. string notecardName = msg1;
  72. string FirstName = msg2;
  73. string LastName = msg3;
  74. if (FirstName == "") FirstName = "Jane";
  75. if (LastName == "") LastName = "Doe";
  76. npc = osNpcCreate(FirstName, LastName, llGetPos() + &lt;5, 5, 0&gt;, notecardName, OS_NPC_SENSE_AS_AGENT);
  77. npcNames += [npc];
  78. osNpcSetProfileImage(npc, "botimage"); // this texture MUST be inside the content for this to work!
  79. osNpcSetProfileAbout(npc, "Hello! I'm just a friendly bot passing by! Please ignore me!");
  80. llOwnerSay("Created npc " + (string) npc + " from notecard " + notecardName);
  81. }
  82. else
  83. {
  84. llOwnerSay("Usage: create <notecard-name> <FirstName> <LastName>");
  85. }
  86. }
  87. else if (msg0 =="createm")
  88. {
  89. // msg1 says number of NPCs to be created
  90. string notecardName = msg2;
  91. if (notecardName == "") notecardName = "appearance";
  92. // osOwnerSaveAppearance(notecardName);
  93. vector pos = llGetPos();
  94. integer i;
  95. npcNames = []; // reset list
  96. key newNPC;
  97. float angle;
  98. rotation npcCurrRot;
  99. rotation npcNewRot;
  100. for (i = 0; i < (integer)msg1; i++)
  101. {
  102. // add some randomness
  103. float xRand = llFrand(30) - 30;
  104. float yRand = llFrand(30) - 30;
  105. newNPC = osNpcCreate("John-" + (string)i, "Doe", pos + &lt;xRand, yRand, 0&gt;, notecardName);
  106. npcNames += [newNPC];
  107. llSleep(1);
  108. npcCurrRot = osNpcGetRot(newNPC);
  109. angle = llAtan2( llVecMag(pos % (pos + &lt;xRand, yRand, 0&gt;)), pos * (pos + &lt;xRand, yRand, 0&gt;) );
  110. npcNewRot = npcCurrRot * llEuler2Rot(&lt;0.0, 0.0, angle&gt;);
  111. osNpcSetRot(newNPC, npcNewRot);
  112. llOwnerSay("NPC <" + newNPC + "> created, old rot: "
  113. + (string)npcCurrRot + ", new rot: " + (string)npcNewRot);
  114. }
  115. }
  116. else if (msg0 == "remove" && npc != NULL_KEY)
  117. {
  118. integer npcToKill = llListFindList(npcNames, npc);
  119. if (npcToKill == -1) {
  120. llOwnerSay("Remove: NPC key '" + (string)npc + "' not found");
  121. } else {
  122. osNpcSay(npc, "You will pay for this with your liiiiiivvveeessss!!!.....");
  123. osNpcRemove(npc);
  124. // inform server to delete this bot from
  125. // database
  126. string myTimestamp = llGetTimestamp();
  127. deleteRequest = llHTTPRequest(deleteAvatarURL, [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
  128. "request=delete"
  129. + "&npc=" + (string)npc
  130. + "&amp;timestamp=" + myTimestamp
  131. + "&signature=" + llMD5String((string)llGetKey() + myTimestamp, LSLSignaturePIN));
  132. }
  133. }
  134. else if (msg0 == "forceremove")
  135. {
  136. llInstantMessage(id, "Trying to remove " +
  137. msg1 + "(" + llKey2Name((key)msg1) + ")");
  138. osNpcRemove((key)msg1);
  139. string myTimestamp = llGetTimestamp();
  140. deleteRequest = llHTTPRequest(deleteAvatarURL, [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
  141. "request=delete"
  142. + "&npc=" + msg1
  143. + "&amp;timestamp=" + myTimestamp
  144. + "&signature=" + llMD5String((string)llGetKey() + myTimestamp, LSLSignaturePIN));
  145. }
  146. else if (msg0 == "removeall")
  147. {
  148. integer i;
  149. for (i = 0; i < llGetListLength(npcNames); i++)
  150. {
  151. osNpcRemove(llList2Key(npcNames, i));
  152. string myTimestamp = llGetTimestamp();
  153. deleteRequest = llHTTPRequest(deleteAvatarURL, [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
  154. "request=delete"
  155. + "&npc=" + llList2String(npcNames, i)
  156. + "&amp;timestamp=" + myTimestamp
  157. + "&signature=" + llMD5String((string)llGetKey() + myTimestamp, LSLSignaturePIN));
  158. }
  159. llOwnerSay("All NPCs removed");
  160. }
  161. else if (msg0 == "say" && npc != NULL_KEY)
  162. {
  163. integer npcToSay = llListFindList(npcNames, npc);
  164. if (npcToSay == -1) {
  165. llOwnerSay("Say: NPC key " + (string)npc + "not found");
  166. } else {
  167. osNpcSay(llList2Key(npcNames, npcToSay), "I am your worst Nightmare!!!!");
  168. }
  169. }
  170. else if (msg0 == "move")
  171. {
  172. integer npcToMove = llListFindList(npcNames, npc);
  173. if (msg1 != "" && msg2 != "" && npc != NULL_KEY && npcToMove != -1)
  174. {
  175. key npcMoving = llList2Key(npcNames, npcToMove);
  176. vector delta = &lt;(integer)msg1, (integer)msg2, 0&gt;;
  177. if (msg3 != "")
  178. {
  179. delta.z = (integer)msg3;
  180. }
  181. osNpcMoveTo(npcMoving, osNpcGetPos(npc) + delta);
  182. }
  183. else
  184. {
  185. llOwnerSay("Usage: move <x> <y> [<z>]");
  186. }
  187. }
  188. else if (msg0 == "moveto")
  189. {
  190. integer npcToMove = llListFindList(npcNames, npc);
  191. if (msg1 != "" && msg2 != "" && npc != NULL_KEY && npcToMove != -1)
  192. {
  193. vector pos = &lt;(integer)msg1, (integer)msg2, 0&gt;;
  194. if (msg3 != "")
  195. {
  196. pos.z = (integer)msg3;
  197. }
  198. osNpcMoveTo(npc, pos);
  199. }
  200. else
  201. {
  202. llOwnerSay("Usage: move <x> <y> [<z>]");
  203. }
  204. }
  205. else if (msg0 == "movetarget" && npc != NULL_KEY)
  206. {
  207. integer npcToMove = llListFindList(npcNames, npc);
  208. if (npcToMove == -1)
  209. {
  210. llOwnerSay("MoveTarget: NPC key " + (string)npc + " not found");
  211. } else {
  212. osNpcMoveToTarget(npc, llGetPos() + &lt;9,9,5&gt;, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET);
  213. }
  214. }
  215. else if (msg0 == "movetargetnoland" && npc != NULL_KEY)
  216. {
  217. integer npcToMove = llListFindList(npcNames, npc);
  218. if (npcToMove == -1) {
  219. llOwnerSay("MoveTargetNoLand: NPC key " + (string)npc + " not found");
  220. } else {
  221. osNpcMoveToTarget(npc, llGetPos() + &lt;9,9,5&gt;, OS_NPC_FLY);
  222. }
  223. }
  224. else if (msg0 == "movetargetwalk" && npc != NULL_KEY)
  225. {
  226. integer npcToMove = llListFindList(npcNames, npc);
  227. if (npcToMove == -1) {
  228. llOwnerSay("MoveTargetWalk: NPC key " + (string)npc + " not found");
  229. } else {
  230. osNpcMoveToTarget(npc, llGetPos() + &lt;9,9,0&gt;, OS_NPC_NO_FLY);
  231. }
  232. }
  233. else if (msg0 == "rot" && npc != NULL_KEY)
  234. {
  235. integer npcToMove = llListFindList(npcNames, npc);
  236. if (npcToMove == -1) {
  237. llOwnerSay("Rot: NPC key " + (string)npc + " not found");
  238. } else {
  239. vector xyz_angles = &lt;0,0,90&gt;; // This is to define a 1 degree change
  240. vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians
  241. rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation
  242. rotation rot = osNpcGetRot(npc);
  243. osNpcSetRot(npc, rot * rot_xyzq);
  244. }
  245. }
  246. else if (msg0 == "rotabs" && msg1 != "")
  247. {
  248. integer npcToMove = llListFindList(npcNames, npc);
  249. if (npcToMove == -1) {
  250. llOwnerSay("Rotabs: NPC key " + (string)npc + " not found");
  251. } else {
  252. vector xyz_angles = &lt;0, 0, (integer)msg1&gt;;
  253. vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians
  254. rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation
  255. osNpcSetRot(npc, rot_xyzq);
  256. }
  257. }
  258. else if (msg0 == "animate" && npc != NULL_KEY)
  259. {
  260. integer npcToMove = llListFindList(npcNames, npc);
  261. if (npcToMove == -1) {
  262. llOwnerSay("Animate: NPC key " + (string)npc + " not found");
  263. } else {
  264. osAvatarPlayAnimation(npc, "stabbed+die_2");
  265. llSleep(3);
  266. osAvatarStopAnimation(npc, "stabbed+die_2");
  267. }
  268. }
  269. else if (msg0 == "getrot" && npc != NULL_KEY)
  270. {
  271. integer npcToMove = llListFindList(npcNames, npc);
  272. if (npcToMove == -1) {
  273. llOwnerSay("Get rotation: NPC key " + (string)npc + " not found");
  274. } else {
  275. llSay(0, "Rotation is: " + (string)osNpcGetRot(npc));
  276. }
  277. }
  278. else if (msg0 == "save" && msg1 != "" && npc != NULL_KEY)
  279. {
  280. integer npcToMove = llListFindList(npcNames, npc);
  281. if (npcToMove == -1) {
  282. llOwnerSay("Save: NPC key " + (string)npc + " not found");
  283. } else {
  284. osNpcSaveAppearance(npc, msg1);
  285. llOwnerSay("Saved appearance " + msg1 + " to " + npc);
  286. }
  287. }
  288. else if (msg0 == "load" && msg1 != "" && npc != NULL_KEY)
  289. {
  290. integer npcToMove = llListFindList(npcNames, npc);
  291. if (npcToMove == -1) {
  292. llOwnerSay("Load appearance: NPC key " + (string)npc + " not found");
  293. } else {
  294. osNpcLoadAppearance(npc, msg1);
  295. llOwnerSay("Loaded appearance " + msg1 + " to " + npc);
  296. }
  297. }
  298. else if (msg0 == "clone")
  299. {
  300. if (msg1 != "")
  301. {
  302. osOwnerSaveAppearance(msg1);
  303. llOwnerSay("Cloned your appearance to " + msg1);
  304. }
  305. else
  306. {
  307. llOwnerSay("Usage: clone <notecard-name-to-save>");
  308. }
  309. }
  310. else if (msg0 == "stop" && npc != NULL_KEY)
  311. {
  312. integer npcToMove = llListFindList(npcNames, npc);
  313. if (npcToMove == -1) {
  314. llOwnerSay("Stop: NPC key " + (string)npc + " not found");
  315. } else {
  316. osNpcStopMoveToTarget(npc);
  317. }
  318. }
  319. else if (msg0 == "sit" && msg1 != "" && npc != NULL_KEY)
  320. {
  321. integer npcToMove = llListFindList(npcNames, npc);
  322. if (npcToMove == -1) {
  323. llOwnerSay("Sit: NPC key " + (string)npc + " not found");
  324. } else {
  325. osNpcSit(npc, msg1, OS_NPC_SIT_NOW);
  326. }
  327. }
  328. else if (msg0 == "stand" && npc != NULL_KEY)
  329. {
  330. integer npcToMove = llListFindList(npcNames, npc);
  331. if (npcToMove == -1) {
  332. llOwnerSay("Stand: NPC key " + (string)npc + " not found");
  333. } else {
  334. osNpcStand(npc);
  335. }
  336. }
  337. else if (msg0 == "swarm")
  338. {
  339. // is list empty?
  340. if (npcNames == []) llOwnerSay("Swarm: no NPCs");
  341. // go through the list
  342. integer i;
  343. vector currPos = llGetPos();
  344. vector npcCurrPos;
  345. vector npcFuturePos;
  346. key currNPC;
  347. for (i = 0; i < llGetListLength(npcNames); i++)
  348. {
  349. currNPC = llList2Key(npcNames, i);
  350. npcCurrPos = osNpcGetPos(currNPC);
  351. // calculate intermediate point (quarter the distance)
  352. npcFuturePos = ((currPos - npcCurrPos) / 4.0) + npcCurrPos;
  353. osNpcMoveToTarget(currNPC, npcFuturePos, OS_NPC_NO_FLY);
  354. osNpcSay(currNPC, "Moving from " + (string)currPos + " to "
  355. + (string)npcFuturePos);
  356. }
  357. llOwnerSay("One moving interaction finished");
  358. }
  359. else if (msg0 == "help")
  360. {
  361. llOwnerSay("Commands are:");
  362. llOwnerSay("create <notecard-name> <FirstName> <LastName> - Create NPC from a stored notecard");
  363. llOwnerSay("createm <N> <notecard-name> - Create N NPCs from a notecard");
  364. llOwnerSay("remove - Remove current NPC");
  365. llOwnerSay("forceremove <uuid> - Remove NPC with key <uuid>");
  366. llOwnerSay("removeall - Remove all NPCs");
  367. llOwnerSay("clone <notecard-name> - Clone own appearance to a notecard");
  368. llOwnerSay("load <notecard-name> - Load appearance on notecard to current npc");
  369. llOwnerSay("save <notecard-name> - Save appearance of current NPC to notecard");
  370. llOwnerSay("animate");
  371. llOwnerSay("move");
  372. llOwnerSay("moveto <x> <y> <z> - move to absolute position");
  373. llOwnerSay("movetarget");
  374. llOwnerSay("movetargetnoland");
  375. llOwnerSay("movetargetwalk");
  376. llOwnerSay("rot");
  377. llOwnerSay("getrot");
  378. llOwnerSay("say");
  379. llOwnerSay("sit <target-uuid>");
  380. llOwnerSay("stop");
  381. llOwnerSay("stand");
  382. llOwnerSay("swarm");
  383. }
  384. else
  385. {
  386. llOwnerSay("I don't understand [" + msg + "]");
  387. }
  388. }
  389. }
  390. http_response(key request_id, integer status, list metadata, string body)
  391. {
  392. if (request_id == registrationRequest || request_id == updateRequest)
  393. {
  394. if (status == 200)
  395. {
  396. llOwnerSay(body);
  397. // new registration? switch to inventory reading
  398. if (request_id == registrationRequest)
  399. state read_inventory;
  400. // if it is just an update, no need to do anything else for now
  401. }
  402. else
  403. {
  404. llSetText("!!! BROKEN !!!", &lt;1.0,0.0,0.0&gt;, 1.0);
  405. llOwnerSay("Error " +(string)status + ": " + body);
  406. llSetTimerEvent(3600.0); // try registering again in an hour
  407. }
  408. }
  409. }
  410. http_request(key id, string method, string body)
  411. {
  412. //llOwnerSay("Entering http_request for registration...");
  413. if (method == URL_REQUEST_GRANTED)
  414. {
  415. externalURL = body;
  416. string myTimestamp = llGetTimestamp();
  417. string formRequest = "permURL=" + llEscapeURL(externalURL)
  418. + "&objecttype=" + llEscapeURL("Bot Controller")
  419. + "&amp;timestamp=" + myTimestamp
  420. + "&signature=" + llMD5String((string)llGetKey() + myTimestamp, LSLSignaturePIN);
  421. // llOwnerSay("Registration URL is " + registrationURL + " Form request is: " + formRequest);
  422. registrationRequest = llHTTPRequest(registrationURL, [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
  423. formRequest);
  424. llSetTimerEvent(3600.0); // if the registration fails, try later
  425. }
  426. else if (method == URL_REQUEST_DENIED)
  427. {
  428. llSetText("!!! BROKEN !!!", &lt;1.0,0.0,0.0&gt;, 1.0);
  429. llOwnerSay("Something went wrong, no url. " + body);
  430. llSetTimerEvent(3600.0);
  431. }
  432. else if (method == "POST" || method == "GET")
  433. {
  434. // incoming request for bot to do things
  435. //llSay(0, "[Request from server:] " + body);
  436. list params = llParseStringKeepNulls(llUnescapeURL(body), ["&", "="], []);
  437. string response; // what we return
  438. // first parameter will always be be npc=
  439. key NPC = llList2String(params, 1);
  440. //if (osIsNpc(NPC))
  441. // llSay(0, "Sanity check: This is an NPC with key " + (string)NPC);
  442. //else
  443. // llSay(0, "Sanity check failed: Key " + (string)NPC + " is NOT an NPC");
  444. // llOwnerSay("List parsed: " + (string) params);
  445. // commands begin on the second parameter
  446. string commandTag = llList2String(params, 2);
  447. string command = llList2String(params, 3);
  448. if (commandTag == "command" && (osIsNpc(NPC) || command == "osNpcCreate" || command == "ping"))
  449. {
  450. if (command == "osNpcGetRot")
  451. {
  452. response = (string)osNpcGetRot(NPC);
  453. }
  454. else if (command == "osNpcSetRot")
  455. {
  456. osNpcSetRot(NPC, llList2Rot(params, 5));
  457. response = "Rotation set.";
  458. }
  459. else if (command == "osNpcGetPos")
  460. {
  461. response = (string)osNpcGetPos(NPC);
  462. }
  463. else if (command == "osNpcGetOwner")
  464. {
  465. response = (string)osNpcGetOwner(NPC);
  466. }
  467. else if (command == "osNpcMoveToTarget")
  468. {
  469. osNpcMoveToTarget(NPC, llList2Vector(params, 5), llList2Integer(params, 7));
  470. response = "Moving to target " + llList2String(params, 5);
  471. }
  472. else if (command == "osNpcStopMoveToTarget")
  473. {
  474. osNpcStopMoveToTarget(NPC);
  475. response = "Stopping.";
  476. }
  477. else if (command == "osNpcSit")
  478. {
  479. osNpcSit(NPC, llList2Key(params, 5), llList2Integer(params, 7));
  480. response = "Sitting on " +
  481. llKey2Name(llList2Key(params, 5))
  482. + " (" + llList2Key(params, 5) + ").";
  483. }
  484. else if (command == "osNpcStand")
  485. {
  486. osNpcStand(NPC);
  487. response = "Standing up.";
  488. }
  489. else if (command == "osNpcSay")
  490. {
  491. osNpcSay(NPC, llList2Integer(params, 5), llUnescapeURL(llList2String(params, 7)));
  492. response = "Saying \"" +
  493. llUnescapeURL(llList2String(params, 7)) + "\".";
  494. }
  495. else if (command == "osNpcShout")
  496. {
  497. osNpcShout(NPC, llList2Integer(params, 5), llUnescapeURL(llList2String(params, 7)));
  498. response = "Shouting \"" +
  499. llUnescapeURL(llList2String(params, 7)) + "\".";
  500. }
  501. else if (command == "osNpcWhisper")
  502. {
  503. osNpcWhisper(NPC, llList2Integer(params, 5), llUnescapeURL(llList2String(params, 7)));
  504. response = "Whispering \"" +
  505. llUnescapeURL(llList2String(params, 7)) + "\".";
  506. }
  507. else if (command == "osNpcPlayAnimation")
  508. {
  509. osNpcPlayAnimation(NPC, llList2String(params, 5));
  510. response = "Playing animation \"" +
  511. llList2String(params, 5) + "\".";
  512. }
  513. else if (command == "osNpcStopAnimation")
  514. {
  515. osNpcStopAnimation(NPC, llList2String(params, 5));
  516. response = "Stopping animation \"" +
  517. llList2String(params, 5) + "\".";
  518. }
  519. else if (command == "osNpcLoadAppearance")
  520. {
  521. osNpcLoadAppearance(NPC, llList2String(params, 5));
  522. response = "Loading appearance \"" +
  523. llList2String(params, 5) + "\".";
  524. }
  525. else if (command == "osNpcTouch")
  526. {
  527. osNpcTouch(NPC, llList2Key(params, 5), llList2Integer(params, 7));
  528. response = "Touching " + llKey2Name(llList2Key(params, 5))
  529. + " (" + llList2Key(params, 5) + ").";
  530. }
  531. else if (command == "osNpcCreate")
  532. {
  533. string FirstName = "My";
  534. string LastName = "Bot";
  535. string fullName = llList2String(params, 5);
  536. integer index = llSubStringIndex(fullName, " ");
  537. if (~index)
  538. FirstName = llDeleteSubString(fullName, index, -1);
  539. else FirstName = fullName;
  540. if (~index)
  541. LastName = llDeleteSubString(fullName, 0, index);
  542. else LastName = "Bot";
  543. // add some randomness
  544. float xRand = llFrand(30) - 30;
  545. float yRand = llFrand(30) - 30;
  546. vector pos = llGetPos();
  547. key newNPC = osNpcCreate(FirstName, LastName, pos + &lt;xRand, yRand, 0&gt;, llList2String(params, 7), OS_NPC_SENSE_AS_AGENT);
  548. llSleep(1);
  549. rotation npcCurrRot = osNpcGetRot(newNPC);
  550. float angle = llAtan2( llVecMag(pos % (pos + &lt;xRand, yRand, 0&gt;)), pos * (pos + &lt;xRand, yRand, 0&gt;) );
  551. rotation npcNewRot = npcCurrRot * llEuler2Rot(&lt;0.0, 0.0, angle&gt;);
  552. osNpcSetRot(newNPC, npcNewRot);
  553. osNpcSetProfileImage(newNPC, "botimage"); // this texture MUST be inside the content for this to work!
  554. osNpcSetProfileAbout(newNPC, "Hello! I'm just a friendly bot passing by! Please ignore me!");
  555. response = "New NPC: " + (string)newNPC;
  556. }
  557. else if (command == "osNpcRemove")
  558. {
  559. osNpcRemove(NPC);
  560. response = "Removing " + llKey2Name(NPC);
  561. // send command to web app to remove from database
  562. string myTimestamp = llGetTimestamp();
  563. deleteRequest = llHTTPRequest(deleteAvatarURL, [HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
  564. "request=delete"
  565. + "&npc=" + NPC
  566. + "&amp;timestamp=" + myTimestamp
  567. + "&signature=" + llMD5String((string)llGetKey() + myTimestamp, LSLSignaturePIN));
  568. }
  569. else if (command == "ping")
  570. {
  571. response = "pong";
  572. }
  573. else
  574. {
  575. response = "";
  576. llHTTPResponse(id, 405, "Unknown engine command " + command + ".");
  577. }
  578. }
  579. else if (!osIsNpc(NPC)) {
  580. llSay(0, "Call made for invalid NPC UUID");
  581. llHTTPResponse(id, 400, "Not an NPC");
  582. }
  583. if (response)
  584. {
  585. //llSay(0, "Sending back response for " +
  586. // command + " '" +
  587. // response + "'...");
  588. llHTTPResponse(id, 200, response);
  589. }
  590. else {
  591. llSay(0, "ERROR: No response or no command found!");
  592. llHTTPResponse(id, 404, "ERROR: No response or no command found!");
  593. }
  594. }
  595. else
  596. {
  597. llHTTPResponse(id, 405, "Method unsupported");
  598. }
  599. }
  600. changed(integer c)
  601. {
  602. // Region changed, get a new PermURL
  603. if (c & (CHANGED_REGION | CHANGED_OWNER | CHANGED_REGION_START | CHANGED_TELEPORT ) )
  604. {
  605. init();
  606. }
  607. // Deal with inventory changes
  608. else if (c & CHANGED_INVENTORY)
  609. state read_inventory;
  610. }
  611. timer()
  612. {
  613. llSetText("Trying to register bot controller...", &lt;1.0,0.0,0.0&gt;, 1.0);
  614. llOwnerSay("Trying to register bot controller...");
  615. llSetTimerEvent(0.0);
  616. init();
  617. }
  618. }
  619. state read_inventory
  620. {
  621. state_entry()
  622. {
  623. llSetText("Sending to webserver - 0%", &lt;0.3, 0.7, 0.2&gt;, 1.0);
  624. // now prepare this line for sending to web server
  625. string httpBody;
  626. string itemName;
  627. string myTimeStamp;
  628. integer i;
  629. integer length = llGetInventoryNumber(INVENTORY_ALL);
  630. serverKey = llGetKey();
  631. llSetTimerEvent(360.0); // timeout if the web server is too slow in responding
  632. // Now add the new items.
  633. // This needs two passes: on the first one, we'll skip textures
  634. // The second pass will add them later
  635. llSetText("Checking inventory...", &lt;1.0,1.0,0.0&gt;, 1.0);
  636. for (i = 0; i < length; i++)
  637. {
  638. itemName = llGetInventoryName(INVENTORY_ALL, i);
  639. if (llGetInventoryType(itemName) != INVENTORY_SCRIPT && llGetInventoryType(itemName) != INVENTORY_TEXTURE) // skip script, skip textures
  640. {
  641. myTimeStamp = llGetTimestamp();
  642. httpBody = "name=" + llEscapeURL(itemName) +
  643. "&amp;timestamp=" + myTimeStamp +
  644. "&permissions=" + (string) llGetInventoryPermMask(itemName, MASK_NEXT) +
  645. "&itemType=" + (string) llGetInventoryType(itemName) +
  646. "&signature=" + llMD5String((string)serverKey + myTimeStamp, LSLSignaturePIN);
  647. llSleep(1.0);
  648. httpRequestKey = llHTTPRequest(webServerURLupdateInventory,
  649. [HTTP_METHOD, "POST",
  650. HTTP_MIMETYPE,"application/x-www-form-urlencoded"],
  651. httpBody);
  652. //llOwnerSay("Object " + (string) i + ": " + httpBody);
  653. if (httpRequestKey == NULL_KEY)
  654. llOwnerSay("Error contacting webserver on item #" + (string)i);
  655. llSetText("Sending to webserver - " + (string) ((integer)((float)i/(float)length*100)) + "%", &lt;0.3, 0.7, 0.2&gt;, 1.0);
  656. }
  657. }
  658. state default;
  659. }
  660. http_response(key request_id, integer status, list metadata, string body)
  661. {
  662. llSetText("", &lt;0.0,0.0,0.0&gt;, 1.0);
  663. if (request_id == httpRequestKey)
  664. {
  665. if (status != 200)
  666. {
  667. llOwnerSay("HTTP Error " + (string)status + ": " + body);
  668. }
  669. else
  670. {
  671. //llOwnerSay("Web-server reply: " + body);
  672. if (body == "closed")
  673. state default;
  674. }
  675. }
  676. }
  677. timer()
  678. {
  679. // HTTP server does not work, go to default state for now
  680. llOwnerSay("Web server did not reply after 6 minutes - not updated - try again later");
  681. llSetTimerEvent(0.0);
  682. init();
  683. }
  684. state_exit()
  685. {
  686. //llOwnerSay("state_exit inventory");
  687. llSetTimerEvent(0.0);
  688. }
  689. }
  690. </code></pre>
  691. {{ end }}