WebStatsModule.cs 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.IO;
  31. using System.Net; // to be used for REST-->Grid shortly
  32. using System.Reflection;
  33. using System.Text;
  34. using System.Threading;
  35. using log4net;
  36. using Nini.Config;
  37. using OpenMetaverse;
  38. using OpenMetaverse.StructuredData;
  39. using OpenSim.Framework;
  40. using OpenSim.Framework.Servers;
  41. using OpenSim.Framework.Servers.HttpServer;
  42. using OpenSim.Region.Framework.Interfaces;
  43. using OpenSim.Region.Framework.Scenes;
  44. using Mono.Data.SqliteClient;
  45. using Caps = OpenSim.Framework.Capabilities.Caps;
  46. using OSD = OpenMetaverse.StructuredData.OSD;
  47. using OSDMap = OpenMetaverse.StructuredData.OSDMap;
  48. namespace OpenSim.Region.UserStatistics
  49. {
  50. public class WebStatsModule : IRegionModule
  51. {
  52. private static readonly ILog m_log =
  53. LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  54. private static SqliteConnection dbConn;
  55. /// <summary>
  56. /// User statistics sessions keyed by agent ID
  57. /// </summary>
  58. private Dictionary<UUID, UserSessionID> m_sessions = new Dictionary<UUID, UserSessionID>();
  59. private List<Scene> m_scenes = new List<Scene>();
  60. private Dictionary<string, IStatsController> reports = new Dictionary<string, IStatsController>();
  61. private Dictionary<UUID, USimStatsData> m_simstatsCounters = new Dictionary<UUID, USimStatsData>();
  62. private const int updateStatsMod = 6;
  63. private int updateLogMod = 1;
  64. private volatile int updateLogCounter = 0;
  65. private volatile int concurrencyCounter = 0;
  66. private bool enabled = false;
  67. private string m_loglines = String.Empty;
  68. private volatile int lastHit = 12000;
  69. public virtual void Initialise(Scene scene, IConfigSource config)
  70. {
  71. IConfig cnfg = config.Configs["WebStats"];
  72. if (cnfg != null)
  73. enabled = cnfg.GetBoolean("enabled", false);
  74. if (!enabled)
  75. return;
  76. lock (m_scenes)
  77. {
  78. if (m_scenes.Count == 0)
  79. {
  80. if (Util.IsWindows())
  81. Util.LoadArchSpecificWindowsDll("sqlite3.dll");
  82. //IConfig startupConfig = config.Configs["Startup"];
  83. dbConn = new SqliteConnection("URI=file:LocalUserStatistics.db,version=3");
  84. dbConn.Open();
  85. CreateTables(dbConn);
  86. Prototype_distributor protodep = new Prototype_distributor();
  87. Updater_distributor updatedep = new Updater_distributor();
  88. ActiveConnectionsAJAX ajConnections = new ActiveConnectionsAJAX();
  89. SimStatsAJAX ajSimStats = new SimStatsAJAX();
  90. LogLinesAJAX ajLogLines = new LogLinesAJAX();
  91. Default_Report defaultReport = new Default_Report();
  92. Clients_report clientReport = new Clients_report();
  93. Sessions_Report sessionsReport = new Sessions_Report();
  94. reports.Add("prototype.js", protodep);
  95. reports.Add("updater.js", updatedep);
  96. reports.Add("activeconnectionsajax.html", ajConnections);
  97. reports.Add("simstatsajax.html", ajSimStats);
  98. reports.Add("activelogajax.html", ajLogLines);
  99. reports.Add("default.report", defaultReport);
  100. reports.Add("clients.report", clientReport);
  101. reports.Add("sessions.report", sessionsReport);
  102. ////
  103. // Add Your own Reports here (Do Not Modify Lines here Devs!)
  104. ////
  105. ////
  106. // End Own reports section
  107. ////
  108. MainServer.Instance.AddHTTPHandler("/SStats/", HandleStatsRequest);
  109. MainServer.Instance.AddHTTPHandler("/CAPS/VS/", HandleUnknownCAPSRequest);
  110. }
  111. m_scenes.Add(scene);
  112. if (m_simstatsCounters.ContainsKey(scene.RegionInfo.RegionID))
  113. m_simstatsCounters.Remove(scene.RegionInfo.RegionID);
  114. m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID));
  115. scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket;
  116. }
  117. }
  118. private void ReceiveClassicSimStatsPacket(SimStats stats)
  119. {
  120. if (!enabled)
  121. {
  122. return;
  123. }
  124. try
  125. {
  126. // Ignore the update if there's a report running right now
  127. // ignore the update if there hasn't been a hit in 30 seconds.
  128. if (concurrencyCounter > 0 || System.Environment.TickCount - lastHit > 30000)
  129. return;
  130. if ((updateLogCounter++ % updateLogMod) == 0)
  131. {
  132. m_loglines = readLogLines(10);
  133. if (updateLogCounter > 10000) updateLogCounter = 1;
  134. }
  135. USimStatsData ss = m_simstatsCounters[stats.RegionUUID];
  136. if ((++ss.StatsCounter % updateStatsMod) == 0)
  137. {
  138. ss.ConsumeSimStats(stats);
  139. }
  140. }
  141. catch (KeyNotFoundException)
  142. {
  143. }
  144. }
  145. private Hashtable HandleUnknownCAPSRequest(Hashtable request)
  146. {
  147. //string regpath = request["uri"].ToString();
  148. int response_code = 200;
  149. string contenttype = "text/html";
  150. UpdateUserStats(ParseViewerStats(request["body"].ToString(), UUID.Zero), dbConn);
  151. Hashtable responsedata = new Hashtable();
  152. responsedata["int_response_code"] = response_code;
  153. responsedata["content_type"] = contenttype;
  154. responsedata["keepalive"] = false;
  155. responsedata["str_response_string"] = string.Empty;
  156. return responsedata;
  157. }
  158. private Hashtable HandleStatsRequest(Hashtable request)
  159. {
  160. lastHit = System.Environment.TickCount;
  161. Hashtable responsedata = new Hashtable();
  162. string regpath = request["uri"].ToString();
  163. int response_code = 404;
  164. string contenttype = "text/html";
  165. string strOut = string.Empty;
  166. regpath = regpath.Remove(0, 8);
  167. if (regpath.Length == 0) regpath = "default.report";
  168. if (reports.ContainsKey(regpath))
  169. {
  170. IStatsController rep = reports[regpath];
  171. Hashtable repParams = new Hashtable();
  172. if (request.ContainsKey("requestvars"))
  173. repParams["RequestVars"] = request["requestvars"];
  174. else
  175. repParams["RequestVars"] = new Hashtable();
  176. if (request.ContainsKey("querystringkeys"))
  177. repParams["QueryStringKeys"] = request["querystringkeys"];
  178. else
  179. repParams["QueryStringKeys"] = new string[0];
  180. repParams["DatabaseConnection"] = dbConn;
  181. repParams["Scenes"] = m_scenes;
  182. repParams["SimStats"] = m_simstatsCounters;
  183. repParams["LogLines"] = m_loglines;
  184. repParams["Reports"] = reports;
  185. concurrencyCounter++;
  186. strOut = rep.RenderView(rep.ProcessModel(repParams));
  187. if (regpath.EndsWith("js"))
  188. {
  189. contenttype = "text/javascript";
  190. }
  191. concurrencyCounter--;
  192. response_code = 200;
  193. }
  194. else
  195. {
  196. strOut = MainServer.Instance.GetHTTP404("");
  197. }
  198. responsedata["int_response_code"] = response_code;
  199. responsedata["content_type"] = contenttype;
  200. responsedata["keepalive"] = false;
  201. responsedata["str_response_string"] = strOut;
  202. return responsedata;
  203. }
  204. private void CreateTables(SqliteConnection db)
  205. {
  206. using (SqliteCommand createcmd = new SqliteCommand(SQL_STATS_TABLE_CREATE, db))
  207. {
  208. createcmd.ExecuteNonQuery();
  209. }
  210. }
  211. public virtual void PostInitialise()
  212. {
  213. if (!enabled)
  214. return;
  215. AddHandlers();
  216. }
  217. public virtual void Close()
  218. {
  219. if (!enabled)
  220. return;
  221. dbConn.Close();
  222. dbConn.Dispose();
  223. m_sessions.Clear();
  224. m_scenes.Clear();
  225. reports.Clear();
  226. m_simstatsCounters.Clear();
  227. }
  228. public virtual string Name
  229. {
  230. get { return "ViewerStatsModule"; }
  231. }
  232. public bool IsSharedModule
  233. {
  234. get { return true; }
  235. }
  236. private void OnRegisterCaps(UUID agentID, Caps caps)
  237. {
  238. // m_log.DebugFormat("[WEB STATS MODULE]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
  239. string capsPath = "/CAPS/VS/" + UUID.Random();
  240. caps.RegisterHandler(
  241. "ViewerStats",
  242. new RestStreamHandler(
  243. "POST",
  244. capsPath,
  245. (request, path, param, httpRequest, httpResponse)
  246. => ViewerStatsReport(request, path, param, agentID, caps),
  247. "ViewerStats",
  248. agentID.ToString()));
  249. }
  250. private void OnDeRegisterCaps(UUID agentID, Caps caps)
  251. {
  252. }
  253. protected virtual void AddHandlers()
  254. {
  255. lock (m_scenes)
  256. {
  257. updateLogMod = m_scenes.Count * 2;
  258. foreach (Scene scene in m_scenes)
  259. {
  260. scene.EventManager.OnRegisterCaps += OnRegisterCaps;
  261. scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps;
  262. scene.EventManager.OnClientClosed += OnClientClosed;
  263. scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
  264. }
  265. }
  266. }
  267. private void OnMakeRootAgent(ScenePresence agent)
  268. {
  269. lock (m_sessions)
  270. {
  271. UserSessionID uid;
  272. if (!m_sessions.ContainsKey(agent.UUID))
  273. {
  274. UserSessionData usd = UserSessionUtil.newUserSessionData();
  275. uid = new UserSessionID();
  276. uid.name_f = agent.Firstname;
  277. uid.name_l = agent.Lastname;
  278. uid.session_data = usd;
  279. m_sessions.Add(agent.UUID, uid);
  280. }
  281. else
  282. {
  283. uid = m_sessions[agent.UUID];
  284. }
  285. uid.region_id = agent.Scene.RegionInfo.RegionID;
  286. uid.session_id = agent.ControllingClient.SessionId;
  287. }
  288. }
  289. private void OnClientClosed(UUID agentID, Scene scene)
  290. {
  291. lock (m_sessions)
  292. {
  293. if (m_sessions.ContainsKey(agentID) && m_sessions[agentID].region_id == scene.RegionInfo.RegionID)
  294. {
  295. m_sessions.Remove(agentID);
  296. }
  297. }
  298. }
  299. private string readLogLines(int amount)
  300. {
  301. Encoding encoding = Encoding.ASCII;
  302. int sizeOfChar = encoding.GetByteCount("\n");
  303. byte[] buffer = encoding.GetBytes("\n");
  304. string logfile = Util.logDir() + "/" + "OpenSim.log";
  305. FileStream fs = new FileStream(logfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  306. Int64 tokenCount = 0;
  307. Int64 endPosition = fs.Length / sizeOfChar;
  308. for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar)
  309. {
  310. fs.Seek(-position, SeekOrigin.End);
  311. fs.Read(buffer, 0, buffer.Length);
  312. if (encoding.GetString(buffer) == "\n")
  313. {
  314. tokenCount++;
  315. if (tokenCount == amount)
  316. {
  317. byte[] returnBuffer = new byte[fs.Length - fs.Position];
  318. fs.Read(returnBuffer, 0, returnBuffer.Length);
  319. fs.Close();
  320. fs.Dispose();
  321. return encoding.GetString(returnBuffer);
  322. }
  323. }
  324. }
  325. // handle case where number of tokens in file is less than numberOfTokens
  326. fs.Seek(0, SeekOrigin.Begin);
  327. buffer = new byte[fs.Length];
  328. fs.Read(buffer, 0, buffer.Length);
  329. fs.Close();
  330. fs.Dispose();
  331. return encoding.GetString(buffer);
  332. }
  333. /// <summary>
  334. /// Callback for a viewerstats cap
  335. /// </summary>
  336. /// <param name="request"></param>
  337. /// <param name="path"></param>
  338. /// <param name="param"></param>
  339. /// <param name="agentID"></param>
  340. /// <param name="caps"></param>
  341. /// <returns></returns>
  342. private string ViewerStatsReport(string request, string path, string param,
  343. UUID agentID, Caps caps)
  344. {
  345. // m_log.DebugFormat("[WEB STATS MODULE]: Received viewer starts report from {0}", agentID);
  346. UpdateUserStats(ParseViewerStats(request, agentID), dbConn);
  347. return String.Empty;
  348. }
  349. private UserSessionID ParseViewerStats(string request, UUID agentID)
  350. {
  351. UserSessionID uid = new UserSessionID();
  352. UserSessionData usd;
  353. OSD message = OSDParser.DeserializeLLSDXml(request);
  354. OSDMap mmap;
  355. lock (m_sessions)
  356. {
  357. if (agentID != UUID.Zero)
  358. {
  359. if (!m_sessions.ContainsKey(agentID))
  360. {
  361. m_log.WarnFormat("[WEB STATS MODULE]: no session for stat disclosure for agent {0}", agentID);
  362. return new UserSessionID();
  363. }
  364. uid = m_sessions[agentID];
  365. }
  366. else
  367. {
  368. // parse through the beginning to locate the session
  369. if (message.Type != OSDType.Map)
  370. return new UserSessionID();
  371. mmap = (OSDMap)message;
  372. {
  373. UUID sessionID = mmap["session_id"].AsUUID();
  374. if (sessionID == UUID.Zero)
  375. return new UserSessionID();
  376. // search through each session looking for the owner
  377. foreach (UUID usersessionid in m_sessions.Keys)
  378. {
  379. // got it!
  380. if (m_sessions[usersessionid].session_id == sessionID)
  381. {
  382. agentID = usersessionid;
  383. uid = m_sessions[usersessionid];
  384. break;
  385. }
  386. }
  387. // can't find a session
  388. if (agentID == UUID.Zero)
  389. {
  390. return new UserSessionID();
  391. }
  392. }
  393. }
  394. }
  395. usd = uid.session_data;
  396. if (message.Type != OSDType.Map)
  397. return new UserSessionID();
  398. mmap = (OSDMap)message;
  399. {
  400. if (mmap["agent"].Type != OSDType.Map)
  401. return new UserSessionID();
  402. OSDMap agent_map = (OSDMap)mmap["agent"];
  403. usd.agent_id = agentID;
  404. usd.name_f = uid.name_f;
  405. usd.name_l = uid.name_l;
  406. usd.region_id = uid.region_id;
  407. usd.a_language = agent_map["language"].AsString();
  408. usd.mem_use = (float)agent_map["mem_use"].AsReal();
  409. usd.meters_traveled = (float)agent_map["meters_traveled"].AsReal();
  410. usd.regions_visited = agent_map["regions_visited"].AsInteger();
  411. usd.run_time = (float)agent_map["run_time"].AsReal();
  412. usd.start_time = (float)agent_map["start_time"].AsReal();
  413. usd.client_version = agent_map["version"].AsString();
  414. UserSessionUtil.UpdateMultiItems(ref usd, agent_map["agents_in_view"].AsInteger(),
  415. (float)agent_map["ping"].AsReal(),
  416. (float)agent_map["sim_fps"].AsReal(),
  417. (float)agent_map["fps"].AsReal());
  418. if (mmap["downloads"].Type != OSDType.Map)
  419. return new UserSessionID();
  420. OSDMap downloads_map = (OSDMap)mmap["downloads"];
  421. usd.d_object_kb = (float)downloads_map["object_kbytes"].AsReal();
  422. usd.d_texture_kb = (float)downloads_map["texture_kbytes"].AsReal();
  423. usd.d_world_kb = (float)downloads_map["workd_kbytes"].AsReal();
  424. usd.session_id = mmap["session_id"].AsUUID();
  425. if (mmap["system"].Type != OSDType.Map)
  426. return new UserSessionID();
  427. OSDMap system_map = (OSDMap)mmap["system"];
  428. usd.s_cpu = system_map["cpu"].AsString();
  429. usd.s_gpu = system_map["gpu"].AsString();
  430. usd.s_os = system_map["os"].AsString();
  431. usd.s_ram = system_map["ram"].AsInteger();
  432. if (mmap["stats"].Type != OSDType.Map)
  433. return new UserSessionID();
  434. OSDMap stats_map = (OSDMap)mmap["stats"];
  435. {
  436. if (stats_map["failures"].Type != OSDType.Map)
  437. return new UserSessionID();
  438. OSDMap stats_failures = (OSDMap)stats_map["failures"];
  439. usd.f_dropped = stats_failures["dropped"].AsInteger();
  440. usd.f_failed_resends = stats_failures["failed_resends"].AsInteger();
  441. usd.f_invalid = stats_failures["invalid"].AsInteger();
  442. usd.f_resent = stats_failures["resent"].AsInteger();
  443. usd.f_send_packet = stats_failures["send_packet"].AsInteger();
  444. if (stats_map["net"].Type != OSDType.Map)
  445. return new UserSessionID();
  446. OSDMap stats_net = (OSDMap)stats_map["net"];
  447. {
  448. if (stats_net["in"].Type != OSDType.Map)
  449. return new UserSessionID();
  450. OSDMap net_in = (OSDMap)stats_net["in"];
  451. usd.n_in_kb = (float)net_in["kbytes"].AsReal();
  452. usd.n_in_pk = net_in["packets"].AsInteger();
  453. if (stats_net["out"].Type != OSDType.Map)
  454. return new UserSessionID();
  455. OSDMap net_out = (OSDMap)stats_net["out"];
  456. usd.n_out_kb = (float)net_out["kbytes"].AsReal();
  457. usd.n_out_pk = net_out["packets"].AsInteger();
  458. }
  459. }
  460. }
  461. uid.session_data = usd;
  462. m_sessions[agentID] = uid;
  463. return uid;
  464. }
  465. private void UpdateUserStats(UserSessionID uid, SqliteConnection db)
  466. {
  467. if (uid.session_id == UUID.Zero)
  468. return;
  469. lock (db)
  470. {
  471. using (SqliteCommand updatecmd = new SqliteCommand(SQL_STATS_TABLE_INSERT, db))
  472. {
  473. updatecmd.Parameters.Add(new SqliteParameter(":session_id", uid.session_data.session_id.ToString()));
  474. updatecmd.Parameters.Add(new SqliteParameter(":agent_id", uid.session_data.agent_id.ToString()));
  475. updatecmd.Parameters.Add(new SqliteParameter(":region_id", uid.session_data.region_id.ToString()));
  476. updatecmd.Parameters.Add(new SqliteParameter(":last_updated", (int) uid.session_data.last_updated));
  477. updatecmd.Parameters.Add(new SqliteParameter(":remote_ip", uid.session_data.remote_ip));
  478. updatecmd.Parameters.Add(new SqliteParameter(":name_f", uid.session_data.name_f));
  479. updatecmd.Parameters.Add(new SqliteParameter(":name_l", uid.session_data.name_l));
  480. updatecmd.Parameters.Add(new SqliteParameter(":avg_agents_in_view", uid.session_data.avg_agents_in_view));
  481. updatecmd.Parameters.Add(new SqliteParameter(":min_agents_in_view",
  482. (int) uid.session_data.min_agents_in_view));
  483. updatecmd.Parameters.Add(new SqliteParameter(":max_agents_in_view",
  484. (int) uid.session_data.max_agents_in_view));
  485. updatecmd.Parameters.Add(new SqliteParameter(":mode_agents_in_view",
  486. (int) uid.session_data.mode_agents_in_view));
  487. updatecmd.Parameters.Add(new SqliteParameter(":avg_fps", uid.session_data.avg_fps));
  488. updatecmd.Parameters.Add(new SqliteParameter(":min_fps", uid.session_data.min_fps));
  489. updatecmd.Parameters.Add(new SqliteParameter(":max_fps", uid.session_data.max_fps));
  490. updatecmd.Parameters.Add(new SqliteParameter(":mode_fps", uid.session_data.mode_fps));
  491. updatecmd.Parameters.Add(new SqliteParameter(":a_language", uid.session_data.a_language));
  492. updatecmd.Parameters.Add(new SqliteParameter(":mem_use", uid.session_data.mem_use));
  493. updatecmd.Parameters.Add(new SqliteParameter(":meters_traveled", uid.session_data.meters_traveled));
  494. updatecmd.Parameters.Add(new SqliteParameter(":avg_ping", uid.session_data.avg_ping));
  495. updatecmd.Parameters.Add(new SqliteParameter(":min_ping", uid.session_data.min_ping));
  496. updatecmd.Parameters.Add(new SqliteParameter(":max_ping", uid.session_data.max_ping));
  497. updatecmd.Parameters.Add(new SqliteParameter(":mode_ping", uid.session_data.mode_ping));
  498. updatecmd.Parameters.Add(new SqliteParameter(":regions_visited", uid.session_data.regions_visited));
  499. updatecmd.Parameters.Add(new SqliteParameter(":run_time", uid.session_data.run_time));
  500. updatecmd.Parameters.Add(new SqliteParameter(":avg_sim_fps", uid.session_data.avg_sim_fps));
  501. updatecmd.Parameters.Add(new SqliteParameter(":min_sim_fps", uid.session_data.min_sim_fps));
  502. updatecmd.Parameters.Add(new SqliteParameter(":max_sim_fps", uid.session_data.max_sim_fps));
  503. updatecmd.Parameters.Add(new SqliteParameter(":mode_sim_fps", uid.session_data.mode_sim_fps));
  504. updatecmd.Parameters.Add(new SqliteParameter(":start_time", uid.session_data.start_time));
  505. updatecmd.Parameters.Add(new SqliteParameter(":client_version", uid.session_data.client_version));
  506. updatecmd.Parameters.Add(new SqliteParameter(":s_cpu", uid.session_data.s_cpu));
  507. updatecmd.Parameters.Add(new SqliteParameter(":s_gpu", uid.session_data.s_gpu));
  508. updatecmd.Parameters.Add(new SqliteParameter(":s_os", uid.session_data.s_os));
  509. updatecmd.Parameters.Add(new SqliteParameter(":s_ram", uid.session_data.s_ram));
  510. updatecmd.Parameters.Add(new SqliteParameter(":d_object_kb", uid.session_data.d_object_kb));
  511. updatecmd.Parameters.Add(new SqliteParameter(":d_texture_kb", uid.session_data.d_texture_kb));
  512. updatecmd.Parameters.Add(new SqliteParameter(":d_world_kb", uid.session_data.d_world_kb));
  513. updatecmd.Parameters.Add(new SqliteParameter(":n_in_kb", uid.session_data.n_in_kb));
  514. updatecmd.Parameters.Add(new SqliteParameter(":n_in_pk", uid.session_data.n_in_pk));
  515. updatecmd.Parameters.Add(new SqliteParameter(":n_out_kb", uid.session_data.n_out_kb));
  516. updatecmd.Parameters.Add(new SqliteParameter(":n_out_pk", uid.session_data.n_out_pk));
  517. updatecmd.Parameters.Add(new SqliteParameter(":f_dropped", uid.session_data.f_dropped));
  518. updatecmd.Parameters.Add(new SqliteParameter(":f_failed_resends", uid.session_data.f_failed_resends));
  519. updatecmd.Parameters.Add(new SqliteParameter(":f_invalid", uid.session_data.f_invalid));
  520. updatecmd.Parameters.Add(new SqliteParameter(":f_off_circuit", uid.session_data.f_off_circuit));
  521. updatecmd.Parameters.Add(new SqliteParameter(":f_resent", uid.session_data.f_resent));
  522. updatecmd.Parameters.Add(new SqliteParameter(":f_send_packet", uid.session_data.f_send_packet));
  523. // StringBuilder parameters = new StringBuilder();
  524. // SqliteParameterCollection spc = updatecmd.Parameters;
  525. // foreach (SqliteParameter sp in spc)
  526. // parameters.AppendFormat("{0}={1},", sp.ParameterName, sp.Value);
  527. //
  528. // m_log.DebugFormat("[WEB STATS MODULE]: Parameters {0}", parameters);
  529. // m_log.DebugFormat("[WEB STATS MODULE]: Database stats update for {0}", uid.session_data.agent_id);
  530. updatecmd.ExecuteNonQuery();
  531. }
  532. }
  533. }
  534. #region SQL
  535. private const string SQL_STATS_TABLE_CREATE = @"CREATE TABLE IF NOT EXISTS stats_session_data (
  536. session_id VARCHAR(36) NOT NULL PRIMARY KEY,
  537. agent_id VARCHAR(36) NOT NULL DEFAULT '',
  538. region_id VARCHAR(36) NOT NULL DEFAULT '',
  539. last_updated INT NOT NULL DEFAULT '0',
  540. remote_ip VARCHAR(16) NOT NULL DEFAULT '',
  541. name_f VARCHAR(50) NOT NULL DEFAULT '',
  542. name_l VARCHAR(50) NOT NULL DEFAULT '',
  543. avg_agents_in_view FLOAT NOT NULL DEFAULT '0',
  544. min_agents_in_view INT NOT NULL DEFAULT '0',
  545. max_agents_in_view INT NOT NULL DEFAULT '0',
  546. mode_agents_in_view INT NOT NULL DEFAULT '0',
  547. avg_fps FLOAT NOT NULL DEFAULT '0',
  548. min_fps FLOAT NOT NULL DEFAULT '0',
  549. max_fps FLOAT NOT NULL DEFAULT '0',
  550. mode_fps FLOAT NOT NULL DEFAULT '0',
  551. a_language VARCHAR(25) NOT NULL DEFAULT '',
  552. mem_use FLOAT NOT NULL DEFAULT '0',
  553. meters_traveled FLOAT NOT NULL DEFAULT '0',
  554. avg_ping FLOAT NOT NULL DEFAULT '0',
  555. min_ping FLOAT NOT NULL DEFAULT '0',
  556. max_ping FLOAT NOT NULL DEFAULT '0',
  557. mode_ping FLOAT NOT NULL DEFAULT '0',
  558. regions_visited INT NOT NULL DEFAULT '0',
  559. run_time FLOAT NOT NULL DEFAULT '0',
  560. avg_sim_fps FLOAT NOT NULL DEFAULT '0',
  561. min_sim_fps FLOAT NOT NULL DEFAULT '0',
  562. max_sim_fps FLOAT NOT NULL DEFAULT '0',
  563. mode_sim_fps FLOAT NOT NULL DEFAULT '0',
  564. start_time FLOAT NOT NULL DEFAULT '0',
  565. client_version VARCHAR(255) NOT NULL DEFAULT '',
  566. s_cpu VARCHAR(255) NOT NULL DEFAULT '',
  567. s_gpu VARCHAR(255) NOT NULL DEFAULT '',
  568. s_os VARCHAR(2255) NOT NULL DEFAULT '',
  569. s_ram INT NOT NULL DEFAULT '0',
  570. d_object_kb FLOAT NOT NULL DEFAULT '0',
  571. d_texture_kb FLOAT NOT NULL DEFAULT '0',
  572. d_world_kb FLOAT NOT NULL DEFAULT '0',
  573. n_in_kb FLOAT NOT NULL DEFAULT '0',
  574. n_in_pk INT NOT NULL DEFAULT '0',
  575. n_out_kb FLOAT NOT NULL DEFAULT '0',
  576. n_out_pk INT NOT NULL DEFAULT '0',
  577. f_dropped INT NOT NULL DEFAULT '0',
  578. f_failed_resends INT NOT NULL DEFAULT '0',
  579. f_invalid INT NOT NULL DEFAULT '0',
  580. f_off_circuit INT NOT NULL DEFAULT '0',
  581. f_resent INT NOT NULL DEFAULT '0',
  582. f_send_packet INT NOT NULL DEFAULT '0'
  583. );";
  584. private const string SQL_STATS_TABLE_INSERT = @"INSERT OR REPLACE INTO stats_session_data (
  585. session_id, agent_id, region_id, last_updated, remote_ip, name_f, name_l, avg_agents_in_view, min_agents_in_view, max_agents_in_view,
  586. mode_agents_in_view, avg_fps, min_fps, max_fps, mode_fps, a_language, mem_use, meters_traveled, avg_ping, min_ping, max_ping, mode_ping,
  587. regions_visited, run_time, avg_sim_fps, min_sim_fps, max_sim_fps, mode_sim_fps, start_time, client_version, s_cpu, s_gpu, s_os, s_ram,
  588. d_object_kb, d_texture_kb, d_world_kb, n_in_kb, n_in_pk, n_out_kb, n_out_pk, f_dropped, f_failed_resends, f_invalid, f_off_circuit,
  589. f_resent, f_send_packet
  590. )
  591. VALUES
  592. (
  593. :session_id, :agent_id, :region_id, :last_updated, :remote_ip, :name_f, :name_l, :avg_agents_in_view, :min_agents_in_view, :max_agents_in_view,
  594. :mode_agents_in_view, :avg_fps, :min_fps, :max_fps, :mode_fps, :a_language, :mem_use, :meters_traveled, :avg_ping, :min_ping, :max_ping, :mode_ping,
  595. :regions_visited, :run_time, :avg_sim_fps, :min_sim_fps, :max_sim_fps, :mode_sim_fps, :start_time, :client_version, :s_cpu, :s_gpu, :s_os, :s_ram,
  596. :d_object_kb, :d_texture_kb, :d_world_kb, :n_in_kb, :n_in_pk, :n_out_kb, :n_out_pk, :f_dropped, :f_failed_resends, :f_invalid, :f_off_circuit,
  597. :f_resent, :f_send_packet
  598. )
  599. ";
  600. #endregion
  601. }
  602. public static class UserSessionUtil
  603. {
  604. public static UserSessionData newUserSessionData()
  605. {
  606. UserSessionData obj = ZeroSession(new UserSessionData());
  607. return obj;
  608. }
  609. public static void UpdateMultiItems(ref UserSessionData s, int agents_in_view, float ping, float sim_fps, float fps)
  610. {
  611. // don't insert zero values here or it'll skew the statistics.
  612. if (agents_in_view == 0 && fps == 0 && sim_fps == 0 && ping == 0)
  613. return;
  614. s._agents_in_view.Add(agents_in_view);
  615. s._fps.Add(fps);
  616. s._sim_fps.Add(sim_fps);
  617. s._ping.Add(ping);
  618. int[] __agents_in_view = s._agents_in_view.ToArray();
  619. s.avg_agents_in_view = ArrayAvg_i(__agents_in_view);
  620. s.min_agents_in_view = ArrayMin_i(__agents_in_view);
  621. s.max_agents_in_view = ArrayMax_i(__agents_in_view);
  622. s.mode_agents_in_view = ArrayMode_i(__agents_in_view);
  623. float[] __fps = s._fps.ToArray();
  624. s.avg_fps = ArrayAvg_f(__fps);
  625. s.min_fps = ArrayMin_f(__fps);
  626. s.max_fps = ArrayMax_f(__fps);
  627. s.mode_fps = ArrayMode_f(__fps);
  628. float[] __sim_fps = s._sim_fps.ToArray();
  629. s.avg_sim_fps = ArrayAvg_f(__sim_fps);
  630. s.min_sim_fps = ArrayMin_f(__sim_fps);
  631. s.max_sim_fps = ArrayMax_f(__sim_fps);
  632. s.mode_sim_fps = ArrayMode_f(__sim_fps);
  633. float[] __ping = s._ping.ToArray();
  634. s.avg_ping = ArrayAvg_f(__ping);
  635. s.min_ping = ArrayMin_f(__ping);
  636. s.max_ping = ArrayMax_f(__ping);
  637. s.mode_ping = ArrayMode_f(__ping);
  638. }
  639. #region Statistics
  640. public static int ArrayMin_i(int[] arr)
  641. {
  642. int cnt = arr.Length;
  643. if (cnt == 0)
  644. return 0;
  645. Array.Sort(arr);
  646. return arr[0];
  647. }
  648. public static int ArrayMax_i(int[] arr)
  649. {
  650. int cnt = arr.Length;
  651. if (cnt == 0)
  652. return 0;
  653. Array.Sort(arr);
  654. return arr[cnt-1];
  655. }
  656. public static float ArrayMin_f(float[] arr)
  657. {
  658. int cnt = arr.Length;
  659. if (cnt == 0)
  660. return 0;
  661. Array.Sort(arr);
  662. return arr[0];
  663. }
  664. public static float ArrayMax_f(float[] arr)
  665. {
  666. int cnt = arr.Length;
  667. if (cnt == 0)
  668. return 0;
  669. Array.Sort(arr);
  670. return arr[cnt - 1];
  671. }
  672. public static float ArrayAvg_i(int[] arr)
  673. {
  674. int cnt = arr.Length;
  675. if (cnt == 0)
  676. return 0;
  677. float result = arr[0];
  678. for (int i = 1; i < cnt; i++)
  679. result += arr[i];
  680. return result / cnt;
  681. }
  682. public static float ArrayAvg_f(float[] arr)
  683. {
  684. int cnt = arr.Length;
  685. if (cnt == 0)
  686. return 0;
  687. float result = arr[0];
  688. for (int i = 1; i < cnt; i++)
  689. result += arr[i];
  690. return result / cnt;
  691. }
  692. public static float ArrayMode_f(float[] arr)
  693. {
  694. List<float> mode = new List<float>();
  695. float[] srtArr = new float[arr.Length];
  696. float[,] freq = new float[arr.Length, 2];
  697. Array.Copy(arr, srtArr, arr.Length);
  698. Array.Sort(srtArr);
  699. float tmp = srtArr[0];
  700. int index = 0;
  701. int i = 0;
  702. while (i < srtArr.Length)
  703. {
  704. freq[index, 0] = tmp;
  705. while (tmp == srtArr[i])
  706. {
  707. freq[index, 1]++;
  708. i++;
  709. if (i > srtArr.Length - 1)
  710. break;
  711. }
  712. if (i < srtArr.Length)
  713. {
  714. tmp = srtArr[i];
  715. index++;
  716. }
  717. }
  718. Array.Clear(srtArr, 0, srtArr.Length);
  719. for (i = 0; i < srtArr.Length; i++)
  720. srtArr[i] = freq[i, 1];
  721. Array.Sort(srtArr);
  722. if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1)
  723. return 0;
  724. float freqtest = (float)freq.Length / freq.Rank;
  725. for (i = 0; i < freqtest; i++)
  726. {
  727. if (freq[i, 1] == srtArr[index])
  728. mode.Add(freq[i, 0]);
  729. }
  730. return mode.ToArray()[0];
  731. }
  732. public static int ArrayMode_i(int[] arr)
  733. {
  734. List<int> mode = new List<int>();
  735. int[] srtArr = new int[arr.Length];
  736. int[,] freq = new int[arr.Length, 2];
  737. Array.Copy(arr, srtArr, arr.Length);
  738. Array.Sort(srtArr);
  739. int tmp = srtArr[0];
  740. int index = 0;
  741. int i = 0;
  742. while (i < srtArr.Length)
  743. {
  744. freq[index, 0] = tmp;
  745. while (tmp == srtArr[i])
  746. {
  747. freq[index, 1]++;
  748. i++;
  749. if (i > srtArr.Length - 1)
  750. break;
  751. }
  752. if (i < srtArr.Length)
  753. {
  754. tmp = srtArr[i];
  755. index++;
  756. }
  757. }
  758. Array.Clear(srtArr, 0, srtArr.Length);
  759. for (i = 0; i < srtArr.Length; i++)
  760. srtArr[i] = freq[i, 1];
  761. Array.Sort(srtArr);
  762. if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1)
  763. return 0;
  764. float freqtest = (float)freq.Length / freq.Rank;
  765. for (i = 0; i < freqtest; i++)
  766. {
  767. if (freq[i, 1] == srtArr[index])
  768. mode.Add(freq[i, 0]);
  769. }
  770. return mode.ToArray()[0];
  771. }
  772. #endregion
  773. private static UserSessionData ZeroSession(UserSessionData s)
  774. {
  775. s.session_id = UUID.Zero;
  776. s.agent_id = UUID.Zero;
  777. s.region_id = UUID.Zero;
  778. s.last_updated = Util.UnixTimeSinceEpoch();
  779. s.remote_ip = "";
  780. s.name_f = "";
  781. s.name_l = "";
  782. s.avg_agents_in_view = 0;
  783. s.min_agents_in_view = 0;
  784. s.max_agents_in_view = 0;
  785. s.mode_agents_in_view = 0;
  786. s.avg_fps = 0;
  787. s.min_fps = 0;
  788. s.max_fps = 0;
  789. s.mode_fps = 0;
  790. s.a_language = "";
  791. s.mem_use = 0;
  792. s.meters_traveled = 0;
  793. s.avg_ping = 0;
  794. s.min_ping = 0;
  795. s.max_ping = 0;
  796. s.mode_ping = 0;
  797. s.regions_visited = 0;
  798. s.run_time = 0;
  799. s.avg_sim_fps = 0;
  800. s.min_sim_fps = 0;
  801. s.max_sim_fps = 0;
  802. s.mode_sim_fps = 0;
  803. s.start_time = 0;
  804. s.client_version = "";
  805. s.s_cpu = "";
  806. s.s_gpu = "";
  807. s.s_os = "";
  808. s.s_ram = 0;
  809. s.d_object_kb = 0;
  810. s.d_texture_kb = 0;
  811. s.d_world_kb = 0;
  812. s.n_in_kb = 0;
  813. s.n_in_pk = 0;
  814. s.n_out_kb = 0;
  815. s.n_out_pk = 0;
  816. s.f_dropped = 0;
  817. s.f_failed_resends = 0;
  818. s.f_invalid = 0;
  819. s.f_off_circuit = 0;
  820. s.f_resent = 0;
  821. s.f_send_packet = 0;
  822. s._ping = new List<float>();
  823. s._fps = new List<float>();
  824. s._sim_fps = new List<float>();
  825. s._agents_in_view = new List<int>();
  826. return s;
  827. }
  828. }
  829. #region structs
  830. public struct UserSessionID
  831. {
  832. public UUID session_id;
  833. public UUID region_id;
  834. public string name_f;
  835. public string name_l;
  836. public UserSessionData session_data;
  837. }
  838. public struct UserSessionData
  839. {
  840. public UUID session_id;
  841. public UUID agent_id;
  842. public UUID region_id;
  843. public float last_updated;
  844. public string remote_ip;
  845. public string name_f;
  846. public string name_l;
  847. public float avg_agents_in_view;
  848. public float min_agents_in_view;
  849. public float max_agents_in_view;
  850. public float mode_agents_in_view;
  851. public float avg_fps;
  852. public float min_fps;
  853. public float max_fps;
  854. public float mode_fps;
  855. public string a_language;
  856. public float mem_use;
  857. public float meters_traveled;
  858. public float avg_ping;
  859. public float min_ping;
  860. public float max_ping;
  861. public float mode_ping;
  862. public int regions_visited;
  863. public float run_time;
  864. public float avg_sim_fps;
  865. public float min_sim_fps;
  866. public float max_sim_fps;
  867. public float mode_sim_fps;
  868. public float start_time;
  869. public string client_version;
  870. public string s_cpu;
  871. public string s_gpu;
  872. public string s_os;
  873. public int s_ram;
  874. public float d_object_kb;
  875. public float d_texture_kb;
  876. public float d_world_kb;
  877. public float n_in_kb;
  878. public int n_in_pk;
  879. public float n_out_kb;
  880. public int n_out_pk;
  881. public int f_dropped;
  882. public int f_failed_resends;
  883. public int f_invalid;
  884. public int f_off_circuit;
  885. public int f_resent;
  886. public int f_send_packet;
  887. public List<float> _ping;
  888. public List<float> _fps;
  889. public List<float> _sim_fps;
  890. public List<int> _agents_in_view;
  891. }
  892. #endregion
  893. public class USimStatsData
  894. {
  895. private UUID m_regionID = UUID.Zero;
  896. private volatile int m_statcounter = 0;
  897. private volatile float m_timeDilation;
  898. private volatile float m_simFps;
  899. private volatile float m_physicsFps;
  900. private volatile float m_agentUpdates;
  901. private volatile float m_rootAgents;
  902. private volatile float m_childAgents;
  903. private volatile float m_totalPrims;
  904. private volatile float m_activePrims;
  905. private volatile float m_totalFrameTime;
  906. private volatile float m_netFrameTime;
  907. private volatile float m_physicsFrameTime;
  908. private volatile float m_otherFrameTime;
  909. private volatile float m_imageFrameTime;
  910. private volatile float m_inPacketsPerSecond;
  911. private volatile float m_outPacketsPerSecond;
  912. private volatile float m_unackedBytes;
  913. private volatile float m_agentFrameTime;
  914. private volatile float m_pendingDownloads;
  915. private volatile float m_pendingUploads;
  916. private volatile float m_activeScripts;
  917. private volatile float m_scriptLinesPerSecond;
  918. public UUID RegionId { get { return m_regionID; } }
  919. public int StatsCounter { get { return m_statcounter; } set { m_statcounter = value;}}
  920. public float TimeDilation { get { return m_timeDilation; } }
  921. public float SimFps { get { return m_simFps; } }
  922. public float PhysicsFps { get { return m_physicsFps; } }
  923. public float AgentUpdates { get { return m_agentUpdates; } }
  924. public float RootAgents { get { return m_rootAgents; } }
  925. public float ChildAgents { get { return m_childAgents; } }
  926. public float TotalPrims { get { return m_totalPrims; } }
  927. public float ActivePrims { get { return m_activePrims; } }
  928. public float TotalFrameTime { get { return m_totalFrameTime; } }
  929. public float NetFrameTime { get { return m_netFrameTime; } }
  930. public float PhysicsFrameTime { get { return m_physicsFrameTime; } }
  931. public float OtherFrameTime { get { return m_otherFrameTime; } }
  932. public float ImageFrameTime { get { return m_imageFrameTime; } }
  933. public float InPacketsPerSecond { get { return m_inPacketsPerSecond; } }
  934. public float OutPacketsPerSecond { get { return m_outPacketsPerSecond; } }
  935. public float UnackedBytes { get { return m_unackedBytes; } }
  936. public float AgentFrameTime { get { return m_agentFrameTime; } }
  937. public float PendingDownloads { get { return m_pendingDownloads; } }
  938. public float PendingUploads { get { return m_pendingUploads; } }
  939. public float ActiveScripts { get { return m_activeScripts; } }
  940. public float ScriptLinesPerSecond { get { return m_scriptLinesPerSecond; } }
  941. public USimStatsData(UUID pRegionID)
  942. {
  943. m_regionID = pRegionID;
  944. }
  945. public void ConsumeSimStats(SimStats stats)
  946. {
  947. m_regionID = stats.RegionUUID;
  948. m_timeDilation = stats.StatsBlock[0].StatValue;
  949. m_simFps = stats.StatsBlock[1].StatValue;
  950. m_physicsFps = stats.StatsBlock[2].StatValue;
  951. m_agentUpdates = stats.StatsBlock[3].StatValue;
  952. m_rootAgents = stats.StatsBlock[4].StatValue;
  953. m_childAgents = stats.StatsBlock[5].StatValue;
  954. m_totalPrims = stats.StatsBlock[6].StatValue;
  955. m_activePrims = stats.StatsBlock[7].StatValue;
  956. m_totalFrameTime = stats.StatsBlock[8].StatValue;
  957. m_netFrameTime = stats.StatsBlock[9].StatValue;
  958. m_physicsFrameTime = stats.StatsBlock[10].StatValue;
  959. m_otherFrameTime = stats.StatsBlock[11].StatValue;
  960. m_imageFrameTime = stats.StatsBlock[12].StatValue;
  961. m_inPacketsPerSecond = stats.StatsBlock[13].StatValue;
  962. m_outPacketsPerSecond = stats.StatsBlock[14].StatValue;
  963. m_unackedBytes = stats.StatsBlock[15].StatValue;
  964. m_agentFrameTime = stats.StatsBlock[16].StatValue;
  965. m_pendingDownloads = stats.StatsBlock[17].StatValue;
  966. m_pendingUploads = stats.StatsBlock[18].StatValue;
  967. m_activeScripts = stats.StatsBlock[19].StatValue;
  968. m_scriptLinesPerSecond = stats.StatsBlock[20].StatValue;
  969. }
  970. }
  971. }