diff --git a/libsecondlife-cs/AvatarManager.cs b/libsecondlife-cs/AvatarManager.cs index 47b31b41..2946872d 100644 --- a/libsecondlife-cs/AvatarManager.cs +++ b/libsecondlife-cs/AvatarManager.cs @@ -32,7 +32,7 @@ using libsecondlife.Packets; namespace libsecondlife { /// - /// Class to manage multiple Avatars + /// Class to manage multiple AvatarList /// public class AvatarManager { @@ -71,8 +71,9 @@ namespace libsecondlife /// Triggered whenever a friend comes online or goes offline public event FriendNotificationCallback OnFriendNotification; + public Dictionary AvatarList; + private SecondLife Client; - private Dictionary Avatars; private AgentNamesCallback OnAgentNames; private Dictionary AvatarPropertiesCallbacks; private Dictionary AvatarStatisticsCallbacks; @@ -85,7 +86,7 @@ namespace libsecondlife public AvatarManager(SecondLife client) { Client = client; - Avatars = new Dictionary(); + AvatarList = new Dictionary(); //Callback Dictionaries AvatarPropertiesCallbacks = new Dictionary(); AvatarStatisticsCallbacks = new Dictionary(); @@ -102,25 +103,25 @@ namespace libsecondlife /// - /// Add an Avatar into the Avatars Dictionary + /// Add an Avatar into the AvatarList Dictionary /// /// Filled-out Avatar class to insert public void AddAvatar(Avatar avatar) { - lock (Avatars) + lock (AvatarList) { - Avatars[avatar.ID] = avatar; + AvatarList[avatar.ID] = avatar; } } /// - /// Used to search all known Avatars for a particular Avatar Key + /// Used to search all known AvatarList for a particular Avatar Key /// /// /// public bool Contains(LLUUID id) { - return Avatars.ContainsKey(id); + return AvatarList.ContainsKey(id); } /// @@ -170,11 +171,11 @@ namespace libsecondlife { string name = ""; - lock (Avatars) + lock (AvatarList) { - if (Avatars.ContainsKey(id)) + if (AvatarList.ContainsKey(id)) { - name = Avatars[id].Name; + name = AvatarList[id].Name; } } @@ -212,9 +213,9 @@ namespace libsecondlife // Fire callbacks for the ones we already have cached foreach (LLUUID id in ids) { - if (Avatars.ContainsKey(id)) + if (AvatarList.ContainsKey(id)) { - havenames[id] = Avatars[id].Name; + havenames[id] = AvatarList[id].Name; } else { @@ -244,7 +245,7 @@ namespace libsecondlife } /// - /// Process an incoming UUIDNameReply Packet and insert Full Names into the Avatars Dictionary + /// Process an incoming UUIDNameReply Packet and insert Full Names into the AvatarList Dictionary /// /// Incoming Packet to process /// Unused @@ -253,20 +254,20 @@ namespace libsecondlife Dictionary names = new Dictionary(); UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet; - lock (Avatars) + lock (AvatarList) { foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock) { - if (!Avatars.ContainsKey(block.ID)) + if (!AvatarList.ContainsKey(block.ID)) { - Avatars[block.ID] = new Avatar(); - Avatars[block.ID].ID = block.ID; + AvatarList[block.ID] = new Avatar(); + AvatarList[block.ID].ID = block.ID; } - Avatars[block.ID].Name = Helpers.FieldToString(block.FirstName) + + AvatarList[block.ID].Name = Helpers.FieldToString(block.FirstName) + " " + Helpers.FieldToString(block.LastName); - names[block.ID] = Avatars[block.ID].Name; + names[block.ID] = AvatarList[block.ID].Name; } } @@ -289,18 +290,18 @@ namespace libsecondlife // If the agent is online... foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock) { - lock (Avatars) + lock (AvatarList) { - if (!Avatars.ContainsKey(block.AgentID)) + if (!AvatarList.ContainsKey(block.AgentID)) { // Mark this avatar for a name request requestids.Add(block.AgentID); - Avatars[block.AgentID] = new Avatar(); - Avatars[block.AgentID].ID = block.AgentID; + AvatarList[block.AgentID] = new Avatar(); + AvatarList[block.AgentID].ID = block.AgentID; } - Avatars[block.AgentID].Online = true; + AvatarList[block.AgentID].Online = true; } if (OnFriendNotification != null) @@ -314,18 +315,18 @@ namespace libsecondlife // If the agent is Offline... foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock) { - lock (Avatars) + lock (AvatarList) { - if (!Avatars.ContainsKey(block.AgentID)) + if (!AvatarList.ContainsKey(block.AgentID)) { // Mark this avatar for a name request requestids.Add(block.AgentID); - Avatars[block.AgentID] = new Avatar(); - Avatars[block.AgentID].ID = block.AgentID; + AvatarList[block.AgentID] = new Avatar(); + AvatarList[block.AgentID].ID = block.AgentID; } - Avatars[block.AgentID].Online = false; + AvatarList[block.AgentID].Online = false; } if (OnFriendNotification != null) @@ -348,17 +349,17 @@ namespace libsecondlife private void AvatarStatisticsHandler(Packet packet, Simulator simulator) { AvatarStatisticsReplyPacket asr = (AvatarStatisticsReplyPacket)packet; - lock(Avatars) + lock(AvatarList) { Avatar av; - if (!Avatars.ContainsKey(asr.AvatarData.AvatarID)) + if (!AvatarList.ContainsKey(asr.AvatarData.AvatarID)) { av = new Avatar(); av.ID = asr.AvatarData.AvatarID; } else { - av = Avatars[asr.AvatarData.AvatarID]; + av = AvatarList[asr.AvatarData.AvatarID]; } foreach(AvatarStatisticsReplyPacket.StatisticsDataBlock b in asr.StatisticsData) @@ -392,9 +393,9 @@ namespace libsecondlife { Avatar av; AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet; - lock(Avatars) + lock(AvatarList) { - if (!Avatars.ContainsKey(reply.AgentData.AvatarID)) + if (!AvatarList.ContainsKey(reply.AgentData.AvatarID)) { //not in our "cache", create a new object av = new Avatar(); @@ -402,7 +403,7 @@ namespace libsecondlife else { //Cache hit, modify existing avatar - av = Avatars[reply.AgentData.AvatarID]; + av = AvatarList[reply.AgentData.AvatarID]; } av.ID = reply.AgentData.AvatarID; av.ProfileImage = reply.PropertiesData.ImageID; @@ -419,7 +420,7 @@ namespace libsecondlife av.Transacted = reply.PropertiesData.Transacted; av.ProfileURL = Helpers.FieldToString(reply.PropertiesData.ProfileURL); //reassign in the cache - Avatars[av.ID] = av; + AvatarList[av.ID] = av; //Heaven forbid that we actually get a packet we didn't ask for. if (AvatarPropertiesCallbacks.ContainsKey(av.ID) && AvatarPropertiesCallbacks[av.ID] != null) AvatarPropertiesCallbacks[av.ID](av); @@ -458,9 +459,9 @@ namespace libsecondlife { AvatarInterestsReplyPacket airp = (AvatarInterestsReplyPacket)packet; Avatar av; - lock (Avatars) + lock (AvatarList) { - if (!Avatars.ContainsKey(airp.AgentData.AvatarID)) + if (!AvatarList.ContainsKey(airp.AgentData.AvatarID)) { //not in our "cache", create a new object av = new Avatar(); @@ -469,7 +470,7 @@ namespace libsecondlife else { //Cache hit, modify existing avatar - av = Avatars[airp.AgentData.AvatarID]; + av = AvatarList[airp.AgentData.AvatarID]; } //The rest of the properties, thanks LL. av.WantToMask = airp.PropertiesData.WantToMask; diff --git a/libsecondlife-cs/MainAvatar.cs b/libsecondlife-cs/MainAvatar.cs index e7e261cc..af2dd857 100644 --- a/libsecondlife-cs/MainAvatar.cs +++ b/libsecondlife-cs/MainAvatar.cs @@ -1122,5 +1122,4 @@ namespace libsecondlife TeleportTimeout = true; } } - -} +} \ No newline at end of file diff --git a/libsecondlife-cs/SecondLife.cs b/libsecondlife-cs/SecondLife.cs index 5ef30591..07c201f5 100644 --- a/libsecondlife-cs/SecondLife.cs +++ b/libsecondlife-cs/SecondLife.cs @@ -52,7 +52,7 @@ namespace libsecondlife public ParcelManager Parcels; /// 'Client's Avatar' Subsystem public MainAvatar Self; - /// Other Avatars Subsystem + /// Other AvatarList Subsystem public AvatarManager Avatars; /// Grid (aka simulator group) Subsystem public GridManager Grid; diff --git a/libsecondlife-cs/examples/TestClient/ClientManager.cs b/libsecondlife-cs/examples/TestClient/ClientManager.cs new file mode 100644 index 00000000..be49553f --- /dev/null +++ b/libsecondlife-cs/examples/TestClient/ClientManager.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Xml; +using libsecondlife; +using libsecondlife.Packets; +using libsecondlife.AssetSystem; + +namespace libsecondlife.TestClient +{ + public class LoginDetails + { + public string FirstName; + public string LastName; + public string Password; + public string Master; + } + + public class ClientManager + { + public Dictionary Clients = new Dictionary(); + public Dictionary> SimPrims = new Dictionary>(); + //public Dictionary Appearances = new Dictionary(); + + public Dictionary SharedValues = new Dictionary(); + public bool Running = true; + + /// + /// + /// + /// + public ClientManager(List accounts) + { + foreach (LoginDetails account in accounts) + Login(account); + } + + /// + /// + /// + /// + /// + public TestClient Login(LoginDetails account) + { + // Check if this client is already logged in + foreach (TestClient c in Clients.Values) + { + if (c.Self.FirstName == account.FirstName && c.Self.LastName == account.LastName) + { + Logout(c); + break; + } + } + + TestClient client = new TestClient(this); + + client.SimPrims = SimPrims; + client.Master = account.Master; + + bool login = client.Network.Login(account.FirstName, account.LastName, account.Password, + "TestClient", "contact@libsecondlife.org"); + + if (login) + { + // Set our appearance + //AppearanceManager appearance = new AppearanceManager(client); + //appearance.SendAgentSetAppearance(); + } + + if (client.Network.Connected) + { + Clients[client.Network.AgentID] = client; + + Console.WriteLine("Logged in " + client.ToString()); + + // Throttle the connection to not receive LayerData or asset packets + client.Throttle.Total = 0.0f; + client.Throttle.Task = 1536000.0f; + client.Throttle.Set(); + } + else + { + Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + + ": " + client.Network.LoginError); + } + + return client; + } + + /// + /// + /// + /// + /// + public TestClient Login(string[] args) + { + LoginDetails account = new LoginDetails(); + account.FirstName = args[0]; + account.LastName = args[1]; + account.Password = args[2]; + + return Login(account); + } + + /// + /// + /// + public void Run() + { + Console.WriteLine("Type quit to exit. Type help for a command list."); + + while (Running) + { + PrintPrompt(); + string input = Console.ReadLine(); + DoCommandAll(input, null, null); + } + + foreach (SecondLife client in Clients.Values) + { + if (client.Network.Connected) + client.Network.Logout(); + } + } + + private void PrintPrompt() + { + //Console.Write(String.Format("{0} {1} - {2}> ", client.Self.FirstName, client.Self.LastName, client.Network.CurrentSim.Region.Name)); + + int online = 0; + + foreach (SecondLife client in Clients.Values) + { + if (client.Network.Connected) online++; + } + + Console.Write(online + " avatars online> "); + } + + /// + /// + /// + /// + /// + /// + public void DoCommandAll(string cmd, LLUUID fromAgentID, LLUUID imSessionID) + { + string[] tokens = cmd.Trim().Split(new char[] { ' ', '\t' }); + string firstToken = tokens[0].ToLower(); + + if (tokens.Length == 0) + return; + + if (firstToken == "login") + { + // Special login case: Only call it once, and allow it with + // no logged in avatars + string[] args = new string[tokens.Length - 1]; + Array.Copy(tokens, 1, args, 0, args.Length); + Login(args); + } + else if (firstToken == "quit") + { + Quit(); + Console.WriteLine("All clients logged out and program finished running."); + } + else + { + // make a copy of the clients list so that it can be iterated without fear of being changed during iteration + Dictionary clientsCopy = new Dictionary(Clients); + + foreach (TestClient client in clientsCopy.Values) + client.DoCommand(cmd, fromAgentID, imSessionID); + } + } + + /// + /// + /// + /// + public void Logout(TestClient client) + { + Clients.Remove(client.Network.AgentID); + client.Network.Logout(); + } + + /// + /// + /// + public void LogoutAll() + { + // make a copy of the clients list so that it can be iterated without fear of being changed during iteration + Dictionary clientsCopy = new Dictionary(Clients); + + foreach (TestClient client in clientsCopy.Values) + Logout(client); + } + + /// + /// + /// + public void Quit() + { + LogoutAll(); + Running = false; + // TODO: It would be really nice if we could figure out a way to abort the ReadLine here in so that Run() will exit. + } + } +} diff --git a/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs new file mode 100644 index 00000000..48efcf64 --- /dev/null +++ b/libsecondlife-cs/examples/TestClient/Commands/EchoMasterCommand.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using libsecondlife; +using libsecondlife.Packets; + +namespace libsecondlife.TestClient +{ + public class EchoMasterCommand: Command + { + public EchoMasterCommand() + { + Name = "echoMaster"; + Description = "Repeat everything that master says."; + } + + public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) + { + if (!Active) + { + Active = true; + Client.Self.OnChat += new ChatCallback(Self_OnChat); + return "Echoing is now on."; + } + else + { + Active = false; + Client.Self.OnChat -= new ChatCallback(Self_OnChat); + return "Echoing is now off."; + } + } + + void Self_OnChat(string message, byte audible, byte type, byte sourcetype, string fromName, LLUUID id, LLUUID ownerid, LLVector3 position) + { + if (message.Length > 0 && TestClient.Master == fromName) + { + TestClient.Self.Chat(message, 0, MainAvatar.ChatType.Normal); + } + } + } +} diff --git a/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs index c5340c05..c8520694 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/ExportCommand.cs @@ -61,26 +61,34 @@ namespace libsecondlife.TestClient { try { - XmlWriter writer = XmlWriter.Create(file); - List prims = new List(); + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + XmlWriter writer = XmlWriter.Create(file, settings); + try + { + List prims = new List(); - lock (TestClient.SimPrims) - { - if (TestClient.SimPrims.ContainsKey(Client.Network.CurrentSim)) - { - foreach (PrimObject prim in TestClient.SimPrims[Client.Network.CurrentSim].Values) - { - if (prim.LocalID == localid || prim.ParentID == localid) - { - prims.Add(prim); - count++; - } - } - } - } - //Serialize it! - Helpers.PrimListToXml(prims, writer); - writer.Close(); + lock (TestClient.SimPrims) + { + if (TestClient.SimPrims.ContainsKey(Client.Network.CurrentSim)) + { + foreach (PrimObject prim in TestClient.SimPrims[Client.Network.CurrentSim].Values) + { + if (prim.LocalID == localid || prim.ParentID == localid) + { + prims.Add(prim); + count++; + } + } + } + } + //Serialize it! + Helpers.PrimListToXml(prims, writer); + } + finally + { + writer.Close(); + } } catch (Exception e) { diff --git a/libsecondlife-cs/examples/TestClient/Commands/ExportOutfitCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ExportOutfitCommand.cs index 65676487..cc99d3d6 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/ExportOutfitCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/ExportOutfitCommand.cs @@ -37,8 +37,17 @@ namespace libsecondlife.TestClient { try { - XmlWriter writer = XmlWriter.Create(args[1]); - TestClient.Appearances[id].ToXml(writer); + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + XmlWriter writer = XmlWriter.Create(args[1], settings); + try + { + TestClient.Appearances[id].ToXml(writer); + } + finally + { + writer.Close(); + } } catch (Exception e) { diff --git a/libsecondlife-cs/examples/TestClient/Commands/FollowCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/FollowCommand.cs index f6bc0b79..0ecfba0f 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/FollowCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/FollowCommand.cs @@ -43,7 +43,7 @@ namespace libsecondlife.TestClient bool Follow(string name) { - foreach (Avatar av in TestClient.Avatars.Values) + foreach (Avatar av in TestClient.Avatars.AvatarList.Values) { if (av.Name == name) { diff --git a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs index 806b90ff..8972f382 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs @@ -3,48 +3,18 @@ using System.Collections.Generic; using libsecondlife; using libsecondlife.Packets; using System.Xml; -using System.Threading; using System.Xml.Serialization; using System.IO; - namespace libsecondlife.TestClient { - public class Linkset - { - public PrimObject RootPrim; - public List Children; - - public Linkset() - { - RootPrim = new PrimObject(); - Children = new List(); - } - - public Linkset(PrimObject rootPrim) - { - RootPrim = rootPrim; - Children = new List(); - } - } - public class ImportCommand : Command { - PrimObject currentPrim; - LLVector3 currentPosition; - SecondLife currentClient; - ManualResetEvent primDone; - List primsCreated; - uint rootLocalID = 0; - bool registeredCreateEvent = false; - bool rezzingRootPrim = false; - bool linking = false; - public ImportCommand() { Name = "import"; Description = "Import prims from an exported xml file. Usage: import [filename.xml]"; - primDone = new ManualResetEvent(false); - registeredCreateEvent = false; + +// Execute(null, new string[] {"prims.xml"}, LLUUID.Zero); } public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) @@ -53,160 +23,30 @@ namespace libsecondlife.TestClient return "Usage: import inputfile.xml"; string name = args[0]; - Dictionary prims; - - currentClient = Client; + List prims; try { - XmlReader reader = XmlReader.Create(name); - List listprims = Helpers.PrimListFromXml(reader); - reader.Close(); + //XmlReader reader = XmlReader.Create(name); + //prims = Helpers.PrimListFromXml(reader); + //reader.Close(); - // Create a dictionary indexed by the old local ID of the prims - prims = new Dictionary(); - foreach (PrimObject prim in listprims) - { - prims.Add(prim.LocalID, prim); - } + XmlSerializer serializer = new XmlSerializer(typeof(List)); + XmlDeserializationEvents events = new XmlDeserializationEvents(); + events.OnUnknownAttribute += new XmlAttributeEventHandler(OnUnknownAttribute); + events.OnUnknownElement += new XmlElementEventHandler(OnUnknownElement); + events.OnUnknownNode += new XmlNodeEventHandler(OnUnknownNode); + events.OnUnreferencedObject += new UnreferencedObjectEventHandler(OnUnreferenced); + prims = (List)serializer.Deserialize(XmlReader.Create(name), events); + //return list; } catch (Exception ex) { return "Deserialize failed: " + ex.ToString(); } - if (!registeredCreateEvent) - { - TestClient.OnPrimCreated += new TestClient.PrimCreatedCallback(TestClient_OnPrimCreated); - registeredCreateEvent = true; - } - - // Build an organized structure from the imported prims - Dictionary linksets = new Dictionary(); - foreach (PrimObject prim in prims.Values) - { - if (prim.ParentID == 0) - { - if (linksets.ContainsKey(prim.LocalID)) - linksets[prim.LocalID].RootPrim = prim; - else - linksets[prim.LocalID] = new Linkset(prim); - } - else - { - if (!linksets.ContainsKey(prim.ParentID)) - linksets[prim.ParentID] = new Linkset(); - - linksets[prim.ParentID].Children.Add(prim); - } - } - - primsCreated = new List(); - linking = false; - Console.WriteLine("Importing " + linksets.Count + " structures."); - - foreach (Linkset linkset in linksets.Values) - { - if (linkset.RootPrim.LocalID != 0) - { - // HACK: Offset the root prim position so it's not lying on top of the original - // We need a more elaborate solution for importing with relative or absolute offsets - linkset.RootPrim.Position.Z += 3.0f; - currentPosition = linkset.RootPrim.Position; - - // Rez the root prim with no rotation - LLQuaternion rootRotation = linkset.RootPrim.Rotation; - linkset.RootPrim.Rotation = LLQuaternion.Identity; - - rezzingRootPrim = true; - currentPrim = linkset.RootPrim; - - Client.Objects.AddPrim(Client.Network.CurrentSim, linkset.RootPrim, linkset.RootPrim.Position); - - if (!primDone.WaitOne(10000, false)) - return "Rez failed, timed out while creating a prim."; - primDone.Reset(); - - rezzingRootPrim = false; - - // Rez the child prims - foreach (PrimObject prim in linkset.Children) - { - currentPrim = prim; - currentPosition = prim.Position + linkset.RootPrim.Position; - - Client.Objects.AddPrim(Client.Network.CurrentSim, prim, currentPosition); - - if (!primDone.WaitOne(10000, false)) - return "Rez failed, timed out while creating a prim."; - primDone.Reset(); - } - - // Create a list of the local IDs of the newly created prims - List primIDs = new List(); - foreach (PrimObject prim in primsCreated) - { - if (prim.LocalID != rootLocalID) - primIDs.Add(prim.LocalID); - } - // Make sure the root object is the last in our list so it becomes the new root - primIDs.Add(rootLocalID); - - // Link and set the permissions + rotation - linking = true; - - Client.Objects.LinkPrims(Client.Network.CurrentSim, primIDs); - - Client.Objects.SetPermissions(Client.Network.CurrentSim, primIDs, - Helpers.PermissionWho.Everyone | Helpers.PermissionWho.Group | Helpers.PermissionWho.NextOwner, - Helpers.PermissionType.Copy | Helpers.PermissionType.Modify | Helpers.PermissionType.Move | - Helpers.PermissionType.Transfer, true); - - Client.Objects.SetRotation(Client.Network.CurrentSim, rootLocalID, rootRotation); - - for (int i = 0; i < linkset.Children.Count + 1; i++) - { - primDone.WaitOne(10000, false); - primDone.Reset(); - } - - linking = false; - } - else - { - // Skip linksets with a missing root prim - Console.WriteLine("WARNING: Skipping a linkset with a missing root prim"); - } - - // Reset everything for the next linkset - primsCreated.Clear(); - } - - return "Import complete."; - } - - void TestClient_OnPrimCreated(Simulator simulator, PrimObject prim) - { - if (rezzingRootPrim) - { - rootLocalID = prim.LocalID; - } - - if (!linking) - { - Console.WriteLine("Setting properties for " + prim.LocalID); - - primsCreated.Add(prim); - - // FIXME: Replace these individual calls with a single ObjectUpdate that sets the - // particle system and everything - currentClient.Objects.SetPosition(simulator, prim.LocalID, currentPosition); - currentClient.Objects.SetTextures(simulator, prim.LocalID, currentPrim.Textures); - //currentClient.Objects.SetLight(simulator, prim.LocalID, currentPrim.Light); - //currentClient.Objects.SetFlexible(simulator, prim.LocalID, currentPrim.Flexible); - } - - primDone.Set(); + // deserialization done, just need to code to rez and link. + return "Deserialized prims, rez code missing."; } void OnUnknownAttribute(object obj, XmlAttributeEventArgs args) diff --git a/libsecondlife-cs/examples/TestClient/Commands/LoginCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/LoginCommand.cs index 532ce46c..df8328ce 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/LoginCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/LoginCommand.cs @@ -19,35 +19,15 @@ namespace libsecondlife.TestClient if (args.Length != 3) return "usage: login firstname lastname password"; - LoginDetails account = new LoginDetails(); - account.FirstName = args[0]; - account.LastName = args[1]; - account.Password = args[2]; - - // Check if this client is already logged in - foreach (SecondLife client in TestClient.Clients.Values) - { - if (client.Self.FirstName == account.FirstName && client.Self.LastName == account.LastName) - { - TestClient.Clients.Remove(client.Network.AgentID); - - client.Network.Logout(); - - break; - } - } - - SecondLife newClient = TestClient.InitializeClient(account); + SecondLife newClient = TestClient.ClientManager.Login(args); if (newClient.Network.Connected) { - TestClient.Clients[newClient.Network.AgentID] = newClient; - return "Logged in " + newClient.ToString(); } else { - return "Failed to login " + account.FirstName + " " + account.LastName + ": " + + return "Failed to login: " + newClient.Network.LoginError; } } diff --git a/libsecondlife-cs/examples/TestClient/Commands/LogoutCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/LogoutCommand.cs index 303e4b0d..c03b24be 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/LogoutCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/LogoutCommand.cs @@ -17,19 +17,8 @@ namespace libsecondlife.TestClient public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) { string name = Client.ToString(); - TestClient.Clients.Remove(Client.Network.AgentID); - Client.Network.Logout(); - Client = null; - - if (TestClient.Clients.Count > 0) - { - return "Logged " + name + " out"; - } - else - { - TestClient.Running = false; - return "All avatars logged out"; - } + TestClient.ClientManager.Logout(TestClient); + return "Logged " + name + " out"; } } } diff --git a/libsecondlife-cs/examples/TestClient/Commands/QuitCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/QuitCommand.cs index 2883676a..185c6f9b 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/QuitCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/QuitCommand.cs @@ -16,12 +16,8 @@ namespace libsecondlife.TestClient public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) { - foreach (SecondLife client in TestClient.Clients.Values) - { - client.Network.Logout(); - } - - TestClient.Running = false; + TestClient.ClientManager.LogoutAll(); + TestClient.ClientManager.Running = false; return "All avatars logged out"; } } diff --git a/libsecondlife-cs/examples/TestClient/Commands/SayCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/SayCommand.cs index d5d1f3bb..9c2f5d37 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/SayCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/SayCommand.cs @@ -11,7 +11,7 @@ namespace libsecondlife.TestClient public SayCommand() { Name = "say"; - Description = "Say something."; + Description = "Say something. (usage: say (optional channel) whatever)"; } public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) @@ -25,15 +25,8 @@ namespace libsecondlife.TestClient } else if (args.Length > 1) { - try - { - channel = Convert.ToInt32(args[0]); - startIndex = 1; - } - catch (FormatException) - { - channel = 0; - } + if (Int32.TryParse(args[0], out channel)) + startIndex = 1; } for (int i = startIndex; i < args.Length; i++) { diff --git a/libsecondlife-cs/examples/TestClient/Commands/SetMasterCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/SetMasterCommand.cs index c7e27f90..60d4e229 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/SetMasterCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/SetMasterCommand.cs @@ -23,13 +23,13 @@ namespace libsecondlife.TestClient masterName = masterName + args[ct] + " "; TestClient.Master = masterName.TrimEnd(); - foreach (Avatar av in TestClient.Avatars.Values) + foreach (Avatar av in TestClient.Avatars.AvatarList.Values) { - if (av.Name == TestClient.Master) - { - Client.Self.InstantMessage(av.ID, "You are now my master. IM me with \"help\" for a command list."); - break; - } + if (av.Name == TestClient.Master) + { + Client.Self.InstantMessage(av.ID, "You are now my master. IM me with \"help\" for a command list."); + break; + } } return "Master set to " + masterName; diff --git a/libsecondlife-cs/examples/TestClient/Commands/SitCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/SitCommand.cs index f6de5cd6..33e069be 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/SitCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/SitCommand.cs @@ -13,7 +13,7 @@ namespace libsecondlife.TestClient Name = "sit"; Description = "Attempt to sit on the closest prim"; } - + public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) { PrimObject closest = null; diff --git a/libsecondlife-cs/examples/TestClient/Commands/WhoCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/WhoCommand.cs index 05520aae..e267a724 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/WhoCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/WhoCommand.cs @@ -17,9 +17,9 @@ namespace libsecondlife.TestClient public override string Execute(SecondLife Client, string[] args, LLUUID fromAgentID) { StringBuilder result = new StringBuilder(); - foreach (Avatar av in TestClient.Avatars.Values) + foreach (Avatar av in TestClient.Avatars.AvatarList.Values) { - result.AppendFormat("\n{0} {1}", av.Name, av.GroupName); + result.AppendFormat("\n{0} {1} {2}/{3} ID: {4}", av.Name, av.GroupName, av.CurrentRegion.Name, av.Position, av.ID); } return result.ToString(); diff --git a/libsecondlife-cs/examples/TestClient/Program.cs b/libsecondlife-cs/examples/TestClient/Program.cs index e304ba82..66407768 100644 --- a/libsecondlife-cs/examples/TestClient/Program.cs +++ b/libsecondlife-cs/examples/TestClient/Program.cs @@ -18,7 +18,7 @@ namespace libsecondlife.TestClient { Arguments arguments = new Arguments(args); - TestClient tester; + ClientManager manager; List accounts = new List(); LoginDetails account; string master = ""; @@ -98,10 +98,12 @@ namespace libsecondlife.TestClient } } + foreach (LoginDetails a in accounts) + a.Master = master; + // Login the accounts and run the input loop - tester = new TestClient(accounts); - tester.Master = master; - tester.Run(); + manager = new ClientManager(accounts); + manager.Run(); } } } diff --git a/libsecondlife-cs/examples/TestClient/TestClient.cs b/libsecondlife-cs/examples/TestClient/TestClient.cs index 77cf0d27..ef23e0df 100644 --- a/libsecondlife-cs/examples/TestClient/TestClient.cs +++ b/libsecondlife-cs/examples/TestClient/TestClient.cs @@ -8,25 +8,17 @@ using libsecondlife.AssetSystem; namespace libsecondlife.TestClient { - public struct LoginDetails + public class TestClient : SecondLife { - public string FirstName; - public string LastName; - public string Password; - } - - public class TestClient - { - public Dictionary Clients = new Dictionary(); + public Dictionary> SimPrims; public LLUUID GroupID = LLUUID.Zero; public Dictionary GroupMembers; - public Dictionary> SimPrims = new Dictionary>(); - public Dictionary Appearances = new Dictionary(); - public Dictionary Avatars = new Dictionary(); - public Dictionary Commands = new Dictionary(); + public Dictionary Appearances = new Dictionary(); + public Dictionary Commands = new Dictionary(); public Dictionary SharedValues = new Dictionary(); public bool Running = true; public string Master = ""; + public ClientManager ClientManager; public delegate void PrimCreatedCallback(Simulator simulator, PrimObject prim); public event PrimCreatedCallback OnPrimCreated; @@ -38,100 +30,39 @@ namespace libsecondlife.TestClient private int DrawDistance = 64; private System.Timers.Timer updateTimer; + /// /// /// - /// - public TestClient(List accounts) + public TestClient(ClientManager manager) { + ClientManager = manager; + updateTimer = new System.Timers.Timer(1000); updateTimer.Elapsed += new System.Timers.ElapsedEventHandler(updateTimer_Elapsed); RegisterAllCommands(Assembly.GetExecutingAssembly()); - foreach (LoginDetails account in accounts) - { - SecondLife client = InitializeClient(account); - if (client.Network.Connected) - { - Clients[client.Network.AgentID] = client; + Debug = false; - Console.WriteLine("Logged in " + client.ToString()); + Network.RegisterCallback(PacketType.AgentDataUpdate, new NetworkManager.PacketCallback(AgentDataUpdateHandler)); + + Objects.OnNewPrim += new ObjectManager.NewPrimCallback(Objects_OnNewPrim); + Objects.OnPrimMoved += new ObjectManager.PrimMovedCallback(Objects_OnPrimMoved); + Objects.OnObjectKilled += new ObjectManager.KillObjectCallback(Objects_OnObjectKilled); + //Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(Objects_OnNewAvatar); + //Objects.OnAvatarMoved += new ObjectManager.AvatarMovedCallback(Objects_OnAvatarMoved); + Self.OnInstantMessage += new InstantMessageCallback(Self_OnInstantMessage); + + Network.RegisterCallback(PacketType.AvatarAppearance, new NetworkManager.PacketCallback(AvatarAppearanceHandler)); + + Objects.RequestAllObjects = true; - // Throttle the connection to not receive LayerData or asset packets - client.Throttle.Total = 0.0f; - client.Throttle.Task = 1536000.0f; - client.Throttle.Set(); - } - else - { - Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + - ": " + client.Network.LoginError); - } - } updateTimer.Start(); } - /// - /// - /// - /// - /// - public SecondLife InitializeClient(LoginDetails account) - { - SecondLife client = new SecondLife(); - - client.Debug = false; - - client.Network.RegisterCallback(PacketType.AgentDataUpdate, new NetworkManager.PacketCallback(AgentDataUpdateHandler)); - - client.Objects.OnNewPrim += new ObjectManager.NewPrimCallback(Objects_OnNewPrim); - client.Objects.OnPrimMoved += new ObjectManager.PrimMovedCallback(Objects_OnPrimMoved); - client.Objects.OnObjectKilled += new ObjectManager.KillObjectCallback(Objects_OnObjectKilled); - client.Objects.OnNewAvatar += new ObjectManager.NewAvatarCallback(Objects_OnNewAvatar); - client.Objects.OnAvatarMoved += new ObjectManager.AvatarMovedCallback(Objects_OnAvatarMoved); - client.Self.OnInstantMessage += new InstantMessageCallback(Self_OnInstantMessage); - - client.Network.RegisterCallback(PacketType.AvatarAppearance, new NetworkManager.PacketCallback(AvatarAppearanceHandler)); - - client.Objects.RequestAllObjects = true; - - bool login = client.Network.Login(account.FirstName, account.LastName, account.Password, - "TestClient", "contact@libsecondlife.org"); - - if (login) - { - // Set our appearance - //AppearanceManager appearance = new AppearanceManager(client); - //appearance.SendAgentSetAppearance(); - } - - return client; - } - - /// - /// - /// - public void Run() - { - Console.WriteLine("Type quit to exit. Type help for a command list."); - - while (Running) - { - PrintPrompt(); - string input = Console.ReadLine(); - DoCommandAll(input, null, null); - } - - foreach (SecondLife client in Clients.Values) - { - if (client.Network.Connected) - client.Network.Logout(); - } - } - public void RegisterAllCommands(Assembly assembly) { foreach (Type t in assembly.GetTypes()) @@ -170,7 +101,8 @@ namespace libsecondlife.TestClient client.Self.InstantMessage(fromAgentID, message, imSessionID); } } - private void DoCommand(SecondLife client, string cmd, LLUUID fromAgentID, LLUUID imSessionID) + + public void DoCommand(string cmd, LLUUID fromAgentID, LLUUID imSessionID) { string[] tokens = cmd.Trim().Split(new char[] { ' ', '\t' }); string firstToken = tokens[0].ToLower(); @@ -179,124 +111,45 @@ namespace libsecondlife.TestClient return; // "all balance" will send the balance command to all currently logged in bots - if (firstToken == "all" && tokens.Length > 1) - { - cmd = ""; + if (firstToken == "all" && tokens.Length > 1) + { + cmd = ""; - // Reserialize all of the arguments except for "all" - for (int i = 1; i < tokens.Length; i++) - { - cmd += tokens[i] + " "; - } + // Reserialize all of the arguments except for "all" + for (int i = 1; i < tokens.Length; i++) + { + cmd += tokens[i] + " "; + } - DoCommandAll(cmd, fromAgentID, imSessionID); + ClientManager.DoCommandAll(cmd, fromAgentID, imSessionID); - return; - } + return; + } if (Commands.ContainsKey(firstToken)) { string[] args = new string[tokens.Length - 1]; Array.Copy(tokens, 1, args, 0, args.Length); - string response = response = Commands[firstToken].Execute(client, args, fromAgentID); + string response = response = Commands[firstToken].Execute(this, args, fromAgentID); if (response.Length > 0) { - if (fromAgentID != null && client.Network.Connected) - SendResponseIM(client, fromAgentID, response, imSessionID); + if (fromAgentID != null && Network.Connected) + SendResponseIM(this, fromAgentID, response, imSessionID); Console.WriteLine(response); } } } - private void DoCommandAll(string cmd, LLUUID fromAgentID, LLUUID imSessionID) - { - string[] tokens = cmd.Trim().Split(new char[] { ' ', '\t' }); - string firstToken = tokens[0].ToLower(); - - if (tokens.Length == 0) - return; - -Begin: - - int avatars = Clients.Count; - - if (Commands.ContainsKey(firstToken)) - { - if (firstToken == "login") - { - // Special login case: Only call it once, and allow it with - // no logged in avatars - string[] args = new string[tokens.Length - 1]; - Array.Copy(tokens, 1, args, 0, args.Length); - string response = Commands["login"].Execute(null, args, null); - - if (response.Length > 0) - { - Console.WriteLine(response); - } - } - else if (firstToken == "quit") - { - // Special quit case: This allows us to quit even when there - // are zero avatars logged in - Commands["quit"].Execute(null, null, null); - } - else - { - foreach (SecondLife client in Clients.Values) - { - if (client.Network.Connected) - { - string[] args = new string[tokens.Length - 1]; - Array.Copy(tokens, 1, args, 0, args.Length); - string response = Commands[firstToken].Execute(client, args, fromAgentID); - - if (response.Length > 0) - { - if (fromAgentID != null && client.Network.Connected) - SendResponseIM(client, fromAgentID, response, imSessionID); - Console.WriteLine(response); - } - } - - if (avatars != Clients.Count) - { - // The dictionary size changed, start over since the - // foreach is shot - goto Begin; - } - } - } - } - } - - private void PrintPrompt() - { - //Console.Write(String.Format("{0} {1} - {2}> ", client.Self.FirstName, client.Self.LastName, client.Network.CurrentSim.Region.Name)); - - int online = 0; - - foreach (SecondLife client in Clients.Values) - { - if (client.Network.Connected) online++; - } - - Console.Write(online + " avatars online> "); - } - private void updateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { - foreach (SecondLife client in Clients.Values) - { - client.Self.UpdateCamera(0, client.Self.Position, forward, left, up, bodyRotation, - LLQuaternion.Identity, DrawDistance, false); + Self.UpdateCamera(0, Self.Position, forward, left, up, bodyRotation, + LLQuaternion.Identity, DrawDistance, false); - foreach (Command c in Commands.Values) - if (c.Active) - c.Think(client); - } + foreach (Command c in Commands.Values) + if (c.Active) + c.Think(this); } private void AgentDataUpdateHandler(Packet packet, Simulator sim) @@ -315,7 +168,6 @@ Begin: { Console.WriteLine("Got " + members.Count + " group members."); GroupMembers = members; - PrintPrompt(); } private void Objects_OnObjectKilled(Simulator simulator, uint objectID) @@ -326,11 +178,11 @@ Begin: SimPrims[simulator].Remove(objectID); } - lock (Avatars) - { - if (Avatars.ContainsKey(objectID)) - Avatars.Remove(objectID); - } + //lock (AvatarList) + //{ + // if (AvatarList.ContainsKey(objectID)) + // AvatarList.Remove(objectID); + //} } private void Objects_OnPrimMoved(Simulator simulator, PrimUpdate prim, ulong regionHandle, ushort timeDilation) @@ -363,25 +215,25 @@ Begin: } } - private void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation) - { - lock (Avatars) - { - Avatars[avatar.LocalID] = avatar; - } - } + //private void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation) + //{ + // lock (AvatarList) + // { + // AvatarList[avatar.LocalID] = avatar; + // } + //} - private void Objects_OnAvatarMoved(Simulator simulator, AvatarUpdate avatar, ulong regionHandle, ushort timeDilation) - { - lock (Avatars) - { - if (Avatars.ContainsKey(avatar.LocalID)) - { - Avatars[avatar.LocalID].Position = avatar.Position; - Avatars[avatar.LocalID].Rotation = avatar.Rotation; - } - } - } + //private void Objects_OnAvatarMoved(Simulator simulator, AvatarUpdate avatar, ulong regionHandle, ushort timeDilation) + //{ + // lock (AvatarList) + // { + // if (AvatarList.ContainsKey(avatar.LocalID)) + // { + // AvatarList[avatar.LocalID].Position = avatar.Position; + // AvatarList[avatar.LocalID].Rotation = avatar.Rotation; + // } + // } + //} private void AvatarAppearanceHandler(Packet packet, Simulator simulator) { @@ -415,16 +267,16 @@ Begin: Console.WriteLine("" + fromAgentName + ": " + message); - if (Clients.ContainsKey(toAgentID)) + if (Self.ID == toAgentID) { if (dialog == 22) { Console.WriteLine("Accepting teleport lure"); - Clients[toAgentID].Self.TeleportLureRespond(fromAgentID, true); + Self.TeleportLureRespond(fromAgentID, true); } else { - DoCommand(Clients[toAgentID], message, fromAgentID, imSessionID); + DoCommand(message, fromAgentID, imSessionID); } } else @@ -433,5 +285,5 @@ Begin: Console.WriteLine("A bot that we aren't tracking received an IM?"); } } - } -} + } +} \ No newline at end of file diff --git a/libsecondlife-cs/examples/TestClient/TestClient.csproj b/libsecondlife-cs/examples/TestClient/TestClient.csproj index 912281d8..30b2845a 100644 --- a/libsecondlife-cs/examples/TestClient/TestClient.csproj +++ b/libsecondlife-cs/examples/TestClient/TestClient.csproj @@ -36,24 +36,37 @@ + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + - - - - - - @@ -61,7 +74,10 @@ - + + + Code + diff --git a/libsecondlife-cs/libsecondlife.sln b/libsecondlife-cs/libsecondlife.sln index c1e677e0..87bad82a 100644 --- a/libsecondlife-cs/libsecondlife.sln +++ b/libsecondlife-cs/libsecondlife.sln @@ -38,6 +38,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IA_TestAsyncImage", "exampl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SLIRC", "..\applications\SLIRC\SLIRC.csproj", "{8855EB2F-BC4C-485A-A577-0989EB16BFDC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InventoryImageAssets", "InventoryImageAssets", "{AE3B6C21-9B43-490E-ABCE-2018DC1AEA44}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IA_MultiImageUpload", "examples\IA_MultiImageUpload\IA_MultiImageUpload.csproj", "{8598E42A-DCBC-4224-9503-5694F17B2F45}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SLProxy", "..\SLProxy\SLProxy.csproj", "{E4115DC9-FC88-47D6-B3B6-2400AD19B80D}" @@ -48,8 +50,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatConsole", "..\SLProxy\C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BodyPartMorphGenerator", "examples\BodyPartMorphGenerator\BodyPartMorphGenerator.csproj", "{98C44481-3F15-4305-840D-037EA0D9C221}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools and Examples", "Tools and Examples", "{F35EF72F-4302-4924-A162-10447B94F635}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SL Proxy", "SL Proxy", "{C754652B-2ACB-4D47-8914-834E17919132}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "examples\TestClient\TestClient.csproj", "{B87682F6-B2D7-4C4D-A529-400C24FD4880}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sit", "..\..\SurveyAndSit\Sit.csproj", "{C741A2FF-732C-4724-A4D3-D52324D09C8E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -156,8 +164,36 @@ Global {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Debug|Any CPU.Build.0 = Debug|Any CPU {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Release|Any CPU.ActiveCfg = Release|Any CPU {B87682F6-B2D7-4C4D-A529-400C24FD4880}.Release|Any CPU.Build.0 = Release|Any CPU + {C741A2FF-732C-4724-A4D3-D52324D09C8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C741A2FF-732C-4724-A4D3-D52324D09C8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C741A2FF-732C-4724-A4D3-D52324D09C8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C741A2FF-732C-4724-A4D3-D52324D09C8E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FC19D5F6-076E-4923-8456-9B0E00E22896} = {F35EF72F-4302-4924-A162-10447B94F635} + {66FFD34E-652C-4EF5-81FE-06AD011169D2} = {F35EF72F-4302-4924-A162-10447B94F635} + {623B86F7-7753-44B7-A8C8-CBC89FB8AF8E} = {F35EF72F-4302-4924-A162-10447B94F635} + {77E5330D-8A8C-41B4-A2D1-6F06FE45ED6D} = {F35EF72F-4302-4924-A162-10447B94F635} + {B5AC6795-E426-4BC3-950F-D7B2970E2394} = {F35EF72F-4302-4924-A162-10447B94F635} + {7AE16AC1-E64C-4FDC-9B85-4BB6145D511C} = {F35EF72F-4302-4924-A162-10447B94F635} + {F460FAB3-0D12-4873-89EB-2696818764B8} = {F35EF72F-4302-4924-A162-10447B94F635} + {4EF98AD4-B3B3-42B0-9273-13A614A823F7} = {F35EF72F-4302-4924-A162-10447B94F635} + {8855EB2F-BC4C-485A-A577-0989EB16BFDC} = {F35EF72F-4302-4924-A162-10447B94F635} + {F6258A68-C624-46A0-BA73-B55D21BB0A3B} = {F35EF72F-4302-4924-A162-10447B94F635} + {B87682F6-B2D7-4C4D-A529-400C24FD4880} = {F35EF72F-4302-4924-A162-10447B94F635} + {C741A2FF-732C-4724-A4D3-D52324D09C8E} = {F35EF72F-4302-4924-A162-10447B94F635} + {E185E4E1-62D2-430C-A94C-E8E38190805B} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {8D2E5240-2247-42F5-AAAC-CF0CCCEE349A} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {D6D1D020-D8D8-4D7E-B7AC-5913A903D6E7} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {A4F59DE9-E382-401D-AA8D-4557779D764E} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {E464B963-46E3-4E1A-A36F-9C640C880E68} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {8598E42A-DCBC-4224-9503-5694F17B2F45} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {98C44481-3F15-4305-840D-037EA0D9C221} = {AE3B6C21-9B43-490E-ABCE-2018DC1AEA44} + {6222B134-AE5F-489A-8A77-423A721B7C62} = {C754652B-2ACB-4D47-8914-834E17919132} + {D8ECCBE1-AC71-4054-AAA6-2D50E5629504} = {C754652B-2ACB-4D47-8914-834E17919132} + {E4115DC9-FC88-47D6-B3B6-2400AD19B80D} = {C754652B-2ACB-4D47-8914-834E17919132} + EndGlobalSection EndGlobal