From 313d16d8864e8d944085e4628677206e5f841feb Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 21 Oct 2015 18:58:36 +0300 Subject: [PATCH] Changed entity ids from int to ushort, inventory sync bugfixes --- .../Source/Characters/AI/EnemyAIController.cs | 9 +-- Subsurface/Source/Characters/CharacterInfo.cs | 10 +-- Subsurface/Source/Characters/Ragdoll.cs | 7 +- Subsurface/Source/Items/CharacterInventory.cs | 69 ++++++++++++++++++ .../Source/Items/Components/ItemContainer.cs | 12 +-- .../Items/Components/Signal/Connection.cs | 10 ++- .../Components/Signal/ConnectionPanel.cs | 2 +- Subsurface/Source/Items/Inventory.cs | 31 ++++---- Subsurface/Source/Items/Item.cs | 2 +- Subsurface/Source/Map/Entity.cs | 9 ++- Subsurface/Source/Map/Gap.cs | 2 +- Subsurface/Source/Map/Hull.cs | 2 +- Subsurface/Source/Map/MapEntity.cs | 2 +- Subsurface/Source/Map/Structure.cs | 2 +- Subsurface/Source/Map/Submarine.cs | 4 +- Subsurface/Source/Map/WayPoint.cs | 2 +- Subsurface/Source/Networking/GameClient.cs | 12 +-- Subsurface/Source/Networking/GameServer.cs | 12 ++- Subsurface/Source/Networking/NetworkEvent.cs | 34 +++++---- Subsurface_Solution.v12.suo | Bin 799232 -> 829952 bytes 20 files changed, 155 insertions(+), 78 deletions(-) diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index cdf6666bc..1e75e725e 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -534,7 +534,7 @@ namespace Barotrauma //message.WriteRangedSingle(MathHelper.Clamp(raycastTimer, 0.0f, RaycastInterval), 0.0f, RaycastInterval, 8); //message.WriteRangedSingle(MathHelper.Clamp(coolDownTimer, 0.0f, attackCoolDown * 2.0f), 0.0f, attackCoolDown * 2.0f, 8); - message.Write(targetEntity==null ? -1 : (targetEntity as Entity).ID); + message.Write(targetEntity==null ? (ushort)0 : (targetEntity as Entity).ID); } public override void ReadNetworkData(NetIncomingMessage message) @@ -548,7 +548,7 @@ namespace Barotrauma Vector2 targetPosition = Vector2.Zero; - int targetID; + ushort targetID; try { @@ -571,7 +571,7 @@ namespace Barotrauma //raycastTimer = message.ReadRangedSingle(0.0f, RaycastInterval, 8); //coolDownTimer = message.ReadRangedSingle(0.0f, attackCoolDown*2.0f, 8); - targetID = message.ReadInt32(); + targetID = message.ReadUInt16(); } catch { return; } @@ -583,8 +583,7 @@ namespace Barotrauma //this.raycastTimer = raycastTimer; //this.coolDownTimer = coolDownTimer; - if (targetID > -1) - targetEntity = Entity.FindEntityByID(targetID) as IDamageable; + if (targetID > 0) targetEntity = Entity.FindEntityByID(targetID) as IDamageable; } } diff --git a/Subsurface/Source/Characters/CharacterInfo.cs b/Subsurface/Source/Characters/CharacterInfo.cs index e44b25d2a..4ed7af120 100644 --- a/Subsurface/Source/Characters/CharacterInfo.cs +++ b/Subsurface/Source/Characters/CharacterInfo.cs @@ -17,7 +17,7 @@ namespace Barotrauma public Job Job; - private List pickedItems; + private List pickedItems; private Vector2[] headSpriteRange; @@ -30,7 +30,7 @@ namespace Barotrauma public bool StartItemsGiven; - public List PickedItemIDs + public List PickedItemIDs { get { return pickedItems; } } @@ -72,7 +72,7 @@ namespace Barotrauma headSpriteRange = new Vector2[2]; - pickedItems = new List(); + pickedItems = new List(); //ID = -1; @@ -218,7 +218,7 @@ namespace Barotrauma HeadSpriteId = ToolBox.GetAttributeInt(element, "headspriteid", 1); StartItemsGiven = ToolBox.GetAttributeBool(element, "startitemsgiven", false); - pickedItems = new List(); + pickedItems = new List(); string pickedItemString = ToolBox.GetAttributeString(element, "items", ""); if (!string.IsNullOrEmpty(pickedItemString)) @@ -226,7 +226,7 @@ namespace Barotrauma string[] itemIds = pickedItemString.Split(','); foreach (string s in itemIds) { - pickedItems.Add(int.Parse(s)); + pickedItems.Add((ushort)int.Parse(s)); } } diff --git a/Subsurface/Source/Characters/Ragdoll.cs b/Subsurface/Source/Characters/Ragdoll.cs index fe78963fc..d36eb7595 100644 --- a/Subsurface/Source/Characters/Ragdoll.cs +++ b/Subsurface/Source/Characters/Ragdoll.cs @@ -685,11 +685,14 @@ namespace Barotrauma //limb.body.SetTransform(limb.SimPosition + newMovement * 0.1f, limb.Rotation); } - correctionMovement = Vector2.Normalize(newMovement) * MathHelper.Clamp(dist*5.0f, 0.1f, 5.0f); + correctionMovement = + Vector2.Lerp(targetMovement, Vector2.Normalize(newMovement) * MathHelper.Clamp(dist * 5.0f, 0.1f, 5.0f), 0.2f); } else { - correctionMovement = Vector2.Normalize(newMovement) * MathHelper.Clamp(dist * 5.0f, 0.1f, 5.0f); + correctionMovement = + Vector2.Lerp(targetMovement, Vector2.Normalize(newMovement) * MathHelper.Clamp(dist * 5.0f, 0.1f, 5.0f), 0.2f); + if (Math.Abs(correctionMovement.Y) < 0.1f) correctionMovement.Y = 0.0f; } } diff --git a/Subsurface/Source/Items/CharacterInventory.cs b/Subsurface/Source/Items/CharacterInventory.cs index e17b11c50..c1b470c64 100644 --- a/Subsurface/Source/Items/CharacterInventory.cs +++ b/Subsurface/Source/Items/CharacterInventory.cs @@ -2,6 +2,9 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; +using Barotrauma.Networking; +using Lidgren.Network; +using System.Collections.Generic; namespace Barotrauma { @@ -292,5 +295,71 @@ namespace Barotrauma } } + public override bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + { + for (int i = 0; i < 5; i++ ) + { + message.Write(items[i]==null ? (ushort)0 : (ushort)items[i].ID); + } + + for (int i = 5; i < capacity; i++) + { + if (items[i] == null) continue; + message.Write((ushort)items[i].ID); + } + + return true; + } + + public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + for (int i = 0; i<5; i++) + { + ushort itemId = message.ReadUInt16(); + if (itemId==0) + { + if (items[i] != null) items[i].Drop(character, false); + } + else + { + Item item = Entity.FindEntityByID(itemId) as Item; + if (item == null) continue; + + TryPutItem(item, i, false); + } + } + + List newItemIDs = new List(); + + try + { + while (message.Position <= message.LengthBits - (sizeof(ushort) * 8)) + { + newItemIDs.Add(message.ReadUInt16()); + } + } + catch + { + return; + } + + for (int i = 5; i < capacity; i++) + { + if (items[i] == null) continue; + if (!newItemIDs.Contains(items[i].ID)) + { + items[i].Drop(null, false); + continue; + } + } + foreach (ushort itemId in newItemIDs) + { + Item item = Entity.FindEntityByID(itemId) as Item; + if (item == null) continue; + + TryPutItem(item, LimbSlot.Any, false); + } + } + } } diff --git a/Subsurface/Source/Items/Components/ItemContainer.cs b/Subsurface/Source/Items/Components/ItemContainer.cs index 0bb746595..e8ccd3022 100644 --- a/Subsurface/Source/Items/Components/ItemContainer.cs +++ b/Subsurface/Source/Items/Components/ItemContainer.cs @@ -228,7 +228,7 @@ namespace Barotrauma.Items.Components { if (itemIds == null) return; - for (int i = 0; i < itemIds.Length; i++) + for (ushort i = 0; i < itemIds.Length; i++) { Item item = MapEntity.FindEntityByID(itemIds[i]) as Item; if (item == null) continue; @@ -247,17 +247,17 @@ namespace Barotrauma.Items.Components string[] itemIdStrings = containedString.Split(','); - itemIds = new int[itemIdStrings.Length]; + itemIds = new ushort[itemIdStrings.Length]; for (int i = 0; i < itemIdStrings.Length; i++) { - int id = -1; - if (!int.TryParse(itemIdStrings[i], out id)) continue; + ushort id = 0; + if (!ushort.TryParse(itemIdStrings[i], out id)) continue; itemIds[i] = id; } } - int[] itemIds; + ushort[] itemIds; public override XElement Save(XElement parentElement) { @@ -266,7 +266,7 @@ namespace Barotrauma.Items.Components string[] itemIdStrings = new string[inventory.items.Length]; for (int i = 0; i < inventory.items.Length; i++) { - itemIdStrings[i] = (inventory.items[i]==null) ? "-1" : inventory.items[i].ID.ToString(); + itemIdStrings[i] = (inventory.items[i]==null) ? "0" : inventory.items[i].ID.ToString(); } componentElement.Add(new XAttribute("contained", string.Join(",",itemIdStrings))); diff --git a/Subsurface/Source/Items/Components/Signal/Connection.cs b/Subsurface/Source/Items/Components/Signal/Connection.cs index 191defac1..16d91ed27 100644 --- a/Subsurface/Source/Items/Components/Signal/Connection.cs +++ b/Subsurface/Source/Items/Components/Signal/Connection.cs @@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components private List effects; - public readonly int[] wireId; + public readonly ushort[] wireId; public bool IsPower { @@ -79,7 +79,7 @@ namespace Barotrauma.Items.Components effects = new List(); - wireId = new int[MaxLinked]; + wireId = new ushort[MaxLinked]; foreach (XElement subElement in element.Elements()) { @@ -93,7 +93,9 @@ namespace Barotrauma.Items.Components } if (index == -1) break; - wireId[index] = ToolBox.GetAttributeInt(subElement, "w", -1); + int id = ToolBox.GetAttributeInt(subElement, "w", 0); + if (id<0) id = 0; + wireId[index] = (ushort)id; break; @@ -441,7 +443,7 @@ namespace Barotrauma.Items.Components for (int i = 0; i < MaxLinked; i++) { - if (wireId[i] == -1) continue; + if (wireId[i] == 0) continue; Item wireItem = MapEntity.FindEntityByID(wireId[i]) as Item; diff --git a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs index 26174d8fe..478e2681d 100644 --- a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs @@ -146,7 +146,7 @@ namespace Barotrauma.Items.Components for (int i = 0; i < wireCount; i++) { - int wireId = message.ReadInt32(); + ushort wireId = message.ReadUInt16(); Item wireItem = MapEntity.FindEntityByID(wireId) as Item; if (wireItem == null) continue; diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index 4f34771ac..558fa1456 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Barotrauma.Networking; using System; +using System.Collections.Generic; namespace Barotrauma { @@ -193,7 +194,7 @@ namespace Barotrauma { if (Owner!=null) { - int[] data = { draggingItem.ID, -1 }; + ushort[] data = { draggingItem.ID, 0 }; new NetworkEvent(NetworkEventType.InventoryUpdate, Owner.ID, true, data); } @@ -288,25 +289,27 @@ namespace Barotrauma spriteBatch.DrawString(GUI.Font, (int)item.Condition + " %", new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.Red); } - public bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + public virtual bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { for (int i = 0; i newItemIDs = new List(); try { - for (int i = 0; i(); diff --git a/Subsurface/Source/Map/Entity.cs b/Subsurface/Source/Map/Entity.cs index 835ccdcec..1449ad68e 100644 --- a/Subsurface/Source/Map/Entity.cs +++ b/Subsurface/Source/Map/Entity.cs @@ -7,15 +7,16 @@ namespace Barotrauma { class Entity { - private static Dictionary dictionary = new Dictionary(); + private static Dictionary dictionary = new Dictionary(); - private int id; + + private ushort id; protected AITarget aiTarget; //protected float soundRange; //protected float sightRange; - public int ID + public ushort ID { get { return id; } set @@ -73,7 +74,7 @@ namespace Barotrauma /// /// Find an entity based on the ID /// - public static Entity FindEntityByID(int ID) + public static Entity FindEntityByID(ushort ID) { Entity matchingEntity; dictionary.TryGetValue(ID, out matchingEntity); diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index e7490e2b3..24408afe3 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -607,7 +607,7 @@ namespace Barotrauma int.Parse(element.Attribute("height").Value)); Gap g = new Gap(rect); - g.ID = int.Parse(element.Attribute("ID").Value); + g.ID = (ushort)int.Parse(element.Attribute("ID").Value); g.linkedToID = new List(); //int i = 0; diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index b75ef5671..97244e295 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -468,7 +468,7 @@ namespace Barotrauma h.volume = ToolBox.GetAttributeFloat(element, "pressure", 0.0f); - h.ID = int.Parse(element.Attribute("ID").Value); + h.ID = (ushort)int.Parse(element.Attribute("ID").Value); } public override bool FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message, object data) diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 1cc9cbc74..4722963ba 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -515,7 +515,7 @@ namespace Barotrauma e.linkedTo.Clear(); - foreach (int i in e.linkedToID) + foreach (ushort i in e.linkedToID) { MapEntity linked = FindEntityByID(i) as MapEntity; diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index d8858e61b..c5b16eb73 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -609,7 +609,7 @@ namespace Barotrauma if (ep.Name == name) { s = new Structure(rect, (StructurePrefab)ep); - s.ID = int.Parse(element.Attribute("ID").Value); + s.ID = (ushort)int.Parse(element.Attribute("ID").Value); break; } } diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index b52386284..1ef28c468 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -149,7 +149,7 @@ namespace Barotrauma } base.Remove(); - ID = -5; + ID = ushort.MaxValue; } //drawing ---------------------------------------------------- @@ -635,7 +635,7 @@ namespace Barotrauma GameMain.LightManager.OnMapLoaded(); - ID = int.MaxValue-10; + ID = ushort.MaxValue-10; loaded = this; } diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index 4f1b8ae7e..c63a00e2a 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -312,7 +312,7 @@ namespace Barotrauma WayPoint w = new WayPoint(rect); - w.ID = int.Parse(element.Attribute("ID").Value); + w.ID = (ushort)int.Parse(element.Attribute("ID").Value); w.spawnType = (SpawnType)Enum.Parse(typeof(SpawnType), ToolBox.GetAttributeString(element, "spawn", "None")); diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 69131d260..d919ab677 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -347,7 +347,7 @@ namespace Barotrauma.Networking } else if (gameStarted) { - new NetworkEvent(myCharacter.ID, true); + myCharacter.SendNetworkEvent(true); } } @@ -525,7 +525,7 @@ namespace Barotrauma.Networking List crew = new List(); - int count = inc.ReadInt32(); + int count = inc.ReadByte(); for (int n = 0; n < count; n++) { int id = inc.ReadInt32(); @@ -631,7 +631,7 @@ namespace Barotrauma.Networking msg.Write((byte)PacketTypes.CharacterInfo); msg.Write(characterInfo.Name); msg.Write(characterInfo.Gender == Gender.Male); - msg.Write(characterInfo.HeadSpriteId); + msg.Write((byte)characterInfo.HeadSpriteId); var jobPreferences = GameMain.NetLobbyScreen.JobPreferences; int count = Math.Min(jobPreferences.Count, 3); @@ -647,10 +647,10 @@ namespace Barotrauma.Networking private Character ReadCharacterData(NetIncomingMessage inc, bool isMyCharacter) { string newName = inc.ReadString(); - int ID = inc.ReadInt32(); + ushort ID = inc.ReadUInt16(); bool isFemale = inc.ReadBoolean(); - int headSpriteID = inc.ReadInt32(); + int headSpriteID = inc.ReadByte(); Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat()); @@ -719,7 +719,7 @@ namespace Barotrauma.Networking { case 0: msg.Write((byte)PacketTypes.NetworkEvent); - msg.Write((byte)NetworkEventType.UpdateEntity); + msg.Write((byte)NetworkEventType.EntityUpdate); msg.Write(Rand.Int(MapEntity.mapEntityList.Count)); break; case 1: diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index cf492b216..16ec29e2d 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -297,7 +297,7 @@ namespace Barotrauma.Networking { if (gameStarted) { - if (myCharacter != null) new NetworkEvent(myCharacter.ID, true); + if (myCharacter != null) myCharacter.SendNetworkEvent(true); foreach (Character c in Character.CharacterList) { @@ -305,7 +305,7 @@ namespace Barotrauma.Networking if (c.SimPosition == Vector2.Zero || c.SimPosition.Length() < 100.0f) { - new NetworkEvent(c.ID, false); + c.SendNetworkEvent(false); } } } @@ -678,8 +678,6 @@ namespace Barotrauma.Networking } else { - - if (server.ConnectionsCount>0) { server.SendMessage(message, recipientConnections, NetDeliveryMethod.Unreliable, 0); @@ -777,7 +775,7 @@ namespace Barotrauma.Networking //msg.Write(GameMain.NetLobbyScreen.GameDuration.TotalMinutes); - msg.Write((myCharacter == null) ? connectedClients.Count : connectedClients.Count + 1); + msg.Write((myCharacter == null) ? (byte)connectedClients.Count : (byte)(connectedClients.Count + 1)); foreach (Client client in connectedClients) { msg.Write(client.ID); @@ -1036,7 +1034,7 @@ namespace Barotrauma.Networking { name = message.ReadString(); gender = message.ReadBoolean() ? Gender.Male : Gender.Female; - headSpriteId = message.ReadInt32(); + headSpriteId = message.ReadByte(); } catch { @@ -1072,7 +1070,7 @@ namespace Barotrauma.Networking message.Write(character.ID); message.Write(character.Info.Gender == Gender.Female); - message.Write(character.Info.HeadSpriteId); + message.Write((byte)character.Info.HeadSpriteId); message.Write(character.SimPosition.X); message.Write(character.SimPosition.Y); diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index d511149a2..b2b3c2f94 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using Lidgren.Network; +using System; namespace Barotrauma.Networking { enum NetworkEventType { - UpdateEntity = 0, + EntityUpdate = 0, KillCharacter = 1, UpdateComponent = 2, DropItem = 3, @@ -13,17 +14,19 @@ namespace Barotrauma.Networking PickItem = 5, UpdateProperty = 6, WallDamage = 7, - SelectCharacter = 8 + + SelectCharacter = 8, + EntityUpdateLarge = 9 } class NetworkEvent { public static List events = new List(); - private static bool[] isImportant = { false, true, false, true, true, true, true, true, true }; - private static bool[] overridePrevious = { true, false, true, false, false, false, true, true, true }; + private static bool[] isImportant = { false, true, false, true, true, true, true, true, true, false }; + private static bool[] overridePrevious = { true, false, true, false, false, false, true, true, true, true }; - private int id; + private ushort id; private NetworkEventType eventType; @@ -33,7 +36,7 @@ namespace Barotrauma.Networking //private NetOutgoingMessage message; - public int ID + public ushort ID { get { return id; } } @@ -53,12 +56,12 @@ namespace Barotrauma.Networking get { return eventType; } } - public NetworkEvent(int id, bool isClient) - : this(NetworkEventType.UpdateEntity, id, isClient) + public NetworkEvent(ushort id, bool isClient) + : this(NetworkEventType.EntityUpdate, id, isClient) { } - public NetworkEvent(NetworkEventType type, int id, bool isClient, object data = null) + public NetworkEvent(NetworkEventType type, ushort id, bool isClient, object data = null) { if (isClient) { @@ -109,12 +112,12 @@ namespace Barotrauma.Networking public static bool ReadData(NetIncomingMessage message) { NetworkEventType eventType; - int id; + ushort id; try { eventType = (NetworkEventType)message.ReadByte(); - id = message.ReadInt32(); + id = message.ReadUInt16(); } catch { @@ -129,16 +132,15 @@ namespace Barotrauma.Networking return false; } - //System.Diagnostics.Debug.WriteLine("Networkevent entity: "+e.ToString()); - - //System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e); try { e.ReadNetworkData(eventType, message); } - catch + catch (Exception exception) { - DebugConsole.ThrowError("Received invalid network message"); +#if DEBUG + DebugConsole.ThrowError("Received invalid network message", exception); +#endif return false; } diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 20d8381f592f9c8f360e1fdfd36d9ba48e7829b7..2f3da2e21b7bee79c132b66784073c96f88875d4 100644 GIT binary patch delta 11203 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 YEB)dJehznoO~07V!OJ#Zm#BsPH_mO>qyPW_