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 } diff --git a/lib/PSWGCommon.jar b/lib/PSWGCommon.jar index 640a8be79..7beb5640e 100644 Binary files a/lib/PSWGCommon.jar and b/lib/PSWGCommon.jar differ 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 d1b0059f9..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,18 +69,18 @@ public class Equipment implements Encodable, Persistable { @Override public byte [] encode() { - byte [] weaponData = (weapon != null) ? getWeaponData() : new byte[0]; - - NetBuffer buffer = NetBuffer.allocate(19 + weaponData.length); + 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(); } - + @Override public void decode(NetBuffer data) { customizationString = data.getArray(); // TODO: Create encodable class for customization string @@ -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/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/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/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(); diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 11a1d3b21..3e41c74e7 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) @@ -975,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(); @@ -988,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(); diff --git a/src/services/combat/LootService.java b/src/services/combat/LootService.java index 5144a3a52..3205a9084 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); + + object.moveToContainer(lootInventory); + new ObjectCreatedIntent(object).broadcast(); } else { new CreateStaticItemIntent(requester, lootInventory, new StaticItemService.ObjectCreationHandler() { @Override diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index fb9187fb0..73889f9d1 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; @@ -155,11 +156,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) { @@ -306,17 +305,26 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { } private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int 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).isStatesBitmask(CreatureState.RIDING_MOUNT)) { + 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); + } if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer()) new PlayerTransformedIntent((CreatureObject) obj, obj.getParent(), parent, obj.getLocation(), requestedLocation).broadcast(); } 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/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 532c253b7..2a6c0369b 100644 --- a/test/resources/objects/awareness/TestObjectAwareness.java +++ b/test/resources/objects/awareness/TestObjectAwareness.java @@ -37,13 +37,20 @@ 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; import test_resources.GenericCreatureObject; @@ -51,7 +58,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 +68,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 +91,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 +103,143 @@ 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()); + } + + @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 + 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("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()); + } + + @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))); + 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("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()); }