From 7672161b57c6743c67e424d852d92431d48f88bd Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 15 Jun 2017 00:49:32 +0200 Subject: [PATCH 01/12] Added support for plain object loot --- src/services/combat/LootService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/services/combat/LootService.java b/src/services/combat/LootService.java index 5144a3a52..d0b7ffbdd 100644 --- a/src/services/combat/LootService.java +++ b/src/services/combat/LootService.java @@ -31,6 +31,7 @@ import com.projectswg.common.control.Service; import com.projectswg.common.data.info.RelationalDatabase; import com.projectswg.common.data.info.RelationalServerFactory; import com.projectswg.common.data.location.Location; +import com.projectswg.common.data.swgfile.ClientFactory; import com.projectswg.common.debug.Log; import intents.chat.ChatBroadcastIntent; import intents.chat.ChatCommandIntent; @@ -348,9 +349,15 @@ public final class LootService extends Service { String[] itemNames = itemGroup.getItemNames(); String randomItemName = itemNames[random.nextInt(itemNames.length)]; // Selects a completely random item from the group - if (randomItemName.startsWith("dynamic_") || randomItemName.endsWith(".iff")) { + if (randomItemName.startsWith("dynamic_")) { // TODO dynamic item handling new ChatBroadcastIntent(requester.getOwner(), "We don't support this loot item yet: " + randomItemName).broadcast(); + } else if (randomItemName.endsWith(".iff")) { + String sharedTemplate = ClientFactory.formatToSharedFile(randomItemName); + SWGObject object = ObjectCreator.createObjectFromTemplate(sharedTemplate); + + new ObjectCreatedIntent(object).broadcast(); + object.moveToContainer(lootInventory); } else { new CreateStaticItemIntent(requester, lootInventory, new StaticItemService.ObjectCreationHandler() { @Override From 60fe9165459ae93bd13886a6c5121ab20374437c Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 17 Jun 2017 06:31:52 +0200 Subject: [PATCH 02/12] Fixed concurrency issue with certain loot --- src/services/combat/LootService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/combat/LootService.java b/src/services/combat/LootService.java index d0b7ffbdd..3205a9084 100644 --- a/src/services/combat/LootService.java +++ b/src/services/combat/LootService.java @@ -356,8 +356,8 @@ public final class LootService extends Service { String sharedTemplate = ClientFactory.formatToSharedFile(randomItemName); SWGObject object = ObjectCreator.createObjectFromTemplate(sharedTemplate); - new ObjectCreatedIntent(object).broadcast(); object.moveToContainer(lootInventory); + new ObjectCreatedIntent(object).broadcast(); } else { new CreateStaticItemIntent(requester, lootInventory, new StaticItemService.ObjectCreationHandler() { @Override From eae94373f2f69ffef11394f5e9e89a99e5cf8753 Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Mon, 19 Jun 2017 21:04:06 -0500 Subject: [PATCH 03/12] Added assertions for TestObjectAwareness and added slotted objects to awareness --- src/resources/objects/SWGObject.java | 13 ++-- src/resources/objects/awareness/Aware.java | 1 + src/services/objects/ObjectAwareness.java | 6 +- .../awareness/TestObjectAwareness.java | 66 ++++++++++++++++--- 4 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java index b8271503c..35fe71e34 100644 --- a/src/resources/objects/SWGObject.java +++ b/src/resources/objects/SWGObject.java @@ -40,6 +40,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import com.projectswg.common.concurrency.SynchronizedMap; import com.projectswg.common.data.CRC; @@ -295,11 +296,15 @@ public abstract class SWGObject extends BaselineObject implements Comparable getSlots() { - synchronized (slots) { - return new HashMap<>(slots); - } + return new HashMap<>(slots); } - + + public List getSlottedObjects() { + return slots.values().stream() + .filter(obj -> (obj != null)) + .collect(Collectors.toList()); + } + public void setOwner(Player player) { if (owner == player) return; diff --git a/src/resources/objects/awareness/Aware.java b/src/resources/objects/awareness/Aware.java index f1c6aff80..3b33ca85a 100644 --- a/src/resources/objects/awareness/Aware.java +++ b/src/resources/objects/awareness/Aware.java @@ -147,6 +147,7 @@ class Aware { private Set getObservers(Player owner, SWGObject original, boolean useAware) { Set observers = new HashSet<>(); addObserversToSet(getObject().getContainedObjects(), observers, owner, original); + addObserversToSet(getObject().getSlottedObjects(), observers, owner, original); if (useAware) { addObserversToSet(getAware(), observers, owner, original); } diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index fb9187fb0..70fc45faf 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -155,11 +155,9 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { private void handleDestroyObjectIntent(DestroyObjectIntent doi) { SWGObject obj = doi.getObject(); - disappearObject(obj, true, true); obj.moveToContainer(null); + disappearObject(obj, true, true); obj.setPosition(Terrain.GONE, 0, 0, 0); - obj.clearObjectsAware(); - obj.clearCustomAware(true); } private void processObjectTeleportIntent(ObjectTeleportIntent oti) { @@ -317,6 +315,8 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { private void disappearObject(SWGObject obj, boolean disappearObjects, boolean disappearCustom) { awarenessHandler.disappearObject(obj, disappearObjects, disappearCustom); + obj.clearObjectsAware(); + obj.clearCustomAware(true); } } \ No newline at end of file diff --git a/test/resources/objects/awareness/TestObjectAwareness.java b/test/resources/objects/awareness/TestObjectAwareness.java index 532c253b7..f72eca6c7 100644 --- a/test/resources/objects/awareness/TestObjectAwareness.java +++ b/test/resources/objects/awareness/TestObjectAwareness.java @@ -39,11 +39,13 @@ import com.projectswg.common.control.Intent; import com.projectswg.common.control.IntentManager; import com.projectswg.common.data.location.Terrain; +import intents.PlayerEventIntent; import intents.object.DestroyObjectIntent; import intents.object.ObjectCreatedIntent; import resources.objects.building.BuildingObject; import resources.objects.cell.CellObject; import resources.objects.creature.CreatureObject; +import resources.player.PlayerEvent; import services.objects.ObjectAwareness; import services.objects.ObjectCreator; import test_resources.GenericCreatureObject; @@ -51,7 +53,8 @@ import test_resources.GenericCreatureObject; @RunWith(JUnit4.class) public class TestObjectAwareness { - private GenericCreatureObject player; + private GenericCreatureObject player1; + private GenericCreatureObject player2; private BuildingObject building; private CellObject firstCell; private CreatureObject npc; @@ -60,11 +63,12 @@ public class TestObjectAwareness { public void initializeTests() { IntentManager.setInstance(new IntentManager(1)); IntentManager.getInstance().initialize(); - player = new GenericCreatureObject(1); - building = (BuildingObject) ObjectCreator.createObjectFromTemplate(2, "object/building/tatooine/shared_starport_tatooine.iff"); - firstCell = new CellObject(3); + player1 = new GenericCreatureObject(1); + player2 = new GenericCreatureObject(2); + building = (BuildingObject) ObjectCreator.createObjectFromTemplate(10, "object/building/tatooine/shared_starport_tatooine.iff"); + firstCell = new CellObject(11); firstCell.setNumber(1); - npc = (CreatureObject) ObjectCreator.createObjectFromTemplate(4, "object/mobile/dressed_tatooine_opening_wh_guard.iff"); + npc = (CreatureObject) ObjectCreator.createObjectFromTemplate(20, "object/mobile/dressed_tatooine_opening_wh_guard.iff"); Assert.assertNotNull("Building is null!", building); Assert.assertNotNull("NPC is null!", npc); building.addObject(firstCell); @@ -82,11 +86,11 @@ public class TestObjectAwareness { * 1) Player is inside Cell #1 * 2) NPC is inside Cell #1 */ - player.setPosition(Terrain.TATOOINE, 0, 0, 0); + player1.setPosition(Terrain.TATOOINE, 0, 0, 0); building.setPosition(Terrain.TATOOINE, 10, 10, 10); npc.setPosition(Terrain.TATOOINE, 0, 0, 0); npc.moveToContainer(firstCell); - player.moveToContainer(firstCell); + player1.moveToContainer(firstCell); // Create service and start sending intents ObjectAwareness awareness = new ObjectAwareness(); @@ -94,12 +98,54 @@ public class TestObjectAwareness { Assert.assertTrue(awareness.start()); Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(building))); Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(npc))); - Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player1))); // Check to make sure NPC is in awareness - Assert.assertTrue("Player isn't inside NPC's observer set!", npc.getObservers().contains(player.getOwner())); + Assert.assertTrue("Player isn't inside NPC's observer set!", npc.getObservers().contains(player1.getOwner())); Assert.assertTrue(fireAndWait(1000, new DestroyObjectIntent(npc))); - Assert.assertFalse("Player is still inside NPC's observer set!", npc.getObservers().contains(player.getOwner())); + Assert.assertFalse("Player is still inside NPC's observer set!", npc.getObservers().contains(player1.getOwner())); + Assert.assertTrue(awareness.stop()); + Assert.assertTrue(awareness.terminate()); + } + + @Test + public void testObjectAwarenessPlayerLogOut() { + /* + * Setup: + * 1) Player 1 is inside Cell #1 + * 2) Player 2 is inside Cell #1 + * 3) NPC is inside Cell #1 + */ + player1.setPosition(Terrain.TATOOINE, 0, 0, 0); + player2.setPosition(Terrain.TATOOINE, 0, 0, 0); + building.setPosition(Terrain.TATOOINE, 10, 10, 10); + npc.setPosition(Terrain.TATOOINE, 0, 0, 0); + npc.moveToContainer(firstCell); + player1.moveToContainer(firstCell); + player2.moveToContainer(firstCell); + + // Create service and start sending intents + ObjectAwareness awareness = new ObjectAwareness(); + Assert.assertTrue(awareness.initialize()); + Assert.assertTrue(awareness.start()); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(building))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(npc))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player1))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player2))); + + // Check to make sure NPC is in awareness + Assert.assertTrue("Player1 isn't inside NPC's observer set!", npc.getObservers().contains(player1.getOwner())); + Assert.assertTrue("Player2 isn't inside NPC's observer set!", npc.getObservers().contains(player2.getOwner())); + Assert.assertTrue("Player2 isn't inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertTrue("Player1 isn't inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); + Assert.assertTrue(fireAndWait(1000, new PlayerEventIntent(player2.getOwner(), PlayerEvent.PE_DISAPPEAR))); + Assert.assertTrue(fireAndWait(1000, new PlayerEventIntent(player2.getOwner(), PlayerEvent.PE_DESTROYED))); + Assert.assertFalse("Player2 is still inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertFalse("Player2 is still inside NPC's observer set!", npc.getObservers().contains(player2.getOwner())); + Assert.assertTrue("Player1 isn't inside NPC's observer set! (round 2)", npc.getObservers().contains(player1.getOwner())); + Assert.assertTrue(fireAndWait(1000, new DestroyObjectIntent(npc))); + Assert.assertFalse("NPC is still inside Player1's observer set!", player1.getObservers().contains(npc.getOwner())); + Assert.assertFalse("Player2 is still inside Player1's observer set! (round 2)", player1.getObservers().contains(player2.getOwner())); Assert.assertTrue(awareness.stop()); Assert.assertTrue(awareness.terminate()); } From b33dda959827d4b81291ba033f9a142bc059d13e Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Tue, 20 Jun 2017 07:08:39 -0500 Subject: [PATCH 04/12] Added CreatureObject.isStatesBitmask() and a test case for testing vehicle mounts --- .../objects/creature/CreatureObject.java | 8 +++ src/services/objects/ObjectAwareness.java | 3 ++ src/services/objects/ObjectCreator.java | 4 +- .../awareness/TestObjectAwareness.java | 51 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 11a1d3b21..44db46e54 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -605,6 +605,14 @@ public class CreatureObject extends TangibleObject { public long getStatesBitmask() { return statesBitmask; } + + public boolean isStatesBitmask(CreatureState ... states) { + for (CreatureState state : states) { + if ((statesBitmask & state.getBitmask()) == 0) + return false; + } + return true; + } public void setStatesBitmask(CreatureState ... states) { for (CreatureState state : states) diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index 70fc45faf..e2ff828d0 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -64,6 +64,7 @@ import resources.objects.awareness.AwarenessHandler; import resources.objects.awareness.DataTransformHandler; import resources.objects.awareness.TerrainMap.TerrainMapCallback; import resources.objects.creature.CreatureObject; +import resources.objects.creature.CreatureState; import resources.player.Player; import resources.player.PlayerEvent; import resources.player.PlayerState; @@ -304,6 +305,8 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { } private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) { + if (obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT)) + parent = obj.getParent(); moveObject(obj, parent, requestedLocation); if (parent == null) dataTransformHandler.handleMove(obj, speed, update); diff --git a/src/services/objects/ObjectCreator.java b/src/services/objects/ObjectCreator.java index fd5546732..29bf30aa8 100644 --- a/src/services/objects/ObjectCreator.java +++ b/src/services/objects/ObjectCreator.java @@ -154,6 +154,7 @@ public final class ObjectCreator { case GOTM_RESOURCE_CONTAINER: return new ResourceContainerObject(objectId); case GOTM_SHIP: return new ShipObject(objectId); case GOTM_WEAPON: return new WeaponObject(objectId); + case GOTM_VEHICLE: return new CreatureObject(objectId); case GOTM_ARMOR: case GOTM_CLOTHING: case GOTM_COMPONENT: @@ -163,8 +164,7 @@ public final class ObjectCreator { case GOTM_CHRONICLES: case GOTM_CYBERNETIC: case GOTM_TERMINAL: - case GOTM_POWERUP_WEAPON: - case GOTM_VEHICLE: return new TangibleObject(objectId); + case GOTM_POWERUP_WEAPON: return new TangibleObject(objectId); default: return null; } } diff --git a/test/resources/objects/awareness/TestObjectAwareness.java b/test/resources/objects/awareness/TestObjectAwareness.java index f72eca6c7..08e2cd748 100644 --- a/test/resources/objects/awareness/TestObjectAwareness.java +++ b/test/resources/objects/awareness/TestObjectAwareness.java @@ -37,14 +37,19 @@ import org.junit.runners.JUnit4; import com.projectswg.common.concurrency.Delay; import com.projectswg.common.control.Intent; import com.projectswg.common.control.IntentManager; +import com.projectswg.common.data.location.Location; import com.projectswg.common.data.location.Terrain; import intents.PlayerEventIntent; +import intents.object.ContainerTransferIntent; import intents.object.DestroyObjectIntent; +import intents.object.MoveObjectIntent; import intents.object.ObjectCreatedIntent; +import resources.Posture; import resources.objects.building.BuildingObject; import resources.objects.cell.CellObject; import resources.objects.creature.CreatureObject; +import resources.objects.creature.CreatureState; import resources.player.PlayerEvent; import services.objects.ObjectAwareness; import services.objects.ObjectCreator; @@ -150,6 +155,52 @@ public class TestObjectAwareness { Assert.assertTrue(awareness.terminate()); } + @Test + public void testVehicleMount() { + CreatureObject vehicle = (CreatureObject) ObjectCreator.createObjectFromTemplate("object/mobile/vehicle/shared_barc_speeder.iff"); + player1.setPosition(Terrain.TATOOINE, 3500, 5, -4800); + player2.setPosition(Terrain.TATOOINE, 3510, 5, -4810); // 12ish meters away + vehicle.setPosition(Terrain.TATOOINE, 3500, 5, -4800); + + ObjectAwareness awareness = new ObjectAwareness(); + Assert.assertTrue(awareness.initialize()); + Assert.assertTrue(awareness.start()); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player1))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player2))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(vehicle))); + + Assert.assertTrue("Player1 is not inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); + Assert.assertTrue("Player2 is not inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertTrue("Vehicle is not inside Player1's awareness set!", player1.getObjectsAware().contains(vehicle)); + Assert.assertTrue("Vehicle is not inside Player2's awareness set!", player2.getObjectsAware().contains(vehicle)); + Assert.assertNull("Player1 is in a parent!", player1.getParent()); + + // Mount + player1.setStatesBitmask(CreatureState.RIDING_MOUNT); + vehicle.setStatesBitmask(CreatureState.MOUNTED_CREATURE); + vehicle.setPosture(Posture.DRIVING_VEHICLE); + Assert.assertTrue(fireAndWait(100, new ContainerTransferIntent(player1, vehicle))); + + Assert.assertTrue("Player1 is not inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); + Assert.assertTrue("Player2 is not inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertFalse("Vehicle is still inside Player1's awareness set!", player1.getObjectsAware().contains(vehicle)); + Assert.assertTrue("Vehicle is not inside Player2's awareness set!", player2.getObjectsAware().contains(vehicle)); + Assert.assertNotNull("Player1 is not in a parent!", player1.getParent()); + Assert.assertEquals("Player1 is not mounted to the vehicle!", vehicle, player1.getParent()); + + Assert.assertTrue(fireAndWait(100, new MoveObjectIntent(player1, new Location(3510, 5, -4810, Terrain.TATOOINE), 7.3, 1))); + + Assert.assertTrue("Player1 is not inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); + Assert.assertTrue("Player2 is not inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertFalse("Vehicle is inside Player1's awareness set!", player1.getObjectsAware().contains(vehicle)); + Assert.assertTrue("Vehicle is not inside Player2's awareness set!", player2.getObjectsAware().contains(vehicle)); + Assert.assertNotNull("Player1 is not in a parent!", player1.getParent()); + Assert.assertEquals("Player1 is not mounted to the vehicle!", vehicle, player1.getParent()); + + Assert.assertTrue(awareness.stop()); + Assert.assertTrue(awareness.terminate()); + } + private static boolean fireAndWait(long timeout, Intent intent) { intent.broadcast(); for (int i = 0; i < timeout; i++) { From 371309a4c9bab9f3c6c071999bbc91b44b37835c Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Tue, 20 Jun 2017 07:46:46 -0500 Subject: [PATCH 05/12] Fixed vehicle movement and added a test case for it --- src/services/objects/ObjectAwareness.java | 5 ++- .../awareness/TestObjectAwareness.java | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index e2ff828d0..e0739e5d7 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -305,8 +305,11 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { } private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) { - if (obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT)) + if (obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT)) { parent = obj.getParent(); + moveObject(parent, null, requestedLocation); + dataTransformHandler.handleMove(parent, speed, update); + } moveObject(obj, parent, requestedLocation); if (parent == null) dataTransformHandler.handleMove(obj, speed, update); diff --git a/test/resources/objects/awareness/TestObjectAwareness.java b/test/resources/objects/awareness/TestObjectAwareness.java index 08e2cd748..aa86350c8 100644 --- a/test/resources/objects/awareness/TestObjectAwareness.java +++ b/test/resources/objects/awareness/TestObjectAwareness.java @@ -201,6 +201,49 @@ public class TestObjectAwareness { Assert.assertTrue(awareness.terminate()); } + @Test + public void testVehicleMove() { + CreatureObject vehicle = (CreatureObject) ObjectCreator.createObjectFromTemplate("object/mobile/vehicle/shared_barc_speeder.iff"); + player1.setPosition(Terrain.TATOOINE, 3500, 5, -4800); + player2.setPosition(Terrain.TATOOINE, 3510, 5, -4810); // 12ish meters away + vehicle.setPosition(Terrain.TATOOINE, 3500, 5, -4800); + npc.setPosition(Terrain.TATOOINE, 3000, 5, 4000); + + ObjectAwareness awareness = new ObjectAwareness(); + Assert.assertTrue(awareness.initialize()); + Assert.assertTrue(awareness.start()); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player1))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player2))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(npc))); + Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(vehicle))); + player1.setStatesBitmask(CreatureState.RIDING_MOUNT); + vehicle.setStatesBitmask(CreatureState.MOUNTED_CREATURE); + vehicle.setPosture(Posture.DRIVING_VEHICLE); + Assert.assertTrue(fireAndWait(100, new ContainerTransferIntent(player1, vehicle))); + + Assert.assertTrue("Player1 is not inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); + Assert.assertTrue("Player2 is not inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertFalse("Vehicle is still inside Player1's awareness set!", player1.getObjectsAware().contains(vehicle)); + Assert.assertTrue("Vehicle is not inside Player2's awareness set!", player2.getObjectsAware().contains(vehicle)); + Assert.assertNotNull("Player1 is not in a parent!", player1.getParent()); + Assert.assertEquals("Player1 is not mounted to the vehicle!", vehicle, player1.getParent()); + + Assert.assertTrue(fireAndWait(100, new MoveObjectIntent(player1, new Location(3000, 5, 4010, Terrain.TATOOINE), 7.3, 1))); + + Assert.assertFalse("Player1 is still inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); + Assert.assertFalse("Player2 is still inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); + Assert.assertFalse("Vehicle is inside Player1's awareness set!", player1.getObjectsAware().contains(vehicle)); + Assert.assertFalse("Vehicle is still inside Player2's awareness set!", player2.getObjectsAware().contains(vehicle)); + Assert.assertNotNull("Player1 is not in a parent!", player1.getParent()); + Assert.assertEquals("Player1 is not mounted to the vehicle!", vehicle, player1.getParent()); + + Assert.assertTrue("Player1 is not inside NPC's observer set!", npc.getObservers().contains(player1.getOwner())); + Assert.assertTrue("Vehicle is not inside NPC's awareness set!", npc.getObjectsAware().contains(vehicle)); + + Assert.assertTrue(awareness.stop()); + Assert.assertTrue(awareness.terminate()); + } + private static boolean fireAndWait(long timeout, Intent intent) { intent.broadcast(); for (int i = 0; i < timeout; i++) { From f695da97f18941cdeab405afe1d38e785d688ccb Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 11:41:19 -0500 Subject: [PATCH 06/12] Fixed encode in Equipment --- src/resources/encodables/player/Equipment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/encodables/player/Equipment.java b/src/resources/encodables/player/Equipment.java index d1b0059f9..57b1c93c0 100644 --- a/src/resources/encodables/player/Equipment.java +++ b/src/resources/encodables/player/Equipment.java @@ -72,7 +72,7 @@ public class Equipment implements Encodable, Persistable { public byte [] encode() { byte [] weaponData = (weapon != null) ? getWeaponData() : new byte[0]; - NetBuffer buffer = NetBuffer.allocate(19 + weaponData.length); + NetBuffer buffer = NetBuffer.allocate(17 + weaponData.length + customizationString.length + template.getLength()); buffer.addArray(customizationString); // TODO: Create encodable class for customization string buffer.addInt(arrangementId); buffer.addLong(objectId); @@ -81,7 +81,7 @@ public class Equipment implements Encodable, Persistable { buffer.addRawArray(weaponData); return buffer.array(); } - + @Override public void decode(NetBuffer data) { customizationString = data.getArray(); // TODO: Create encodable class for customization string From 14a12920beba790093857e61db8f89b5dee948b5 Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 13:16:49 -0500 Subject: [PATCH 07/12] Added string identifiers to assertions in BuildingObject --- lib/PSWGCommon.jar | Bin 2485993 -> 2486076 bytes .../objects/building/BuildingObject.java | 34 +++++++++++------- src/resources/objects/cell/CellObject.java | 7 +++- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/PSWGCommon.jar b/lib/PSWGCommon.jar index 640a8be796b09a38cee5e39112e659ff55931a6d..7beb5640e181a6b55daab85c05418c5fe3d28e4f 100644 GIT binary patch delta 31969 zcmZ6x1yEc~6E4gy?hxGF-95Owy9Xz@OE$Q>EEZgY1Pku&8Z5YLkl>o17w&(nzPnXj zr+22iPj`==XXbDf3}{;e1CUkZA)yc;AmHF2psf}Yke?u5A4y06{{NnJvH)%)Ca*+p3;iFH)Jh}xR`5WB2z+DU=r!Tq7!!7k z_Yx1zukXw~H|+aMgilltn;;+{t{_rJaS6drqf{7RCkra*Br)Ee|8&8>cYriWPJBp7 z{Cr4n#$@>{q2K%~J|tguKdV z@2<TEE_9$PB9AwSJlCL!x&76RfoE(8Sce}dqDrBq1ZjbJ?JI!i~Y_Z-D~Ailpb%Uc5Q z7R9zt?^`C4%KU}i3J(2C-rYfzh7bxrooHtag-d!y0s3%n=C`a}`^t*Yr6fchWP*oFisd#eLme*mbw6}5f987Uu*cr~a63;t>ZUkD6h@3FFy;*)_*Sny#{AtC-}P&cAE0Epi0usQ>&K{vuy0H)r0YUA%(z#RNr zQ25LCc95?Pc!&6|qVkpMLnBf<0O#Es>K?$t+nXCk01xj@KaT+z-$P=Y0LZ_)>N5?H z2EN%t8bnbff6b~iFi9nX5UdhPMbrrQ>;Ko_nFEBpJKebjcz6%4Z684KR`xoeXuwX` zKm?-yC!caC-P@sRuSQp1)6WY=X8}@xnZl`X8||(E2Jfzn+`RUXHwP@rsXlF>LqM=o zf~$deuk#W%kgW0O9&r9P5*k+#fi?(lX_LhU4!o-<^Wp&0u^=Ge8Ismb-ZbzV)$xFp zSZ{KWEWi$wH`5NRyE!edQ}+j~x8c}$E(Bb9w>2gMM0(HcvJx=%Ehk`mHK6F5foe72 z9{d}ZZU7{EABXLBKyA#oGMukK+xH$l@Hf@9?{(IWg#$aQA`>Qc#X>h$hXU8$Q=AY9 z6nc+!Itpls@}|q50d#vC^5B|$puk(vSw1lJt*9})9C-WQahd9EFcx2Xxg7%d#}y?d zSfm>W17bF}H+6MQ(fq7TsEPUI^px?jr|a)L!W}S*iLn(?-lj~?Hf1KC))7Iht2EtE z3RlN+VM+9NY0g!Fo-qd7v~_+r>KEDw=z<7Edov7BBHO_J1WIqRSuo4~TC?w@clKqL z>*>q=BugiNH>?>q<6H+b#>*gsrm&fG+$4I>41}b-mAE2AH9k9oB5L!qu-UM>z4|DabE zlWsqjkRzBUwf4z?A;UmnnI5~gptv&gCRe1QqO)eAGH<&4DBl$ShAP0M)>lgekC~a> z9^^O1dL427Gs|Jzx*4d9KM?6%$y%-(`s9I%<-0_S_^Zlh{z%1p(R>ap_yO!Z{wvkZ zP6fDt`$7?rZ7!MnufB3ud+=wXJd@?!9cv>rylTe@xON!BR8K&gssqH4lCIRPqshEOQs(_OXK1z=eV`%G`%7Te@~hUTLc}} z>QL!{1b8Gsrfg_rvQ7rR{?8>I)kC6|Ns;~Y(a$!c+L(Mgr|-apj*67!pMgboQJ{ro z*A|C{M|@#2@3!16NjyJ%4%4st92bxxqy^r6fi7C=7Ylc zol!-3x5jzVuZ9+Xy~9i@e^gQNy$;mCZ-;zVeS+qdX6!16W1z3ridMG41XT!M+{Z*a z6WlunZWDzx83Y3ee$z0h=QVqfj_ksd7pA@I*P$Gyi~&@~xWX zEXl}WKvy$mE+{PbrC}>x@YEh@t{b_RLwOUczY4YlCD;?KIa6NnHi;x|`lI=T703hW zjHOgr3TS6m;Asp7kfmXQ$x5yaL&sEF|6D!wonf-I$O`RtW0TuxvCq3&A=V(uO@71P zz2RQ9rBlW+Y{hLDP12o5PqP9^;xzfD7aDDjYcGlXKvyw;b~%t-BU@A=p&KObu8>zb zExZFrKluckfeOf?iFf62+d-ZC6A&yZbfdb;1tH#>dOScULKu z%Ce32F}w0E@LVHmml>JPKgz{EBagoIBf`Gmub9lNU@;Jd{p9PAzo-3cjOW)W`IpQ@ z+GUqDpv{r$;Cjl)6rHd5er6*kS->s)9YFq$OueHU=-21DWghsYC-pwL7Bm3=w4$8_ zV0i|tKZT%GXlD%nMS=x2_SCKu<-?`_I?LVM3*h@LNI8mfkGlP^zj9lkJJUJJIPX@B@92#}C+fjqY#*$J2VvZ|Kdgn~!q8?g3{s)Q|YNS$Uq!YaRW} zck46ySs29@d?d9d??0CRS(xQ)7pBq<5g{OyiT>Mo@`ALzO)%$(UW{s6>z0t<+X#T#4X-Xl4v2y28AFrh2}Hrm;dsbCy(aJL*UhKmM)uJ~t2*`eGAJ?jixr z;v;_FibPkf~$D5IR!;sRhl24a5c->Z*D@-&LWExwrLdx9 z)nVmvFCb@zgJvE#(K0UL1H&x-g=6x5=-EHN7h?o<7qABm7FMzZpGyi>ZO9{J!)+>@ zAa9M<{)@ElpwIk4cjp7Q-o;~a$ppQgdec#`(F%0^9UbgI$q(x;)FK;~rlleBnFDFn zHpZ@@av4}OE8!+jx;Q_*G&eKdqLtg>AI4qCMTt@4CEmwLE<2j<7nI5hYnBj-U9w#0 zxX=v3pU#9r;5YXVO_kAW>6%YJsIrYqgG3HWZ5EHCJTdhDg^SCIT$WZn<`qP605d-I z;8tFiUL|KfF+<(XVoAfugwBC%sgbqk7Q`*nEKpKa;Zou}eqiV1G&Ig(SiEFYGOB5F z`A3$59Mam_0&N(uIgh<{ zi%igbN^j3O&RdB&5UoRA+;0v(X3Y6b*l;CCf0QA*+C^Ws!nlFhsOec}$5qg>lAo66 zNeS!YUM_ze<62a>8f|^yFdjMF+;|{=HE6FJz(I!uljYRo$dx1|CQ=U{Os$SKggOe6 zv2>@dA5zi^+Ak-~(}!fkW*(IE_vzs}TFOPJ9NTjl;Il#2?u2^OSg}jjm)($vw za+sswKxYe}YK}-kXWXwonCG9(aB2|D@+Qqt(pZdD?-%k}`L=`?7N{f<99z7!!;h}- zBsL3lEy3T&TrD+($_mC6X zW_Nx6JR(G^h_CBWN2;knsP7{YhkTg&CmNPB8?u0@z5I371f8c z+#=M324cuJ*vwGR?lJe(Z#@Wa_2J?IQI-$ zhWsBhOndraJFMJ983@lN~UrdM|B(tS+NQ)#X$`r zTjK&zu;0#+zMGh(Qp?rpPRzUxOa6c8Zp{5r^G@)=*5cmV-Zlopnzs6-#NI1if}rBGF3(1e4{d@=~e@962wZ$eQAXFkx<$gh9EWbX5@?Z4WiO0RL@s$mpy&Qh1 zCoJ8AgHU2J%VSaxXo->%!6vNCHLZeAgcW(5wKa<%R_3Aw(Hh-bF0RhFqBtJ2`p=WRA1I}Ds0sRv#R}-#O)iTetFj}7dy^hFFnjsvB~>-6fS77K&(gw_gPJNUXu~O$xAD_@fcIv_S^SZM zBxYH@PI76J>>5rKb&=UXe6zZvwdWdtn^659ys$;0tMI_#Wh1@VQIDM!Q}rhK7^9TGFq}yu3O}iy-XojOa(Al~calI=*yby+c#v z%BrGqcN^l75??Q-25(|5UOIp%&q-pd8U%FKbpxlk=wy7?ajy3Ra&||~!^kS$2*FJ9 zGu^HcNJ4N|9xqO!x#EUx3}4ztS#}9`1a;w8tf_;g2TsjupV|TC7egBXjnv=Jpi zwcwJ9j6VkjXoe9?1_*z}D;#>}WMj)8H;xazBs_kGv9h3|=H*XoZ~7u(ZKqAT@HbEI z`&FD1_0Zif8f+fhTnVdfmJ0+!duKhwok*LSgTDGpZa4|iZ7IrTW;0)^1U#x7`Ev+~GIA5@78Vu=b5%BjAC8!sa0RLGa)K&xS+q zATQ)apU};Y=yxea%uq+r^-O}8H=Qyp?zgym9JXy2OJm98@9DloYzdrtL@AJBNN2+rq#rAwlVS80 zO__Ma1AD-|pD4mF{NP3Edw00Y>Zsu}>If8!Ukoy3+8_GLb^5gHzW#8A_-xsA*#vUe zMw=3>f+9M9nn+0ds&yIbj72w_qAx3~J)`yo;nPBBdaX0pepTCHfFJ+UAa3bozzx2F zh)*E-&*N4T*vg@+qkt^ph=#M6aANlf11iF>m(qtTxO}ippSM#Ww;FZ}{$Y zfsQlm?_=Eg!^fK9X`WaP*=nnl3c~&+OaUv*n0wURLsBixJCrR0?H5etjtaH|W~gr= zk-6g4A98owYAsmc-TC6$LO6NknMR`a*`2QgIt|@t59dMjHq$udK@~p}$yfj~_zwlXydL+s*yd^-5I+vQ_ z6GjP{E@@sk*_R`^Z~Yxi8`wgUoz*v-=%wDg$2ya&`^wpX+{Xr2G546uv)T=vi3C?%0C9C(2K zu-Yz3H;m>wJaiB_G*J_I;HBzmZ5*EUZcH`gpN7RMO(FU{cR36aJ-4|ye9avoZMQwK zM+fz?W(c$*{arCCxURKf)i92mNd3sZn#Q2Zto>48@EZ=rKGbaV;!owG-STGDlET;2U$hwwm&@gV2PT9oW!Y<4W(-o{wOgvw!Qwl`bEHqs(AwRs zvev1Bpk(4j8p?_ICz){5NPE3L6gm(-$MX{wWnWpo>}5^@e^B2ghWP!$(D!}ardR@U zrCaG#4vmCDocMhlOQnR5FY?VGg}*ZV4pCWrpPa%@n$a?0HkG#-3jGz8vdLWoDGg{$8+4M3+=>URBx|e+6Mk5`s)Pm+azt4_`K%{?`%9k#fI~T z`_#PR<2~a4e%XUcpiBRQhPBV7=@%~|)y(@KsPo4^v&vb%mTp+;Sw9;J^#uZy%qt5OLJW1wO5$j_Cg}0;d4t?46fX>H8}`3)u61;9Y0pmZ*&kqrpWWx8ok6l85BhIOp5XS8&#or zQec#o@>uQHJZ=@azUQy%F+NHLEC}Cp2^B+1l4#cRIHQv}(6@hiA_RW(&Snn6ilp59 zQe~>d5hN5nvxos^$IxKHMXBq+r>|ym!;Ss-Gb8V^?vz?nFLX=W`O#5B&AvO=ZhEpH z=)=g4;s}xsW{m>z5AF%&D%!UE>} zibJG6rjXg`i0%Gw>Kj*;A#vWP&0AGS$+y`{SPe4w9b4~8xk`J*^C2i;z&Rc$ME|t~ zbR()Jq%`E)A6&xx|os3X_L~Y+YUp;D{Iy1S!Gv!-?d zUu;SLU3H=Y#~T5CQlTU6Uz-{34g9$v-D0Xka!9aSu4cKdt{tWe&XmoOCM?+}JI&0N zomgH?(4YG}$@_>Fn&#~vgg*I4<0sh*m5<+R1{?V{4C95_?Vm*i>H_N&_1t&cBzv^- z%;(6D4gQXB9rgLIPkClBYZ}ZBL0=Bh+eGoxxW@4J6ksBmsC4@tXrDtr;N_CWLP|>@_xj4ny_OQzR|}E>&EpH4SD4Q(9jn=S@n-{YM4& zc0q?;UR5eOLylYYtAVR-KiMms!lr2D%-NBCYTKmdWfKOa{$i*GbHbT~|JJ&wCRylO zrNkx;xGjs@fJAETHn-{)CR0I8ErSgSrR`(b*okv8ZUO4nd^q40-GJ&oBMUJi#eAxX zFvq1ML$jt4mK<8)S-Er88^Dn%(xh2b z#A0_7Qvq_C=brd}SFIHPm$F95FxfqtG(+c+*Up#Yn8@s#jS119bYO}D05somHUYBu zMzQPL$G_fnRQgb>l*Q*ILmIEiYkO66?nC6BU|&Z$uiSF1-3)cj2B6MWdr@JiAAdX$ zo2Z*h6X~Xt!zs#ZolSWORr_10TkCoXMRb@m&G$Db7vm=eno}2+ryrB1_yQt*&iZlL zBQ0IEQh%LjNFvQ6=E0+IZsBrv-7siI(L3w`&0o<-Yhq~#?j@!$I9=<5g&nK3yXgfV6^!?s%ninIE9-=PB~fq}i%?_1~$`BScniaf>ss!R{`QfZLvB z07Pg|X&Oc#*(7CD<-Qr^U=Vh^diTL0ehqAK$3S&Q?~uGl&C?P3W3+D^-@)0OzCr#H z*~Y=W0Daxfw4}{%G-lbUHQcWiYQ5XD=sr9244jMLnY^KNlDNk#+Ns~`z>~9FgqLh7 zlVNJ(AzLSn&-}ShXl9wA-M6S&Im4gb1Jrp%9KQ$q_d(p|%T7-mm*ByGxR*{wPWWkD zQ;o4t+!h;)L}fcKsqQ)aGY785G<4{8Dr^o6MRbaE z&ZI0G8&o&$dLAx@F&pH8f{rzbCm=_f+>Phs9e&K9ADOGVabq0ZXj7Z8>D5gJJV-9J z(a6M}&CkEgsc!*ashC%v5YBTjTEnB4`Ej&_&KgHhtcG$LHZ9d?s0yvPL0*+l zE&oWW-oNmb!hUNvhb2t57zyopRCT^zAO6Iosp1dFglw~g#KM#9$4KVXpD#94eBRvcNb=%tO*}NPvh)ZxsKR0ApJ(*}+jS$-(lw zlvwV1J1@mSKwH%|a>d-a9Z_euqx&mJtDW{jjDK_o`9eFMXjske(D)>~Z&DJI_Y^l8W7 z?5QwE*m9x9N>`oRa4AJxTavewp=5@0*X-kzCL0R-H8-BWEC*<3m2+d#mo)AeR70C4ODM9oahA+^SrYc-P zEiy=&uuQ$Ys}vChCva9Qr*%mrFrQfs#=Uh_ghqok%b{sPZ_Z{ z!K$!lH|1?kA_#PB0%PR^t}`N4b~L%E`70#Ehmc)GLXH=88};dQB3-sw#D2nnH+3(u zD7*b482!TCk{`L7ltEOmro!aA^T=>`g7LGjewzrfTIqKOd`CTVBKsrsey?_${Lkta z3FpIl!a2h;vhxjMwN*~sC?zqu`~9!T2V3Hwz4_$+@KBIvUj^8Ydnw--GwuS3HtTQ4 zDcgs~I+~`BiAybeu>)KHqpwj~Ts+zC^vyr(e;D;jYl}+@NM{Q0&$~@&BL|4MN?Wa^ zpVTLY8xYKNQJme|7D;nB*zn}5G4_eN!0u0v>)Ro_*es=yVG~5w$E?tgNUnwVo13mF z&BhlI;xmDQ5Y~<|E*J`XRJRqZc;y1k+i}a4Z)F_=!r1( z78^I+I_ko8w;2`vl=f*yDYdhPd6E*xmP*Ts0>7^!EdDvbedmh?0NMOG_Zpysp}L2E(`*RmLeFGRPKAG14ByTkC+9;-{go-35Gs zVOs?m*JE67vs91_L?`sy6cc}`@NWKB;d&w7 z2PaVd6iErrd6=RDO^B)`N$iYN@(qyoOLOBV6)hcx01D6UM^?F8GZZ1tTV}8OFVfhK z2%0bp0{2=$Xibj#Bd4y{RGZhYE z3%&>DxsY-vcJm>H9LQ+=1@3Nu_}4!0YBf~pvj(H;{FoESYb^1-zFoHw1)EB7>sWq3 zt}jixp)DX!p=1x5RFg5Evf^Psp`Q@V-7>YK@!+9P;ISq?BAtLm={$-%~DvuRd z&I0F&yjewrGa=s8QR7VM)T_gb{YNU}skn{zx0rfhaszh$4ui}gcjW3vKv87~?LC6( zZ%H{%tU^we@EK=lx>fm_N9x?SKOwY2GIFR`xt!`9;_SE!35kT+x`Gs-&w^Zo^Ycq# z&QHMOou*}+Pm?i0c)9Gp4zUd%b}0z`ldI#LPn}aIX_~g2H8zK{>NpsDJuG#X5ffoa z%(3Nii0FO7%{-7v_R(jY&@pqAqIUYzTYBZ>vA#l5E*Wt}OMa2=hy8ZD8GOCtvqg#x zJ1?Lkt%To~*!4hulM15)l6jKEdLg${^>NVk(e#-ZUUTWxGS1YG7nD5QGB3fz7)q11 zS}T;kY!$5XkBa&ewVHA|vX$waKd5KKPTh7_ zcb7LOl%&!;N_FIhF5}qd;Sz5?&B|NZI~K;9n60g=_nzEgcmMhe=wnWwe3m!fk4>!6 zt9@1@hkn1%GpaV~DJNYT!XaK!A8>8ca`?Z+ zXGH5U@_RZ@L0eTmC;;Z@w`GAL$_(-B*^nN&e}qp=2BArO`@}il*Cj6sf=@++ih3<* zOEU-t$EM#q5+6e=?i$;J)R1#D9WRtNdiHDQEVYV%W71)_P3vvfhnBZM%f%v5M$&OZc%xKUcwmdAYTdUm7re<=9OSy3GS8PUkTxr5MK%D zm5^Tv<&{uh3GJ28U+KduVZ0LND`CA7_AB8uGTPy4S|b1A{UxWZiqt&vdQ-6#96OE6 z4$^mA=EnTe0P45N+3ES|+0B}(8`_edR?YUM@N)@_3K>`N<31GA7Din14ud^UuX_)ADt|&NCe$j)4$!{Ui+-d zUvjk?U5T{^`w|0%?!d4;gGFjDYlfx-N}2P=EdYhepE>WFPk*$UaSOb)FSh|9IMT$FwkfWJcF`T z5T8uCQwb|{5F#QsRDYChU4bCesaAgquLa>DuoCW#&6Mb?Yz|R1vtZF@S-R5Mi_K5x zv#5O8}PB;KoY z`0vatI_)IJuHt+DMd9WTLc_E3q6AfNm}Qd%B`_&S`hV(GlO|u|4BR3c;o61|UaGIB z%A8=XGDRC)vF6>5B-mNTx?l=+MR&1C$iE?0L^L8V*e8f{mpf8IaNkUx$>ht^#A)jC zhfI^jUfd-!Ng_7l-srHgRMUZezSR#cqlYjcaZ4{*?kQl-^Pg7oi-q8ms~HjX;? z(qA>*b>ts!((0oqI?%Gpgc~;w7H2k-BG@Tnz;MKsMTAkEPKB6%nRSS#Y< zV;ow8aG(nl1&o&HyQ=S5>6%y8Yn~>N{%vPDB_sYqnd?8;e-sVFwBbAX)E?=?8=597 zpZK?3?P^Vm4v$BJde_6B#yb!7_M|%p0ON* zfKiZghiI&&CsF-r@3IS&jcE+kY?G7LLz)h3@+wc$XB$o*)2B3pqmS$y;X2z;vTJO+ zR96Y34yLZtfBg8}dy7YjNtQMXewv^|-Q=)K!uCp@OK$PrS_`rHXLHVo)+e5(&MSs9 zWY2U}Rw83m(Z0#vlb*v3KlUq;3{HK-(b2CvCsmRKpS~|bQ`~xi@+3mKVO>Po%)YdD zheS+rp7o;SI+zLt*-1^LIn6hQM9MLCa-&J6?m)4_?Hl2H?F$y)h<;*H^2Lp!=%{Nh zd*MLS6v2<|Jdd}e0J(Z~5#|W!hrqhElp;u8pf%gXNK4?@de7@S@u-y&8mR;-?f)}> zx-qGw*Z8QHD(|xoivDiW7W>3Nv*xU!E}Gv%vC&k@^QG)sC7o_jO4zaNNCOKYvUE4M z-Io4A?uL~WoD8_)N_FC?PWmj0I7IbLY-gc=Uit@F{UjVi454!D01~S3*uhAIqvQc3 z{&T*DgVP#lct@LyeE*n9dAdBmeakt{>oIMt^=TE`HS(OyirmwH_ejaSV;X6o#H+(UJM8-JdF4OZ8tUecP1Afak?I^%5gu zyHH{t+(>S?+OiXDeOOM)9rdeHw{(BS?;P&i5?{14M=HL=eQf*#f@u7*fIAG0viUmM z1D3@;i@lySITQVN)tV686-R{v-iW7y1LrS z<{`(Z4HH<3D%|;UsEvyf=2Ws{EXdB<+F85axcoV-nWNn>G{K-ZAWX#giA?3k2OCPx zG!Rx;h|2G-og?5Kzj9gv0`?uqGA+=znA4yp*dNSBew9E9i$T#XFq zl_m^{P*ddu=-YNv2A`Wa$mADe8Ac7Q@j=7C!Z}};F&Xes^(Q+Ny$Z>|NYb)7&fTl# zAcLgSnZz)sI3Q*iY;nX-D6CL}Ex+{q9^IFCxOVd6gDHfeEmn;#9v>qTP$&3l9?91z^qPDqYpiOuFmmqRh-S)?#+eB(>q!QE5LR^cW8f2DZCJPQj>8E@+pH9 z2?AOUSwgc@qwi{@C;E#ZRQXN1}}0Q$g8L#NP<)Nz+u*x3JE0V z6a#tt^=Eib&GVi3NO0X19^pdSrhHX)w{wv071Ce2!Ka-jfcrV46GuLGgk=f8aS}fC zbT=;_*)(%o@8vy$Xa1d%+ClcG=zH-FBu7fyFUidxB0dyz#XpyMPlD7GP@q91*ReiL zrS{g-GceMkJ3JXe<`ejS6iwMe=9Dco{Xf%5?qQ%Lf8{k6De}m~igM_~_ki<%EhTfbT1$bc9v%(T?$jbBH!aBf9ljtV#EW@rb9Iz33QB=W*45I%7uC((Vv> zhXEVGZE9Bf{pXK~SC~A_aAC7?pXG3A)xnqF_2WUaH}Z*}_BelINQp|) z?t_knSf)h(6rsyLbL{iQ?JIz0nC4n*v2|~@^aj@_4^t_KlRr84)oZgkk&xP+hBe|! ze3y3t>;fe&WGE?7sy{Q!A>na^1*5=VR8J9xD4`uj=GZm%`J<6>$%j4@e#dbbcQpzh;b@ z_(m5(xxy5l!W@ph%q-q8Alt^FaW(}-rLGSUmA>aU^!e4DyV@g&t!W0!LM4l)%hw?& zSkpKxdP|SQUTJ57ZCH@|S#H<`11ctVnFj2mGk7N>+!76%^VJJxj79%EIrR~ysL$J; zdOz>${^39IHH2n&_qnVNX3EGPyP>d#WakAHOQbh?y=3A~C{=>O(w=&>uxnmA@*0r4S8eE4)^1~bd;$XIwom>}dyr}6g1G#dX zqq1|wuvJByj0{=v3JpoDmbY}OqaRIv`3^oe3smDY+-nOkze7i;B>F)I3SRufNcXf+JJ^a2UWe->1){t# zC-=pHMU=0O_g@25i{LliNEa|J`Pi(djqrQX(h9XvRzUjc@ayx|Hr6mw-@=du6%x<=E0~&jb0}K#!*+ zQ}*1OdX}Jbk>2y0m3o}05aBw1TegcJ#sDEybbrv_uyFna8S*?4Wj+ZpdgSLz=5xxp z^a)CdJAq#cJxoam6S`wBm^9CdUBQ?e~ww7F(DXIxCM zs{=?gt>1pQNY_vX|5F*Z&BS<`3{8_s&j%DaN^&Vtr|$``dk+wNGx-t+$7-1VCsMxX z^!y@*d@&44>`_eU?A>jhQaT&yZ=u}^kz?`uHg^M8=X}asbdQjg!Wrag3E_MJ{N`#& z(KxzU(CIrV`GQ{$l7uC$#xaB^X>0%_qWB=$*p6gLpMWnjfflLaUx^vKX_HgUvI<}e z8ml?&^LZ$BY4a_pRI?;%sAC{aIjO#%7hk?0yXA+EB29RYP(3Vj-Rnbu$v4Wxa5Q}> z-M>T-3mM_-Sy7%&MIc&Xt|>$@eulu|Wd=~lni9MiIjvJ2HE}~={Y|6(M)iqG8xkTVkWL*MvPwTWA4U-m2r;XwmTl$pj(sL{sZ`l&i3KNE^UTIZnndEUO zoAH>Q#c3`t%O!mJ^}^st_<2?^t7d~C(FZbZY9a<7Lg*HS681I!0!sTG4+F2mS~Q z74^l;N@IM{e4%>rA8G>(dPefMZa7&IQH7_gIi)LZ{OAqpgH3i`wXxara)c!r9 zJPR0DU14cT`058>P!aaKI{IUlXnwS3|8>(Q{qlN@OFgTNA}L35VUOzC*7R=ga-V0E z%D|vLv9Ksvm=MLzJx45hrMK?mR(a8kAm1+fsQI6{h`GkvkCzI;!E!W6M4;d`{BfW{ zxl>;GWX}|X)XlU&1Yc5Y^xpeqy5Y z10=dLXf|Sr)6KrY+LN;u)6&33Ghn0_#_#Zh59vKC?v@L;7xiPMd_W$Z$Br_)j-jJU zlqcI58o_-io0Uy;;oLP-LSGjO3o8jyu64B8nrjgkG9U5*38Q$$em&dRnktsmN^HTH z4*{4yORN{7R%S9yYE6pwmQThyIn{kFmNip9#{dw@&JG`me_a7x@t@|2{Ceu*^XBkS zT7y*66D=`(vn-{KRzTmpM%bIvOEZ}E?aAFTPSX`GRABm;dQP+0!)+lR;xaBoB)lF9 zQpN=og8Mp!SFM}t6B<4%>_1~^t&KRv&RTv2)dlva^vQSsMA)#pME`D+ndCR?#$mfAFsU@o?{{L=4312i2oAiqqA&Al&*; zwt*G*b%FjKcwqKt^#07r{;2I!zOkNXz!S^<$*^P5j3mb%V?ZS9G_+l;? zm-P3{Q;lW|^+pZkOZig9PU#zbiDlaneY**c<%i}zOVrAIsU-%; zlwUn5$T~ERnCO38{-`O?gPHUdh<;#&!L+?X7h1{RZIerWq51R8p_!RKuv$dVyE<-0eoFo{T;x4&6HN4NM-`FeNy9QY=UXAV-I z5gGWBYbQS>R|i)#Bj%4z3k(P^-~Dduss(neW1R=5JCI}w{(^L2*N246s zd+-(P0NKNlOUSc1#~(+?C#-Xn+{&MmzLVHr+eayB7c92?WUBjPUT2rdjcnd}IWlCy z(a_DB#tt8F{QtFg!TP3{46dDoqm2lMD?+rX&r9i==u=O4*4F z5lU&$q%!5#EJKr0y++BC(o?T^hTl5-95?s9KYxGzuFvPVYk$|U*Is9zd(Ovl)-wJX zcCmiTj^aZ3k$jaQx!*rum2`4-Z|ael)0io1A|GkhW&7)|=7sQ#k@PeE&JOsmH!b<3 zR|({Q;*|LQfx|2mdSuF0?w8NIziV6W!i5GthII!N4CNFOr**6i^+OgZ+*9GFP<9uc=fS${KV#8$S%X zcRt!g`BQp!R`WdlLs9!{?0QXZ42&#VTA30#(D=d(PNkfg!AyO9^}LzK4E9b`_Bi)I z-`Bt=ce3-CC^<*j4GveeoF&dq^V+AN<6|;L+xyJ>$($`4E0mW>Zrd7N5j*+JU|-V` zPo9xav2oq$^%F-#^C#X@l&LA!9d`cln1dfn7rXFot>|cVx{M@vo;PU8-q|!gSTMYC|E$*if4-MlbRzc9=tZlp?|B@)Xr3g0{zbEt^CLBa z54MFYl%AF9ddermZh=$rv(@uA1&OtebvTt7uQaal`R&H#G7;L@V-FntAd%a0EoXQ7 zDeET(v#pjs+-|h^_o`r|5ta62oC{Ll8s|Q~;fH%C z&)w7{?m5Pxps>1xN>`0ZvI)NE^`J1#xxn6b(@k6-`=F<&{)^L>v~{PqKD7599bNE7 zt-;K9Z(hm0)W|TI?9{JoCp0In8nJV*c|%O;kqO6ZW(K>&B(IDU!6`YPZ3R1dt2t9P_Fe4aU9?9Gp=hPNjl2j_ir zO?EogG)ggXcFW#L-r=V0^0v#q$J}s#s(B}(qiE2KIpb1ZpG?cBZ*gdIGd^TqJ3eIp zvY)5j3hF<$ExTM7q$#IuC6y>uKYvxVlE^omE~|uqI@h z)NVbK3cmdw75r$L)erN|Cp`Z|x9ke1?TRgTEc`d?cg;~wIN>B!J>vIQUQrIKvYmpy zme$%AWQ6Vwe0F9;uZdB@wcN@jiSu+;jL7NjuyikLl#T0+f7;&JSXi`m=lYqdZ#6Gn zkGgte@xq4tPD7u_Y@B~3YP!|9JPt99Kb z)#rlvw|ME?lPZ6GI$l+2baC^jF#T_jukEV)dSzdoQJh{`dI#rZecN`APod>2YUar; zy;7#}wEghI>HM18+v~afHU*WEjxOZ@dyA5?+eh-c+b=9|aZ|i=!ZknToL!0RpA8ll z^5Qa$+efrMn;Caeu0GS<#O||ItnN;mM`hHG21|7~cyMUIp$Uf;9NKW`z@ZDrP&kIc zG5k`4rCxcFC`TvC(TQ?&q8yzlM<>eBiE?zJ9GxgfC(6-@a&)2`ohU~q%F&5(bfO%c zC`TvC(TQ?&q8yzlM<>eBiE?zJ9GxgfC(6-@a&-Sojt=r}>YF9;Iyb}=q2KI=3`jjM zoa2sW&~maw5p1HLM#GyjVNf3#}xqE-&Ou2p?|* z9SOksdT*9xy%wz|NcLJ5k@7)n2omIjLI~01gH{p3$rnWuqS_Y)6Jp9bw1g0uJCFu0 zUWd%c=+ipnMhGWA*2aJQ*yWD!XIVS_(KI4c=a1$QLO+0YGbsSgA;|jxHd>Pd+0f<$ zva#t2L>q`qND%U8go98RAy%$u?bNJC8woN!n4L5~nDs|z1KLVhX&X=gA!I{PAR+i6 z$cGRuA#AW0Ze&@vHnPGKLfJ|4L)l1ZZ9;xTYxgErxMvd^E&gUUwC^{w$eJyztM9g; z^+aniv`8TY^h()Ah)rRv8}GtU3_;e0qXlW{V|V52IQ9%q-oc(z*fNep`11~Se=XR_E~j=UT1!|~yI5A%F7|Y* z{>E-k{%>sP+J9pW&W&gHMnODsChFaNg!688_OrXu3c^y{!(JspeMHS3w1}`qCa|+7 zC9rlH6Ijc(d(liH6Tg?eB0mBlOBd!7bb`RniO5lxhJKYq=Wxv7P2Rqroalq+@xElF zCV2Nsofz#(L>konasf*jJ62GGu=74dDiUJ1eZYs>zrR#ReNdCeC-xyyVGx(n{AZQ@ z`0!fS0Niq$Qo_9bh!iQr=KDct-R*x0h3!Xr#OX_472mIds$v$vs9D*`RP*p2IN`QW{??P?EyVgWxSQrzF;0&m{2VUVT4)dJ2Z0);GW#(e^^KtYSq?DR(rrx)wmK*!O!r@*zR zN*vBuAtn5Ru@6e>_+uhcB$Lo0Oe-1=XP7`_+3RG8t-+r(V2Pt(EqDrM z!w%b9gTn1JOO2_9nTFZDSAc=_m}R}jXdLlSaBo(_Fkv=*BtjfU3%E~#)hqlDb#nH1m>Su+#E$e~u%o9fhU;iM znc8AXWa|)K5X}qIA$q2`u5ONjGbCFmA$Y`QUSg@`j;hfJZp@Y3f>K;hnP#H z1)y;a?Xn4ON<})12WbG?P9}Q8@vJmtKvqIK-?tAn1uLQVp|qnIo;x3DjdF41QYvDw znV5!WOl?rmse(Nwywx2TmEriBqnOga^!{1kv2V7V=r2xWs^N5rM|B*2gk4Ge5pemi zGs{zO5tB0dr!W28AK7P+L%V+-_Z~qOWa$&rkp{Pm9vk7PbTpjosb6IcIUH+&CVf?9 zEJ#jAhFI51T^@I(BSjJ?p)!X;;zO1Y2leojt*k6vdrTF++de_90}~?##z8vY| z0V~Ah{}mvi!el~v`W}oP6e&|8o4bG224f-7iP$_79@?Dz#BkPclqPof6H~z2AjWbrGLHN}@WWtTY++%17b#O7ZKMvPVVilUiwZ;|4&`Rz!YCZ^${bI)wc)6I zw3Mof6)^3%z1Z$JT1P$EOJizS_bgN+tv!w=QJqNw1UhBa!D1v+2$G}_sfII7KuFrs z8IkgQSy?)u}QJ4 z3{H3m`r$Vj82Yyj!5KH9lUv&(IOzjUAs%IWPnai#&uDT7;-E)h?nbpR51O&5a#iuG zQ^33bkl{r=L8`bs2+YkX1lZ$QKb4b(aMr&6-Vf0_jjXBb%>vbhlwmtFa8ckHW4SZn zZA2%7kPd87^>BU`^)N_cB%HBK4W2rde+q&FVH=-E69tXXl<{5~)}#9l0<3cweC;_4 zPN^#~2()t>f+Nm>40nhCk;SzeA$~v3f#7i-3m2iO)Cz5Z%mCaP#D!l)AmcWSf!i#2 zgYlejB#FyydE)p<4us$~l2d>NG|W#<^c$Rc9`PwnBRup13ZwMQ7)@y0HZ(bqCxQ8w zKu7i>oCTGMG|Lh5r^-n)@E#iG*fJVlULsq{Vk$F;^yCevWelltjtmlT9oEx%37k7J zmqFkiCr7Gw5j`-Wo9BsRg%F+!_J08P+QiF999u3I@^n5PT8!3G^*#a(Wn5JZ1|0$! zBy%G)fShy%kmwCGf^fzg*oTFo^gs)jhe88GnXBN_`Dg}#_Gt<@vV1R2^}I*psoMAb5a&`rqW|cJRJ&5r_+lxj7Jg%3g%4;lrQ5{}R+qum zZy<%gmcgn#0^m!gybPIglW|)ae97e9#EQ*umA{UMev|9U!HE1l8qvqL`{2>Q^q!go zzE=*7)m0 zIFTMa?PdmTm1rKf0K2pxUy3J%=eB@A5Ip?gXa9kPCo3@sJY5aJ7B|3r9gjhvhPf)v z_ykKEt0$0QAD;O35T>3_5TSp)jVZL!SEF!UE7Ha~j_|nd(hBmzZ&q+PBZTA~$20Qo zbHpTZW-H9CYDFX7)N5<{(~!QoE8*^&upc9C!Id?^hEZ_Igi~y1qb`o+K0_!)_d)um xD7p{cloj0v;TGy4x(|x(gQEN3|I>YNh>N2H`~io<@#1=MeG@sHYi4DOK=Z?<%NIG+3(*u zQ|H!nSC_Ti+fU!=SOWtNSHS>eWqC*_1PBN?I0)bH=>+5_$k$5}5@a`9eD+Tr#b&FL#|i*-fG@R_g#JFMc`Bf90bH28U%za_=^cLeNq5F zLW7HW72KQoo|}el99Rg5WjqK7zBlvJmefcMUmO_T_PBFTEtPl36}L z2#8H6FgiRWQIaD+5|}Lj8K!~AC-N=oq;fyuw-^uoO5Z(B`g-@CkC?EgcO}l*q|1=d z5D;Ss;EN+5QPQ*@0=U(V8nYoH0{JbJr1V(6Hzo72B7$#r4*+0R`&YY!P)UI@L}0Xi zYBX?{JvDxV*{J8c-Hq`NZ<6_&nQ;cM@fJ{n*_H5#z_mlj$Y7%me7FX~2|Kj^v?fih z9==`M*JR(Uz^;?M_aVc+;Cm`9Pd=f)Rki#Bp#H}+cOU`E-|E1Y7y#wBqIL{`|69VF zg>V5(Z4q;x7eFC`2g$h z?f66htB7yj;mHBE-+Hn6Sp~50F1M%-kcWBGiaT0467i~46BZm`0O0sfD+aj6lN!4@ zTo;h^CJiPr0I0vIax(x_0pFU5y7QViz6NCi4?f=k5`mQ&QMj7}z5qb)lMnI=ruP@% zHL_O`DR5mSKmZ)=O^w>DZ4TIan*q%tmH@eTU!tr5RFH2#R3GVeH+Kk1i^s# z_uA?(0O@;rreXom?N;uVr8V)@5>oq1| z6=30QfHyDK0~X-lA|Y6@wfS#g-y^;WgPEJCahuUQ0T}Oo(DVU*zS;af3V3{X_un{x z={*>xNr3#jlio7`>GyOa4WoRbc+ICY@NJ^JkLB7>YP4p!`Tw^a?*bs?-RbTvz~g&x z9s2;vx3bsSNDFqv0U{9pU-^_n>D>-heO3MYHS>I6bXFiGI3S!Fui54Xp!e?3=+v~!Pf&G6b>=rflcM;&oyS{N5 zAkur9R}_JyM_u${SbUh&X`$X-u0e;4ME5i){TECC>q03a4f!CWt zEF4%24wbyQHWaw_p6i52pzwRRGf_Zul(%*Q89>*!^#EK~02F*HIx7ICz7;ixR|4+1tD9lfxHBx1aY^Njdo7JIGk*=_nnqu& zSfGTdMWP!lu31bN9RIy-UR=J~7wvw6*Z11(zxkll@q_$!SxY60}+HS6!E3i zM=qnZ7(ZdJfU8hU4`I_8Dh4kJHaDR)C&q|q3i`qq4HJkA-B7@Sy--oi3?D8Ts9ab7 z-C&U^X2oqmeax(yX=CgoU$-CV7O|5R0BWosXavo`uTGcnEFxs^SK^FbH!7+}T6-ySyFL8j`?pwcfNiOx zHnEU!R5r!{=EDHXE-1@q@KXg6x_y(+d(pzj%WaARgO9!5$RYd_*!OgEw^-v zAb8nUoZb`-94Sm_)<)DfIt|~CqldR49Vq&cIp%E8oO&k{s?X}vQvupxR^yfHP^I9n zE9fdWAFEIqUF=ZJsxXgHxA?6YvApi3L@05Yg4|FRbEw)LN-!EQUbNval(zw;$k3vc zf#S+=kVq4jLso*NU?L@kfe+nzpvH$r53T*+1w$Wr>L;=-!CG#nIXcT?NN;_dYQPSq zxUgY7X3bra`stH?8v%&XG6VgJ%U|!(Q>-_N!;7RrtfR;XB{*6nvI;oym#YNvk||}? zV}d0!WkP4WC{Be&PGh#psnEJyDxpq$6x8uuIH9H4puVo)boGt}jbhePyV`ke`Lv?` zk7>mL0C#LMzJn#qNKSw5$*GI?k8j=6`G~jkeiC)lXUzqJ_7hN3f`yhloY0SNUXRc} zC+ArtDo$y9XXlFtKHV3?+pgrJ&ba62+@goxQACSyk&8`J&6Q417$)mb4A`crg%=J3 zv@Kvrf1V~A?mfc?icXV(uThTdun3jJ7rgSE;WoJDP%h0a(yp06KJ%dh+VcA!MppZw zu&8J`!`+P=^oc>&OrJ-XJ`A*xt{%^?OnyPV2}oj{j3UN45yTU6w{%SOo)40ik-|VO zofV~Q37+msL;o9Q^_6YARpnQw3ZB*J*KC&UB1*`h7)QgL0zDUPc zG5$c7jvrqQDx&*=3s*ebu55eOX+1Eab!*vY&}pq+9y+)RgKu}agI(d2g8YD^-T4XS z%ya%n%LzvfhyXv1VB%aYhHe4RWAI07p~obCr}F4x!E+wY8AbS=5HXIkAk)6$;@{Xj z{-vCsGxv-HxH_5c=$+Kk6W|i8iKh|6Ox~BZ4lk+dFt0$=@Eh_cfCucHJPhpek;BGT+sluLmLmFf!u*C84SAhVz)sME>FSwwWlK=@L0FoKnjRVvKX zFKlOys&iK_$ZU0gUCwnw{_+Ju#dkvbj1YAl1?&)}Mjpl*HB0$2fzpe}=NZVnpCABr zrn}`Q-EkIi*N=VyMao*<*9rps&;3}=MuPEy2?+vXkofh<_4U?m;$X+>=nU4G2I7Gg z6RDBGYmGoykd~Jb)&lXfL4D?uZ79`OD7bJa0#_HN&DsK4C=)1nWOP}i7-^?^K`O+| zbiM|}uhvaBi$S*%wstnG-oF;hsEjI$Q0h?S&qt*SNc{ z{t}BcOsl3QK0%Dq7R8cdq8h`fOyYT&rcyvQ78iLe*E*n?qG$( zzWXupH<(C-teG?!iZro5Y`501R~mS4iMD&6R`lQrafh>sd0!NyMBl<1-ujF2u3s`O z`0ws{0N1t{H0+K!RX8+WN@cJl-`B7mwvvrG)%kuiuD>efJ7rv(4PsS&`&dFW`@__| z`@#3X91vaK;eEmNmMs0TyqGV(p~|@iv?1b_!U^)mvGa2%Q5Suf1O2@_+Ip|Y?zu4Z zI_eheeLj&tRYBX6)h1r=l9kSnD8Y?y2RG4+19|yQ7WXCA{fRx zqqQhX22I*sOO*6c$c5vg+sxye^Rzj>VV#F8TetByWvJ+f(wQF+j|3X59?g1h3(?!~ zQtEW*HW)jf5kF%5W7jp`P{#;{p(VP~od6XjprP3Td*N}REN|5DuLt*D83^ODW*&Eu(S(iC$~40K z3qyL#_v>(yl^Wge4J6ZAuu_Khl1#g;H;s>GVmiH3o@NZ5avP)5vadVR2sKBspr*4T z0hexo0sKm2mcI!t*IEz|S?!p^XSgA+rl$VxKN>n zuhA*dqa>VqQh96|9E+@6xbdBWImyc}P0(~<_WK7Hs?$|KuOg?FY=_aYgpz%o6 zma(n*ej0>BwfS9xWToY>cc2Dq-CRWr8e#a9`dQEXN6M4c(h_U~tdzxPy+M%VuOIH& zSJ`x}{`m!%`V0nP6Ab92tWCy5P^__e97626kgs%;GtGg=(5vgk9W z6k)}W5}XRp;MNh3`dmz**HoA*sk&QCNZ-lEtyxY%7rB#HVVpr%GKDG5dXi#KS0I61 zY}`gPda#LQuo0P-AL*=;ADOO5_SJAB3#{(yyJD*62wZ5DF%3>*>xg-fkAj~cSJw5R ziByEW3Smi5BNw!aLjMDr$2^^(&N}(Zf6#TvB6lP!yj;ArVksp_*Ir~b{7-XZ<(m?M zAanLIbX&RkCHe?#5|t<;qE`*D(UPmb&B@tYtw;Cw?@3dHX(rTZ{QL~2IOg0x9qy2I zT<6xyw83(!#fA-n*V(5ZKZ~35WnL=dKn5G9rcLCg7bMPWCQev_Jgx60ULKB=u8d4( z**bJ(XFQG)ihPf!5X=hW>54Qo_tA6@AbPpD`cIrsg(wCQYd=K&NE8<9Nc$uvYqYW; zpL%}OSo&3ckrYwQr~F$IO|rxdTtjGq7C!C|Qx=lxDm!NPN`5xHpVV;B%Nu$2Y=8Qe zB8_Jgel}-t95k4^foS~5zO+fFFYO*;q~#Llm#t-<*jVz8&{;atMP-A{<9be%7r_xv zT>n~rv^%7e9WykQ!=r=kBv6|cRHXkDY znKX&yP5LewReK(K??{;Ti@qQ-(2{q;PS!n}cCPvsss+5P1DXgQa%*2Mw#|s{kHFh? zD962<(eQF!%NQ}#ZSw3$`co>4jtGUbyEEk!muu^)6~LAOxM!C(*g?}8)+aFt=6Fz0 z_#@b|?-O^-@gU=3F4E42n-m&{6r0)79+-H^m#Z&`pduLBk)yIh8W1Nh3WkQwbHqPL z*VW{U+yG!Xfl6<8;*6I8SU+R)On<4|>BN?bZ{AzqJJQJ22EpWnpiK_pTkcfA3qJ|W zX@tr<(r;9;^zU*>{@Bm6P^Gnk+P_?eRXHIPI~2JOZDk?obuDLJpV5>O?vca#g1yeQ zZw#YDgj_LpY}L}2k9(;sPk(TUr7i>gpza7R4HzBe0C8t2Ay?A7A)6V*m*gAQhZ;x- zA&(J(0|IPrqkYuKu=)0nXQ7~l#DFRXB%t8oGum!vLl_rnf(FsS4ro7;tKCNF0zHE} zzLJ>yj?yY)EKby)i!sKUB_4$Q?i|!@;p>A|=nP7dWkmnNRqFRk$8)NGNl{c_fG_EK zQ0pF@KpG9i8mN6g=Pt^!j!^PBP^H;QXFebL5T%%;jWr4s&+PkFT@&|p_}W%n-*7#W zR$niW_LV2=9g4P*O2$BEF(RUbwZii-Y!;@BZhl#A&wyXu{aQDi&X^`hU=edZvb2BS z91RzUCE~5BV91bWwJ=Z;IzN_%P$|=%L4IsB4Pv3Z{Sz=LJUtLMwJwKC5t$uH;2~^XEUPb7S2=1*GMC4c< z&-irvF5ani?uZWzqiq7vp6#%n9yI~DU*k|p^95g^r*GzFQ>v)W9bz~?=f&ayZfx0! z6+yRluI9MAgH8e+~}V05>J+zqYHvd6c4^ z=6pRb!0KCl{Y^_QbS6ojMGN9;TcG4omJS>*aaE93=0-X8#$_V~ToIdN`SzP=O@Fk+ zMTp+gAlB1BlsQNJlF%bEo=`al<)!3s4f?71c-&VA_eb=0o^64_Hu0*=mgMha2Ub}V zob&I=Z-Q)uf`?CNgoi9a-q1hB8|`4((8PcF#!*^F?5nkJ(TT*$Lv$a%3|%eLyJK z2sNJ_`x~&BW;$mk3=wVw*_o1J>b}>AJfU^=!CCWuaxKZ53#{pP?VwI!tv8HmSKVu& z%}Q})n9}~#QsBK_1iAfl^Ap{`M9biDJR&iIbsH&?-9PlNXD;3c5^;WlhYo{wBz}U_ zGR(zA``OKDNPkRi(^~#UmJ_xlE!2YIJl6HCvKz4++)r2v_S;)AioGr)cFa~Zb8s#z z{m|q!?~l@ie6mq)?KJdJFml&*$2L)^4kn~=*&lo5{PX3C!BJ?xiOR9F2T+*iG z%QTIlv>XNDO~5)S7N3!6wq2KcUhl>L?#MQX)M2tT8S=G88FeS?-B`?=+N(iaTjTa0 z2o7`psw+ipHglIc48j>_8c<>Uhf1F&kh4#UV79=8DvRgIBNzOykk0|D8a2AGrF4&Fu99 zxBAHe$^DLmp^8ENABzg&ki+ko7>+z7|C^#25t#DNujt`86!_ck zS1g!K6%qzqoBg`=ipUCbRa2^y;#z>hJ{{Z|-$=N=Q-Vp;p?^s>X_?zfA>H&K882NWYnr7bj-l&0j`|0QOpV(9F4zXX7uGd{7CLq_mk&09k`PS7V z*sYX&l}0K)Jiun!std|&DJBbP4Dzz)kl`FxM6d|Q`+giBppXyZ<5901(@prX3-U1k zMbtfJ6vc?^w>q+zqLc0)rTthspc6X=^v_N1IeyE3J$uaI&k!_26I*tqn{2R;!fO85@b0?3(p zFD=na-I77U)c?pZE3#>eJ|+c?StyM+1myFm%MAnvsKt0I>a!wbdl5~1Elr|b&*zFx z=EP2#_dp0*mCPXt#*U=g^so6M#u+ReIr|IqGY6(RGhS?c7Xd>pvn#&hb5lnC--c5j z4V}=f&rbUe>Z-Q=c{Vdsg;5xzJD*08w6N+FNR}AL{rZqD_j)%Of61?@QQGVuB-212 zVYx-JFqu$ymqp-cM)ddSh|EcG=8nd@aVb9l|0li8rAm;v@9S-^3Z&%QvIegTnfHEp zQKb&)3W@yRu^?vSi8tRX7M=%Y)`DaQ>8lZH;0?dfYS)j{cSr^?Q>YVAe@#7~Z46OG zko;ij$(~O)U6ENET0O$ju$1f%(;c`b*g2iK6+Lkfy>=L#JL2R!xn%dNh+dWDw&UdL zDR-TJAC&s^@9bMQWJ_R25MC<9!Qe2uUY|1_BLcGK!!{2B0S#uHdFpc%Jul~JQhPsI2B$)Q zx&<7Klm~th^|7+W!6;rymp1dh8^}j`3~Wi0QHP*o^|=9AvL5Jc-i{eVcr=nOcLtDXa*9$&jim5C1 zrvZC(qs;ll4$%r*l(;y{aZ04-JYh%&#PX-YEee@f1E(_ClPLX#T;dh=Tu|9xjK9?> z#oxFK(^u~VT#M4feA7QZ2iPvSM%NoO2em6i67aFg{E`!xv8|t^?$#?*tlA|M7;Y6G z;fzUasBjlEWamq_n0Lx4-tt4L@I-}VxMlBwz%Fd5*`kbQL&|}_`m_8UX>R6^H*$`z z{Gi=MDanR-BO!A5UVKo0eC%Fzkf}>G->Gr)<+H_sJrCT{ zGf+JVY1?7nRp6*;VWRqkqhCSD!6S{NKzej| z*K7*M$PLu^;{Es_QEsPbGIA&&F=F(kbi(!J9M%xzj;VS@el`A==*w>pXr>vhG-y9o za|QrG6yLvvPJ4vYyoiJL9pdkX3@G$Ier1FSF{W+uZO7v9itZTI`1@NyDdlcxFqB3D zCrTwhg144oqKY@1do|H*E2awMyy!kz@!hEN(`ol-NA52zen#Zu4+2Km>@_>Y%fv+Js|2Ck7OS^Sbef3Y5ryr@jiWCwZ< zOTu(KFPI_=PaQr!Icc`#MgnzRy}p;rq$)B>LzB)+w7jk1Z{}Q z{76Hb8O{L-8n%~eb4z5RUZF<_*|(H7v`E|GA>Ig+T;6Jl3<|DkSakjM3i*l?a^ly) zxQIrj_;`#mhgxEwXV9Y1UF~fpa{0ecd~z|*#Y(L>Zxb7hS`Ql= zB^!-LP($V?o@iNr?E3YNGtZZ^RR{AIpSu&t+7Ib@UjX0B?;~Mgzhi%)sZn)mg5d?# zB#bmMeD`t6hGDG%KOhgbpc|%azCMBUO%Qa&I#}M**sC~IhDQ9N>2Ee|`Sek^>(3xc z`tO=e6kqB4F5T#}&A%K^9|lpQR3ZeU>M+|)TjXn6bO-HYrtCPbN2Xc(1CZKHe^Ell zEpC(K-y*nuL%JKORa!j7Jp$nSQ_Ycr#?CQWNz^amL&Gk@k>VovL>Foe%ZAT6SxLYn zm>gcSQKei@dISDQN>RWm&x@+aeJsz+B>jm!7|xOp4sulE>L15tN+-BHGIT!vajQm- zK``eDOS<@=#Nd^WKUu`<4$T)siy0W++SSbX;p?DK7rWW@bsD{MDz~AfEgP^Cly1p1 zP4vm4MHeg5ojO{1GI78ZJNS9J#3|u^JlrGuU)} zwvQ*&+v`MzQk)A2>bRvC7_NR7fEee9izlg*D_Jl`A}aDqtUGi@QjUhix||wj#yjfC z%?Zk5J9cTE%jwrJIk&k&5+Z0Y5tfI(3fZ&4;8-wYaboBPD$`(pa%DE)wY9qO7ORE2 zJ*UV)%OvV8=pi1HQop%(D7lym+UHQ!SKpz_^l8P4{3o;|QXZ&!oo0#l=*%6>~x*6vP zM2Kf-OSSE#xfN1Wwy5_uI*yv&?|f=9=JS`-nA2;Qxh1aCS|^OT@HbwQLr?u`(z=`< zlGs^Z+seTpDOPdhyE$J+V8M|UCa0m#U}l?IU%JgzWlR>MI=HMbOAn$xy$#K(x*sRPEB2&$oyBS3B7$`Bp@t`g#iCoUO;EMVhdNmKLYIbgkVwO zDLGu}`Dsv$IPAPF((w2m*}u|g^si;jw#R)Z5@`BD`bjpB^5Ex%MMtj$)?)X;J>#0G z+|fPZbImsCeQ6N?b|uJK;5h_T=_xObJ2^kY{Ewq@!@vyPH?vUj1zy&hVaArxisdp0`Etftz2$F;W1G`;+;4R z57XF6zA8>rY<;Ht{V+_Pb}an z?^Vo&?HAi}GSY&UxSdm<5uFE!1P@PUS147$c_K~jN8JvmjAd2Ucy_vGXI2L|AC>!I z7K0U>r@50VY^nLpvi=Rk{v@3E>v>7$9e5^LU{AR!7p#pjqf+ygn?9Ll-EwsVYFobd z67~9&?1IxI8q6j4y`-+%-!1%G`YCqIjYYQ9Qijgwn!Q3$VNG>PWsIF&<;p>2n{ruQ zbtl>PpJ>)~tHJJ7tYKFy^H~T&YAt#16ri^+murX-owbtYVCi@A7+WB1{rF7d+ax zSl@o|$TNXcYq-V9!=u&ZOKPxi;S+b;u%+NHTHHqi;a<=;TLrbiRuxiS;N-)VA|=U> zZTd-NNa9FpqtJFE2mXL1E3D0k*l-qa@Olg>rUja z!R4$2BABQz(i4wj?WbZIR-LaiTioX0+F;PR(+LfG3YtT}}M!s77 z9UHeLG)A%FBr?U%G}_~RnX;t{Gu(P@kL#By8KJeg?F}ohk#=TO!)?vL;+bj8U*xjV zBC#5566-p`&{i}uM2%wg@0N};R>F!#E(%!=q-=oTOebHR)KjE?E*g~BnI?-m(T3Eh-Gjd z>;i|(gmv$N$YxO(uB1MQ-rl6ibVm{GR;YUV%a5@ zO=(qo6pOO4tiaVWnSUe%dNrwT(SHkuzW-1xf~BAR$V2fd3H(GfPNk)3!&S*=bE@d`uD)(Gw$N6;*|sA1%U4eXLN#S-lP`TfXLaBK1TG! zhkaOOC=m_2a3(b8^kv=51R{ey`tq|dzdmyPZ4bWxrgiFgnF@Jd1zMN^wh-!8$GXGR z4DZXxE_Ea1T1=@iH*55IrS+<1xe@BSv-_-WteZWlc@FDpaV3;J`HoAjXd25a6Vc*i z?46ztmrqO)$6c-?;IOPjTe7 zI}|sSXFRWpD}Iz)6DO?OTEAeCQ}-3~BE-b6H5~WIJZ!b!*8FuI>K!wzBa#zuSf6+_ z=Ia#SsCrMqj)`3Q_ze3$d+#tQ8^~9JdL`&rf_Wv_SAu&b_*X)BCB#=kdL`snLU|?B zS3-Lw^jE@oCCpdCdL`^v!g(d!W+@vyV{`O4wKO@l54D^By@i^D0|7w~R+$EpgGma3 zFyM_DJXnyO!wL`D^T|`Bjp&l3^*6>)z=Xj3MArPr45;tHWTaMyrcTAOF-2S6AD{o8 z(k-PL?^e4sz{43Q%)59@b=Kcm-%YJC@^LNAj%pv}6T58;lde5MbsC^fRzE;eqHd3E zpLc07wQ1Mw;WXHr?l}(k=Lv%F8r37X6H$C4<7Gg{LRloJ5c{ndYg#e~4ksZY@?VIF z5UIQ5TpP2F@l4nraSqN}b^4}6RxRe%17w6SdX^x*E?$3&_V zExCGokc_Nh7Rqz};x8u+8nnh)8vTgK`9LqK-*QAnBm#| zgMUWNfr%Vu6qY>A1!829M=ck>1Xdl6$zca5WiE>%tw8diMS6yfw@x87CeJn8Wg--! zz&4{@gNr!V;De~_Gy$JfOzPI3tZn8XO#FOztb$XNPh%}b;W;dji|CbtEDIr&iVmB_ zGk*gTZ<&>8_`SoL84p+Tis${vf|vUmCU6vn*oZk?;gc%~i=+pBHSk$)$KjbKw3m;7 z_zp*)C`7SR=$)TwTbH{DlgT!m`5s)|*#n;svGs1H3!e{njsnPhtu4Vq668Y_5{t4j``%@%84{we*IuFj1Ak&rxU*N?~N==7dJzH`Jb?_a=Z9Eyf`ty*W z)tL;ykXDP4qGVAGP@!WL;;M50ED<;WO_IWN7Bo5yWJi)?|CBkz%dSiN@p663f$oRS zN%gqb3=_}4OT{Z`GvB*rpm|PPl1v-~1D1i`H!2fuVz~GD*7UPL#C^ig(>hul#9*gp zBS8c&9pPHrDG%fbAz{>|#^WZ<$Ex^490uCE&fWVOES`5!#NyTc%fUk{A+=O-u^KMy zE*@@IOSP^UqqC_lQGrn4vvp@zLi=%U2@`)Md;>MJ&2n@u@GBF+Mof6q`7EN&3jGj786wO8d zytE#SX7)AvOF`)nKxGo=v2=9O z;|{znTo)00e!n5+nX4=uCw|D2cOu&57bMcvAQMrz&;%kEw1Q#R=xKh7@M(?B0fuJ6 z&-A}o-y@k~$A@iw!@ID>vPZc~!*sT&cFa`*YvePL)U#Dzg#QlH$#Jtd2+K$=M3ifv zdw9X?7SXCiNQv94q;4QD1Tm!5P`*SC=t^*7Q?UqPtbuQ6n3_%*In5d`rU+{UG(wh( zeZw1ytRP#8^o+`(wX z8TPaw*Q55w*1(Pzjzq(GV5^ox2%0eK3L`OUI4|l*2XXhY@hL)0BPhbDy-p~y@wWQj zT9mqoWU(-Ljr9hw5488{1Y#x^@)`;v2P5U zp63c{Smt-;WJz?^=H9_@Q1CS$b@q`&xO>~ovs($lXtuK~v1h3~$O{|GFfBT6y0*zw#iqY0o($E;a z{u!i<`29WW|K3*y*RJBB{I}ZwK!0X>iR6cbE>kZ_WQ+vX73NIV6641Hkwb{~)k~tZ zV#Nag(w9>~eLl-xqLx%G%4#`ZEk%f+P7s{x5WW&eUhjB5K0wZ@w5{AVwjvLIJil7; z0EKNj!rkfo3rz?)>`(dIwPf2jZpfJ_iC2zInr*@yCGg{MKgBRJ1rz>x0$h{IbYfi3M22_;mR z2}m}oSFO=SGCfQ#gq5GDxPc?N@rLpGqGEtP#0 zCfGY>ErbbOcV4lr5yYiho?;bynxlYARg1<5MfsV7{Nq7c1DB-#y14x>_ubgA^A8AI zeT8gHZICIcAGb`>6MMCv(_|V<&+ZG<;;qVwgi0u9t$?O$m2;h}x8l+GGO zOXoZh?_Z0X7TET*kqwunwId{jffz!r?OPzj$h613Zh0rNjwLY!KNj4Wh>bFS|MkcW zvvRejjA&1tC^bk$L&P|~%N@+HT|kRVcVAO3MDg=QNC_rP3?4{QV?lZ*GG*lZ7n)=! zY2@Dhb~l;qM0G~L#-;Q_O}#{-2*P)q@>OSrb}>GYx7WkH@Lz6 z`St8r?Np(~T9heDU!6ZCV9s|p{E7Q4POMH9>CHo1ymnr*AI~6|S03p2y^+U9l;|?q zpvbH2+BnUVij$}*=t8>nvgnTE5ss|1z?7nVP!4f>uY z&1PCzTW3DVnBQ*wFWvZM(hq@CBD4b$h2=s$8-h^sZz>*#!Kpa8YI;d|!ba4EjwfsW z8Vq6yT_3gtnp!^m14a-U&mhJs#otFX0^EL#zi$3{WbI374D0ZB^Mk!E8v2H*XwOD17K>Ie4V=6f73~1u9OyiIS`^)6#GoP@FEvr&;Nm*fD zuXp&YoaHWly5d34+&IZvr5ndb4uE&j3~lHTB2bCiSHjeDT=Afs%sSnEn43dl&mn7q z&FLxuJy%oDj04W{Y#Y00ha4P}nG#bqzdVpecRGkLPmm=n%^%{OW|=qqaS8QKb#JNd z&!@PT)KHz~w|sW(kJ-`-Yv+zAVGWU)ThB9Jf{5*uq}5JuP3j&P#x|H(g^`VPgmOv1 za~-Zsn#?NIaa@!eQtm$6BHu@w-$+;4=E^AmAy-Y-t(Q^?$MF25@8=~(jM!avXWXie z!mv2@_CM0xseZ|!N%4xhO?FCzAKmYlY*0SPzqK6HdC4}3y--N|R$}Ij8zpPXI_37{ z7uyWHC`$>a{DOc9`2tgqxDg*K7~`&^gaqztk~1uozNq;Ba{Xmr=gd8I)LCdQ&tjR<76~UX zy0kL89Sid(Rny4H%>4(4Y|xKG`q4r@J<$D5z7IA``y#jR7>jC-o%M(_Ayf>?@BMh3 zT7&b57gv6p+bTFoBFyESLO;1tl>Ktq$&c_;b1$VGDbBb@75x9Sag}^jx_DRHT#-8)i?7%F(L5^dhjC?CG6Fa?I3JD z%il!KZG79z&q1MgDcvEA8Xa63V|l8Y%TIdYD+Dmgp?(ms9p{mEH1PiY;Uoc&1hBM} z4(SUkLBLb1kx$Z*J+AUZh9TuL3*&ayU9<md6g z$DS^By$Qfw(aE|ZgkrbEzq*vN2FNRkm64^pG%$)zXa_?pb%dGRGbU^L zXzMaKt>~x@^){5kQ8Le>`;kKtJbnHV{CEP{aWlA(v?+J+&BUeVK#Y{Ww&P)5&H|*4 zOX%(b>yZhun%~*VUou9#g8mO8xiGSvoU6|4UnZ;rNgjQB$^3!_Hld>0{5KG93yO`P zYP00caP*$)Wjky8qT>1yZt(Z}A9a0LDL$T8^b`l5m1$=?sYJQ$oW4}i9a;>F>h=!} z7x5^K67Hhm_0*8rmm`f{I^d5Y2dxP_);F~7w1`TQv{ZI&;%1DJ|Iy7B)HwSq%-Va^ zR&NLU^MMO3dF_w-{9Y^WZiO4DSf5`D)%-yX$qtl$o}8M^m#c$&k&zkWpMMqHqTQd!8k52w;yq-GQV z{QkdfD1=l=GwL4RvNZZt{IHxghu9$U>odw21q1}ue}_GkAnPygUlz1q90&w4Uicpv z_u_L9koA{y6lulOh(F2p(UYUc<7(WU!Zz6SeESQ%WN@&i_Hvh5*Et51Pn41^v`;p* zi`yTwFt?3szIi;%?f>bR$izW!?t<*r4LDox`U~5r!`zZOlKcB7TbnTFp9Sk(X4_ny z2_ex4qp6Pyxu7TV5=H7+8M=kj z+=B1R5Cw{y1ciiT{JbTB%VVxsh0NVRypLlPkSYvbjjPFsflJJ|E?Q+%QUE?ABgt*v z3W}*n2FZs6)_u7dUKg%CWZXhMf-3M`x7WmsC5*Vg_6 z*Tg2JOerYQgo#?Mvyac}V`|Zc$$%UQTyb$)5neAIGZTsq95poP#L#`d3#t%)$i%`2@h;T6IeZ7l=gIi{gM>B|M3kNW6~dFLo1 zGJBw7<$DxVe}#7M&w8W_lz;jVe58;xfsqJZXO#!a$2Y2};TI+w@{cTBe1`k*>vuLS zqMSDupqDpl^sCyl(p+t8E`5E#%JcoxkvY<8FK){pl(5y_dwE#x1FxXMn7pmtV_V1ngUWKPY!{vS=vdy4EmWXo^?QTNax3opySH5=)o!Ad;va zHGOQHXMvk$jcwx7<`jIZM(<>EJuPdP;t>cU73bZ#SM)NYC1SCqyC+veU`fP(a`SZb z3|Ki&j768*7T?c&em3GdMN#y2L~^_{Sjv}JLZjgX zRrW7#5HL(rFeo!de8j(jq4&;nD!g@OFTytz3)~BfyOE-$b)F@ffI)C$R_iL!Lv$Nh zA*uQqv@x-}o5>V>b>)uLlyWIms1l>)%X-|ZAo0Q6J-FwKs$qXpp$YPc-MJRp{8fVA zlcM-!kxAi^E|M=n)miomtu965+KEvSXbQTfgZ%4tbHuQMtObwWO{U}PzY(0LuFm;* z)ih%%;llTwM#R?UjX6h?_Y(Gl?4!5AEz)r6b7Utag%tNv#Kw^;cYb$l?ghi4Uao*$r`Szw|u@N%C-AMbwz_I0}XEdka|}VzcO-`#9XgPjQ5P z_TV}~9yU|Vm^g7t!>?BJFs47=p_}2-ja^!p)NTt)yUa%l&-}Vd&rDkZj|UR9?)76I zoFkmDUEQQGecrZ-OHH3>w6kbC_;!A|=)6y5M|1B{siig2k*p^P6xEGZ{Ez@V$vRs}Ef@Jtw!-#WbK%(Cl?(5NPL zU8H&JIn$c97^&vE%HvN4Yt1oJ8TX|U1`d231kYLr!cSb6ePW}e=tU@Tur&ZBqQbw~ z7eZpAAR(FTNKt;dKMD6>9r7j0AXeThvw|~}_*~kjUvXiEHQV-2b}67A)a*WQJI8*$4HM{xvCCCm4^oH#}vPS9pL zS|j=rodon8m~IvxOR+&tlV^K8tbF#P9?y|1L5E6pHxzNXXg)9RhN&RkXW z*)VxCd-@4(m)7-?fn$v10ZFpM6_n?{M)+IDGYRZ)OP;PN*syM2OEtT43iG)Y7r+)m zij;Jkz}eZ9eb}4N-QCt)b^Yo|zn*L})&Ch)I_SC*3@%pNrQ4Ba3a>Pdv3+m<7H!}@ z$b_xtnu zeBSPPzvrCioM(BT=heB+>A902clYioLA?3Bt~hVm3;LhG`nrhg%3SAClNT=Q$v4q> znDGOc2|U6LtyE%@7JYjv`Syz|4!286c1+4%;Vh>vGf3#6SO03<%lx~(_k86_-aNKF z9>2X}*2%55W97^4zOA>~yS!%3%iFJK^bYkoxOo4E?~bZmE&l77^75(EO7A3O%!obQS=>}V6!o3@744kw9XISdb#qrM(R6FxsOy)UiKTA+1vewyF91{u}D{rLx{_nb<&HThMoY4~Mb>n1t#WqIwxcX%7m zYVuTjx-U;YY@)`V+;{vT_1VcYayGm-KWQJhu;TH`o?@+(oK^92eoY8`Ir))E;o|+3 znYFxv8T$NpZ!@l)2soY5IBR#1(8u%OLeKoXYUgZ~A8LYJPgd@3bgmWn)eaw(HGJu` z!)p15$2bILlv;ES-BL2zDmSEqA6B@h=zw~p$G)NsI;S?I7B&X#Nb>yk?B&QkxTE=K z_O;Vdk;cKNBRXqy=O11oz5RBK(vP!}@}CxO>)F_J?CxN9d)1>8mMN7)m=zCe8**Uo z-tGMBm0d?X+&*SVDId!{cv*eQW`X_hAB!eV^Xdy6tzDj|Ht0cjkyUle=qlNsh`V~} zEs5>DG7i7FHhX)uKH@bhT)Ls3aww;2Y;mAP+oCHl|NJ9YSM8p?1v~FUsIWbA&GO8HZ3v_PJKJKZqKdN z^DVRUPfWL;c@sxPSOkV(BzVV$|*L#o2eITkrj%JLTd`to~s)8RKYSLoQx z8vn_={7!J>y73R529Gh1hK#;mv@biAvT)y2$t!tuYD zJNg-|lo#-WgeQfT_oKHopZK2@iSLFhg^fv==XPGFTH%@Ww8;ngBe%Gny)Z#4wZL`ljrAGxFA8y`pCQ^>y@bHIi4K@O;!fzY{oV@-v)oA&t(00=r z%iBTnZHq?jI^y};+g8i;@GHNc`*U-X*V|EpTh$Y~qE0RBHgOyGuvzKP(a+4)S zp7$gxI5Z2a1j)G#pr3DZf!bdoTgBupm>(@DZ~k}#bl zOeYD`Ny2oJFr6e!CkfL@!gP`_og_^6e+<)+(3{qR!Fb*mMEU3Id|@FP(6c7P6s27oDr_Xcnpp98>) zBJ-BOd`eU;fd!OMT?)%75wnzIjm;))yjcnpDQvuq^DK24*I36gSWH=N%OQvoXO}}T zCC04aSm`TZ2}R!bBi@0qlp@uE5J?G}APAsDK@h}JVq`F^qQuT%m`91%!QeuPsUhG? ziGmPLT_cnmh4rBvt0feisEkneboDw;!1Msu!Ou7{OWtAtd3p~UnU zNTft<4CjXB21uaDnGLX!61E$;ejVKi(G)R_g$1hO4tk$6zSd!QIrTv;s(4i ziCd87$=nQPCv$U3R*Meuw}2VdaM;4Fui`CSpT0?f6_k~h!m-|^aC08Cm0O;-wsPa= zl*+X#Oyw5H=TvZ`>h3>t#J-<77Y1#Eg_N~q8~2n{_Y;O`Fqg7o(m3lc(m3_W>0Ha1 z=`f4RJWA&tOWWGf8!!Y5j(&?R{8~37%Bw#B0?*0*H_mNs>5ufKU~xX2A?<+B=K1AKJvW zZ-7%fNDY*NEKsE#9GW}nlM2yaNA$-I&}V+b<}9$L4X}G1CSXBAB2(8HGc9#z<8uLPg zy1xV?)JM+ow>C0E_YsF5kjs34<0>yj@_(^rtCw=}=5sU+d886doRXB3-2i#%KdC&) z5NR^b-G#>tlNQ&ha$errs@g}SCy`8v9s{)e-t=o3qTmgqg>B)WNQaaaBjWT&kNcF8 zQjzL(Xm~5~jLi;-UC52#XC$3eeD{ULCXPgsI8qH~=W#N@yNFD5w@9X77g$hBou^E* zCX(;lpBN@3HC}9qU7g3@i$zsYF9+19yzd|S=Z}!p#x$LnYqtXSK5|Yh4lRqCR2G)|J-8*}FMapWTG-t;g~W&`(cV2Tzj~L%oo7 zOnm=4|2eAd>v4ix`FGK8;NBB{WV%0$ycNJuClOyTY<6N#=H&`Ki7gk7>_-=6o^8YL2BHdbEf5(&RRfC5s06 zF!Vp`&_vuGyAC@%#7b(oF`w((&-tXiiJoHCXzb``ePCI4eiXCR(0(r%P%Glxfn;5M zvBLJ(b)Hy$FuKo^R>YFMpeV;~ZERLBJiix)GqpZn3HAC&tI_eGTbno~n1+-Ti2Y|< z9*^G^z$9uuK>E9(wS?(T25#H{A+_MIS{`y86!35Xm@=b6D4caMO$HnJ$|@Mk79$%` z2-=L55jGzt^Xgs*vzRgy(apu{Fq3f`Pj9j!cnWB+52i5H-}VEt7|R0A7X5$%K4Af{ z5$VU-DCy&)>!66?wV=n#!T4XmhuJw@#5Gu~rHhs&!~-KC@!;$%Q7Z;k>KG~{-bT%) zz|LST4XhW!80L_Bzl1#6hiNI}=>0H;Hy{0P!aOF-3k#dT1PcPm1~RjVwCd!eG)$p? zScLKFIIOHYY&V(FixSN0yS|`uPBh1Crud3RqqW6PWr^5n@rMq=F&8 zvjbR01~9im1pn2_lf%$5BG`ITtU4I4mw_eoQw@tuK;=dyS?p8esp7qRFbo$QBKn$j zU-7orDJkQ_d!+4JmY3fk<{66MmfK1yUyW`N`X7cco;5Zdh6r8*I+T;H=C+Hp9+bmW zM*6MjCNBcWH@hpy1ke3PgghxFON&%n8-h(0#B0;fBDmnQk}LCtxUr^pq4Ocq;rXY?dKriDPhbU85hxOIPuA8zv!|pPy>PJ-t*2+pELhDVfX`2Z z4KEoD&OjiuEgnan0Yi*^27~e9?_kdSw1q_`qV`#GgE?Zh1!Gqrx*>JchT_As#IfdI zSY#a93;9a;>KXAVxrG1qgnQ!3bKu21EM+b0W~R&Ht8+weR|~zz{B2~GUC*;!{6mD; zZ3B5Me@+(Rf(|jFhVmDPO!7N1Ldx^K<11s>1=7(EA4RZ1I(df9u!7{>!J`#)QSKt? zXgIpJk|#f|1`98NCf(JZ)M%0-cdbOXt(HX%kbenA(1&oufV46FtBHIHRFC-MLQ diff --git a/src/resources/objects/building/BuildingObject.java b/src/resources/objects/building/BuildingObject.java index 4d789cd43..9bf1936af 100644 --- a/src/resources/objects/building/BuildingObject.java +++ b/src/resources/objects/building/BuildingObject.java @@ -27,6 +27,7 @@ ***********************************************************************************/ package resources.objects.building; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -50,10 +51,13 @@ public class BuildingObject extends TangibleObject { private final Map nameToCell; private final Map idToCell; + private WeakReference portalLayoutData; + public BuildingObject(long objectId) { super(objectId, BaselineType.BUIO); - nameToCell = new SynchronizedMap<>(); - idToCell = new SynchronizedMap<>(); + this.nameToCell = new SynchronizedMap<>(); + this.idToCell = new SynchronizedMap<>(); + this.portalLayoutData = null; } public CellObject getCellByName(String cellName) { @@ -71,14 +75,14 @@ public class BuildingObject extends TangibleObject { @Override public void addObject(SWGObject object) { super.addObject(object); - Assert.test(object instanceof CellObject); + Assert.test(object instanceof CellObject, "Object added to building is not a cell!"); CellObject cell = (CellObject) object; - Assert.test(cell.getNumber() > 0); - Assert.test(cell.getNumber() < getCellCount()); + Assert.test(cell.getNumber() > 0, "Cell Number must be greater than 0!"); + Assert.test(cell.getNumber() < getCellCount(), "Cell Number must be less than the cell count!"); cell.setCellName(getCellName(cell.getNumber())); synchronized (idToCell) { - Assert.isNull(idToCell.get(cell.getNumber())); + Assert.isNull(idToCell.get(cell.getNumber()), "Multiple cells have the same number!"); idToCell.put(cell.getNumber(), cell); nameToCell.put(cell.getCellName(), cell); // Can be multiple cells with the same name } @@ -91,7 +95,7 @@ public class BuildingObject extends TangibleObject { if (idToCell.get(i) != null) continue; CellObject cell = (CellObject) ObjectCreator.createObjectFromTemplate("object/cell/shared_cell.iff"); - Assert.notNull(cell); + Assert.notNull(cell, "Failed to create a cell!"); cell.setNumber(i); cell.setTerrain(getTerrain()); addObject(cell); @@ -115,12 +119,16 @@ public class BuildingObject extends TangibleObject { } private PortalLayoutData getPortalLayoutData() { - String portalFile = (String) getDataAttribute(ObjectDataAttribute.PORTAL_LAYOUT_FILENAME); - if (portalFile == null || portalFile.isEmpty()) - return null; - - PortalLayoutData portalLayoutData = (PortalLayoutData) ClientFactory.getInfoFromFile(portalFile, true); - Assert.test(portalLayoutData != null && portalLayoutData.getCells() != null && portalLayoutData.getCells().size() > 0); + PortalLayoutData portalLayoutData = (this.portalLayoutData == null) ? null : this.portalLayoutData.get(); + if (portalLayoutData == null) { + String portalFile = (String) getDataAttribute(ObjectDataAttribute.PORTAL_LAYOUT_FILENAME); + if (portalFile == null || portalFile.isEmpty()) + return null; + + portalLayoutData = (PortalLayoutData) ClientFactory.getInfoFromFile(portalFile, true); + this.portalLayoutData = new WeakReference<>(portalLayoutData); + } + Assert.test(portalLayoutData != null && portalLayoutData.getCells() != null && portalLayoutData.getCells().size() > 0, "Invalid portal layout data!"); return portalLayoutData; } diff --git a/src/resources/objects/cell/CellObject.java b/src/resources/objects/cell/CellObject.java index ac3e0e6b7..8d04c33e4 100644 --- a/src/resources/objects/cell/CellObject.java +++ b/src/resources/objects/cell/CellObject.java @@ -87,11 +87,13 @@ public class CellObject extends SWGObject { this.labelZ = z; } + @Override protected void sendFinalBaselinePackets(Player target) { super.sendFinalBaselinePackets(target); target.sendPacket(new UpdateCellPermissionMessage((byte) 1, getObjectId())); } + @Override protected void createBaseline3(Player target, BaselineBuilder bb) { super.createBaseline3(target, bb); bb.addBoolean(isPublic); @@ -99,21 +101,24 @@ public class CellObject extends SWGObject { bb.incrementOperandCount(2); } + @Override protected void createBaseline6(Player target, BaselineBuilder bb) { super.createBaseline6(target, bb); bb.addUnicode(label); bb.addFloat((float) labelX); - bb.addFloat((float) 0); + bb.addFloat(0); bb.addFloat((float) labelZ); bb.incrementOperandCount(2); } + @Override protected void parseBaseline3(NetBuffer buffer) { super.parseBaseline3(buffer); isPublic = buffer.getBoolean(); number = buffer.getInt(); } + @Override protected void parseBaseline6(NetBuffer buffer) { super.parseBaseline6(buffer); label = buffer.getUnicode(); From 3966df4b66403872e12ea436627135ceb5739e76 Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 13:22:43 -0500 Subject: [PATCH 08/12] Changed data transforms to be about the vehicle instead of the creature --- src/services/objects/ObjectAwareness.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index e0739e5d7..a8f065106 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -306,9 +306,8 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) { if (obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT)) { - parent = obj.getParent(); - moveObject(parent, null, requestedLocation); - dataTransformHandler.handleMove(parent, speed, update); + dataTransformHandler.handleMove(obj.getParent(), speed, update); + return; } moveObject(obj, parent, requestedLocation); if (parent == null) From d1cb86ec7b48fa945ba5cc4f974b581df33876b0 Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 15:10:36 -0500 Subject: [PATCH 09/12] Reset object awareness code to previous commit --- src/services/objects/ObjectAwareness.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index a8f065106..e0739e5d7 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -306,8 +306,9 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) { if (obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT)) { - dataTransformHandler.handleMove(obj.getParent(), speed, update); - return; + parent = obj.getParent(); + moveObject(parent, null, requestedLocation); + dataTransformHandler.handleMove(parent, speed, update); } moveObject(obj, parent, requestedLocation); if (parent == null) From d45d218562f22ec6002e71ff8c1f40dd8dfff64b Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 20:22:24 -0500 Subject: [PATCH 10/12] Trying something new in regards to vehicles --- src/services/objects/ObjectAwareness.java | 18 ++++++++++-------- .../objects/awareness/TestObjectAwareness.java | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index e0739e5d7..73889f9d1 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -306,15 +306,17 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) { if (obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT)) { - parent = obj.getParent(); - moveObject(parent, null, requestedLocation); - dataTransformHandler.handleMove(parent, speed, update); + SWGObject vehicle = obj.getParent(); + moveObject(vehicle, null, requestedLocation); + dataTransformHandler.handleMove(vehicle, speed, update); + moveObject(obj, null, requestedLocation); + } else { + moveObject(obj, parent, requestedLocation); + if (parent == null) + dataTransformHandler.handleMove(obj, speed, update); + else + dataTransformHandler.handleMove(obj, parent, speed, update); } - moveObject(obj, parent, requestedLocation); - if (parent == null) - dataTransformHandler.handleMove(obj, speed, update); - else - dataTransformHandler.handleMove(obj, parent, speed, update); if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer()) new PlayerTransformedIntent((CreatureObject) obj, obj.getParent(), parent, obj.getLocation(), requestedLocation).broadcast(); } diff --git a/test/resources/objects/awareness/TestObjectAwareness.java b/test/resources/objects/awareness/TestObjectAwareness.java index aa86350c8..2a6c0369b 100644 --- a/test/resources/objects/awareness/TestObjectAwareness.java +++ b/test/resources/objects/awareness/TestObjectAwareness.java @@ -176,10 +176,10 @@ public class TestObjectAwareness { Assert.assertNull("Player1 is in a parent!", player1.getParent()); // Mount + Assert.assertTrue(fireAndWait(100, new ContainerTransferIntent(player1, vehicle))); player1.setStatesBitmask(CreatureState.RIDING_MOUNT); vehicle.setStatesBitmask(CreatureState.MOUNTED_CREATURE); vehicle.setPosture(Posture.DRIVING_VEHICLE); - Assert.assertTrue(fireAndWait(100, new ContainerTransferIntent(player1, vehicle))); Assert.assertTrue("Player1 is not inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); Assert.assertTrue("Player2 is not inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); @@ -216,10 +216,10 @@ public class TestObjectAwareness { Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(player2))); Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(npc))); Assert.assertTrue(fireAndWait(100, new ObjectCreatedIntent(vehicle))); + Assert.assertTrue(fireAndWait(100, new ContainerTransferIntent(player1, vehicle))); player1.setStatesBitmask(CreatureState.RIDING_MOUNT); vehicle.setStatesBitmask(CreatureState.MOUNTED_CREATURE); vehicle.setPosture(Posture.DRIVING_VEHICLE); - Assert.assertTrue(fireAndWait(100, new ContainerTransferIntent(player1, vehicle))); Assert.assertTrue("Player1 is not inside Player2's observer set!", player2.getObservers().contains(player1.getOwner())); Assert.assertTrue("Player2 is not inside Player1's observer set!", player1.getObservers().contains(player2.getOwner())); From b13fb15490b0588d8d973c3bcf43095d235c24d2 Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 20:22:50 -0500 Subject: [PATCH 11/12] Wandered in the dark until I figured out what broke zone-in --- src/network/PacketType.java | 2 +- .../encodables/OutOfBandPackage.java | 4 +- src/resources/encodables/ProsePackage.java | 2 +- .../encodables/player/Equipment.java | 43 +++++++++------- src/resources/network/BaselineObject.java | 51 +++++++++++-------- .../objects/awareness/AwarenessHandler.java | 6 ++- .../objects/creature/CreatureObject.java | 6 --- .../objects/mission/MissionObject.java | 12 ----- .../objects/staticobject/StaticObject.java | 7 +-- .../objects/tangible/TangibleObject.java | 8 --- 10 files changed, 65 insertions(+), 76 deletions(-) diff --git a/src/network/PacketType.java b/src/network/PacketType.java index 7848d74ff..324617278 100644 --- a/src/network/PacketType.java +++ b/src/network/PacketType.java @@ -362,7 +362,7 @@ public enum PacketType { public static SWGPacket getForCrc(int crc) { PacketType type = packetMap.get(crc); - if (type == null) + if (type == null || type == UNKNOWN) return null; Class c = type.c; try { diff --git a/src/resources/encodables/OutOfBandPackage.java b/src/resources/encodables/OutOfBandPackage.java index 2189a0a21..d193ccfbb 100644 --- a/src/resources/encodables/OutOfBandPackage.java +++ b/src/resources/encodables/OutOfBandPackage.java @@ -81,7 +81,7 @@ public class OutOfBandPackage implements Encodable, Persistable { int padding = data.getShort(); Type type = Type.getTypeForByte(data.getByte()); unpackOutOfBandData(data, type); - data.position(data.position() + padding); + data.seek(padding); remaining -= data.position() - start; } } @@ -170,7 +170,7 @@ public class OutOfBandPackage implements Encodable, Persistable { private static final EnumLookup LOOKUP = new EnumLookup<>(Type.class, t -> t.getType()); - byte type; + private byte type; Type(int type) { this.type = (byte) type; diff --git a/src/resources/encodables/ProsePackage.java b/src/resources/encodables/ProsePackage.java index 7301c0dd7..b1ac3bca9 100644 --- a/src/resources/encodables/ProsePackage.java +++ b/src/resources/encodables/ProsePackage.java @@ -274,7 +274,7 @@ public class ProsePackage implements OutOfBandData { return base.hashCode() * 3 + actor.hashCode() * 7 + target.hashCode() * 13 + other.hashCode() * 17 + (grammarFlag ? 1 : 0) + di * 19 + ((int) (df * 23)); } - private static class Prose implements Encodable, Persistable { + public static class Prose implements Encodable, Persistable { private long objectId; private StringId stringId; diff --git a/src/resources/encodables/player/Equipment.java b/src/resources/encodables/player/Equipment.java index 57b1c93c0..c5bf2518b 100644 --- a/src/resources/encodables/player/Equipment.java +++ b/src/resources/encodables/player/Equipment.java @@ -39,13 +39,12 @@ import resources.objects.SWGObject; import resources.objects.tangible.TangibleObject; import resources.objects.weapon.WeaponObject; import resources.persistable.SWGObjectFactory; -import resources.player.Player; import services.objects.ObjectCreator; public class Equipment implements Encodable, Persistable { private TangibleObject weapon; - private byte [] customizationString; + private byte[] customizationString; private int arrangementId; private long objectId; private CRC template; @@ -70,14 +69,14 @@ public class Equipment implements Encodable, Persistable { @Override public byte [] encode() { - byte [] weaponData = (weapon != null) ? getWeaponData() : new byte[0]; - - NetBuffer buffer = NetBuffer.allocate(17 + weaponData.length + customizationString.length + template.getLength()); + boolean hasWeapon = weapon != null; + byte [] weaponData = hasWeapon ? getWeaponData() : new byte[0]; + NetBuffer buffer = NetBuffer.allocate(getLength(weaponData.length)); buffer.addArray(customizationString); // TODO: Create encodable class for customization string buffer.addInt(arrangementId); buffer.addLong(objectId); buffer.addEncodable(template); - buffer.addBoolean(weapon != null); + buffer.addBoolean(hasWeapon); buffer.addRawArray(weaponData); return buffer.array(); } @@ -94,7 +93,13 @@ public class Equipment implements Encodable, Persistable { @Override public int getLength() { - return 19 + (weapon != null ? getWeaponData().length : 0); + boolean hasWeapon = weapon != null; + int weaponLength = hasWeapon ? getWeaponData().length : 0; + return getLength(weaponLength); + } + + private int getLength(int weaponLength) { + return 15 + weaponLength + customizationString.length + template.getLength(); } @Override @@ -120,8 +125,8 @@ public class Equipment implements Encodable, Persistable { weapon = (TangibleObject) SWGObjectFactory.create(stream); } - public byte[] getCustomizationString() {return customizationString;} - public void setCustomizationString(byte[] customizationString) { this.customizationString = customizationString; } + public byte [] getCustomizationString() {return customizationString;} + public void setCustomizationString(byte [] customizationString) { this.customizationString = customizationString; } public int getArrangementId() { return arrangementId; } public void setArrangementId(int arrangementId) { this.arrangementId = arrangementId; } @@ -136,17 +141,17 @@ public class Equipment implements Encodable, Persistable { public void setWeapon(TangibleObject weapon) { this.weapon = weapon; } private byte[] getWeaponData() { - Player target = weapon.getOwner(); - NetBuffer data3 = weapon.createBaseline3(target).encode(); - data3.position(0); - - NetBuffer data6 = weapon.createBaseline6(target).encode(); - data6.position(0); + if (weapon == null) + return new byte[0]; + NetBuffer data3 = weapon.createBaseline3(null).encode(); + NetBuffer data6 = weapon.createBaseline6(null).encode(); - NetBuffer ret = NetBuffer.allocate(data3.remaining() + data6.remaining()); - ret.addRawArray(data3.array()); - ret.addRawArray(data6.array()); - return ret.array(); + int data3Size = data3.limit(); + int data6Size = data6.limit(); + byte [] ret = new byte[data3Size + data6Size]; + System.arraycopy(data3.array(), 0, ret, 0, data3Size); + System.arraycopy(data6.array(), 0, ret, data3Size, data6Size); + return ret; } private TangibleObject createWeaponFromData(NetBuffer data) { diff --git a/src/resources/network/BaselineObject.java b/src/resources/network/BaselineObject.java index e15cdf582..b60f16574 100644 --- a/src/resources/network/BaselineObject.java +++ b/src/resources/network/BaselineObject.java @@ -30,6 +30,8 @@ package resources.network; import java.io.IOException; import java.io.ObjectInputStream; import java.lang.ref.SoftReference; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; import java.util.ArrayList; import java.util.List; @@ -71,28 +73,33 @@ public class BaselineObject { public void parseBaseline(Baseline baseline) { NetBuffer buffer = NetBuffer.wrap(baseline.getBaselineData()); buffer.getShort(); - switch (baseline.getNum()) { - case 1: - parseBaseline1(buffer); - break; - case 3: - parseBaseline3(buffer); - break; - case 4: - parseBaseline4(buffer); - break; - case 6: - parseBaseline6(buffer); - break; - case 7: - parseBaseline7(buffer); - break; - case 8: - parseBaseline8(buffer); - break; - case 9: - parseBaseline9(buffer); - break; + try { + switch (baseline.getNum()) { + case 1: + parseBaseline1(buffer); + break; + case 3: + parseBaseline3(buffer); + break; + case 4: + parseBaseline4(buffer); + break; + case 6: + parseBaseline6(buffer); + break; + case 7: + parseBaseline7(buffer); + break; + case 8: + parseBaseline8(buffer); + break; + case 9: + parseBaseline9(buffer); + break; + } + } catch (BufferUnderflowException | BufferOverflowException e) { + Log.e("Failed to parse baseline %s %d with object: %s", baseline.getType(), baseline.getNum(), this); + Log.e(e); } } diff --git a/src/resources/objects/awareness/AwarenessHandler.java b/src/resources/objects/awareness/AwarenessHandler.java index 763374c91..4048fd4ba 100644 --- a/src/resources/objects/awareness/AwarenessHandler.java +++ b/src/resources/objects/awareness/AwarenessHandler.java @@ -37,6 +37,8 @@ import com.projectswg.common.debug.Log; import resources.objects.SWGObject; import resources.objects.awareness.TerrainMap.TerrainMapCallback; +import resources.objects.creature.CreatureObject; +import resources.objects.creature.CreatureState; public class AwarenessHandler implements AutoCloseable { @@ -80,7 +82,7 @@ public class AwarenessHandler implements AutoCloseable { } // Update location obj.setLocation(requestedLocation); - if (obj.getParent() != null) + if (obj.getParent() != null && !(obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT))) obj.moveToContainer(null); obj.onObjectMoved(); // Update awareness @@ -101,7 +103,7 @@ public class AwarenessHandler implements AutoCloseable { if (oldMap != null) oldMap.removeWithoutUpdate(obj); // Update location - if (obj.getParent() != parent) + if (obj.getParent() != parent && !(obj instanceof CreatureObject && ((CreatureObject) obj).isStatesBitmask(CreatureState.RIDING_MOUNT))) obj.moveToContainer(parent); obj.setLocation(requestedLocation); obj.onObjectMoved(); diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 44db46e54..3e41c74e7 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -983,8 +983,6 @@ public class CreatureObject extends TangibleObject { @Override protected void parseBaseline3(NetBuffer buffer) { super.parseBaseline3(buffer); - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; posture = Posture.getFromId(buffer.getByte()); factionRank = buffer.getByte(); ownerId = buffer.getLong(); @@ -996,16 +994,12 @@ public class CreatureObject extends TangibleObject { @Override protected void parseBaseline4(NetBuffer buffer) { super.parseBaseline4(buffer); - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; creo4.parseBaseline4(buffer); } @Override protected void parseBaseline6(NetBuffer buffer) { super.parseBaseline6(buffer); - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; creo6.parseBaseline6(buffer); } diff --git a/src/resources/objects/mission/MissionObject.java b/src/resources/objects/mission/MissionObject.java index 018747233..d7f8c111f 100644 --- a/src/resources/objects/mission/MissionObject.java +++ b/src/resources/objects/mission/MissionObject.java @@ -78,12 +78,6 @@ public class MissionObject extends IntangibleObject { bb.addObject(waypoint); } - @Override - public void createBaseline6(Player target, BaselineBuilder bb) { - super.createBaseline6(target, bb); - bb.addInt(-1); - } - @Override public void parseBaseline3(NetBuffer buffer) { super.parseBaseline3(buffer); @@ -106,12 +100,6 @@ public class MissionObject extends IntangibleObject { waypoint.decode(buffer); } - @Override - public void parseBaseline6(NetBuffer buffer) { - super.parseBaseline6(buffer); - buffer.getInt(); - } - @Override public void save(NetBufferStream stream) { super.save(stream); diff --git a/src/resources/objects/staticobject/StaticObject.java b/src/resources/objects/staticobject/StaticObject.java index 461fbf8a4..f4b13ca8f 100644 --- a/src/resources/objects/staticobject/StaticObject.java +++ b/src/resources/objects/staticobject/StaticObject.java @@ -29,12 +29,13 @@ package resources.objects.staticobject; import com.projectswg.common.network.NetBufferStream; -import resources.objects.creature.CreatureObject; +import network.packets.swg.zone.baselines.Baseline.BaselineType; +import resources.objects.SWGObject; -public class StaticObject extends CreatureObject { +public class StaticObject extends SWGObject { public StaticObject(long objectId) { - super(objectId); + super(objectId, BaselineType.STAO); } @Override diff --git a/src/resources/objects/tangible/TangibleObject.java b/src/resources/objects/tangible/TangibleObject.java index a2168b1b3..4d56d7036 100644 --- a/src/resources/objects/tangible/TangibleObject.java +++ b/src/resources/objects/tangible/TangibleObject.java @@ -294,8 +294,6 @@ public class TangibleObject extends SWGObject { @Override protected void createBaseline3(Player target, BaselineBuilder bb) { super.createBaseline3(target, bb); // 4 variables - BASE3 (4) - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; bb.addInt(pvpFaction.getCrc()); // Faction - 4 bb.addInt(pvpStatus.getValue()); // Faction Status - 5 bb.addArray(appearanceData); // - 6 @@ -313,8 +311,6 @@ public class TangibleObject extends SWGObject { @Override protected void createBaseline6(Player target, BaselineBuilder bb) { super.createBaseline6(target, bb); - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; bb.addBoolean(inCombat); // 2 - Combat flag bb.addObject(defenders); // 3 - Defenders List (Set, Long) bb.addInt(0); // 4 - Map color @@ -330,8 +326,6 @@ public class TangibleObject extends SWGObject { @Override protected void parseBaseline3(NetBuffer buffer) { super.parseBaseline3(buffer); - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; pvpFaction = PvpFaction.getFactionForCrc(buffer.getInt()); pvpStatus = PvpStatus.getStatusForValue(buffer.getInt()); appearanceData = buffer.getArray(); @@ -346,8 +340,6 @@ public class TangibleObject extends SWGObject { @Override protected void parseBaseline6(NetBuffer buffer) { super.parseBaseline6(buffer); - if (getStringId().toString().equals("@obj_n:unknown_object")) - return; inCombat = buffer.getBoolean(); defenders = SWGSet.getSwgSet(buffer, 6, 3, Long.TYPE); buffer.getInt(); From 5535e32dfc53758faf56905269df5466d6b4a487 Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 24 Jun 2017 20:23:14 -0500 Subject: [PATCH 12/12] Updated build.gradle to output print test information once --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b7ecb493b..5f8580598 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,9 @@ test { events "skipped", "failed" // just good to know in terms of what failed } afterSuite { TestDescriptor td, TestResult tr -> - println 'Tests run: ('+tr.getTestCount()+'), Failures: ('+tr.getFailedTestCount()+')' // IMPORTANT - this is required for Lightspeed + if (td.parent == null) { + println 'Tests run: ('+tr.getTestCount()+'), Failures: ('+tr.getFailedTestCount()+')' // IMPORTANT - this is required for Lightspeed + } } include 'main/TestAll.class' // to run only a single test case }