diff options
Diffstat (limited to 'OpenSim/Region/OptionalModules')
25 files changed, 2008 insertions, 542 deletions
diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index dd72cfb..2112b71 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs | |||
@@ -58,6 +58,8 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
58 | 58 | ||
59 | public ISceneAgent SceneAgent { get; set; } | 59 | public ISceneAgent SceneAgent { get; set; } |
60 | 60 | ||
61 | public int PingTimeMS { get { return 0; } } | ||
62 | |||
61 | private string m_username; | 63 | private string m_username; |
62 | private string m_nick; | 64 | private string m_nick; |
63 | 65 | ||
@@ -660,6 +662,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
660 | public event BakeTerrain OnBakeTerrain; | 662 | public event BakeTerrain OnBakeTerrain; |
661 | public event EstateChangeInfo OnEstateChangeInfo; | 663 | public event EstateChangeInfo OnEstateChangeInfo; |
662 | public event EstateManageTelehub OnEstateManageTelehub; | 664 | public event EstateManageTelehub OnEstateManageTelehub; |
665 | public event CachedTextureRequest OnCachedTextureRequest; | ||
663 | public event SetAppearance OnSetAppearance; | 666 | public event SetAppearance OnSetAppearance; |
664 | public event AvatarNowWearing OnAvatarNowWearing; | 667 | public event AvatarNowWearing OnAvatarNowWearing; |
665 | public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; | 668 | public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; |
@@ -687,6 +690,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
687 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; | 690 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; |
688 | public event UpdateAgent OnPreAgentUpdate; | 691 | public event UpdateAgent OnPreAgentUpdate; |
689 | public event UpdateAgent OnAgentUpdate; | 692 | public event UpdateAgent OnAgentUpdate; |
693 | public event UpdateAgent OnAgentCameraUpdate; | ||
690 | public event AgentRequestSit OnAgentRequestSit; | 694 | public event AgentRequestSit OnAgentRequestSit; |
691 | public event AgentSit OnAgentSit; | 695 | public event AgentSit OnAgentSit; |
692 | public event AvatarPickerRequest OnAvatarPickerRequest; | 696 | public event AvatarPickerRequest OnAvatarPickerRequest; |
@@ -906,7 +910,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
906 | 910 | ||
907 | public void Start() | 911 | public void Start() |
908 | { | 912 | { |
909 | m_scene.AddNewClient(this, PresenceType.User); | 913 | m_scene.AddNewAgent(this, PresenceType.User); |
910 | 914 | ||
911 | // Mimicking LLClientView which gets always set appearance from client. | 915 | // Mimicking LLClientView which gets always set appearance from client. |
912 | AvatarAppearance appearance; | 916 | AvatarAppearance appearance; |
@@ -943,13 +947,18 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
943 | { | 947 | { |
944 | 948 | ||
945 | } | 949 | } |
950 | |||
951 | public void SendCachedTextureResponse(ISceneEntity avatar, int serial, List<CachedTextureResponseArg> cachedTextures) | ||
952 | { | ||
946 | 953 | ||
954 | } | ||
955 | |||
947 | public void SendStartPingCheck(byte seq) | 956 | public void SendStartPingCheck(byte seq) |
948 | { | 957 | { |
949 | 958 | ||
950 | } | 959 | } |
951 | 960 | ||
952 | public void SendKillObject(ulong regionHandle, List<uint> localID) | 961 | public void SendKillObject(List<uint> localID) |
953 | { | 962 | { |
954 | 963 | ||
955 | } | 964 | } |
@@ -1681,7 +1690,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
1681 | { | 1690 | { |
1682 | } | 1691 | } |
1683 | 1692 | ||
1684 | public void StopFlying(ISceneEntity presence) | 1693 | public void SendAgentTerseUpdate(ISceneEntity presence) |
1685 | { | 1694 | { |
1686 | } | 1695 | } |
1687 | 1696 | ||
@@ -1693,5 +1702,8 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
1693 | { | 1702 | { |
1694 | } | 1703 | } |
1695 | 1704 | ||
1705 | public void SendPartFullUpdate(ISceneEntity ent, uint? parentID) | ||
1706 | { | ||
1707 | } | ||
1696 | } | 1708 | } |
1697 | } | 1709 | } |
diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs index 992f38e..ec18db0 100644 --- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Linq; | ||
30 | using System.Reflection; | 31 | using System.Reflection; |
31 | using System.Text; | 32 | using System.Text; |
32 | using log4net; | 33 | using log4net; |
@@ -51,7 +52,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
51 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LindenUDPInfoModule")] | 52 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LindenUDPInfoModule")] |
52 | public class LindenUDPInfoModule : ISharedRegionModule | 53 | public class LindenUDPInfoModule : ISharedRegionModule |
53 | { | 54 | { |
54 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 55 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
55 | 56 | ||
56 | protected Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>(); | 57 | protected Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>(); |
57 | 58 | ||
@@ -130,6 +131,15 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
130 | "Go on/off emergency monitoring mode", | 131 | "Go on/off emergency monitoring mode", |
131 | "Go on/off emergency monitoring mode", | 132 | "Go on/off emergency monitoring mode", |
132 | HandleEmergencyMonitoring); | 133 | HandleEmergencyMonitoring); |
134 | |||
135 | scene.AddCommand( | ||
136 | "Comms", this, "show client stats", | ||
137 | "show client stats [first_name last_name]", | ||
138 | "Show client request stats", | ||
139 | "Without the 'first_name last_name' option, all clients are shown." | ||
140 | + " With the 'first_name last_name' option only a specific client is shown.", | ||
141 | (mod, cmd) => MainConsole.Instance.Output(HandleClientStatsReport(cmd))); | ||
142 | |||
133 | } | 143 | } |
134 | 144 | ||
135 | public void RemoveRegion(Scene scene) | 145 | public void RemoveRegion(Scene scene) |
@@ -294,7 +304,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
294 | private string GetImageQueuesReport(string[] showParams) | 304 | private string GetImageQueuesReport(string[] showParams) |
295 | { | 305 | { |
296 | if (showParams.Length < 5 || showParams.Length > 6) | 306 | if (showParams.Length < 5 || showParams.Length > 6) |
297 | return "Usage: image queues show <first-name> <last-name> [full]"; | 307 | return "Usage: show image queues <first-name> <last-name> [full]"; |
298 | 308 | ||
299 | string firstName = showParams[3]; | 309 | string firstName = showParams[3]; |
300 | string lastName = showParams[4]; | 310 | string lastName = showParams[4]; |
@@ -385,7 +395,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
385 | report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding)); | 395 | report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding)); |
386 | 396 | ||
387 | report.AppendFormat( | 397 | report.AppendFormat( |
388 | "{0,7} {1,7} {2,7} {3,7} {4,9} {5,7} {6,7} {7,7} {8,7} {9,7} {10,8} {11,7} {12,7}\n", | 398 | "{0,7} {1,7} {2,7} {3,7} {4,9} {5,7} {6,7} {7,7} {8,7} {9,7} {10,8} {11,7}\n", |
389 | "Since", | 399 | "Since", |
390 | "Pkts", | 400 | "Pkts", |
391 | "Pkts", | 401 | "Pkts", |
@@ -397,12 +407,11 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
397 | "Q Pkts", | 407 | "Q Pkts", |
398 | "Q Pkts", | 408 | "Q Pkts", |
399 | "Q Pkts", | 409 | "Q Pkts", |
400 | "Q Pkts", | ||
401 | "Q Pkts"); | 410 | "Q Pkts"); |
402 | 411 | ||
403 | report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", ""); | 412 | report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", ""); |
404 | report.AppendFormat( | 413 | report.AppendFormat( |
405 | "{0,7} {1,7} {2,7} {3,7} {4,9} {5,7} {6,7} {7,7} {8,7} {9,7} {10,8} {11,7} {12,7}\n", | 414 | "{0,7} {1,7} {2,7} {3,7} {4,9} {5,7} {6,7} {7,7} {8,7} {9,7} {10,8} {11,7}\n", |
406 | "Last In", | 415 | "Last In", |
407 | "In", | 416 | "In", |
408 | "Out", | 417 | "Out", |
@@ -414,8 +423,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
414 | "Cloud", | 423 | "Cloud", |
415 | "Task", | 424 | "Task", |
416 | "Texture", | 425 | "Texture", |
417 | "Asset", | 426 | "Asset"); |
418 | "State"); | ||
419 | 427 | ||
420 | lock (m_scenes) | 428 | lock (m_scenes) |
421 | { | 429 | { |
@@ -424,24 +432,24 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
424 | scene.ForEachClient( | 432 | scene.ForEachClient( |
425 | delegate(IClientAPI client) | 433 | delegate(IClientAPI client) |
426 | { | 434 | { |
427 | bool isChild = client.SceneAgent.IsChildAgent; | ||
428 | if (isChild && !showChildren) | ||
429 | return; | ||
430 | |||
431 | string name = client.Name; | ||
432 | if (pname != "" && name != pname) | ||
433 | return; | ||
434 | |||
435 | string regionName = scene.RegionInfo.RegionName; | ||
436 | |||
437 | report.Append(GetColumnEntry(name, maxNameLength, columnPadding)); | ||
438 | report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding)); | ||
439 | report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding)); | ||
440 | |||
441 | if (client is IStatsCollector) | 435 | if (client is IStatsCollector) |
442 | { | 436 | { |
443 | IStatsCollector stats = (IStatsCollector)client; | 437 | |
438 | bool isChild = client.SceneAgent.IsChildAgent; | ||
439 | if (isChild && !showChildren) | ||
440 | return; | ||
444 | 441 | ||
442 | string name = client.Name; | ||
443 | if (pname != "" && name != pname) | ||
444 | return; | ||
445 | |||
446 | string regionName = scene.RegionInfo.RegionName; | ||
447 | |||
448 | report.Append(GetColumnEntry(name, maxNameLength, columnPadding)); | ||
449 | report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding)); | ||
450 | report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding)); | ||
451 | |||
452 | IStatsCollector stats = (IStatsCollector)client; | ||
445 | report.AppendLine(stats.Report()); | 453 | report.AppendLine(stats.Report()); |
446 | } | 454 | } |
447 | }); | 455 | }); |
@@ -587,6 +595,115 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
587 | (throttleRates.Asset * 8) / 1000); | 595 | (throttleRates.Asset * 8) / 1000); |
588 | 596 | ||
589 | return report.ToString(); | 597 | return report.ToString(); |
590 | } | 598 | } |
599 | |||
600 | /// <summary> | ||
601 | /// Show client stats data | ||
602 | /// </summary> | ||
603 | /// <param name="showParams"></param> | ||
604 | /// <returns></returns> | ||
605 | protected string HandleClientStatsReport(string[] showParams) | ||
606 | { | ||
607 | // NOTE: This writes to m_log on purpose. We want to store this information | ||
608 | // in case we need to analyze it later. | ||
609 | // | ||
610 | if (showParams.Length <= 4) | ||
611 | { | ||
612 | m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}", "Region", "Name", "Root", "Time", "Reqs/min", "AgentUpdates"); | ||
613 | foreach (Scene scene in m_scenes.Values) | ||
614 | { | ||
615 | scene.ForEachClient( | ||
616 | delegate(IClientAPI client) | ||
617 | { | ||
618 | if (client is LLClientView) | ||
619 | { | ||
620 | LLClientView llClient = client as LLClientView; | ||
621 | ClientInfo cinfo = llClient.UDPClient.GetClientInfo(); | ||
622 | int avg_reqs = cinfo.AsyncRequests.Values.Sum() + cinfo.GenericRequests.Values.Sum() + cinfo.SyncRequests.Values.Sum(); | ||
623 | avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); | ||
624 | |||
625 | string childAgentStatus; | ||
626 | |||
627 | if (llClient.SceneAgent != null) | ||
628 | childAgentStatus = llClient.SceneAgent.IsChildAgent ? "N" : "Y"; | ||
629 | else | ||
630 | childAgentStatus = "Off!"; | ||
631 | |||
632 | m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}", | ||
633 | scene.RegionInfo.RegionName, llClient.Name, | ||
634 | childAgentStatus, | ||
635 | (DateTime.Now - cinfo.StartedTime).Minutes, | ||
636 | avg_reqs, | ||
637 | string.Format( | ||
638 | "{0} ({1:0.00}%)", | ||
639 | llClient.TotalAgentUpdates, | ||
640 | (float)cinfo.SyncRequests["AgentUpdate"] / llClient.TotalAgentUpdates * 100)); | ||
641 | } | ||
642 | }); | ||
643 | } | ||
644 | return string.Empty; | ||
645 | } | ||
646 | |||
647 | string fname = "", lname = ""; | ||
648 | |||
649 | if (showParams.Length > 3) | ||
650 | fname = showParams[3]; | ||
651 | if (showParams.Length > 4) | ||
652 | lname = showParams[4]; | ||
653 | |||
654 | foreach (Scene scene in m_scenes.Values) | ||
655 | { | ||
656 | scene.ForEachClient( | ||
657 | delegate(IClientAPI client) | ||
658 | { | ||
659 | if (client is LLClientView) | ||
660 | { | ||
661 | LLClientView llClient = client as LLClientView; | ||
662 | |||
663 | if (llClient.Name == fname + " " + lname) | ||
664 | { | ||
665 | |||
666 | ClientInfo cinfo = llClient.GetClientInfo(); | ||
667 | AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(llClient.CircuitCode); | ||
668 | if (aCircuit == null) // create a dummy one | ||
669 | aCircuit = new AgentCircuitData(); | ||
670 | |||
671 | if (!llClient.SceneAgent.IsChildAgent) | ||
672 | m_log.InfoFormat("[INFO]: {0} # {1} # {2}", llClient.Name, aCircuit.Viewer, aCircuit.Id0); | ||
673 | |||
674 | int avg_reqs = cinfo.AsyncRequests.Values.Sum() + cinfo.GenericRequests.Values.Sum() + cinfo.SyncRequests.Values.Sum(); | ||
675 | avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); | ||
676 | |||
677 | m_log.InfoFormat("[INFO]:"); | ||
678 | m_log.InfoFormat("[INFO]: {0} # {1} # Time: {2}min # Avg Reqs/min: {3}", scene.RegionInfo.RegionName, | ||
679 | (llClient.SceneAgent.IsChildAgent ? "Child" : "Root"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs); | ||
680 | |||
681 | Dictionary<string, int> sortedDict = (from entry in cinfo.AsyncRequests orderby entry.Value descending select entry) | ||
682 | .ToDictionary(pair => pair.Key, pair => pair.Value); | ||
683 | PrintRequests("TOP ASYNC", sortedDict, cinfo.AsyncRequests.Values.Sum()); | ||
684 | |||
685 | sortedDict = (from entry in cinfo.SyncRequests orderby entry.Value descending select entry) | ||
686 | .ToDictionary(pair => pair.Key, pair => pair.Value); | ||
687 | PrintRequests("TOP SYNC", sortedDict, cinfo.SyncRequests.Values.Sum()); | ||
688 | |||
689 | sortedDict = (from entry in cinfo.GenericRequests orderby entry.Value descending select entry) | ||
690 | .ToDictionary(pair => pair.Key, pair => pair.Value); | ||
691 | PrintRequests("TOP GENERIC", sortedDict, cinfo.GenericRequests.Values.Sum()); | ||
692 | } | ||
693 | } | ||
694 | }); | ||
695 | } | ||
696 | return string.Empty; | ||
697 | } | ||
698 | |||
699 | private void PrintRequests(string type, Dictionary<string, int> sortedDict, int sum) | ||
700 | { | ||
701 | m_log.InfoFormat("[INFO]:"); | ||
702 | m_log.InfoFormat("[INFO]: {0,25}", type); | ||
703 | foreach (KeyValuePair<string, int> kvp in sortedDict.Take(12)) | ||
704 | m_log.InfoFormat("[INFO]: {0,25} {1,-6}", kvp.Key, kvp.Value); | ||
705 | m_log.InfoFormat("[INFO]: {0,25}", "..."); | ||
706 | m_log.InfoFormat("[INFO]: {0,25} {1,-6}", "Total", sum); | ||
707 | } | ||
591 | } | 708 | } |
592 | } \ No newline at end of file | 709 | } \ No newline at end of file |
diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs index 5a37fad..b5d9fda 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/ChannelState.cs | |||
@@ -461,7 +461,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat | |||
461 | 461 | ||
462 | string result = instr; | 462 | string result = instr; |
463 | 463 | ||
464 | if (result == null || result.Length == 0) | 464 | if (string.IsNullOrEmpty(result)) |
465 | return result; | 465 | return result; |
466 | 466 | ||
467 | // Repeatedly scan the string until all possible | 467 | // Repeatedly scan the string until all possible |
diff --git a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs index 018357a..c48e585 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs | |||
@@ -375,11 +375,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Concierge | |||
375 | scene.GetRootAgentCount(), scene.RegionInfo.RegionName, | 375 | scene.GetRootAgentCount(), scene.RegionInfo.RegionName, |
376 | scene.RegionInfo.RegionID, | 376 | scene.RegionInfo.RegionID, |
377 | DateTime.UtcNow.ToString("s"))); | 377 | DateTime.UtcNow.ToString("s"))); |
378 | |||
378 | scene.ForEachRootScenePresence(delegate(ScenePresence sp) | 379 | scene.ForEachRootScenePresence(delegate(ScenePresence sp) |
379 | { | 380 | { |
380 | list.Append(String.Format(" <avatar name=\"{0}\" uuid=\"{1}\" />\n", sp.Name, sp.UUID)); | 381 | list.Append(String.Format(" <avatar name=\"{0}\" uuid=\"{1}\" />\n", sp.Name, sp.UUID)); |
381 | list.Append("</avatars>"); | ||
382 | }); | 382 | }); |
383 | |||
384 | list.Append("</avatars>"); | ||
383 | string payload = list.ToString(); | 385 | string payload = list.ToString(); |
384 | 386 | ||
385 | // post via REST to broker | 387 | // post via REST to broker |
diff --git a/OpenSim/Region/OptionalModules/Avatar/SitStand/SitStandCommandsModule.cs b/OpenSim/Region/OptionalModules/Avatar/SitStand/SitStandCommandsModule.cs new file mode 100644 index 0000000..5a6b284 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/SitStand/SitStandCommandsModule.cs | |||
@@ -0,0 +1,220 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Linq; | ||
31 | using System.Reflection; | ||
32 | using System.Text; | ||
33 | using System.Text.RegularExpressions; | ||
34 | using log4net; | ||
35 | using Mono.Addins; | ||
36 | using NDesk.Options; | ||
37 | using Nini.Config; | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Framework.Console; | ||
41 | using OpenSim.Region.Framework.Interfaces; | ||
42 | using OpenSim.Region.Framework.Scenes; | ||
43 | |||
44 | namespace OpenSim.Region.OptionalModules.Avatar.SitStand | ||
45 | { | ||
46 | /// <summary> | ||
47 | /// A module that just holds commands for changing avatar sitting and standing states. | ||
48 | /// </summary> | ||
49 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AnimationsCommandModule")] | ||
50 | public class SitStandCommandModule : INonSharedRegionModule | ||
51 | { | ||
52 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | private Scene m_scene; | ||
55 | |||
56 | public string Name { get { return "SitStand Command Module"; } } | ||
57 | |||
58 | public Type ReplaceableInterface { get { return null; } } | ||
59 | |||
60 | public void Initialise(IConfigSource source) | ||
61 | { | ||
62 | // m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: INITIALIZED MODULE"); | ||
63 | } | ||
64 | |||
65 | public void PostInitialise() | ||
66 | { | ||
67 | // m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: POST INITIALIZED MODULE"); | ||
68 | } | ||
69 | |||
70 | public void Close() | ||
71 | { | ||
72 | // m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: CLOSED MODULE"); | ||
73 | } | ||
74 | |||
75 | public void AddRegion(Scene scene) | ||
76 | { | ||
77 | // m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName); | ||
78 | } | ||
79 | |||
80 | public void RemoveRegion(Scene scene) | ||
81 | { | ||
82 | // m_log.DebugFormat("[ATTACHMENTS COMMAND MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName); | ||
83 | } | ||
84 | |||
85 | public void RegionLoaded(Scene scene) | ||
86 | { | ||
87 | // m_log.DebugFormat("[ANIMATIONS COMMAND MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); | ||
88 | |||
89 | m_scene = scene; | ||
90 | |||
91 | scene.AddCommand( | ||
92 | "Users", this, "sit user name", | ||
93 | "sit user name [--regex] <first-name> <last-name>", | ||
94 | "Sit the named user on an unoccupied object with a sit target.", | ||
95 | "If there are no such objects then nothing happens.\n" | ||
96 | + "If --regex is specified then the names are treated as regular expressions.", | ||
97 | HandleSitUserNameCommand); | ||
98 | |||
99 | scene.AddCommand( | ||
100 | "Users", this, "stand user name", | ||
101 | "stand user name [--regex] <first-name> <last-name>", | ||
102 | "Stand the named user.", | ||
103 | "If --regex is specified then the names are treated as regular expressions.", | ||
104 | HandleStandUserNameCommand); | ||
105 | } | ||
106 | |||
107 | private void HandleSitUserNameCommand(string module, string[] cmd) | ||
108 | { | ||
109 | if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null) | ||
110 | return; | ||
111 | |||
112 | if (cmd.Length < 5) | ||
113 | { | ||
114 | MainConsole.Instance.Output("Usage: sit user name [--regex] <first-name> <last-name>"); | ||
115 | return; | ||
116 | } | ||
117 | |||
118 | List<ScenePresence> scenePresences = GetScenePresences(cmd); | ||
119 | |||
120 | foreach (ScenePresence sp in scenePresences) | ||
121 | { | ||
122 | if (sp.SitGround || sp.IsSatOnObject) | ||
123 | continue; | ||
124 | |||
125 | SceneObjectPart sitPart = null; | ||
126 | List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups(); | ||
127 | |||
128 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
129 | { | ||
130 | if (sceneObject.IsAttachment) | ||
131 | continue; | ||
132 | |||
133 | foreach (SceneObjectPart part in sceneObject.Parts) | ||
134 | { | ||
135 | if (part.IsSitTargetSet && part.SitTargetAvatar == UUID.Zero) | ||
136 | { | ||
137 | sitPart = part; | ||
138 | break; | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | if (sitPart != null) | ||
144 | { | ||
145 | MainConsole.Instance.OutputFormat( | ||
146 | "Sitting {0} on {1} {2} in {3}", | ||
147 | sp.Name, sitPart.ParentGroup.Name, sitPart.ParentGroup.UUID, m_scene.Name); | ||
148 | |||
149 | sp.HandleAgentRequestSit(sp.ControllingClient, sp.UUID, sitPart.UUID, Vector3.Zero); | ||
150 | sp.HandleAgentSit(sp.ControllingClient, sp.UUID); | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | MainConsole.Instance.OutputFormat( | ||
155 | "Could not find any unoccupied set seat on which to sit {0} in {1}. Aborting", | ||
156 | sp.Name, m_scene.Name); | ||
157 | |||
158 | break; | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | private void HandleStandUserNameCommand(string module, string[] cmd) | ||
164 | { | ||
165 | if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null) | ||
166 | return; | ||
167 | |||
168 | if (cmd.Length < 5) | ||
169 | { | ||
170 | MainConsole.Instance.Output("Usage: stand user name [--regex] <first-name> <last-name>"); | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | List<ScenePresence> scenePresences = GetScenePresences(cmd); | ||
175 | |||
176 | foreach (ScenePresence sp in scenePresences) | ||
177 | { | ||
178 | if (sp.SitGround || sp.IsSatOnObject) | ||
179 | { | ||
180 | MainConsole.Instance.OutputFormat("Standing {0} in {1}", sp.Name, m_scene.Name); | ||
181 | sp.StandUp(); | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | private List<ScenePresence> GetScenePresences(string[] cmdParams) | ||
187 | { | ||
188 | bool useRegex = false; | ||
189 | OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); | ||
190 | |||
191 | List<string> mainParams = options.Parse(cmdParams); | ||
192 | |||
193 | string firstName = mainParams[3]; | ||
194 | string lastName = mainParams[4]; | ||
195 | |||
196 | List<ScenePresence> scenePresencesMatched = new List<ScenePresence>(); | ||
197 | |||
198 | if (useRegex) | ||
199 | { | ||
200 | Regex nameRegex = new Regex(string.Format("{0} {1}", firstName, lastName)); | ||
201 | List<ScenePresence> scenePresences = m_scene.GetScenePresences(); | ||
202 | |||
203 | foreach (ScenePresence sp in scenePresences) | ||
204 | { | ||
205 | if (!sp.IsChildAgent && nameRegex.IsMatch(sp.Name)) | ||
206 | scenePresencesMatched.Add(sp); | ||
207 | } | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | ScenePresence sp = m_scene.GetScenePresence(firstName, lastName); | ||
212 | |||
213 | if (sp != null && !sp.IsChildAgent) | ||
214 | scenePresencesMatched.Add(sp); | ||
215 | } | ||
216 | |||
217 | return scenePresencesMatched; | ||
218 | } | ||
219 | } | ||
220 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs index 0cec959..2b33084 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/FreeSwitchVoice/FreeSwitchVoiceModule.cs | |||
@@ -326,15 +326,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice | |||
326 | "ParcelVoiceInfoRequest", | 326 | "ParcelVoiceInfoRequest", |
327 | agentID.ToString())); | 327 | agentID.ToString())); |
328 | 328 | ||
329 | caps.RegisterHandler( | 329 | //caps.RegisterHandler( |
330 | "ChatSessionRequest", | 330 | // "ChatSessionRequest", |
331 | new RestStreamHandler( | 331 | // new RestStreamHandler( |
332 | "POST", | 332 | // "POST", |
333 | capsBase + m_chatSessionRequestPath, | 333 | // capsBase + m_chatSessionRequestPath, |
334 | (request, path, param, httpRequest, httpResponse) | 334 | // (request, path, param, httpRequest, httpResponse) |
335 | => ChatSessionRequest(scene, request, path, param, agentID, caps), | 335 | // => ChatSessionRequest(scene, request, path, param, agentID, caps), |
336 | "ChatSessionRequest", | 336 | // "ChatSessionRequest", |
337 | agentID.ToString())); | 337 | // agentID.ToString())); |
338 | } | 338 | } |
339 | 339 | ||
340 | /// <summary> | 340 | /// <summary> |
diff --git a/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs b/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs index 9e6cc1a..38ba54d 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Voice/VivoxVoice/VivoxVoiceModule.cs | |||
@@ -117,6 +117,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
117 | 117 | ||
118 | private IConfig m_config; | 118 | private IConfig m_config; |
119 | 119 | ||
120 | private object m_Lock; | ||
121 | |||
120 | public void Initialise(IConfigSource config) | 122 | public void Initialise(IConfigSource config) |
121 | { | 123 | { |
122 | 124 | ||
@@ -128,6 +130,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
128 | if (!m_config.GetBoolean("enabled", false)) | 130 | if (!m_config.GetBoolean("enabled", false)) |
129 | return; | 131 | return; |
130 | 132 | ||
133 | m_Lock = new object(); | ||
134 | |||
131 | try | 135 | try |
132 | { | 136 | { |
133 | // retrieve configuration variables | 137 | // retrieve configuration variables |
@@ -429,15 +433,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
429 | "ParcelVoiceInfoRequest", | 433 | "ParcelVoiceInfoRequest", |
430 | agentID.ToString())); | 434 | agentID.ToString())); |
431 | 435 | ||
432 | caps.RegisterHandler( | 436 | //caps.RegisterHandler( |
433 | "ChatSessionRequest", | 437 | // "ChatSessionRequest", |
434 | new RestStreamHandler( | 438 | // new RestStreamHandler( |
435 | "POST", | 439 | // "POST", |
436 | capsBase + m_chatSessionRequestPath, | 440 | // capsBase + m_chatSessionRequestPath, |
437 | (request, path, param, httpRequest, httpResponse) | 441 | // (request, path, param, httpRequest, httpResponse) |
438 | => ChatSessionRequest(scene, request, path, param, agentID, caps), | 442 | // => ChatSessionRequest(scene, request, path, param, agentID, caps), |
439 | "ChatSessionRequest", | 443 | // "ChatSessionRequest", |
440 | agentID.ToString())); | 444 | // agentID.ToString())); |
441 | } | 445 | } |
442 | 446 | ||
443 | /// <summary> | 447 | /// <summary> |
@@ -823,11 +827,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
823 | { | 827 | { |
824 | string requrl = String.Format(m_vivoxChannelPath, m_vivoxServer, "create", channelId, m_authToken); | 828 | string requrl = String.Format(m_vivoxChannelPath, m_vivoxServer, "create", channelId, m_authToken); |
825 | 829 | ||
826 | if (parent != null && parent != String.Empty) | 830 | if (!string.IsNullOrEmpty(parent)) |
827 | { | 831 | { |
828 | requrl = String.Format("{0}&chan_parent={1}", requrl, parent); | 832 | requrl = String.Format("{0}&chan_parent={1}", requrl, parent); |
829 | } | 833 | } |
830 | if (description != null && description != String.Empty) | 834 | if (!string.IsNullOrEmpty(description)) |
831 | { | 835 | { |
832 | requrl = String.Format("{0}&chan_desc={1}", requrl, description); | 836 | requrl = String.Format("{0}&chan_desc={1}", requrl, description); |
833 | } | 837 | } |
@@ -837,7 +841,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
837 | requrl = String.Format("{0}&chan_roll_off={1}", requrl, m_vivoxChannelRollOff); | 841 | requrl = String.Format("{0}&chan_roll_off={1}", requrl, m_vivoxChannelRollOff); |
838 | requrl = String.Format("{0}&chan_dist_model={1}", requrl, m_vivoxChannelDistanceModel); | 842 | requrl = String.Format("{0}&chan_dist_model={1}", requrl, m_vivoxChannelDistanceModel); |
839 | requrl = String.Format("{0}&chan_max_range={1}", requrl, m_vivoxChannelMaximumRange); | 843 | requrl = String.Format("{0}&chan_max_range={1}", requrl, m_vivoxChannelMaximumRange); |
840 | requrl = String.Format("{0}&chan_ckamping_distance={1}", requrl, m_vivoxChannelClampingDistance); | 844 | requrl = String.Format("{0}&chan_clamping_distance={1}", requrl, m_vivoxChannelClampingDistance); |
841 | 845 | ||
842 | XmlElement resp = VivoxCall(requrl, true); | 846 | XmlElement resp = VivoxCall(requrl, true); |
843 | if (XmlFind(resp, "response.level0.body.chan_uri", out channelUri)) | 847 | if (XmlFind(resp, "response.level0.body.chan_uri", out channelUri)) |
@@ -863,7 +867,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
863 | // requrl = String.Format("{0}&chan_parent={1}", requrl, parent); | 867 | // requrl = String.Format("{0}&chan_parent={1}", requrl, parent); |
864 | // } | 868 | // } |
865 | 869 | ||
866 | if (description != null && description != String.Empty) | 870 | if (!string.IsNullOrEmpty(description)) |
867 | { | 871 | { |
868 | requrl = String.Format("{0}&chan_desc={1}", requrl, description); | 872 | requrl = String.Format("{0}&chan_desc={1}", requrl, description); |
869 | } | 873 | } |
@@ -1049,7 +1053,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
1049 | private XmlElement VivoxDeleteChannel(string parent, string channelid) | 1053 | private XmlElement VivoxDeleteChannel(string parent, string channelid) |
1050 | { | 1054 | { |
1051 | string requrl = String.Format(m_vivoxChannelDel, m_vivoxServer, "delete", channelid, m_authToken); | 1055 | string requrl = String.Format(m_vivoxChannelDel, m_vivoxServer, "delete", channelid, m_authToken); |
1052 | if (parent != null && parent != String.Empty) | 1056 | if (!string.IsNullOrEmpty(parent)) |
1053 | { | 1057 | { |
1054 | requrl = String.Format("{0}&chan_parent={1}", requrl, parent); | 1058 | requrl = String.Format("{0}&chan_parent={1}", requrl, parent); |
1055 | } | 1059 | } |
@@ -1118,25 +1122,32 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice | |||
1118 | 1122 | ||
1119 | doc = new XmlDocument(); | 1123 | doc = new XmlDocument(); |
1120 | 1124 | ||
1121 | try | 1125 | // Let's serialize all calls to Vivox. Most of these are driven by |
1126 | // the clients (CAPs), when the user arrives at the region. We don't | ||
1127 | // want to issue many simultaneous http requests to Vivox, because mono | ||
1128 | // doesn't like that | ||
1129 | lock (m_Lock) | ||
1122 | { | 1130 | { |
1123 | // Otherwise prepare the request | 1131 | try |
1124 | // m_log.DebugFormat("[VivoxVoice] Sending request <{0}>", requrl); | 1132 | { |
1133 | // Otherwise prepare the request | ||
1134 | //m_log.DebugFormat("[VivoxVoice] Sending request <{0}>", requrl); | ||
1125 | 1135 | ||
1126 | HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requrl); | 1136 | HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requrl); |
1127 | 1137 | ||
1128 | // We are sending just parameters, no content | 1138 | // We are sending just parameters, no content |
1129 | req.ContentLength = 0; | 1139 | req.ContentLength = 0; |
1130 | 1140 | ||
1131 | // Send request and retrieve the response | 1141 | // Send request and retrieve the response |
1132 | using (HttpWebResponse rsp = (HttpWebResponse)req.GetResponse()) | 1142 | using (HttpWebResponse rsp = (HttpWebResponse)req.GetResponse()) |
1133 | using (Stream s = rsp.GetResponseStream()) | 1143 | using (Stream s = rsp.GetResponseStream()) |
1134 | using (XmlTextReader rdr = new XmlTextReader(s)) | 1144 | using (XmlTextReader rdr = new XmlTextReader(s)) |
1135 | doc.Load(rdr); | 1145 | doc.Load(rdr); |
1136 | } | 1146 | } |
1137 | catch (Exception e) | 1147 | catch (Exception e) |
1138 | { | 1148 | { |
1139 | m_log.ErrorFormat("[VivoxVoice] Error in admin call : {0}", e.Message); | 1149 | m_log.ErrorFormat("[VivoxVoice] Error in admin call : {0}", e.Message); |
1150 | } | ||
1140 | } | 1151 | } |
1141 | 1152 | ||
1142 | // If we're debugging server responses, dump the whole | 1153 | // If we're debugging server responses, dump the whole |
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index d0a5989..2764465 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs | |||
@@ -39,6 +39,7 @@ using OpenSim.Framework.Communications; | |||
39 | using OpenSim.Region.Framework.Interfaces; | 39 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
41 | using OpenSim.Services.Interfaces; | 41 | using OpenSim.Services.Interfaces; |
42 | using System.Text; | ||
42 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; | 43 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; |
43 | 44 | ||
44 | namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | 45 | namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups |
@@ -194,6 +195,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
194 | } | 195 | } |
195 | 196 | ||
196 | scene.EventManager.OnNewClient += OnNewClient; | 197 | scene.EventManager.OnNewClient += OnNewClient; |
198 | scene.EventManager.OnMakeRootAgent += OnMakeRoot; | ||
199 | scene.EventManager.OnMakeChildAgent += OnMakeChild; | ||
197 | scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; | 200 | scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; |
198 | // The InstantMessageModule itself doesn't do this, | 201 | // The InstantMessageModule itself doesn't do this, |
199 | // so lets see if things explode if we don't do it | 202 | // so lets see if things explode if we don't do it |
@@ -244,20 +247,34 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
244 | #endregion | 247 | #endregion |
245 | 248 | ||
246 | #region EventHandlers | 249 | #region EventHandlers |
250 | |||
251 | private void OnMakeRoot(ScenePresence sp) | ||
252 | { | ||
253 | if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); | ||
254 | |||
255 | sp.ControllingClient.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; | ||
256 | // Used for Notices and Group Invites/Accept/Reject | ||
257 | sp.ControllingClient.OnInstantMessage += OnInstantMessage; | ||
258 | // Send client their groups information. | ||
259 | // SendAgentGroupDataUpdate(sp.ControllingClient, sp.UUID); | ||
260 | // only send data viwer will ask rest later | ||
261 | OnAgentDataUpdateRequest(sp.ControllingClient, sp.UUID, sp.UUID); | ||
262 | } | ||
263 | |||
264 | private void OnMakeChild(ScenePresence sp) | ||
265 | { | ||
266 | if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); | ||
267 | |||
268 | sp.ControllingClient.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; | ||
269 | sp.ControllingClient.OnInstantMessage -= OnInstantMessage; | ||
270 | } | ||
271 | |||
247 | private void OnNewClient(IClientAPI client) | 272 | private void OnNewClient(IClientAPI client) |
248 | { | 273 | { |
249 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); | 274 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); |
250 | 275 | ||
251 | client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; | ||
252 | client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; | 276 | client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; |
253 | client.OnDirFindQuery += OnDirFindQuery; | ||
254 | client.OnRequestAvatarProperties += OnRequestAvatarProperties; | 277 | client.OnRequestAvatarProperties += OnRequestAvatarProperties; |
255 | |||
256 | // Used for Notices and Group Invites/Accept/Reject | ||
257 | client.OnInstantMessage += OnInstantMessage; | ||
258 | |||
259 | // Send client their groups information. | ||
260 | SendAgentGroupDataUpdate(client, client.AgentId); | ||
261 | } | 278 | } |
262 | 279 | ||
263 | private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) | 280 | private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) |
@@ -303,21 +320,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
303 | } | 320 | } |
304 | */ | 321 | */ |
305 | 322 | ||
306 | void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart) | ||
307 | { | ||
308 | if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) | ||
309 | { | ||
310 | if (m_debugEnabled) | ||
311 | m_log.DebugFormat( | ||
312 | "[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", | ||
313 | System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); | ||
314 | |||
315 | // TODO: This currently ignores pretty much all the query flags including Mature and sort order | ||
316 | remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentID(remoteClient), queryText).ToArray()); | ||
317 | } | ||
318 | |||
319 | } | ||
320 | |||
321 | private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) | 323 | private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) |
322 | { | 324 | { |
323 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); | 325 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); |
@@ -437,44 +439,75 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
437 | string Subject = im.message.Substring(0, im.message.IndexOf('|')); | 439 | string Subject = im.message.Substring(0, im.message.IndexOf('|')); |
438 | string Message = im.message.Substring(Subject.Length + 1); | 440 | string Message = im.message.Substring(Subject.Length + 1); |
439 | 441 | ||
442 | InventoryItemBase item = null; | ||
443 | bool hasAttachment = false; | ||
444 | UUID itemID = UUID.Zero; //Assignment to quiet compiler | ||
445 | UUID ownerID = UUID.Zero; //Assignment to quiet compiler | ||
440 | byte[] bucket; | 446 | byte[] bucket; |
441 | 447 | ||
442 | if ((im.binaryBucket.Length == 1) && (im.binaryBucket[0] == 0)) | 448 | if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) |
443 | { | ||
444 | bucket = new byte[19]; | ||
445 | bucket[0] = 0; //dunno | ||
446 | bucket[1] = 0; //dunno | ||
447 | GroupID.ToBytes(bucket, 2); | ||
448 | bucket[18] = 0; //dunno | ||
449 | } | ||
450 | else | ||
451 | { | 449 | { |
452 | string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); | 450 | string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); |
453 | binBucket = binBucket.Remove(0, 14).Trim(); | 451 | binBucket = binBucket.Remove(0, 14).Trim(); |
454 | if (m_debugEnabled) | 452 | |
453 | OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); | ||
454 | if (binBucketOSD is OSD) | ||
455 | { | 455 | { |
456 | m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket); | 456 | OSDMap binBucketMap = (OSDMap)binBucketOSD; |
457 | |||
458 | itemID = binBucketMap["item_id"].AsUUID(); | ||
459 | ownerID = binBucketMap["owner_id"].AsUUID(); | ||
457 | 460 | ||
458 | OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); | 461 | //Attempt to get the details of the attached item. |
459 | 462 | //If sender doesn't own the attachment, the item | |
460 | foreach (string key in binBucketOSD.Keys) | 463 | //variable will be set to null and attachment will |
464 | //not be included with the group notice. | ||
465 | Scene scene = (Scene)remoteClient.Scene; | ||
466 | item = new InventoryItemBase(itemID, ownerID); | ||
467 | item = scene.InventoryService.GetItem(item); | ||
468 | |||
469 | if (item != null) | ||
461 | { | 470 | { |
462 | if (binBucketOSD.ContainsKey(key)) | 471 | //Got item details so include the attachment. |
463 | { | 472 | hasAttachment = true; |
464 | m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString()); | ||
465 | } | ||
466 | } | 473 | } |
467 | } | 474 | } |
468 | 475 | else | |
469 | // treat as if no attachment | 476 | { |
477 | m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType()); | ||
478 | } | ||
479 | } | ||
480 | |||
481 | if (hasAttachment) | ||
482 | { | ||
483 | //Bucket contains information about attachment. | ||
484 | // | ||
485 | //Byte offset and description of bucket data: | ||
486 | //0: 1 byte indicating if attachment is present | ||
487 | //1: 1 byte indicating the type of attachment | ||
488 | //2: 16 bytes - Group UUID | ||
489 | //18: 16 bytes - UUID of the attachment owner | ||
490 | //34: 16 bytes - UUID of the attachment | ||
491 | //50: variable - Name of the attachment | ||
492 | //??: NUL byte to terminate the attachment name | ||
493 | byte[] name = Encoding.UTF8.GetBytes(item.Name); | ||
494 | bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name | ||
495 | bucket[0] = 1; //Has attachment flag | ||
496 | bucket[1] = (byte)item.InvType; //Type of Attachment | ||
497 | GroupID.ToBytes(bucket, 2); | ||
498 | ownerID.ToBytes(bucket, 18); | ||
499 | itemID.ToBytes(bucket, 34); | ||
500 | name.CopyTo(bucket, 50); | ||
501 | } | ||
502 | else | ||
503 | { | ||
470 | bucket = new byte[19]; | 504 | bucket = new byte[19]; |
471 | bucket[0] = 0; //dunno | 505 | bucket[0] = 0; //Has attachment flag |
472 | bucket[1] = 0; //dunno | 506 | bucket[1] = 0; //Type of attachment |
473 | GroupID.ToBytes(bucket, 2); | 507 | GroupID.ToBytes(bucket, 2); |
474 | bucket[18] = 0; //dunno | 508 | bucket[18] = 0; //NUL terminate name of attachment |
475 | } | 509 | } |
476 | 510 | ||
477 | |||
478 | m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); | 511 | m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); |
479 | if (OnNewGroupNotice != null) | 512 | if (OnNewGroupNotice != null) |
480 | { | 513 | { |
@@ -499,7 +532,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
499 | 532 | ||
500 | if (member.AcceptNotices) | 533 | if (member.AcceptNotices) |
501 | { | 534 | { |
502 | // Build notice IIM | 535 | // Build notice IM |
503 | GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); | 536 | GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); |
504 | 537 | ||
505 | msg.toAgentID = member.AgentID.Guid; | 538 | msg.toAgentID = member.AgentID.Guid; |
@@ -508,10 +541,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
508 | } | 541 | } |
509 | } | 542 | } |
510 | } | 543 | } |
511 | 544 | ||
545 | if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) | ||
546 | { | ||
547 | //Is bucket large enough to hold UUID of the attachment? | ||
548 | if (im.binaryBucket.Length < 16) | ||
549 | return; | ||
550 | |||
551 | UUID noticeID = new UUID(im.imSessionID); | ||
552 | |||
553 | GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID); | ||
554 | if (notice != null) | ||
555 | { | ||
556 | UUID giver = new UUID(notice.BinaryBucket, 18); | ||
557 | UUID attachmentUUID = new UUID(notice.BinaryBucket, 34); | ||
558 | |||
559 | if (m_debugEnabled) | ||
560 | m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId); | ||
561 | |||
562 | InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, | ||
563 | giver, attachmentUUID); | ||
564 | |||
565 | if (itemCopy == null) | ||
566 | { | ||
567 | remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); | ||
572 | } | ||
573 | } | ||
574 | |||
512 | // Interop, received special 210 code for ejecting a group member | 575 | // Interop, received special 210 code for ejecting a group member |
513 | // this only works within the comms servers domain, and won't work hypergrid | 576 | // this only works within the comms servers domain, and won't work hypergrid |
514 | // TODO:FIXME: Use a presense server of some kind to find out where the | 577 | // TODO:FIXME: Use a presence server of some kind to find out where the |
515 | // client actually is, and try contacting that region directly to notify them, | 578 | // client actually is, and try contacting that region directly to notify them, |
516 | // or provide the notification via xmlrpc update queue | 579 | // or provide the notification via xmlrpc update queue |
517 | if ((im.dialog == 210)) | 580 | if ((im.dialog == 210)) |
@@ -889,26 +952,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
889 | 952 | ||
890 | if (data != null) | 953 | if (data != null) |
891 | { | 954 | { |
892 | GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); | 955 | GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); |
893 | |||
894 | GridInstantMessage msg = new GridInstantMessage(); | ||
895 | msg.imSessionID = UUID.Zero.Guid; | ||
896 | msg.fromAgentID = data.GroupID.Guid; | ||
897 | msg.toAgentID = GetRequestingAgentID(remoteClient).Guid; | ||
898 | msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); | ||
899 | msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName; | ||
900 | msg.message = data.noticeData.Subject + "|" + data.Message; | ||
901 | msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested; | ||
902 | msg.fromGroup = true; | ||
903 | msg.offline = (byte)0; | ||
904 | msg.ParentEstateID = 0; | ||
905 | msg.Position = Vector3.Zero; | ||
906 | msg.RegionID = UUID.Zero.Guid; | ||
907 | msg.binaryBucket = data.BinaryBucket; | ||
908 | 956 | ||
909 | OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); | 957 | OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); |
910 | } | 958 | } |
911 | |||
912 | } | 959 | } |
913 | 960 | ||
914 | public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) | 961 | public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) |
@@ -916,10 +963,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
916 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); | 963 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); |
917 | 964 | ||
918 | GridInstantMessage msg = new GridInstantMessage(); | 965 | GridInstantMessage msg = new GridInstantMessage(); |
919 | msg.imSessionID = UUID.Zero.Guid; | 966 | byte[] bucket; |
967 | |||
968 | msg.imSessionID = groupNoticeID.Guid; | ||
920 | msg.toAgentID = agentID.Guid; | 969 | msg.toAgentID = agentID.Guid; |
921 | msg.dialog = dialog; | 970 | msg.dialog = dialog; |
922 | // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; | ||
923 | msg.fromGroup = true; | 971 | msg.fromGroup = true; |
924 | msg.offline = (byte)1; // Allow this message to be stored for offline use | 972 | msg.offline = (byte)1; // Allow this message to be stored for offline use |
925 | msg.ParentEstateID = 0; | 973 | msg.ParentEstateID = 0; |
@@ -933,13 +981,38 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
933 | msg.timestamp = info.noticeData.Timestamp; | 981 | msg.timestamp = info.noticeData.Timestamp; |
934 | msg.fromAgentName = info.noticeData.FromName; | 982 | msg.fromAgentName = info.noticeData.FromName; |
935 | msg.message = info.noticeData.Subject + "|" + info.Message; | 983 | msg.message = info.noticeData.Subject + "|" + info.Message; |
936 | msg.binaryBucket = info.BinaryBucket; | 984 | |
985 | if (info.BinaryBucket[0] > 0) | ||
986 | { | ||
987 | //32 is due to not needing space for two of the UUIDs. | ||
988 | //(Don't need UUID of attachment or its owner in IM) | ||
989 | //50 offset gets us to start of attachment name. | ||
990 | //We are skipping the attachment flag, type, and | ||
991 | //the three UUID fields at the start of the bucket. | ||
992 | bucket = new byte[info.BinaryBucket.Length-32]; | ||
993 | bucket[0] = 1; //Has attachment | ||
994 | bucket[1] = info.BinaryBucket[1]; | ||
995 | Array.Copy(info.BinaryBucket, 50, | ||
996 | bucket, 18, info.BinaryBucket.Length-50); | ||
997 | } | ||
998 | else | ||
999 | { | ||
1000 | bucket = new byte[19]; | ||
1001 | bucket[0] = 0; //No attachment | ||
1002 | bucket[1] = 0; //Attachment type | ||
1003 | bucket[18] = 0; //NUL terminate name | ||
1004 | } | ||
1005 | |||
1006 | info.GroupID.ToBytes(bucket, 2); | ||
1007 | msg.binaryBucket = bucket; | ||
937 | } | 1008 | } |
938 | else | 1009 | else |
939 | { | 1010 | { |
940 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); | 1011 | if (m_debugEnabled) |
1012 | m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); | ||
1013 | |||
941 | msg.fromAgentID = UUID.Zero.Guid; | 1014 | msg.fromAgentID = UUID.Zero.Guid; |
942 | msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; | 1015 | msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); |
943 | msg.fromAgentName = string.Empty; | 1016 | msg.fromAgentName = string.Empty; |
944 | msg.message = string.Empty; | 1017 | msg.message = string.Empty; |
945 | msg.binaryBucket = new byte[0]; | 1018 | msg.binaryBucket = new byte[0]; |
@@ -1063,7 +1136,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
1063 | // Message to ejector | 1136 | // Message to ejector |
1064 | // Interop, received special 210 code for ejecting a group member | 1137 | // Interop, received special 210 code for ejecting a group member |
1065 | // this only works within the comms servers domain, and won't work hypergrid | 1138 | // this only works within the comms servers domain, and won't work hypergrid |
1066 | // TODO:FIXME: Use a presense server of some kind to find out where the | 1139 | // TODO:FIXME: Use a presence server of some kind to find out where the |
1067 | // client actually is, and try contacting that region directly to notify them, | 1140 | // client actually is, and try contacting that region directly to notify them, |
1068 | // or provide the notification via xmlrpc update queue | 1141 | // or provide the notification via xmlrpc update queue |
1069 | 1142 | ||
@@ -1178,6 +1251,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
1178 | } | 1251 | } |
1179 | } | 1252 | } |
1180 | 1253 | ||
1254 | public List<DirGroupsReplyData> FindGroups(IClientAPI remoteClient, string query) | ||
1255 | { | ||
1256 | return m_groupData.FindGroups(GetRequestingAgentID(remoteClient), query); | ||
1257 | } | ||
1258 | |||
1259 | |||
1181 | #endregion | 1260 | #endregion |
1182 | 1261 | ||
1183 | #region Client/Update Tools | 1262 | #region Client/Update Tools |
@@ -1222,7 +1301,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
1222 | AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID)); | 1301 | AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID)); |
1223 | AgentData.Add(AgentDataMap); | 1302 | AgentData.Add(AgentDataMap); |
1224 | 1303 | ||
1225 | |||
1226 | OSDArray GroupData = new OSDArray(data.Length); | 1304 | OSDArray GroupData = new OSDArray(data.Length); |
1227 | OSDArray NewGroupData = new OSDArray(data.Length); | 1305 | OSDArray NewGroupData = new OSDArray(data.Length); |
1228 | 1306 | ||
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs index 7bae8f7..8095b28 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs | |||
@@ -212,8 +212,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
212 | m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name); | 212 | m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name); |
213 | 213 | ||
214 | m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); | 214 | m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); |
215 | if ((m_groupsServerURI == null) || | 215 | if (string.IsNullOrEmpty(m_groupsServerURI)) |
216 | (m_groupsServerURI == string.Empty)) | ||
217 | { | 216 | { |
218 | m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]"); | 217 | m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]"); |
219 | m_connectorEnabled = false; | 218 | m_connectorEnabled = false; |
@@ -438,7 +437,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
438 | return null; | 437 | return null; |
439 | } | 438 | } |
440 | } | 439 | } |
441 | else if ((groupName != null) && (groupName != string.Empty)) | 440 | else if (!string.IsNullOrEmpty(groupName)) |
442 | { | 441 | { |
443 | if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap)) | 442 | if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap)) |
444 | { | 443 | { |
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs index c1bdacb..26d2597 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs | |||
@@ -26,13 +26,23 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
30 | using System.Net; | ||
29 | using System.Reflection; | 31 | using System.Reflection; |
30 | using Nini.Config; | 32 | using Nini.Config; |
31 | using NUnit.Framework; | 33 | using NUnit.Framework; |
32 | using OpenMetaverse; | 34 | using OpenMetaverse; |
35 | using OpenMetaverse.Messages.Linden; | ||
36 | using OpenMetaverse.Packets; | ||
37 | using OpenMetaverse.StructuredData; | ||
33 | using OpenSim.Framework; | 38 | using OpenSim.Framework; |
34 | using OpenSim.Framework.Communications; | 39 | using OpenSim.Framework.Communications; |
40 | using OpenSim.Framework.Servers; | ||
41 | using OpenSim.Framework.Servers.HttpServer; | ||
42 | using OpenSim.Region.ClientStack.Linden; | ||
43 | using OpenSim.Region.CoreModules.Framework; | ||
35 | using OpenSim.Region.Framework.Scenes; | 44 | using OpenSim.Region.Framework.Scenes; |
45 | using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups; | ||
36 | using OpenSim.Tests.Common; | 46 | using OpenSim.Tests.Common; |
37 | using OpenSim.Tests.Common.Mock; | 47 | using OpenSim.Tests.Common.Mock; |
38 | 48 | ||
@@ -44,11 +54,28 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests | |||
44 | [TestFixture] | 54 | [TestFixture] |
45 | public class GroupsModuleTests : OpenSimTestCase | 55 | public class GroupsModuleTests : OpenSimTestCase |
46 | { | 56 | { |
57 | [SetUp] | ||
58 | public override void SetUp() | ||
59 | { | ||
60 | base.SetUp(); | ||
61 | |||
62 | uint port = 9999; | ||
63 | uint sslPort = 9998; | ||
64 | |||
65 | // This is an unfortunate bit of clean up we have to do because MainServer manages things through static | ||
66 | // variables and the VM is not restarted between tests. | ||
67 | MainServer.RemoveHttpServer(port); | ||
68 | |||
69 | BaseHttpServer server = new BaseHttpServer(port, false, sslPort, ""); | ||
70 | MainServer.AddHttpServer(server); | ||
71 | MainServer.Instance = server; | ||
72 | } | ||
73 | |||
47 | [Test] | 74 | [Test] |
48 | public void TestBasic() | 75 | public void TestSendAgentGroupDataUpdate() |
49 | { | 76 | { |
50 | TestHelpers.InMethod(); | 77 | TestHelpers.InMethod(); |
51 | // log4net.Config.XmlConfigurator.Configure(); | 78 | // TestHelpers.EnableLogging(); |
52 | 79 | ||
53 | TestScene scene = new SceneHelpers().SetupScene(); | 80 | TestScene scene = new SceneHelpers().SetupScene(); |
54 | IConfigSource configSource = new IniConfigSource(); | 81 | IConfigSource configSource = new IniConfigSource(); |
@@ -56,8 +83,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests | |||
56 | config.Set("Enabled", true); | 83 | config.Set("Enabled", true); |
57 | config.Set("Module", "GroupsModule"); | 84 | config.Set("Module", "GroupsModule"); |
58 | config.Set("DebugEnabled", true); | 85 | config.Set("DebugEnabled", true); |
86 | |||
87 | GroupsModule gm = new GroupsModule(); | ||
88 | EventQueueGetModule eqgm = new EventQueueGetModule(); | ||
89 | |||
90 | // We need a capabilities module active so that adding the scene presence creates an event queue in the | ||
91 | // EventQueueGetModule | ||
59 | SceneHelpers.SetupSceneModules( | 92 | SceneHelpers.SetupSceneModules( |
60 | scene, configSource, new object[] { new MockGroupsServicesConnector() }); | 93 | scene, configSource, gm, new MockGroupsServicesConnector(), new CapabilitiesModule(), eqgm); |
94 | |||
95 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseStem("1")); | ||
96 | |||
97 | gm.SendAgentGroupDataUpdate(sp.ControllingClient); | ||
98 | |||
99 | Hashtable eventsResponse = eqgm.GetEvents(UUID.Zero, sp.UUID); | ||
100 | |||
101 | Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.OK)); | ||
102 | |||
103 | // Console.WriteLine("Response [{0}]", (string)eventsResponse["str_response_string"]); | ||
104 | |||
105 | OSDMap rawOsd = (OSDMap)OSDParser.DeserializeLLSDXml((string)eventsResponse["str_response_string"]); | ||
106 | OSDArray eventsOsd = (OSDArray)rawOsd["events"]; | ||
107 | |||
108 | bool foundUpdate = false; | ||
109 | foreach (OSD osd in eventsOsd) | ||
110 | { | ||
111 | OSDMap eventOsd = (OSDMap)osd; | ||
112 | |||
113 | if (eventOsd["message"] == "AgentGroupDataUpdate") | ||
114 | foundUpdate = true; | ||
115 | } | ||
116 | |||
117 | Assert.That(foundUpdate, Is.True, "Did not find AgentGroupDataUpdate in response"); | ||
118 | |||
119 | // TODO: More checking of more actual event data. | ||
61 | } | 120 | } |
62 | } | 121 | } |
63 | } \ No newline at end of file | 122 | } \ No newline at end of file |
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs index 71b24ac..e28d0c2 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs | |||
@@ -168,8 +168,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
168 | m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name); | 168 | m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name); |
169 | 169 | ||
170 | m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); | 170 | m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); |
171 | if ((m_groupsServerURI == null) || | 171 | if (string.IsNullOrEmpty(m_groupsServerURI)) |
172 | (m_groupsServerURI == string.Empty)) | ||
173 | { | 172 | { |
174 | m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]"); | 173 | m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]"); |
175 | m_connectorEnabled = false; | 174 | m_connectorEnabled = false; |
@@ -354,7 +353,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
354 | { | 353 | { |
355 | param["GroupID"] = GroupID.ToString(); | 354 | param["GroupID"] = GroupID.ToString(); |
356 | } | 355 | } |
357 | if ((GroupName != null) && (GroupName != string.Empty)) | 356 | if (!string.IsNullOrEmpty(GroupName)) |
358 | { | 357 | { |
359 | param["Name"] = GroupName.ToString(); | 358 | param["Name"] = GroupName.ToString(); |
360 | } | 359 | } |
diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs deleted file mode 100644 index 6e74ce0..0000000 --- a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs +++ /dev/null | |||
@@ -1,339 +0,0 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Diagnostics; | ||
31 | using System.Linq; | ||
32 | using System.Net.NetworkInformation; | ||
33 | using System.Text; | ||
34 | using System.Threading; | ||
35 | |||
36 | using log4net; | ||
37 | using Mono.Addins; | ||
38 | using Nini.Config; | ||
39 | |||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Framework.Console; | ||
42 | using OpenSim.Framework.Monitoring; | ||
43 | using OpenSim.Region.Framework.Interfaces; | ||
44 | using OpenSim.Region.Framework.Scenes; | ||
45 | |||
46 | using OpenMetaverse.StructuredData; | ||
47 | |||
48 | namespace OpenSim.Region.OptionalModules.Framework.Monitoring | ||
49 | { | ||
50 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ServerStatistics")] | ||
51 | public class ServerStats : ISharedRegionModule | ||
52 | { | ||
53 | private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
54 | private readonly string LogHeader = "[SERVER STATS]"; | ||
55 | |||
56 | public bool Enabled = false; | ||
57 | private static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>(); | ||
58 | |||
59 | public readonly string CategoryServer = "server"; | ||
60 | |||
61 | public readonly string ContainerProcessor = "processor"; | ||
62 | public readonly string ContainerMemory = "memory"; | ||
63 | public readonly string ContainerNetwork = "network"; | ||
64 | public readonly string ContainerProcess = "process"; | ||
65 | |||
66 | public string NetworkInterfaceTypes = "Ethernet"; | ||
67 | |||
68 | readonly int performanceCounterSampleInterval = 500; | ||
69 | int lastperformanceCounterSampleTime = 0; | ||
70 | |||
71 | private class PerfCounterControl | ||
72 | { | ||
73 | public PerformanceCounter perfCounter; | ||
74 | public int lastFetch; | ||
75 | public string name; | ||
76 | public PerfCounterControl(PerformanceCounter pPc) | ||
77 | : this(pPc, String.Empty) | ||
78 | { | ||
79 | } | ||
80 | public PerfCounterControl(PerformanceCounter pPc, string pName) | ||
81 | { | ||
82 | perfCounter = pPc; | ||
83 | lastFetch = 0; | ||
84 | name = pName; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | PerfCounterControl processorPercentPerfCounter = null; | ||
89 | |||
90 | #region ISharedRegionModule | ||
91 | // IRegionModuleBase.Name | ||
92 | public string Name { get { return "Server Stats"; } } | ||
93 | // IRegionModuleBase.ReplaceableInterface | ||
94 | public Type ReplaceableInterface { get { return null; } } | ||
95 | // IRegionModuleBase.Initialize | ||
96 | public void Initialise(IConfigSource source) | ||
97 | { | ||
98 | IConfig cfg = source.Configs["Monitoring"]; | ||
99 | |||
100 | if (cfg != null) | ||
101 | Enabled = cfg.GetBoolean("ServerStatsEnabled", true); | ||
102 | |||
103 | if (Enabled) | ||
104 | { | ||
105 | NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet"); | ||
106 | } | ||
107 | } | ||
108 | // IRegionModuleBase.Close | ||
109 | public void Close() | ||
110 | { | ||
111 | if (RegisteredStats.Count > 0) | ||
112 | { | ||
113 | foreach (Stat stat in RegisteredStats.Values) | ||
114 | { | ||
115 | StatsManager.DeregisterStat(stat); | ||
116 | stat.Dispose(); | ||
117 | } | ||
118 | RegisteredStats.Clear(); | ||
119 | } | ||
120 | } | ||
121 | // IRegionModuleBase.AddRegion | ||
122 | public void AddRegion(Scene scene) | ||
123 | { | ||
124 | } | ||
125 | // IRegionModuleBase.RemoveRegion | ||
126 | public void RemoveRegion(Scene scene) | ||
127 | { | ||
128 | } | ||
129 | // IRegionModuleBase.RegionLoaded | ||
130 | public void RegionLoaded(Scene scene) | ||
131 | { | ||
132 | } | ||
133 | // ISharedRegionModule.PostInitialize | ||
134 | public void PostInitialise() | ||
135 | { | ||
136 | if (RegisteredStats.Count == 0) | ||
137 | { | ||
138 | RegisterServerStats(); | ||
139 | } | ||
140 | } | ||
141 | #endregion ISharedRegionModule | ||
142 | |||
143 | private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act) | ||
144 | { | ||
145 | string desc = pDesc; | ||
146 | if (desc == null) | ||
147 | desc = pName; | ||
148 | Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info); | ||
149 | StatsManager.RegisterStat(stat); | ||
150 | RegisteredStats.Add(pName, stat); | ||
151 | } | ||
152 | |||
153 | public void RegisterServerStats() | ||
154 | { | ||
155 | lastperformanceCounterSampleTime = Util.EnvironmentTickCount(); | ||
156 | PerformanceCounter tempPC; | ||
157 | Stat tempStat; | ||
158 | string tempName; | ||
159 | |||
160 | try | ||
161 | { | ||
162 | tempName = "CPUPercent"; | ||
163 | tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total"); | ||
164 | processorPercentPerfCounter = new PerfCounterControl(tempPC); | ||
165 | // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy. | ||
166 | tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor, | ||
167 | StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); }, | ||
168 | StatVerbosity.Info); | ||
169 | StatsManager.RegisterStat(tempStat); | ||
170 | RegisteredStats.Add(tempName, tempStat); | ||
171 | |||
172 | MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor, | ||
173 | (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; }); | ||
174 | |||
175 | MakeStat("UserProcessorTime", null, "sec", ContainerProcessor, | ||
176 | (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; }); | ||
177 | |||
178 | MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor, | ||
179 | (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; }); | ||
180 | |||
181 | MakeStat("Threads", null, "threads", ContainerProcessor, | ||
182 | (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; }); | ||
183 | } | ||
184 | catch (Exception e) | ||
185 | { | ||
186 | m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e); | ||
187 | } | ||
188 | |||
189 | try | ||
190 | { | ||
191 | List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(',')); | ||
192 | |||
193 | IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces(); | ||
194 | foreach (NetworkInterface nic in nics) | ||
195 | { | ||
196 | if (nic.OperationalStatus != OperationalStatus.Up) | ||
197 | continue; | ||
198 | |||
199 | string nicInterfaceType = nic.NetworkInterfaceType.ToString(); | ||
200 | if (!okInterfaceTypes.Contains(nicInterfaceType)) | ||
201 | { | ||
202 | m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.", | ||
203 | LogHeader, nic.Name, nicInterfaceType); | ||
204 | m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}", | ||
205 | LogHeader, NetworkInterfaceTypes); | ||
206 | continue; | ||
207 | } | ||
208 | |||
209 | if (nic.Supports(NetworkInterfaceComponent.IPv4)) | ||
210 | { | ||
211 | IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics(); | ||
212 | if (nicStats != null) | ||
213 | { | ||
214 | MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork, | ||
215 | (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }); | ||
216 | MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork, | ||
217 | (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }); | ||
218 | MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork, | ||
219 | (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }); | ||
220 | } | ||
221 | } | ||
222 | // TODO: add IPv6 (it may actually happen someday) | ||
223 | } | ||
224 | } | ||
225 | catch (Exception e) | ||
226 | { | ||
227 | m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e); | ||
228 | } | ||
229 | |||
230 | MakeStat("ProcessMemory", null, "MB", ContainerMemory, | ||
231 | (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; }); | ||
232 | MakeStat("ObjectMemory", null, "MB", ContainerMemory, | ||
233 | (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; }); | ||
234 | MakeStat("LastMemoryChurn", null, "MB/sec", ContainerMemory, | ||
235 | (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); }); | ||
236 | MakeStat("AverageMemoryChurn", null, "MB/sec", ContainerMemory, | ||
237 | (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); }); | ||
238 | } | ||
239 | |||
240 | // Notes on performance counters: | ||
241 | // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx | ||
242 | // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c | ||
243 | // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters | ||
244 | private delegate double PerfCounterNextValue(); | ||
245 | private void GetNextValue(Stat stat, PerfCounterControl perfControl) | ||
246 | { | ||
247 | GetNextValue(stat, perfControl, 1.0); | ||
248 | } | ||
249 | private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor) | ||
250 | { | ||
251 | if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval) | ||
252 | { | ||
253 | if (perfControl != null && perfControl.perfCounter != null) | ||
254 | { | ||
255 | try | ||
256 | { | ||
257 | // Kludge for factor to run double duty. If -1, subtract the value from one | ||
258 | if (factor == -1) | ||
259 | stat.Value = 1 - perfControl.perfCounter.NextValue(); | ||
260 | else | ||
261 | stat.Value = perfControl.perfCounter.NextValue() / factor; | ||
262 | } | ||
263 | catch (Exception e) | ||
264 | { | ||
265 | m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e); | ||
266 | } | ||
267 | perfControl.lastFetch = Util.EnvironmentTickCount(); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | // Lookup the nic that goes with this stat and set the value by using a fetch action. | ||
273 | // Not sure about closure with delegates inside delegates. | ||
274 | private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat); | ||
275 | private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor) | ||
276 | { | ||
277 | // Get the one nic that has the name of this stat | ||
278 | IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where( | ||
279 | (network) => network.Name == stat.Description); | ||
280 | try | ||
281 | { | ||
282 | foreach (NetworkInterface nic in nics) | ||
283 | { | ||
284 | IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics(); | ||
285 | if (intrStats != null) | ||
286 | { | ||
287 | double newVal = Math.Round(getter(intrStats) / factor, 3); | ||
288 | stat.Value = newVal; | ||
289 | } | ||
290 | break; | ||
291 | } | ||
292 | } | ||
293 | catch | ||
294 | { | ||
295 | // There are times interfaces go away so we just won't update the stat for this | ||
296 | m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description); | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | public class ServerStatsAggregator : Stat | ||
302 | { | ||
303 | public ServerStatsAggregator( | ||
304 | string shortName, | ||
305 | string name, | ||
306 | string description, | ||
307 | string unitName, | ||
308 | string category, | ||
309 | string container | ||
310 | ) | ||
311 | : base( | ||
312 | shortName, | ||
313 | name, | ||
314 | description, | ||
315 | unitName, | ||
316 | category, | ||
317 | container, | ||
318 | StatType.Push, | ||
319 | MeasuresOfInterest.None, | ||
320 | null, | ||
321 | StatVerbosity.Info) | ||
322 | { | ||
323 | } | ||
324 | public override string ToConsoleString() | ||
325 | { | ||
326 | StringBuilder sb = new StringBuilder(); | ||
327 | |||
328 | return sb.ToString(); | ||
329 | } | ||
330 | |||
331 | public override OSDMap ToOSDMap() | ||
332 | { | ||
333 | OSDMap ret = new OSDMap(); | ||
334 | |||
335 | return ret; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | } | ||
diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs new file mode 100644 index 0000000..afb788b --- /dev/null +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs | |||
@@ -0,0 +1,590 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using System.Security.Cryptography; // for computing md5 hash | ||
33 | using log4net; | ||
34 | using Mono.Addins; | ||
35 | using Nini.Config; | ||
36 | |||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.StructuredData; | ||
39 | |||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Framework.Servers; | ||
42 | using OpenSim.Framework.Servers.HttpServer; | ||
43 | using OpenSim.Region.Framework.Interfaces; | ||
44 | using OpenSim.Region.Framework.Scenes; | ||
45 | using OpenSimAssetType = OpenSim.Framework.SLUtil.OpenSimAssetType; | ||
46 | |||
47 | using Ionic.Zlib; | ||
48 | |||
49 | // You will need to uncomment these lines if you are adding a region module to some other assembly which does not already | ||
50 | // specify its assembly. Otherwise, the region modules in the assembly will not be picked up when OpenSimulator scans | ||
51 | // the available DLLs | ||
52 | //[assembly: Addin("MaterialsModule", "1.0")] | ||
53 | //[assembly: AddinDependency("OpenSim", "0.5")] | ||
54 | |||
55 | namespace OpenSim.Region.OptionalModules.Materials | ||
56 | { | ||
57 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MaterialsModule")] | ||
58 | public class MaterialsModule : INonSharedRegionModule | ||
59 | { | ||
60 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
61 | |||
62 | public string Name { get { return "MaterialsModule"; } } | ||
63 | |||
64 | public Type ReplaceableInterface { get { return null; } } | ||
65 | |||
66 | private Scene m_scene = null; | ||
67 | private bool m_enabled = false; | ||
68 | |||
69 | public Dictionary<UUID, OSDMap> m_regionMaterials = new Dictionary<UUID, OSDMap>(); | ||
70 | |||
71 | public void Initialise(IConfigSource source) | ||
72 | { | ||
73 | IConfig config = source.Configs["Materials"]; | ||
74 | if (config == null) | ||
75 | return; | ||
76 | |||
77 | m_enabled = config.GetBoolean("enable_materials", true); | ||
78 | if (!m_enabled) | ||
79 | return; | ||
80 | |||
81 | m_log.DebugFormat("[Materials]: Initialized"); | ||
82 | } | ||
83 | |||
84 | public void Close() | ||
85 | { | ||
86 | if (!m_enabled) | ||
87 | return; | ||
88 | } | ||
89 | |||
90 | public void AddRegion(Scene scene) | ||
91 | { | ||
92 | if (!m_enabled) | ||
93 | return; | ||
94 | |||
95 | m_scene = scene; | ||
96 | m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; | ||
97 | m_scene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; | ||
98 | } | ||
99 | |||
100 | private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) | ||
101 | { | ||
102 | foreach (var part in obj.Parts) | ||
103 | if (part != null) | ||
104 | GetStoredMaterialsInPart(part); | ||
105 | } | ||
106 | |||
107 | private void OnRegisterCaps(OpenMetaverse.UUID agentID, OpenSim.Framework.Capabilities.Caps caps) | ||
108 | { | ||
109 | string capsBase = "/CAPS/" + caps.CapsObjectPath; | ||
110 | |||
111 | IRequestHandler renderMaterialsPostHandler | ||
112 | = new RestStreamHandler("POST", capsBase + "/", | ||
113 | (request, path, param, httpRequest, httpResponse) | ||
114 | => RenderMaterialsPostCap(request, agentID), | ||
115 | "RenderMaterials", null); | ||
116 | caps.RegisterHandler("RenderMaterials", renderMaterialsPostHandler); | ||
117 | |||
118 | // OpenSimulator CAPs infrastructure seems to be somewhat hostile towards any CAP that requires both GET | ||
119 | // and POST handlers, (at least at the time this was originally written), so we first set up a POST | ||
120 | // handler normally and then add a GET handler via MainServer | ||
121 | |||
122 | IRequestHandler renderMaterialsGetHandler | ||
123 | = new RestStreamHandler("GET", capsBase + "/", | ||
124 | (request, path, param, httpRequest, httpResponse) | ||
125 | => RenderMaterialsGetCap(request), | ||
126 | "RenderMaterials", null); | ||
127 | MainServer.Instance.AddStreamHandler(renderMaterialsGetHandler); | ||
128 | |||
129 | // materials viewer seems to use either POST or PUT, so assign POST handler for PUT as well | ||
130 | IRequestHandler renderMaterialsPutHandler | ||
131 | = new RestStreamHandler("PUT", capsBase + "/", | ||
132 | (request, path, param, httpRequest, httpResponse) | ||
133 | => RenderMaterialsPostCap(request, agentID), | ||
134 | "RenderMaterials", null); | ||
135 | MainServer.Instance.AddStreamHandler(renderMaterialsPutHandler); | ||
136 | } | ||
137 | |||
138 | public void RemoveRegion(Scene scene) | ||
139 | { | ||
140 | if (!m_enabled) | ||
141 | return; | ||
142 | |||
143 | m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; | ||
144 | m_scene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene; | ||
145 | } | ||
146 | |||
147 | public void RegionLoaded(Scene scene) | ||
148 | { | ||
149 | } | ||
150 | |||
151 | /// <summary> | ||
152 | /// Finds any legacy materials stored in DynAttrs that may exist for this part and add them to 'm_regionMaterials'. | ||
153 | /// </summary> | ||
154 | /// <param name="part"></param> | ||
155 | private void GetLegacyStoredMaterialsInPart(SceneObjectPart part) | ||
156 | { | ||
157 | if (part.DynAttrs == null) | ||
158 | return; | ||
159 | |||
160 | OSD OSMaterials = null; | ||
161 | OSDArray matsArr = null; | ||
162 | |||
163 | lock (part.DynAttrs) | ||
164 | { | ||
165 | if (part.DynAttrs.ContainsStore("OpenSim", "Materials")) | ||
166 | { | ||
167 | OSDMap materialsStore = part.DynAttrs.GetStore("OpenSim", "Materials"); | ||
168 | |||
169 | if (materialsStore == null) | ||
170 | return; | ||
171 | |||
172 | materialsStore.TryGetValue("Materials", out OSMaterials); | ||
173 | } | ||
174 | |||
175 | if (OSMaterials != null && OSMaterials is OSDArray) | ||
176 | matsArr = OSMaterials as OSDArray; | ||
177 | else | ||
178 | return; | ||
179 | } | ||
180 | |||
181 | if (matsArr == null) | ||
182 | return; | ||
183 | |||
184 | foreach (OSD elemOsd in matsArr) | ||
185 | { | ||
186 | if (elemOsd != null && elemOsd is OSDMap) | ||
187 | { | ||
188 | OSDMap matMap = elemOsd as OSDMap; | ||
189 | if (matMap.ContainsKey("ID") && matMap.ContainsKey("Material")) | ||
190 | { | ||
191 | try | ||
192 | { | ||
193 | lock (m_regionMaterials) | ||
194 | m_regionMaterials[matMap["ID"].AsUUID()] = (OSDMap)matMap["Material"]; | ||
195 | } | ||
196 | catch (Exception e) | ||
197 | { | ||
198 | m_log.Warn("[Materials]: exception decoding persisted legacy material: " + e.ToString()); | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | |||
205 | /// <summary> | ||
206 | /// Find the materials used in the SOP, and add them to 'm_regionMaterials'. | ||
207 | /// </summary> | ||
208 | private void GetStoredMaterialsInPart(SceneObjectPart part) | ||
209 | { | ||
210 | if (part.Shape == null) | ||
211 | return; | ||
212 | |||
213 | var te = new Primitive.TextureEntry(part.Shape.TextureEntry, 0, part.Shape.TextureEntry.Length); | ||
214 | if (te == null) | ||
215 | return; | ||
216 | |||
217 | GetLegacyStoredMaterialsInPart(part); | ||
218 | |||
219 | GetStoredMaterialInFace(part, te.DefaultTexture); | ||
220 | |||
221 | foreach (Primitive.TextureEntryFace face in te.FaceTextures) | ||
222 | { | ||
223 | if (face != null) | ||
224 | GetStoredMaterialInFace(part, face); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | /// <summary> | ||
229 | /// Find the materials used in one Face, and add them to 'm_regionMaterials'. | ||
230 | /// </summary> | ||
231 | private void GetStoredMaterialInFace(SceneObjectPart part, Primitive.TextureEntryFace face) | ||
232 | { | ||
233 | UUID id = face.MaterialID; | ||
234 | if (id == UUID.Zero) | ||
235 | return; | ||
236 | |||
237 | lock (m_regionMaterials) | ||
238 | { | ||
239 | if (m_regionMaterials.ContainsKey(id)) | ||
240 | return; | ||
241 | |||
242 | byte[] data = m_scene.AssetService.GetData(id.ToString()); | ||
243 | if (data == null) | ||
244 | { | ||
245 | m_log.WarnFormat("[Materials]: Prim \"{0}\" ({1}) contains unknown material ID {2}", part.Name, part.UUID, id); | ||
246 | return; | ||
247 | } | ||
248 | |||
249 | OSDMap mat; | ||
250 | try | ||
251 | { | ||
252 | mat = (OSDMap)OSDParser.DeserializeLLSDXml(data); | ||
253 | } | ||
254 | catch (Exception e) | ||
255 | { | ||
256 | m_log.WarnFormat("[Materials]: cannot decode material asset {0}: {1}", id, e.Message); | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | m_regionMaterials[id] = mat; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | public string RenderMaterialsPostCap(string request, UUID agentID) | ||
265 | { | ||
266 | OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); | ||
267 | OSDMap resp = new OSDMap(); | ||
268 | |||
269 | OSDMap materialsFromViewer = null; | ||
270 | |||
271 | OSDArray respArr = new OSDArray(); | ||
272 | |||
273 | if (req.ContainsKey("Zipped")) | ||
274 | { | ||
275 | OSD osd = null; | ||
276 | |||
277 | byte[] inBytes = req["Zipped"].AsBinary(); | ||
278 | |||
279 | try | ||
280 | { | ||
281 | osd = ZDecompressBytesToOsd(inBytes); | ||
282 | |||
283 | if (osd != null) | ||
284 | { | ||
285 | if (osd is OSDArray) // assume array of MaterialIDs designating requested material entries | ||
286 | { | ||
287 | foreach (OSD elem in (OSDArray)osd) | ||
288 | { | ||
289 | try | ||
290 | { | ||
291 | UUID id = new UUID(elem.AsBinary(), 0); | ||
292 | |||
293 | lock (m_regionMaterials) | ||
294 | { | ||
295 | if (m_regionMaterials.ContainsKey(id)) | ||
296 | { | ||
297 | OSDMap matMap = new OSDMap(); | ||
298 | matMap["ID"] = OSD.FromBinary(id.GetBytes()); | ||
299 | matMap["Material"] = m_regionMaterials[id]; | ||
300 | respArr.Add(matMap); | ||
301 | } | ||
302 | else | ||
303 | { | ||
304 | m_log.Warn("[Materials]: request for unknown material ID: " + id.ToString()); | ||
305 | |||
306 | // Theoretically we could try to load the material from the assets service, | ||
307 | // but that shouldn't be necessary because the viewer should only request | ||
308 | // materials that exist in a prim on the region, and all of these materials | ||
309 | // are already stored in m_regionMaterials. | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | catch (Exception e) | ||
314 | { | ||
315 | m_log.Error("Error getting materials in response to viewer request", e); | ||
316 | continue; | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | else if (osd is OSDMap) // request to assign a material | ||
321 | { | ||
322 | materialsFromViewer = osd as OSDMap; | ||
323 | |||
324 | if (materialsFromViewer.ContainsKey("FullMaterialsPerFace")) | ||
325 | { | ||
326 | OSD matsOsd = materialsFromViewer["FullMaterialsPerFace"]; | ||
327 | if (matsOsd is OSDArray) | ||
328 | { | ||
329 | OSDArray matsArr = matsOsd as OSDArray; | ||
330 | |||
331 | try | ||
332 | { | ||
333 | foreach (OSDMap matsMap in matsArr) | ||
334 | { | ||
335 | uint primLocalID = 0; | ||
336 | try { | ||
337 | primLocalID = matsMap["ID"].AsUInteger(); | ||
338 | } | ||
339 | catch (Exception e) { | ||
340 | m_log.Warn("[Materials]: cannot decode \"ID\" from matsMap: " + e.Message); | ||
341 | continue; | ||
342 | } | ||
343 | |||
344 | OSDMap mat = null; | ||
345 | try | ||
346 | { | ||
347 | mat = matsMap["Material"] as OSDMap; | ||
348 | } | ||
349 | catch (Exception e) | ||
350 | { | ||
351 | m_log.Warn("[Materials]: cannot decode \"Material\" from matsMap: " + e.Message); | ||
352 | continue; | ||
353 | } | ||
354 | |||
355 | SceneObjectPart sop = m_scene.GetSceneObjectPart(primLocalID); | ||
356 | if (sop == null) | ||
357 | { | ||
358 | m_log.WarnFormat("[Materials]: SOP not found for localId: {0}", primLocalID.ToString()); | ||
359 | continue; | ||
360 | } | ||
361 | |||
362 | if (!m_scene.Permissions.CanEditObject(sop.UUID, agentID)) | ||
363 | { | ||
364 | m_log.WarnFormat("User {0} can't edit object {1} {2}", agentID, sop.Name, sop.UUID); | ||
365 | continue; | ||
366 | } | ||
367 | |||
368 | Primitive.TextureEntry te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length); | ||
369 | if (te == null) | ||
370 | { | ||
371 | m_log.WarnFormat("[Materials]: Error in TextureEntry for SOP {0} {1}", sop.Name, sop.UUID); | ||
372 | continue; | ||
373 | } | ||
374 | |||
375 | |||
376 | UUID id; | ||
377 | if (mat == null) | ||
378 | { | ||
379 | // This happens then the user removes a material from a prim | ||
380 | id = UUID.Zero; | ||
381 | } | ||
382 | else | ||
383 | { | ||
384 | id = StoreMaterialAsAsset(agentID, mat, sop); | ||
385 | } | ||
386 | |||
387 | |||
388 | int face = -1; | ||
389 | |||
390 | if (matsMap.ContainsKey("Face")) | ||
391 | { | ||
392 | face = matsMap["Face"].AsInteger(); | ||
393 | Primitive.TextureEntryFace faceEntry = te.CreateFace((uint)face); | ||
394 | faceEntry.MaterialID = id; | ||
395 | } | ||
396 | else | ||
397 | { | ||
398 | if (te.DefaultTexture == null) | ||
399 | m_log.WarnFormat("[Materials]: TextureEntry.DefaultTexture is null in {0} {1}", sop.Name, sop.UUID); | ||
400 | else | ||
401 | te.DefaultTexture.MaterialID = id; | ||
402 | } | ||
403 | |||
404 | //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id); | ||
405 | |||
406 | // We can't use sop.UpdateTextureEntry(te) because it filters, so do it manually | ||
407 | sop.Shape.TextureEntry = te.GetBytes(); | ||
408 | |||
409 | if (sop.ParentGroup != null) | ||
410 | { | ||
411 | sop.TriggerScriptChangedEvent(Changed.TEXTURE); | ||
412 | sop.UpdateFlag = UpdateRequired.FULL; | ||
413 | sop.ParentGroup.HasGroupChanged = true; | ||
414 | sop.ScheduleFullUpdate(); | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | catch (Exception e) | ||
419 | { | ||
420 | m_log.Warn("[Materials]: exception processing received material ", e); | ||
421 | } | ||
422 | } | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | |||
427 | } | ||
428 | catch (Exception e) | ||
429 | { | ||
430 | m_log.Warn("[Materials]: exception decoding zipped CAP payload ", e); | ||
431 | //return ""; | ||
432 | } | ||
433 | } | ||
434 | |||
435 | |||
436 | resp["Zipped"] = ZCompressOSD(respArr, false); | ||
437 | string response = OSDParser.SerializeLLSDXmlString(resp); | ||
438 | |||
439 | //m_log.Debug("[Materials]: cap request: " + request); | ||
440 | //m_log.Debug("[Materials]: cap request (zipped portion): " + ZippedOsdBytesToString(req["Zipped"].AsBinary())); | ||
441 | //m_log.Debug("[Materials]: cap response: " + response); | ||
442 | return response; | ||
443 | } | ||
444 | |||
445 | private UUID StoreMaterialAsAsset(UUID agentID, OSDMap mat, SceneObjectPart sop) | ||
446 | { | ||
447 | UUID id; | ||
448 | // Material UUID = hash of the material's data. | ||
449 | // This makes materials deduplicate across the entire grid (but isn't otherwise required). | ||
450 | byte[] data = System.Text.Encoding.ASCII.GetBytes(OSDParser.SerializeLLSDXmlString(mat)); | ||
451 | using (var md5 = MD5.Create()) | ||
452 | id = new UUID(md5.ComputeHash(data), 0); | ||
453 | |||
454 | lock (m_regionMaterials) | ||
455 | { | ||
456 | if (!m_regionMaterials.ContainsKey(id)) | ||
457 | { | ||
458 | m_regionMaterials[id] = mat; | ||
459 | |||
460 | // This asset might exist already, but it's ok to try to store it again | ||
461 | string name = "Material " + ChooseMaterialName(mat, sop); | ||
462 | name = name.Substring(0, Math.Min(64, name.Length)).Trim(); | ||
463 | AssetBase asset = new AssetBase(id, name, (sbyte)OpenSimAssetType.Material, agentID.ToString()); | ||
464 | asset.Data = data; | ||
465 | m_scene.AssetService.Store(asset); | ||
466 | } | ||
467 | } | ||
468 | return id; | ||
469 | } | ||
470 | |||
471 | /// <summary> | ||
472 | /// Use heuristics to choose a good name for the material. | ||
473 | /// </summary> | ||
474 | private string ChooseMaterialName(OSDMap mat, SceneObjectPart sop) | ||
475 | { | ||
476 | UUID normMap = mat["NormMap"].AsUUID(); | ||
477 | if (normMap != UUID.Zero) | ||
478 | { | ||
479 | AssetBase asset = m_scene.AssetService.GetCached(normMap.ToString()); | ||
480 | if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR")) | ||
481 | return asset.Name; | ||
482 | } | ||
483 | |||
484 | UUID specMap = mat["SpecMap"].AsUUID(); | ||
485 | if (specMap != UUID.Zero) | ||
486 | { | ||
487 | AssetBase asset = m_scene.AssetService.GetCached(specMap.ToString()); | ||
488 | if ((asset != null) && (asset.Name.Length > 0) && !asset.Name.Equals("From IAR")) | ||
489 | return asset.Name; | ||
490 | } | ||
491 | |||
492 | if (sop.Name != "Primitive") | ||
493 | return sop.Name; | ||
494 | |||
495 | if ((sop.ParentGroup != null) && (sop.ParentGroup.Name != "Primitive")) | ||
496 | return sop.ParentGroup.Name; | ||
497 | |||
498 | return ""; | ||
499 | } | ||
500 | |||
501 | |||
502 | public string RenderMaterialsGetCap(string request) | ||
503 | { | ||
504 | OSDMap resp = new OSDMap(); | ||
505 | int matsCount = 0; | ||
506 | OSDArray allOsd = new OSDArray(); | ||
507 | |||
508 | lock (m_regionMaterials) | ||
509 | { | ||
510 | foreach (KeyValuePair<UUID, OSDMap> kvp in m_regionMaterials) | ||
511 | { | ||
512 | OSDMap matMap = new OSDMap(); | ||
513 | |||
514 | matMap["ID"] = OSD.FromBinary(kvp.Key.GetBytes()); | ||
515 | matMap["Material"] = kvp.Value; | ||
516 | allOsd.Add(matMap); | ||
517 | matsCount++; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | resp["Zipped"] = ZCompressOSD(allOsd, false); | ||
522 | |||
523 | return OSDParser.SerializeLLSDXmlString(resp); | ||
524 | } | ||
525 | |||
526 | private static string ZippedOsdBytesToString(byte[] bytes) | ||
527 | { | ||
528 | try | ||
529 | { | ||
530 | return OSDParser.SerializeJsonString(ZDecompressBytesToOsd(bytes)); | ||
531 | } | ||
532 | catch (Exception e) | ||
533 | { | ||
534 | return "ZippedOsdBytesToString caught an exception: " + e.ToString(); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | /// <summary> | ||
539 | /// computes a UUID by hashing a OSD object | ||
540 | /// </summary> | ||
541 | /// <param name="osd"></param> | ||
542 | /// <returns></returns> | ||
543 | private static UUID HashOsd(OSD osd) | ||
544 | { | ||
545 | byte[] data = OSDParser.SerializeLLSDBinary(osd, false); | ||
546 | using (var md5 = MD5.Create()) | ||
547 | return new UUID(md5.ComputeHash(data), 0); | ||
548 | } | ||
549 | |||
550 | public static OSD ZCompressOSD(OSD inOsd, bool useHeader) | ||
551 | { | ||
552 | OSD osd = null; | ||
553 | |||
554 | byte[] data = OSDParser.SerializeLLSDBinary(inOsd, useHeader); | ||
555 | |||
556 | using (MemoryStream msSinkCompressed = new MemoryStream()) | ||
557 | { | ||
558 | using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkCompressed, | ||
559 | Ionic.Zlib.CompressionMode.Compress, CompressionLevel.BestCompression, true)) | ||
560 | { | ||
561 | zOut.Write(data, 0, data.Length); | ||
562 | } | ||
563 | |||
564 | msSinkCompressed.Seek(0L, SeekOrigin.Begin); | ||
565 | osd = OSD.FromBinary(msSinkCompressed.ToArray()); | ||
566 | } | ||
567 | |||
568 | return osd; | ||
569 | } | ||
570 | |||
571 | |||
572 | public static OSD ZDecompressBytesToOsd(byte[] input) | ||
573 | { | ||
574 | OSD osd = null; | ||
575 | |||
576 | using (MemoryStream msSinkUnCompressed = new MemoryStream()) | ||
577 | { | ||
578 | using (Ionic.Zlib.ZlibStream zOut = new Ionic.Zlib.ZlibStream(msSinkUnCompressed, CompressionMode.Decompress, true)) | ||
579 | { | ||
580 | zOut.Write(input, 0, input.Length); | ||
581 | } | ||
582 | |||
583 | msSinkUnCompressed.Seek(0L, SeekOrigin.Begin); | ||
584 | osd = OSDParser.DeserializeLLSDBinary(msSinkUnCompressed.ToArray()); | ||
585 | } | ||
586 | |||
587 | return osd; | ||
588 | } | ||
589 | } | ||
590 | } | ||
diff --git a/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs b/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs index 70bda72..ba0b578 100644 --- a/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs | |||
@@ -30,7 +30,7 @@ using Mono.Addins; | |||
30 | // Build Number | 30 | // Build Number |
31 | // Revision | 31 | // Revision |
32 | // | 32 | // |
33 | [assembly: AssemblyVersion("0.7.6.*")] | 33 | [assembly: AssemblyVersion("0.8.0.*")] |
34 | 34 | ||
35 | 35 | ||
36 | [assembly: Addin("OpenSim.Region.OptionalModules", "0.1")] | 36 | [assembly: Addin("OpenSim.Region.OptionalModules", "0.1")] |
diff --git a/OpenSim/Region/OptionalModules/Scripting/ExtendedPhysics/ExtendedPhysics.cs b/OpenSim/Region/OptionalModules/Scripting/ExtendedPhysics/ExtendedPhysics.cs index 6009dc5..9daf9d7 100755 --- a/OpenSim/Region/OptionalModules/Scripting/ExtendedPhysics/ExtendedPhysics.cs +++ b/OpenSim/Region/OptionalModules/Scripting/ExtendedPhysics/ExtendedPhysics.cs | |||
@@ -29,12 +29,14 @@ using System.Collections.Generic; | |||
29 | using System.Linq; | 29 | using System.Linq; |
30 | using System.Reflection; | 30 | using System.Reflection; |
31 | using System.Text; | 31 | using System.Text; |
32 | using System.Threading; | ||
32 | 33 | ||
33 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Region.CoreModules; | ||
34 | using OpenSim.Region.Framework; | 36 | using OpenSim.Region.Framework; |
35 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
36 | using OpenSim.Region.Framework.Scenes; | 38 | using OpenSim.Region.Framework.Scenes; |
37 | using OpenSim.Region.CoreModules; | 39 | using OpenSim.Region.Physics.Manager; |
38 | 40 | ||
39 | using Mono.Addins; | 41 | using Mono.Addins; |
40 | using Nini.Config; | 42 | using Nini.Config; |
@@ -49,6 +51,24 @@ public class ExtendedPhysics : INonSharedRegionModule | |||
49 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
50 | private static string LogHeader = "[EXTENDED PHYSICS]"; | 52 | private static string LogHeader = "[EXTENDED PHYSICS]"; |
51 | 53 | ||
54 | // ============================================================= | ||
55 | // Since BulletSim is a plugin, this these values aren't defined easily in one place. | ||
56 | // This table must correspond to an identical table in BSScene. | ||
57 | |||
58 | // Per scene functions. See BSScene. | ||
59 | |||
60 | // Per avatar functions. See BSCharacter. | ||
61 | |||
62 | // Per prim functions. See BSPrim. | ||
63 | public const string PhysFunctGetLinksetType = "BulletSim.GetLinksetType"; | ||
64 | public const string PhysFunctSetLinksetType = "BulletSim.SetLinksetType"; | ||
65 | public const string PhysFunctChangeLinkFixed = "BulletSim.ChangeLinkFixed"; | ||
66 | public const string PhysFunctChangeLinkType = "BulletSim.ChangeLinkType"; | ||
67 | public const string PhysFunctGetLinkType = "BulletSim.GetLinkType"; | ||
68 | public const string PhysFunctChangeLinkParams = "BulletSim.ChangeLinkParams"; | ||
69 | |||
70 | // ============================================================= | ||
71 | |||
52 | private IConfig Configuration { get; set; } | 72 | private IConfig Configuration { get; set; } |
53 | private bool Enabled { get; set; } | 73 | private bool Enabled { get; set; } |
54 | private Scene BaseScene { get; set; } | 74 | private Scene BaseScene { get; set; } |
@@ -119,6 +139,7 @@ public class ExtendedPhysics : INonSharedRegionModule | |||
119 | 139 | ||
120 | // Register as LSL functions all the [ScriptInvocation] marked methods. | 140 | // Register as LSL functions all the [ScriptInvocation] marked methods. |
121 | Comms.RegisterScriptInvocations(this); | 141 | Comms.RegisterScriptInvocations(this); |
142 | Comms.RegisterConstants(this); | ||
122 | 143 | ||
123 | // When an object is modified, we might need to update its extended physics parameters | 144 | // When an object is modified, we might need to update its extended physics parameters |
124 | BaseScene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; | 145 | BaseScene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene; |
@@ -132,7 +153,6 @@ public class ExtendedPhysics : INonSharedRegionModule | |||
132 | 153 | ||
133 | private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) | 154 | private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj) |
134 | { | 155 | { |
135 | throw new NotImplementedException(); | ||
136 | } | 156 | } |
137 | 157 | ||
138 | // Event generated when some property of a prim changes. | 158 | // Event generated when some property of a prim changes. |
@@ -141,14 +161,7 @@ public class ExtendedPhysics : INonSharedRegionModule | |||
141 | } | 161 | } |
142 | 162 | ||
143 | [ScriptConstant] | 163 | [ScriptConstant] |
144 | public static int PHYS_CENTER_OF_MASS = 1 << 0; | 164 | public const int PHYS_CENTER_OF_MASS = 1 << 0; |
145 | |||
146 | [ScriptConstant] | ||
147 | public static int PHYS_LINKSET_TYPE_CONSTRAINT = 1; | ||
148 | [ScriptConstant] | ||
149 | public static int PHYS_LINKSET_TYPE_COMPOUND = 2; | ||
150 | [ScriptConstant] | ||
151 | public static int PHYS_LINKSET_TYPE_MANUAL = 3; | ||
152 | 165 | ||
153 | [ScriptInvocation] | 166 | [ScriptInvocation] |
154 | public string physGetEngineType(UUID hostID, UUID scriptID) | 167 | public string physGetEngineType(UUID hostID, UUID scriptID) |
@@ -163,9 +176,406 @@ public class ExtendedPhysics : INonSharedRegionModule | |||
163 | return ret; | 176 | return ret; |
164 | } | 177 | } |
165 | 178 | ||
179 | [ScriptConstant] | ||
180 | public const int PHYS_LINKSET_TYPE_CONSTRAINT = 0; | ||
181 | [ScriptConstant] | ||
182 | public const int PHYS_LINKSET_TYPE_COMPOUND = 1; | ||
183 | [ScriptConstant] | ||
184 | public const int PHYS_LINKSET_TYPE_MANUAL = 2; | ||
185 | |||
186 | [ScriptInvocation] | ||
187 | public int physSetLinksetType(UUID hostID, UUID scriptID, int linksetType) | ||
188 | { | ||
189 | int ret = -1; | ||
190 | |||
191 | if (!Enabled) return ret; | ||
192 | |||
193 | // The part that is requesting the change. | ||
194 | SceneObjectPart requestingPart = BaseScene.GetSceneObjectPart(hostID); | ||
195 | |||
196 | if (requestingPart != null) | ||
197 | { | ||
198 | // The change is always made to the root of a linkset. | ||
199 | SceneObjectGroup containingGroup = requestingPart.ParentGroup; | ||
200 | SceneObjectPart rootPart = containingGroup.RootPart; | ||
201 | |||
202 | if (rootPart != null) | ||
203 | { | ||
204 | PhysicsActor rootPhysActor = rootPart.PhysActor; | ||
205 | if (rootPhysActor != null) | ||
206 | { | ||
207 | if (rootPhysActor.IsPhysical) | ||
208 | { | ||
209 | // Change a physical linkset by making non-physical, waiting for one heartbeat so all | ||
210 | // the prim and linkset state is updated, changing the type and making the | ||
211 | // linkset physical again. | ||
212 | containingGroup.ScriptSetPhysicsStatus(false); | ||
213 | Thread.Sleep(150); // longer than one heartbeat tick | ||
214 | |||
215 | // A kludge for the moment. | ||
216 | // Since compound linksets move the children but don't generate position updates to the | ||
217 | // simulator, it is possible for compound linkset children to have out-of-sync simulator | ||
218 | // and physical positions. The following causes the simulator to push the real child positions | ||
219 | // down into the physics engine to get everything synced. | ||
220 | containingGroup.UpdateGroupPosition(containingGroup.AbsolutePosition); | ||
221 | containingGroup.UpdateGroupRotationR(containingGroup.GroupRotation); | ||
222 | |||
223 | object[] parms2 = { rootPhysActor, null, linksetType }; | ||
224 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctSetLinksetType, parms2)); | ||
225 | Thread.Sleep(150); // longer than one heartbeat tick | ||
226 | |||
227 | containingGroup.ScriptSetPhysicsStatus(true); | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | // Non-physical linksets don't have a physical instantiation so there is no state to | ||
232 | // worry about being updated. | ||
233 | object[] parms2 = { rootPhysActor, null, linksetType }; | ||
234 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctSetLinksetType, parms2)); | ||
235 | } | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | m_log.WarnFormat("{0} physSetLinksetType: root part does not have a physics actor. rootName={1}, hostID={2}", | ||
240 | LogHeader, rootPart.Name, hostID); | ||
241 | } | ||
242 | } | ||
243 | else | ||
244 | { | ||
245 | m_log.WarnFormat("{0} physSetLinksetType: root part does not exist. RequestingPartName={1}, hostID={2}", | ||
246 | LogHeader, requestingPart.Name, hostID); | ||
247 | } | ||
248 | } | ||
249 | else | ||
250 | { | ||
251 | m_log.WarnFormat("{0} physSetLinsetType: cannot find script object in scene. hostID={1}", LogHeader, hostID); | ||
252 | } | ||
253 | return ret; | ||
254 | } | ||
255 | |||
166 | [ScriptInvocation] | 256 | [ScriptInvocation] |
167 | public void physSetLinksetType(UUID hostID, UUID scriptID, int linksetType) | 257 | public int physGetLinksetType(UUID hostID, UUID scriptID) |
168 | { | 258 | { |
259 | int ret = -1; | ||
260 | if (!Enabled) return ret; | ||
261 | |||
262 | // The part that is requesting the change. | ||
263 | SceneObjectPart requestingPart = BaseScene.GetSceneObjectPart(hostID); | ||
264 | |||
265 | if (requestingPart != null) | ||
266 | { | ||
267 | // The type is is always on the root of a linkset. | ||
268 | SceneObjectGroup containingGroup = requestingPart.ParentGroup; | ||
269 | SceneObjectPart rootPart = containingGroup.RootPart; | ||
270 | |||
271 | if (rootPart != null) | ||
272 | { | ||
273 | PhysicsActor rootPhysActor = rootPart.PhysActor; | ||
274 | if (rootPhysActor != null) | ||
275 | { | ||
276 | object[] parms2 = { rootPhysActor, null }; | ||
277 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctGetLinksetType, parms2)); | ||
278 | } | ||
279 | else | ||
280 | { | ||
281 | m_log.WarnFormat("{0} physGetLinksetType: root part does not have a physics actor. rootName={1}, hostID={2}", | ||
282 | LogHeader, rootPart.Name, hostID); | ||
283 | } | ||
284 | } | ||
285 | else | ||
286 | { | ||
287 | m_log.WarnFormat("{0} physGetLinksetType: root part does not exist. RequestingPartName={1}, hostID={2}", | ||
288 | LogHeader, requestingPart.Name, hostID); | ||
289 | } | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | m_log.WarnFormat("{0} physGetLinsetType: cannot find script object in scene. hostID={1}", LogHeader, hostID); | ||
294 | } | ||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | [ScriptConstant] | ||
299 | public const int PHYS_LINK_TYPE_FIXED = 1234; | ||
300 | [ScriptConstant] | ||
301 | public const int PHYS_LINK_TYPE_HINGE = 4; | ||
302 | [ScriptConstant] | ||
303 | public const int PHYS_LINK_TYPE_SPRING = 9; | ||
304 | [ScriptConstant] | ||
305 | public const int PHYS_LINK_TYPE_6DOF = 6; | ||
306 | [ScriptConstant] | ||
307 | public const int PHYS_LINK_TYPE_SLIDER = 7; | ||
308 | |||
309 | // physChangeLinkType(integer linkNum, integer typeCode) | ||
310 | [ScriptInvocation] | ||
311 | public int physChangeLinkType(UUID hostID, UUID scriptID, int linkNum, int typeCode) | ||
312 | { | ||
313 | int ret = -1; | ||
314 | if (!Enabled) return ret; | ||
315 | |||
316 | PhysicsActor rootPhysActor; | ||
317 | PhysicsActor childPhysActor; | ||
318 | |||
319 | if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor)) | ||
320 | { | ||
321 | object[] parms2 = { rootPhysActor, childPhysActor, typeCode }; | ||
322 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkType, parms2)); | ||
323 | } | ||
324 | |||
325 | return ret; | ||
326 | } | ||
327 | |||
328 | // physGetLinkType(integer linkNum) | ||
329 | [ScriptInvocation] | ||
330 | public int physGetLinkType(UUID hostID, UUID scriptID, int linkNum) | ||
331 | { | ||
332 | int ret = -1; | ||
333 | if (!Enabled) return ret; | ||
334 | |||
335 | PhysicsActor rootPhysActor; | ||
336 | PhysicsActor childPhysActor; | ||
337 | |||
338 | if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor)) | ||
339 | { | ||
340 | object[] parms2 = { rootPhysActor, childPhysActor }; | ||
341 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctGetLinkType, parms2)); | ||
342 | } | ||
343 | |||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | // physChangeLinkFixed(integer linkNum) | ||
348 | // Change the link between the root and the linkNum into a fixed, static physical connection. | ||
349 | [ScriptInvocation] | ||
350 | public int physChangeLinkFixed(UUID hostID, UUID scriptID, int linkNum) | ||
351 | { | ||
352 | int ret = -1; | ||
353 | if (!Enabled) return ret; | ||
354 | |||
355 | PhysicsActor rootPhysActor; | ||
356 | PhysicsActor childPhysActor; | ||
357 | |||
358 | if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor)) | ||
359 | { | ||
360 | object[] parms2 = { rootPhysActor, childPhysActor , PHYS_LINK_TYPE_FIXED }; | ||
361 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkType, parms2)); | ||
362 | } | ||
363 | |||
364 | return ret; | ||
365 | } | ||
366 | |||
367 | // Code for specifying params. | ||
368 | // The choice if 14400 is arbitrary and only serves to catch parameter code misuse. | ||
369 | public const int PHYS_PARAM_MIN = 14401; | ||
370 | |||
371 | [ScriptConstant] | ||
372 | public const int PHYS_PARAM_FRAMEINA_LOC = 14401; | ||
373 | [ScriptConstant] | ||
374 | public const int PHYS_PARAM_FRAMEINA_ROT = 14402; | ||
375 | [ScriptConstant] | ||
376 | public const int PHYS_PARAM_FRAMEINB_LOC = 14403; | ||
377 | [ScriptConstant] | ||
378 | public const int PHYS_PARAM_FRAMEINB_ROT = 14404; | ||
379 | [ScriptConstant] | ||
380 | public const int PHYS_PARAM_LINEAR_LIMIT_LOW = 14405; | ||
381 | [ScriptConstant] | ||
382 | public const int PHYS_PARAM_LINEAR_LIMIT_HIGH = 14406; | ||
383 | [ScriptConstant] | ||
384 | public const int PHYS_PARAM_ANGULAR_LIMIT_LOW = 14407; | ||
385 | [ScriptConstant] | ||
386 | public const int PHYS_PARAM_ANGULAR_LIMIT_HIGH = 14408; | ||
387 | [ScriptConstant] | ||
388 | public const int PHYS_PARAM_USE_FRAME_OFFSET = 14409; | ||
389 | [ScriptConstant] | ||
390 | public const int PHYS_PARAM_ENABLE_TRANSMOTOR = 14410; | ||
391 | [ScriptConstant] | ||
392 | public const int PHYS_PARAM_TRANSMOTOR_MAXVEL = 14411; | ||
393 | [ScriptConstant] | ||
394 | public const int PHYS_PARAM_TRANSMOTOR_MAXFORCE = 14412; | ||
395 | [ScriptConstant] | ||
396 | public const int PHYS_PARAM_CFM = 14413; | ||
397 | [ScriptConstant] | ||
398 | public const int PHYS_PARAM_ERP = 14414; | ||
399 | [ScriptConstant] | ||
400 | public const int PHYS_PARAM_SOLVER_ITERATIONS = 14415; | ||
401 | [ScriptConstant] | ||
402 | public const int PHYS_PARAM_SPRING_AXIS_ENABLE = 14416; | ||
403 | [ScriptConstant] | ||
404 | public const int PHYS_PARAM_SPRING_DAMPING = 14417; | ||
405 | [ScriptConstant] | ||
406 | public const int PHYS_PARAM_SPRING_STIFFNESS = 14418; | ||
407 | [ScriptConstant] | ||
408 | public const int PHYS_PARAM_LINK_TYPE = 14419; | ||
409 | [ScriptConstant] | ||
410 | public const int PHYS_PARAM_USE_LINEAR_FRAMEA = 14420; | ||
411 | [ScriptConstant] | ||
412 | public const int PHYS_PARAM_SPRING_EQUILIBRIUM_POINT = 14421; | ||
413 | |||
414 | public const int PHYS_PARAM_MAX = 14421; | ||
415 | |||
416 | // Used when specifying a parameter that has settings for the three linear and three angular axis | ||
417 | [ScriptConstant] | ||
418 | public const int PHYS_AXIS_ALL = -1; | ||
419 | [ScriptConstant] | ||
420 | public const int PHYS_AXIS_LINEAR_ALL = -2; | ||
421 | [ScriptConstant] | ||
422 | public const int PHYS_AXIS_ANGULAR_ALL = -3; | ||
423 | [ScriptConstant] | ||
424 | public const int PHYS_AXIS_LINEAR_X = 0; | ||
425 | [ScriptConstant] | ||
426 | public const int PHYS_AXIS_LINEAR_Y = 1; | ||
427 | [ScriptConstant] | ||
428 | public const int PHYS_AXIS_LINEAR_Z = 2; | ||
429 | [ScriptConstant] | ||
430 | public const int PHYS_AXIS_ANGULAR_X = 3; | ||
431 | [ScriptConstant] | ||
432 | public const int PHYS_AXIS_ANGULAR_Y = 4; | ||
433 | [ScriptConstant] | ||
434 | public const int PHYS_AXIS_ANGULAR_Z = 5; | ||
435 | |||
436 | // physChangeLinkParams(integer linkNum, [ PHYS_PARAM_*, value, PHYS_PARAM_*, value, ...]) | ||
437 | [ScriptInvocation] | ||
438 | public int physChangeLinkParams(UUID hostID, UUID scriptID, int linkNum, object[] parms) | ||
439 | { | ||
440 | int ret = -1; | ||
441 | if (!Enabled) return ret; | ||
442 | |||
443 | PhysicsActor rootPhysActor; | ||
444 | PhysicsActor childPhysActor; | ||
445 | |||
446 | if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor)) | ||
447 | { | ||
448 | object[] parms2 = AddToBeginningOfArray(rootPhysActor, childPhysActor, parms); | ||
449 | ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkParams, parms2)); | ||
450 | } | ||
451 | |||
452 | return ret; | ||
453 | } | ||
454 | |||
455 | private bool GetRootPhysActor(UUID hostID, out PhysicsActor rootPhysActor) | ||
456 | { | ||
457 | SceneObjectGroup containingGroup; | ||
458 | SceneObjectPart rootPart; | ||
459 | return GetRootPhysActor(hostID, out containingGroup, out rootPart, out rootPhysActor); | ||
460 | } | ||
461 | |||
462 | private bool GetRootPhysActor(UUID hostID, out SceneObjectGroup containingGroup, out SceneObjectPart rootPart, out PhysicsActor rootPhysActor) | ||
463 | { | ||
464 | bool ret = false; | ||
465 | rootPhysActor = null; | ||
466 | containingGroup = null; | ||
467 | rootPart = null; | ||
468 | |||
469 | SceneObjectPart requestingPart; | ||
470 | |||
471 | requestingPart = BaseScene.GetSceneObjectPart(hostID); | ||
472 | if (requestingPart != null) | ||
473 | { | ||
474 | // The type is is always on the root of a linkset. | ||
475 | containingGroup = requestingPart.ParentGroup; | ||
476 | if (containingGroup != null && !containingGroup.IsDeleted) | ||
477 | { | ||
478 | rootPart = containingGroup.RootPart; | ||
479 | if (rootPart != null) | ||
480 | { | ||
481 | rootPhysActor = rootPart.PhysActor; | ||
482 | if (rootPhysActor != null) | ||
483 | { | ||
484 | ret = true; | ||
485 | } | ||
486 | else | ||
487 | { | ||
488 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: Root part does not have a physics actor. rootName={1}, hostID={2}", | ||
489 | LogHeader, rootPart.Name, hostID); | ||
490 | } | ||
491 | } | ||
492 | else | ||
493 | { | ||
494 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: Root part does not exist. RequestingPartName={1}, hostID={2}", | ||
495 | LogHeader, requestingPart.Name, hostID); | ||
496 | } | ||
497 | } | ||
498 | else | ||
499 | { | ||
500 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: Containing group missing or deleted. hostID={1}", LogHeader, hostID); | ||
501 | } | ||
502 | } | ||
503 | else | ||
504 | { | ||
505 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: cannot find script object in scene. hostID={1}", LogHeader, hostID); | ||
506 | } | ||
507 | |||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | // Find the root and child PhysActors based on the linkNum. | ||
512 | // Return 'true' if both are found and returned. | ||
513 | private bool GetRootAndChildPhysActors(UUID hostID, int linkNum, out PhysicsActor rootPhysActor, out PhysicsActor childPhysActor) | ||
514 | { | ||
515 | bool ret = false; | ||
516 | rootPhysActor = null; | ||
517 | childPhysActor = null; | ||
518 | |||
519 | SceneObjectGroup containingGroup; | ||
520 | SceneObjectPart rootPart; | ||
521 | |||
522 | if (GetRootPhysActor(hostID, out containingGroup, out rootPart, out rootPhysActor)) | ||
523 | { | ||
524 | SceneObjectPart linkPart = containingGroup.GetLinkNumPart(linkNum); | ||
525 | if (linkPart != null) | ||
526 | { | ||
527 | childPhysActor = linkPart.PhysActor; | ||
528 | if (childPhysActor != null) | ||
529 | { | ||
530 | ret = true; | ||
531 | } | ||
532 | else | ||
533 | { | ||
534 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: Link part has no physical actor. rootName={1}, hostID={2}, linknum={3}", | ||
535 | LogHeader, rootPart.Name, hostID, linkNum); | ||
536 | } | ||
537 | } | ||
538 | else | ||
539 | { | ||
540 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: Could not find linknum part. rootName={1}, hostID={2}, linknum={3}", | ||
541 | LogHeader, rootPart.Name, hostID, linkNum); | ||
542 | } | ||
543 | } | ||
544 | else | ||
545 | { | ||
546 | m_log.WarnFormat("{0} GetRootAndChildPhysActors: Root part does not have a physics actor. rootName={1}, hostID={2}", | ||
547 | LogHeader, rootPart.Name, hostID); | ||
548 | } | ||
549 | |||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | // Return an array of objects with the passed object as the first object of a new array | ||
554 | private object[] AddToBeginningOfArray(object firstOne, object secondOne, object[] prevArray) | ||
555 | { | ||
556 | object[] newArray = new object[2 + prevArray.Length]; | ||
557 | newArray[0] = firstOne; | ||
558 | newArray[1] = secondOne; | ||
559 | prevArray.CopyTo(newArray, 2); | ||
560 | return newArray; | ||
561 | } | ||
562 | |||
563 | // Extension() returns an object. Convert that object into the integer error we expect to return. | ||
564 | private int MakeIntError(object extensionRet) | ||
565 | { | ||
566 | int ret = -1; | ||
567 | if (extensionRet != null) | ||
568 | { | ||
569 | try | ||
570 | { | ||
571 | ret = (int)extensionRet; | ||
572 | } | ||
573 | catch | ||
574 | { | ||
575 | ret = -1; | ||
576 | } | ||
577 | } | ||
578 | return ret; | ||
169 | } | 579 | } |
170 | } | 580 | } |
171 | } | 581 | } |
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreCommands.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreCommands.cs new file mode 100644 index 0000000..d4b19dd --- /dev/null +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreCommands.cs | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors | ||
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 OpenSim 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 Mono.Addins; | ||
28 | |||
29 | using System; | ||
30 | using System.Reflection; | ||
31 | using System.Threading; | ||
32 | using System.Text; | ||
33 | using System.Net; | ||
34 | using System.Net.Sockets; | ||
35 | using log4net; | ||
36 | using Nini.Config; | ||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.StructuredData; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.Framework.Interfaces; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | using System.Collections.Generic; | ||
43 | using System.Text.RegularExpressions; | ||
44 | |||
45 | namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | ||
46 | { | ||
47 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "JsonStoreCommandsModule")] | ||
48 | |||
49 | public class JsonStoreCommandsModule : INonSharedRegionModule | ||
50 | { | ||
51 | private static readonly ILog m_log = | ||
52 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | private IConfig m_config = null; | ||
55 | private bool m_enabled = false; | ||
56 | |||
57 | private Scene m_scene = null; | ||
58 | //private IJsonStoreModule m_store; | ||
59 | private JsonStoreModule m_store; | ||
60 | |||
61 | #region Region Module interface | ||
62 | |||
63 | // ----------------------------------------------------------------- | ||
64 | /// <summary> | ||
65 | /// Name of this shared module is it's class name | ||
66 | /// </summary> | ||
67 | // ----------------------------------------------------------------- | ||
68 | public string Name | ||
69 | { | ||
70 | get { return this.GetType().Name; } | ||
71 | } | ||
72 | |||
73 | // ----------------------------------------------------------------- | ||
74 | /// <summary> | ||
75 | /// Initialise this shared module | ||
76 | /// </summary> | ||
77 | /// <param name="scene">this region is getting initialised</param> | ||
78 | /// <param name="source">nini config, we are not using this</param> | ||
79 | // ----------------------------------------------------------------- | ||
80 | public void Initialise(IConfigSource config) | ||
81 | { | ||
82 | try | ||
83 | { | ||
84 | if ((m_config = config.Configs["JsonStore"]) == null) | ||
85 | { | ||
86 | // There is no configuration, the module is disabled | ||
87 | // m_log.InfoFormat("[JsonStore] no configuration info"); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | m_enabled = m_config.GetBoolean("Enabled", m_enabled); | ||
92 | } | ||
93 | catch (Exception e) | ||
94 | { | ||
95 | m_log.Error("[JsonStore]: initialization error: {0}", e); | ||
96 | return; | ||
97 | } | ||
98 | |||
99 | if (m_enabled) | ||
100 | m_log.DebugFormat("[JsonStore]: module is enabled"); | ||
101 | } | ||
102 | |||
103 | // ----------------------------------------------------------------- | ||
104 | /// <summary> | ||
105 | /// everything is loaded, perform post load configuration | ||
106 | /// </summary> | ||
107 | // ----------------------------------------------------------------- | ||
108 | public void PostInitialise() | ||
109 | { | ||
110 | } | ||
111 | |||
112 | // ----------------------------------------------------------------- | ||
113 | /// <summary> | ||
114 | /// Nothing to do on close | ||
115 | /// </summary> | ||
116 | // ----------------------------------------------------------------- | ||
117 | public void Close() | ||
118 | { | ||
119 | } | ||
120 | |||
121 | // ----------------------------------------------------------------- | ||
122 | /// <summary> | ||
123 | /// </summary> | ||
124 | // ----------------------------------------------------------------- | ||
125 | public void AddRegion(Scene scene) | ||
126 | { | ||
127 | if (m_enabled) | ||
128 | { | ||
129 | m_scene = scene; | ||
130 | |||
131 | } | ||
132 | } | ||
133 | |||
134 | // ----------------------------------------------------------------- | ||
135 | /// <summary> | ||
136 | /// </summary> | ||
137 | // ----------------------------------------------------------------- | ||
138 | public void RemoveRegion(Scene scene) | ||
139 | { | ||
140 | // need to remove all references to the scene in the subscription | ||
141 | // list to enable full garbage collection of the scene object | ||
142 | } | ||
143 | |||
144 | // ----------------------------------------------------------------- | ||
145 | /// <summary> | ||
146 | /// Called when all modules have been added for a region. This is | ||
147 | /// where we hook up events | ||
148 | /// </summary> | ||
149 | // ----------------------------------------------------------------- | ||
150 | public void RegionLoaded(Scene scene) | ||
151 | { | ||
152 | if (m_enabled) | ||
153 | { | ||
154 | m_scene = scene; | ||
155 | |||
156 | m_store = (JsonStoreModule) m_scene.RequestModuleInterface<IJsonStoreModule>(); | ||
157 | if (m_store == null) | ||
158 | { | ||
159 | m_log.ErrorFormat("[JsonStoreCommands]: JsonModule interface not defined"); | ||
160 | m_enabled = false; | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | scene.AddCommand("JsonStore", this, "jsonstore stats", "jsonstore stats", | ||
165 | "Display statistics about the state of the JsonStore module", "", | ||
166 | CmdStats); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /// ----------------------------------------------------------------- | ||
171 | /// <summary> | ||
172 | /// </summary> | ||
173 | // ----------------------------------------------------------------- | ||
174 | public Type ReplaceableInterface | ||
175 | { | ||
176 | get { return null; } | ||
177 | } | ||
178 | |||
179 | #endregion | ||
180 | |||
181 | #region Commands | ||
182 | |||
183 | private void CmdStats(string module, string[] cmd) | ||
184 | { | ||
185 | if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null) | ||
186 | return; | ||
187 | |||
188 | JsonStoreStats stats = m_store.GetStoreStats(); | ||
189 | MainConsole.Instance.OutputFormat("{0}\t{1}",m_scene.RegionInfo.RegionName,stats.StoreCount); | ||
190 | } | ||
191 | |||
192 | #endregion | ||
193 | |||
194 | } | ||
195 | } | ||
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs index 5fbfcc5..b502a55 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs | |||
@@ -42,7 +42,6 @@ using OpenSim.Region.Framework.Scenes; | |||
42 | using System.Collections.Generic; | 42 | using System.Collections.Generic; |
43 | using System.Text.RegularExpressions; | 43 | using System.Text.RegularExpressions; |
44 | 44 | ||
45 | |||
46 | namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | 45 | namespace OpenSim.Region.OptionalModules.Scripting.JsonStore |
47 | { | 46 | { |
48 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "JsonStoreModule")] | 47 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "JsonStoreModule")] |
@@ -60,6 +59,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
60 | private Scene m_scene = null; | 59 | private Scene m_scene = null; |
61 | 60 | ||
62 | private Dictionary<UUID,JsonStore> m_JsonValueStore; | 61 | private Dictionary<UUID,JsonStore> m_JsonValueStore; |
62 | |||
63 | private UUID m_sharedStore; | 63 | private UUID m_sharedStore; |
64 | 64 | ||
65 | #region Region Module interface | 65 | #region Region Module interface |
@@ -140,6 +140,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
140 | m_sharedStore = UUID.Zero; | 140 | m_sharedStore = UUID.Zero; |
141 | m_JsonValueStore = new Dictionary<UUID,JsonStore>(); | 141 | m_JsonValueStore = new Dictionary<UUID,JsonStore>(); |
142 | m_JsonValueStore.Add(m_sharedStore,new JsonStore("")); | 142 | m_JsonValueStore.Add(m_sharedStore,new JsonStore("")); |
143 | |||
144 | scene.EventManager.OnObjectBeingRemovedFromScene += EventManagerOnObjectBeingRemovedFromScene; | ||
143 | } | 145 | } |
144 | } | 146 | } |
145 | 147 | ||
@@ -149,6 +151,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
149 | // ----------------------------------------------------------------- | 151 | // ----------------------------------------------------------------- |
150 | public void RemoveRegion(Scene scene) | 152 | public void RemoveRegion(Scene scene) |
151 | { | 153 | { |
154 | scene.EventManager.OnObjectBeingRemovedFromScene -= EventManagerOnObjectBeingRemovedFromScene; | ||
155 | |||
152 | // need to remove all references to the scene in the subscription | 156 | // need to remove all references to the scene in the subscription |
153 | // list to enable full garbage collection of the scene object | 157 | // list to enable full garbage collection of the scene object |
154 | } | 158 | } |
@@ -161,7 +165,9 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
161 | // ----------------------------------------------------------------- | 165 | // ----------------------------------------------------------------- |
162 | public void RegionLoaded(Scene scene) | 166 | public void RegionLoaded(Scene scene) |
163 | { | 167 | { |
164 | if (m_enabled) {} | 168 | if (m_enabled) |
169 | { | ||
170 | } | ||
165 | } | 171 | } |
166 | 172 | ||
167 | /// ----------------------------------------------------------------- | 173 | /// ----------------------------------------------------------------- |
@@ -175,8 +181,39 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
175 | 181 | ||
176 | #endregion | 182 | #endregion |
177 | 183 | ||
184 | #region SceneEvents | ||
185 | // ----------------------------------------------------------------- | ||
186 | /// <summary> | ||
187 | /// | ||
188 | /// </summary> | ||
189 | // ----------------------------------------------------------------- | ||
190 | public void EventManagerOnObjectBeingRemovedFromScene(SceneObjectGroup obj) | ||
191 | { | ||
192 | obj.ForEachPart(delegate(SceneObjectPart sop) { DestroyStore(sop.UUID); } ); | ||
193 | } | ||
194 | |||
195 | #endregion | ||
196 | |||
178 | #region ScriptInvocationInteface | 197 | #region ScriptInvocationInteface |
179 | 198 | ||
199 | |||
200 | // ----------------------------------------------------------------- | ||
201 | /// <summary> | ||
202 | /// | ||
203 | /// </summary> | ||
204 | // ----------------------------------------------------------------- | ||
205 | public JsonStoreStats GetStoreStats() | ||
206 | { | ||
207 | JsonStoreStats stats; | ||
208 | |||
209 | lock (m_JsonValueStore) | ||
210 | { | ||
211 | stats.StoreCount = m_JsonValueStore.Count; | ||
212 | } | ||
213 | |||
214 | return stats; | ||
215 | } | ||
216 | |||
180 | // ----------------------------------------------------------------- | 217 | // ----------------------------------------------------------------- |
181 | /// <summary> | 218 | /// <summary> |
182 | /// | 219 | /// |
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs index 1bb5aee..9fbfb66 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs | |||
@@ -59,7 +59,9 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
59 | 59 | ||
60 | private IScriptModuleComms m_comms; | 60 | private IScriptModuleComms m_comms; |
61 | private IJsonStoreModule m_store; | 61 | private IJsonStoreModule m_store; |
62 | 62 | ||
63 | private Dictionary<UUID,HashSet<UUID>> m_scriptStores = new Dictionary<UUID,HashSet<UUID>>(); | ||
64 | |||
63 | #region Region Module interface | 65 | #region Region Module interface |
64 | 66 | ||
65 | // ----------------------------------------------------------------- | 67 | // ----------------------------------------------------------------- |
@@ -126,6 +128,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
126 | // ----------------------------------------------------------------- | 128 | // ----------------------------------------------------------------- |
127 | public void AddRegion(Scene scene) | 129 | public void AddRegion(Scene scene) |
128 | { | 130 | { |
131 | scene.EventManager.OnScriptReset += HandleScriptReset; | ||
132 | scene.EventManager.OnRemoveScript += HandleScriptReset; | ||
129 | } | 133 | } |
130 | 134 | ||
131 | // ----------------------------------------------------------------- | 135 | // ----------------------------------------------------------------- |
@@ -134,12 +138,34 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
134 | // ----------------------------------------------------------------- | 138 | // ----------------------------------------------------------------- |
135 | public void RemoveRegion(Scene scene) | 139 | public void RemoveRegion(Scene scene) |
136 | { | 140 | { |
141 | scene.EventManager.OnScriptReset -= HandleScriptReset; | ||
142 | scene.EventManager.OnRemoveScript -= HandleScriptReset; | ||
143 | |||
137 | // need to remove all references to the scene in the subscription | 144 | // need to remove all references to the scene in the subscription |
138 | // list to enable full garbage collection of the scene object | 145 | // list to enable full garbage collection of the scene object |
139 | } | 146 | } |
140 | 147 | ||
141 | // ----------------------------------------------------------------- | 148 | // ----------------------------------------------------------------- |
142 | /// <summary> | 149 | /// <summary> |
150 | /// </summary> | ||
151 | // ----------------------------------------------------------------- | ||
152 | private void HandleScriptReset(uint localID, UUID itemID) | ||
153 | { | ||
154 | HashSet<UUID> stores; | ||
155 | |||
156 | lock (m_scriptStores) | ||
157 | { | ||
158 | if (! m_scriptStores.TryGetValue(itemID, out stores)) | ||
159 | return; | ||
160 | m_scriptStores.Remove(itemID); | ||
161 | } | ||
162 | |||
163 | foreach (UUID id in stores) | ||
164 | m_store.DestroyStore(id); | ||
165 | } | ||
166 | |||
167 | // ----------------------------------------------------------------- | ||
168 | /// <summary> | ||
143 | /// Called when all modules have been added for a region. This is | 169 | /// Called when all modules have been added for a region. This is |
144 | /// where we hook up events | 170 | /// where we hook up events |
145 | /// </summary> | 171 | /// </summary> |
@@ -250,6 +276,13 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
250 | if (! m_store.CreateStore(value, ref uuid)) | 276 | if (! m_store.CreateStore(value, ref uuid)) |
251 | GenerateRuntimeError("Failed to create Json store"); | 277 | GenerateRuntimeError("Failed to create Json store"); |
252 | 278 | ||
279 | lock (m_scriptStores) | ||
280 | { | ||
281 | if (! m_scriptStores.ContainsKey(scriptID)) | ||
282 | m_scriptStores[scriptID] = new HashSet<UUID>(); | ||
283 | |||
284 | m_scriptStores[scriptID].Add(uuid); | ||
285 | } | ||
253 | return uuid; | 286 | return uuid; |
254 | } | 287 | } |
255 | 288 | ||
@@ -261,6 +294,12 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
261 | [ScriptInvocation] | 294 | [ScriptInvocation] |
262 | public int JsonDestroyStore(UUID hostID, UUID scriptID, UUID storeID) | 295 | public int JsonDestroyStore(UUID hostID, UUID scriptID, UUID storeID) |
263 | { | 296 | { |
297 | lock(m_scriptStores) | ||
298 | { | ||
299 | if (m_scriptStores.ContainsKey(scriptID)) | ||
300 | m_scriptStores[scriptID].Remove(storeID); | ||
301 | } | ||
302 | |||
264 | return m_store.DestroyStore(storeID) ? 1 : 0; | 303 | return m_store.DestroyStore(storeID) ? 1 : 0; |
265 | } | 304 | } |
266 | 305 | ||
diff --git a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs index c550c44..eb386fe 100644 --- a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs | |||
@@ -105,7 +105,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady | |||
105 | m_scene.LoginLock = true; | 105 | m_scene.LoginLock = true; |
106 | m_scene.EventManager.OnEmptyScriptCompileQueue += OnEmptyScriptCompileQueue; | 106 | m_scene.EventManager.OnEmptyScriptCompileQueue += OnEmptyScriptCompileQueue; |
107 | 107 | ||
108 | m_log.InfoFormat("[RegionReady]: Region {0} - LOGINS DISABLED DURING INITIALIZATION.", m_scene.Name); | 108 | // Warn level because the region cannot be used while logins are disabled |
109 | m_log.WarnFormat("[RegionReady]: Region {0} - LOGINS DISABLED DURING INITIALIZATION.", m_scene.Name); | ||
109 | 110 | ||
110 | if (m_uri != string.Empty) | 111 | if (m_uri != string.Empty) |
111 | { | 112 | { |
@@ -215,8 +216,11 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady | |||
215 | // m_log.InfoFormat("[RegionReady]: Logins enabled for {0}, Oar {1}", | 216 | // m_log.InfoFormat("[RegionReady]: Logins enabled for {0}, Oar {1}", |
216 | // m_scene.RegionInfo.RegionName, m_oarFileLoading.ToString()); | 217 | // m_scene.RegionInfo.RegionName, m_oarFileLoading.ToString()); |
217 | 218 | ||
218 | m_log.InfoFormat( | 219 | // Putting this out to console to make it eye-catching for people who are running OpenSimulator |
219 | "[RegionReady]: INITIALIZATION COMPLETE FOR {0} - LOGINS ENABLED", m_scene.Name); | 220 | // without info log messages enabled. Making this a warning is arguably misleading since it isn't a |
221 | // warning, and monitor scripts looking for warn/error/fatal messages will received false positives. | ||
222 | // Arguably, log4net needs a status log level (like Apache). | ||
223 | MainConsole.Instance.OutputFormat("INITIALIZATION COMPLETE FOR {0} - LOGINS ENABLED", m_scene.Name); | ||
220 | } | 224 | } |
221 | 225 | ||
222 | m_scene.SceneGridService.InformNeighborsThatRegionisUp( | 226 | m_scene.SceneGridService.InformNeighborsThatRegionisUp( |
diff --git a/OpenSim/Region/OptionalModules/ViewerSupport/DynamicMenuModule.cs b/OpenSim/Region/OptionalModules/ViewerSupport/DynamicMenuModule.cs index c68fe99..3b3b300 100644 --- a/OpenSim/Region/OptionalModules/ViewerSupport/DynamicMenuModule.cs +++ b/OpenSim/Region/OptionalModules/ViewerSupport/DynamicMenuModule.cs | |||
@@ -263,7 +263,7 @@ namespace OpenSim.Region.OptionalModules.ViewerSupport | |||
263 | m_module = module; | 263 | m_module = module; |
264 | } | 264 | } |
265 | 265 | ||
266 | public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | 266 | protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
267 | { | 267 | { |
268 | StreamReader reader = new StreamReader(request); | 268 | StreamReader reader = new StreamReader(request); |
269 | string requestBody = reader.ReadToEnd(); | 269 | string requestBody = reader.ReadToEnd(); |
diff --git a/OpenSim/Region/OptionalModules/World/MoneyModule/SampleMoneyModule.cs b/OpenSim/Region/OptionalModules/World/MoneyModule/SampleMoneyModule.cs index 5d10e93..bcb21d0 100644 --- a/OpenSim/Region/OptionalModules/World/MoneyModule/SampleMoneyModule.cs +++ b/OpenSim/Region/OptionalModules/World/MoneyModule/SampleMoneyModule.cs | |||
@@ -696,7 +696,7 @@ namespace OpenSim.Region.OptionalModules.World.MoneyModule | |||
696 | /// <param name="agentId"></param> | 696 | /// <param name="agentId"></param> |
697 | public void EconomyDataRequestHandler(IClientAPI user) | 697 | public void EconomyDataRequestHandler(IClientAPI user) |
698 | { | 698 | { |
699 | Scene s = LocateSceneClientIn(user.AgentId); | 699 | Scene s = (Scene)user.Scene; |
700 | 700 | ||
701 | user.SendEconomyData(EnergyEfficiency, s.RegionInfo.ObjectCapacity, ObjectCount, PriceEnergyUnit, PriceGroupCreate, | 701 | user.SendEconomyData(EnergyEfficiency, s.RegionInfo.ObjectCapacity, ObjectCount, PriceEnergyUnit, PriceGroupCreate, |
702 | PriceObjectClaim, PriceObjectRent, PriceObjectScaleFactor, PriceParcelClaim, PriceParcelClaimFactor, | 702 | PriceObjectClaim, PriceObjectRent, PriceObjectScaleFactor, PriceParcelClaim, PriceParcelClaimFactor, |
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 4674489..fa1d38a 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs | |||
@@ -44,6 +44,20 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
44 | { | 44 | { |
45 | public bool SenseAsAgent { get; set; } | 45 | public bool SenseAsAgent { get; set; } |
46 | 46 | ||
47 | public delegate void ChatToNPC( | ||
48 | string message, byte type, Vector3 fromPos, string fromName, | ||
49 | UUID fromAgentID, UUID ownerID, byte source, byte audible); | ||
50 | |||
51 | /// <summary> | ||
52 | /// Fired when the NPC receives a chat message. | ||
53 | /// </summary> | ||
54 | public event ChatToNPC OnChatToNPC; | ||
55 | |||
56 | /// <summary> | ||
57 | /// Fired when the NPC receives an instant message. | ||
58 | /// </summary> | ||
59 | public event Action<GridInstantMessage> OnInstantMessageToNPC; | ||
60 | |||
47 | private readonly string m_firstname; | 61 | private readonly string m_firstname; |
48 | private readonly string m_lastname; | 62 | private readonly string m_lastname; |
49 | private readonly Vector3 m_startPos; | 63 | private readonly Vector3 m_startPos; |
@@ -67,6 +81,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
67 | get { return m_scene; } | 81 | get { return m_scene; } |
68 | } | 82 | } |
69 | 83 | ||
84 | public int PingTimeMS { get { return 0; } } | ||
85 | |||
70 | public UUID OwnerID | 86 | public UUID OwnerID |
71 | { | 87 | { |
72 | get { return m_ownerID; } | 88 | get { return m_ownerID; } |
@@ -259,6 +275,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
259 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; | 275 | public event Action<IClientAPI, bool> OnCompleteMovementToRegion; |
260 | public event UpdateAgent OnPreAgentUpdate; | 276 | public event UpdateAgent OnPreAgentUpdate; |
261 | public event UpdateAgent OnAgentUpdate; | 277 | public event UpdateAgent OnAgentUpdate; |
278 | public event UpdateAgent OnAgentCameraUpdate; | ||
262 | public event AgentRequestSit OnAgentRequestSit; | 279 | public event AgentRequestSit OnAgentRequestSit; |
263 | public event AgentSit OnAgentSit; | 280 | public event AgentSit OnAgentSit; |
264 | public event AvatarPickerRequest OnAvatarPickerRequest; | 281 | public event AvatarPickerRequest OnAvatarPickerRequest; |
@@ -393,6 +410,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
393 | public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; | 410 | public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; |
394 | public event EstateChangeInfo OnEstateChangeInfo; | 411 | public event EstateChangeInfo OnEstateChangeInfo; |
395 | public event EstateManageTelehub OnEstateManageTelehub; | 412 | public event EstateManageTelehub OnEstateManageTelehub; |
413 | public event CachedTextureRequest OnCachedTextureRequest; | ||
396 | public event ScriptReset OnScriptReset; | 414 | public event ScriptReset OnScriptReset; |
397 | public event GetScriptRunning OnGetScriptRunning; | 415 | public event GetScriptRunning OnGetScriptRunning; |
398 | public event SetScriptRunning OnSetScriptRunning; | 416 | public event SetScriptRunning OnSetScriptRunning; |
@@ -573,6 +591,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
573 | { | 591 | { |
574 | } | 592 | } |
575 | 593 | ||
594 | public void SendCachedTextureResponse(ISceneEntity avatar, int serial, List<CachedTextureResponseArg> cachedTextures) | ||
595 | { | ||
596 | |||
597 | } | ||
598 | |||
576 | public virtual void Kick(string message) | 599 | public virtual void Kick(string message) |
577 | { | 600 | { |
578 | } | 601 | } |
@@ -590,7 +613,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
590 | 613 | ||
591 | } | 614 | } |
592 | 615 | ||
593 | public virtual void SendKillObject(ulong regionHandle, List<uint> localID) | 616 | public virtual void SendKillObject(List<uint> localID) |
594 | { | 617 | { |
595 | } | 618 | } |
596 | 619 | ||
@@ -617,17 +640,18 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
617 | string message, byte type, Vector3 fromPos, string fromName, | 640 | string message, byte type, Vector3 fromPos, string fromName, |
618 | UUID fromAgentID, UUID ownerID, byte source, byte audible) | 641 | UUID fromAgentID, UUID ownerID, byte source, byte audible) |
619 | { | 642 | { |
620 | } | 643 | ChatToNPC ctn = OnChatToNPC; |
621 | 644 | ||
622 | public virtual void SendChatMessage( | 645 | if (ctn != null) |
623 | byte[] message, byte type, Vector3 fromPos, string fromName, | 646 | ctn(message, type, fromPos, fromName, fromAgentID, ownerID, source, audible); |
624 | UUID fromAgentID, UUID ownerID, byte source, byte audible) | ||
625 | { | ||
626 | } | 647 | } |
627 | 648 | ||
628 | public void SendInstantMessage(GridInstantMessage im) | 649 | public void SendInstantMessage(GridInstantMessage im) |
629 | { | 650 | { |
630 | 651 | Action<GridInstantMessage> oimtn = OnInstantMessageToNPC; | |
652 | |||
653 | if (oimtn != null) | ||
654 | oimtn(im); | ||
631 | } | 655 | } |
632 | 656 | ||
633 | public void SendGenericMessage(string method, UUID invoice, List<string> message) | 657 | public void SendGenericMessage(string method, UUID invoice, List<string> message) |
@@ -1236,7 +1260,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
1236 | { | 1260 | { |
1237 | } | 1261 | } |
1238 | 1262 | ||
1239 | public void StopFlying(ISceneEntity presence) | 1263 | public void SendAgentTerseUpdate(ISceneEntity presence) |
1240 | { | 1264 | { |
1241 | } | 1265 | } |
1242 | 1266 | ||
@@ -1248,5 +1272,9 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
1248 | { | 1272 | { |
1249 | } | 1273 | } |
1250 | 1274 | ||
1275 | public void SendPartFullUpdate(ISceneEntity ent, uint? parentID) | ||
1276 | { | ||
1277 | } | ||
1278 | |||
1251 | } | 1279 | } |
1252 | } | 1280 | } |
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index 8c9c006..226608a 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs | |||
@@ -116,7 +116,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
116 | return false; | 116 | return false; |
117 | 117 | ||
118 | // Delete existing npc attachments | 118 | // Delete existing npc attachments |
119 | scene.AttachmentsModule.DeleteAttachmentsFromScene(npc, false); | 119 | if(scene.AttachmentsModule != null) |
120 | scene.AttachmentsModule.DeleteAttachmentsFromScene(npc, false); | ||
120 | 121 | ||
121 | // XXX: We can't just use IAvatarFactoryModule.SetAppearance() yet | 122 | // XXX: We can't just use IAvatarFactoryModule.SetAppearance() yet |
122 | // since it doesn't transfer attachments | 123 | // since it doesn't transfer attachments |
@@ -125,7 +126,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
125 | npc.Appearance = npcAppearance; | 126 | npc.Appearance = npcAppearance; |
126 | 127 | ||
127 | // Rez needed npc attachments | 128 | // Rez needed npc attachments |
128 | scene.AttachmentsModule.RezAttachments(npc); | 129 | if (scene.AttachmentsModule != null) |
130 | scene.AttachmentsModule.RezAttachments(npc); | ||
129 | 131 | ||
130 | IAvatarFactoryModule module = | 132 | IAvatarFactoryModule module = |
131 | scene.RequestModuleInterface<IAvatarFactoryModule>(); | 133 | scene.RequestModuleInterface<IAvatarFactoryModule>(); |
@@ -168,26 +170,27 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
168 | } | 170 | } |
169 | */ | 171 | */ |
170 | 172 | ||
171 | ManualResetEvent ev = new ManualResetEvent(false); | 173 | // ManualResetEvent ev = new ManualResetEvent(false); |
172 | 174 | ||
173 | Util.FireAndForget(delegate(object x) { | 175 | // Util.FireAndForget(delegate(object x) { |
174 | lock (m_avatars) | 176 | lock (m_avatars) |
175 | { | 177 | { |
176 | scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd); | 178 | scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd); |
177 | scene.AddNewClient(npcAvatar, PresenceType.Npc); | 179 | scene.AddNewAgent(npcAvatar, PresenceType.Npc); |
178 | 180 | ||
179 | ScenePresence sp; | 181 | ScenePresence sp; |
180 | if (scene.TryGetScenePresence(npcAvatar.AgentId, out sp)) | 182 | if (scene.TryGetScenePresence(npcAvatar.AgentId, out sp)) |
181 | { | 183 | { |
184 | |||
182 | sp.CompleteMovement(npcAvatar, false); | 185 | sp.CompleteMovement(npcAvatar, false); |
183 | m_avatars.Add(npcAvatar.AgentId, npcAvatar); | 186 | m_avatars.Add(npcAvatar.AgentId, npcAvatar); |
184 | // m_log.DebugFormat("[NPC MODULE]: Created NPC {0} {1}", npcAvatar.AgentId, sp.Name); | 187 | // m_log.DebugFormat("[NPC MODULE]: Created NPC {0} {1}", npcAvatar.AgentId, sp.Name); |
185 | } | 188 | } |
186 | } | 189 | } |
187 | ev.Set(); | 190 | // ev.Set(); |
188 | }); | 191 | // }); |
189 | 192 | ||
190 | ev.WaitOne(); | 193 | // ev.WaitOne(); |
191 | 194 | ||
192 | // m_log.DebugFormat("[NPC MODULE]: Created NPC with id {0}", npcAvatar.AgentId); | 195 | // m_log.DebugFormat("[NPC MODULE]: Created NPC with id {0}", npcAvatar.AgentId); |
193 | 196 | ||
@@ -205,8 +208,9 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
205 | if (scene.TryGetScenePresence(agentID, out sp)) | 208 | if (scene.TryGetScenePresence(agentID, out sp)) |
206 | { | 209 | { |
207 | // m_log.DebugFormat( | 210 | // m_log.DebugFormat( |
208 | // "[NPC MODULE]: Moving {0} to {1} in {2}, noFly {3}, landAtTarget {4}", | 211 | // "[NPC MODULE]: Moving {0} to {1} in {2}, noFly {3}, landAtTarget {4}", |
209 | // sp.Name, pos, scene.RegionInfo.RegionName, noFly, landAtTarget); | 212 | // sp.Name, pos, scene.RegionInfo.RegionName, |
213 | // noFly, landAtTarget); | ||
210 | 214 | ||
211 | sp.MoveToTarget(pos, noFly, landAtTarget); | 215 | sp.MoveToTarget(pos, noFly, landAtTarget); |
212 | sp.SetAlwaysRun = running; | 216 | sp.SetAlwaysRun = running; |
@@ -283,9 +287,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
283 | ScenePresence sp; | 287 | ScenePresence sp; |
284 | if (scene.TryGetScenePresence(agentID, out sp)) | 288 | if (scene.TryGetScenePresence(agentID, out sp)) |
285 | { | 289 | { |
286 | sp.HandleAgentRequestSit(m_avatars[agentID], agentID, | 290 | sp.HandleAgentRequestSit(m_avatars[agentID], agentID, partID, Vector3.Zero); |
287 | partID, Vector3.Zero); | ||
288 | //sp.HandleAgentSit(m_avatars[agentID], agentID); | ||
289 | 291 | ||
290 | return true; | 292 | return true; |
291 | } | 293 | } |
@@ -375,10 +377,15 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
375 | m_log.DebugFormat("[NPC MODULE]: Found {0} {1} to remove", | 377 | m_log.DebugFormat("[NPC MODULE]: Found {0} {1} to remove", |
376 | agentID, av.Name); | 378 | agentID, av.Name); |
377 | */ | 379 | */ |
378 | scene.RemoveClient(agentID, false); | 380 | |
381 | scene.CloseAgent(agentID, false); | ||
382 | |||
379 | m_avatars.Remove(agentID); | 383 | m_avatars.Remove(agentID); |
380 | 384 | ||
381 | // m_log.DebugFormat("[NPC MODULE]: Removed NPC {0} {1}", agentID, av.Name); | 385 | /* |
386 | m_log.DebugFormat("[NPC MODULE]: Removed NPC {0} {1}", | ||
387 | agentID, av.Name); | ||
388 | */ | ||
382 | return true; | 389 | return true; |
383 | } | 390 | } |
384 | } | 391 | } |
diff --git a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs index 34362af..14eb25b 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs | |||
@@ -155,7 +155,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests | |||
155 | public void TestCreateWithAttachments() | 155 | public void TestCreateWithAttachments() |
156 | { | 156 | { |
157 | TestHelpers.InMethod(); | 157 | TestHelpers.InMethod(); |
158 | // log4net.Config.XmlConfigurator.Configure(); | 158 | // TestHelpers.EnableLogging(); |
159 | 159 | ||
160 | UUID userId = TestHelpers.ParseTail(0x1); | 160 | UUID userId = TestHelpers.ParseTail(0x1); |
161 | UserAccountHelpers.CreateUserWithInventory(m_scene, userId); | 161 | UserAccountHelpers.CreateUserWithInventory(m_scene, userId); |
@@ -321,9 +321,9 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests | |||
321 | 321 | ||
322 | Assert.That(part.SitTargetAvatar, Is.EqualTo(npcId)); | 322 | Assert.That(part.SitTargetAvatar, Is.EqualTo(npcId)); |
323 | Assert.That(npc.ParentID, Is.EqualTo(part.LocalId)); | 323 | Assert.That(npc.ParentID, Is.EqualTo(part.LocalId)); |
324 | Assert.That( | 324 | // Assert.That( |
325 | npc.AbsolutePosition, | 325 | // npc.AbsolutePosition, |
326 | Is.EqualTo(part.AbsolutePosition + part.SitTargetPosition + ScenePresence.SIT_TARGET_ADJUSTMENT)); | 326 | // Is.EqualTo(part.AbsolutePosition + part.SitTargetPosition + ScenePresence.SIT_TARGET_ADJUSTMENT)); |
327 | 327 | ||
328 | m_npcMod.Stand(npc.UUID, m_scene); | 328 | m_npcMod.Stand(npc.UUID, m_scene); |
329 | 329 | ||
@@ -335,7 +335,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests | |||
335 | public void TestSitAndStandWithNoSitTarget() | 335 | public void TestSitAndStandWithNoSitTarget() |
336 | { | 336 | { |
337 | TestHelpers.InMethod(); | 337 | TestHelpers.InMethod(); |
338 | // log4net.Config.XmlConfigurator.Configure(); | 338 | // TestHelpers.EnableLogging(); |
339 | 339 | ||
340 | ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, TestHelpers.ParseTail(0x1)); | 340 | ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, TestHelpers.ParseTail(0x1)); |
341 | 341 | ||
@@ -353,13 +353,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests | |||
353 | Assert.That(part.SitTargetAvatar, Is.EqualTo(UUID.Zero)); | 353 | Assert.That(part.SitTargetAvatar, Is.EqualTo(UUID.Zero)); |
354 | Assert.That(npc.ParentID, Is.EqualTo(part.LocalId)); | 354 | Assert.That(npc.ParentID, Is.EqualTo(part.LocalId)); |
355 | 355 | ||
356 | // FIXME: This is different for live avatars - z position is adjusted. This is half the height of the | 356 | // We should really be using the NPC size but this would mean preserving the physics actor since it is |
357 | // default avatar. | 357 | // removed on sit. |
358 | // Curiously, Vector3.ToString() will not display the last two places of the float. For example, | ||
359 | // printing out npc.AbsolutePosition will give <0, 0, 0.8454993> not <0, 0, 0.845499337> | ||
360 | Assert.That( | 358 | Assert.That( |
361 | npc.AbsolutePosition, | 359 | npc.AbsolutePosition, |
362 | Is.EqualTo(part.AbsolutePosition + new Vector3(0, 0, 0.845499337f))); | 360 | Is.EqualTo(part.AbsolutePosition + new Vector3(0, 0, sp.PhysicsActor.Size.Z / 2))); |
363 | 361 | ||
364 | m_npcMod.Stand(npc.UUID, m_scene); | 362 | m_npcMod.Stand(npc.UUID, m_scene); |
365 | 363 | ||
diff --git a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewRequestHandler.cs b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewRequestHandler.cs index 550b5d4..8720cc7 100644 --- a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewRequestHandler.cs +++ b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewRequestHandler.cs | |||
@@ -55,7 +55,7 @@ namespace OpenSim.Region.OptionalModules.World.WorldView | |||
55 | m_WorldViewModule = fmodule; | 55 | m_WorldViewModule = fmodule; |
56 | } | 56 | } |
57 | 57 | ||
58 | public override byte[] Handle(string path, Stream requestData, | 58 | protected override byte[] ProcessRequest(string path, Stream requestData, |
59 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | 59 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
60 | { | 60 | { |
61 | httpResponse.ContentType = "image/jpeg"; | 61 | httpResponse.ContentType = "image/jpeg"; |