diff --git a/src/resources/objects/buildouts/BuildoutLoader.java b/src/resources/objects/buildouts/BuildoutLoader.java new file mode 100644 index 000000000..b9e85a8a4 --- /dev/null +++ b/src/resources/objects/buildouts/BuildoutLoader.java @@ -0,0 +1,43 @@ +package resources.objects.buildouts; + +import java.util.LinkedList; +import java.util.List; + +import resources.Terrain; +import resources.client_info.ClientFactory; +import resources.client_info.visitors.CrcStringTableData; +import resources.client_info.visitors.DatatableData; +import resources.objects.SWGObject; + +public class BuildoutLoader { + + private static final ClientFactory clientFactory = new ClientFactory(); + private static final CrcStringTableData crcTable = (CrcStringTableData) clientFactory.getInfoFromFile("misc/object_template_crc_string_table.iff"); + + public static List loadAllBuildouts() { + List objects = new LinkedList(); + for (Terrain t : getTerrainsToLoad()) + objects.addAll(loadBuildoutsForTerrain(t)); + return objects; + } + + public static List loadBuildoutsForTerrain(Terrain terrain) { + TerrainBuildoutLoader loader = new TerrainBuildoutLoader(clientFactory, crcTable, terrain); + loader.load(); + return loader.getObjects(); + } + + private static List getTerrainsToLoad() { + DatatableData table = (DatatableData) clientFactory.getInfoFromFile("datatables/buildout/buildout_scenes.iff"); + List terrains = new LinkedList(); + for (int row = 0; row < table.getRowCount(); row++) { + Terrain t = Terrain.getTerrainFromName((String) table.getCell(row, 0)); + if (t != null) + terrains.add(t); + else + System.err.println("Couldn't find terrain: " + table.getCell(row, 0)); + } + return terrains; + } + +} diff --git a/src/resources/objects/buildouts/TerrainBuildoutLoader.java b/src/resources/objects/buildouts/TerrainBuildoutLoader.java new file mode 100644 index 000000000..76c5e6790 --- /dev/null +++ b/src/resources/objects/buildouts/TerrainBuildoutLoader.java @@ -0,0 +1,167 @@ +package resources.objects.buildouts; + +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import resources.Location; +import resources.Terrain; +import resources.client_info.ClientFactory; +import resources.client_info.visitors.CrcStringTableData; +import resources.client_info.visitors.DatatableData; +import resources.objects.SWGObject; +import resources.objects.cell.CellObject; +import services.objects.ObjectCreator; + +class TerrainBuildoutLoader { + + private static final String BASE_PATH = "datatables/buildout/"; + + private final ClientFactory clientFactory; + private final CrcStringTableData crcTable; + private final Terrain terrain; + private final Hashtable objectTable; + private final List objects; + private final List parentless; + private final Location areaLocation; + + public TerrainBuildoutLoader(ClientFactory clientFactory, CrcStringTableData crcTable, Terrain terrain) { + this.clientFactory = clientFactory; + this.crcTable = crcTable; + this.terrain = terrain; + this.objectTable = new Hashtable(12*1024); + this.objects = new LinkedList(); + this.parentless = new LinkedList(); + this.areaLocation = new Location(0, 0, 0, terrain); + } + + public void load() { + objects.clear(); + loadAreas(); + } + + public List getObjects() { + return objects; + } + + private void loadAreas() { + String file = BASE_PATH+"areas_"+terrain.getName()+".iff"; + DatatableData areaTable = (DatatableData) clientFactory.getInfoFromFile(file); + for (int row = 0; row < areaTable.getRowCount(); row++) { + if (!((String) areaTable.getCell(row, 0)).startsWith(terrain.getName())) + continue; + areaLocation.setX((Float) areaTable.getCell(row, 1)); + areaLocation.setZ((Float) areaTable.getCell(row, 2)); + loadArea((String) areaTable.getCell(row, 0)); + } + updateParentless(); + } + + private void loadArea(String areaName) { + String file = BASE_PATH+terrain.getName()+"/"+areaName.replaceAll("server", "client")+".iff"; + DatatableData area = (DatatableData) clientFactory.getInfoFromFile(file); + switch (area.getColumnCount()) { + case 11: + loadColumnsSmall(area); + break; + case 14: + loadColumnsLarge(area); + break; + default: + System.err.println("Unable to process iff with " + area.getColumnCount() + " columns. File: " + file); + break; + } + } + + private void loadColumnsLarge(DatatableData area) { + int rows = area.getRowCount(); + for (int row = 0; row < rows; row++) { + long objectId = (Integer) area.getCell(row, 0); + String template = crcTable.getTemplateString((Integer) area.getCell(row, 3)); + long container = ((Integer) area.getCell(row, 1)).longValue(); + loadRow(area, row, objectId, template, container, 4); + } + } + + private void loadColumnsSmall(DatatableData area) { + int rows = area.getRowCount(); + for (int row = 0; row < rows; row++) { + int crc = (Integer) area.getCell(row, 0); + long objectId = ((long) crc) << 32 + 0xFFFF86F9L; + String template = crcTable.getTemplateString(crc); + loadRow(area, row, objectId, template, 0, 1); + } + } + + private void loadRow(DatatableData area, int row, long objectId, String template, long container, int cellOffset) { + SWGObject obj = ObjectCreator.createObjectFromTemplate(objectId, template); + if (obj == null) { + System.err.println("Could not load object with template " + template); + return; + } + obj.setLocation(readLocation(area, row, cellOffset+1)); + if (obj instanceof CellObject) + ((CellObject) obj).setNumber((Integer) area.getCell(row, cellOffset)); + matchParent(obj, container); + } + + private void matchParent(SWGObject obj, long containerId) { + objectTable.put(obj.getObjectId(), obj); + if (containerId == 0) { + obj.setLocation(obj.getLocation().translate(areaLocation)); + objects.add(obj); + return; + } + SWGObject container = objectTable.get(containerId); + if (container == null) { + parentless.add(new Orphan(obj, containerId)); + } else { + container.addChild(obj); + objects.add(obj); + } + } + + private void updateParentless() { + Iterator orphanIt = parentless.iterator(); + while (orphanIt.hasNext()) { + Orphan orphan = orphanIt.next(); + SWGObject container = objectTable.get(orphan.getContainer()); + if (container != null) { + container.addChild(orphan.getObject()); + orphanIt.remove(); + } + } + } + + private Location readLocation(DatatableData area, int row, int startCol) { + Location l = new Location(); + l.setTerrain(terrain); + l.setX(readFloat(area, row, startCol+0)); + l.setY(readFloat(area, row, startCol+1)); + l.setZ(readFloat(area, row, startCol+2)); + l.setOrientationW(readFloat(area, row, startCol+3)); + l.setOrientationX(readFloat(area, row, startCol+4)); + l.setOrientationY(readFloat(area, row, startCol+5)); + l.setOrientationZ(readFloat(area, row, startCol+6)); + return l; + } + + private double readFloat(DatatableData area, int row, int col) { + return (Float) area.getCell(row, col); + } + + private class Orphan { + private SWGObject obj; + private long container; + + public Orphan(SWGObject obj, long container) { + this.obj = obj; + this.container = container; + } + + public SWGObject getObject() { return obj; } + public long getContainer() { return container; } + } + +} diff --git a/src/services/objects/ObjectCreator.java b/src/services/objects/ObjectCreator.java index 2aafcebf5..f705a51f1 100644 --- a/src/services/objects/ObjectCreator.java +++ b/src/services/objects/ObjectCreator.java @@ -1,34 +1,100 @@ package services.objects; +import java.util.Map.Entry; + +import resources.client_info.ClientFactory; +import resources.client_info.visitors.ObjectData; +import resources.client_info.visitors.SlotArrangementData; +import resources.client_info.visitors.SlotDescriptorData; import resources.objects.SWGObject; +import resources.objects.building.BuildingObject; +import resources.objects.cell.CellObject; import resources.objects.creature.CreatureObject; +import resources.objects.installation.InstallationObject; import resources.objects.intangible.IntangibleObject; +import resources.objects.mobile.MobileObject; import resources.objects.player.PlayerObject; +import resources.objects.resource.ResourceContainerObject; +import resources.objects.ship.ShipObject; +import resources.objects.sound.SoundObject; +import resources.objects.staticobject.StaticObject; import resources.objects.tangible.TangibleObject; import resources.objects.waypoint.WaypointObject; import resources.objects.weapon.WeaponObject; -final class ObjectCreator { +public final class ObjectCreator { + + private static final ClientFactory clientFac = new ClientFactory(); public static final SWGObject createObjectFromTemplate(long objectId, String template) { if (!template.startsWith("object/")) return null; if (!template.endsWith(".iff")) return null; - template = template.substring(7, template.length()-7-4); - switch (getFirstTemplatePart(template)) { - case "creature": return createCreatureObject(objectId, template); - case "player": return createPlayerObject(objectId, template); - case "tangible": return createTangibleObject(objectId, template); - case "intangible": return createIntangibleObject(objectId, template); - case "waypoint": return createWaypointObject(objectId, template); - case "weapon": return createWeaponObject(objectId, template); - case "building": break; - case "cell": break; + SWGObject obj = createObjectFromType(objectId, getFirstTemplatePart(template.substring(7, template.length()-7-4))); + if (obj == null) + return null; + addObjectAttributes(obj, template); + obj.setTemplate(template); + return obj; + } + + private static SWGObject createObjectFromType(long objectId, String type) { + switch (type) { + case "creature": return new CreatureObject(objectId); + case "player": return new PlayerObject(objectId); + case "tangible": return new TangibleObject(objectId); + case "intangible": return new IntangibleObject(objectId); + case "waypoint": return new WaypointObject(objectId); + case "weapon": return new WeaponObject(objectId); + case "building": return new BuildingObject(objectId); + case "cell": return new CellObject(objectId); + case "static": return new StaticObject(objectId); + case "resource_container": return new ResourceContainerObject(objectId); + case "installation": return new InstallationObject(objectId); + case "ship": return new ShipObject(objectId); + case "soundobject": return new SoundObject(objectId); + case "mobile": return new MobileObject(objectId); } return null; } + private static void addObjectAttributes(SWGObject obj, String template) { + ObjectData attributes = (ObjectData) clientFac.getInfoFromFile(ClientFactory.formatToSharedFile(template)); + + String stf = (String) attributes.getAttribute(ObjectData.OBJ_STF); + String detailStf = (String) attributes.getAttribute(ObjectData.DETAIL_STF); + Integer volumeLimit = (Integer) attributes.getAttribute(ObjectData.VOLUME_LIMIT); + + obj.setStf(stf); + if (detailStf != null) + obj.setDetailStf(detailStf); + if (volumeLimit != null) + obj.setVolume(volumeLimit); + for (Entry e : attributes.getAttributes().entrySet()) { + obj.setTemplateAttribute(e.getKey(), e.getValue()); + } + + addSlotsToObject(obj, attributes); + } + + private static void addSlotsToObject(SWGObject obj, ObjectData attributes) { + if (attributes.getAttribute(ObjectData.SLOT_DESCRIPTOR) != null) { + // These are the slots that the object *HAS* + SlotDescriptorData descriptor = (SlotDescriptorData) clientFac.getInfoFromFile((String) attributes.getAttribute(ObjectData.SLOT_DESCRIPTOR)); + + for (String slotName : descriptor.getSlots()) { + obj.addObjectSlot(slotName, null); + } + } + + if (attributes.getAttribute(ObjectData.ARRANGEMENT_FILE) != null) { + // This is what slots the object *USES* + SlotArrangementData arrangementData = (SlotArrangementData) clientFac.getInfoFromFile((String) attributes.getAttribute(ObjectData.ARRANGEMENT_FILE)); + obj.setArrangment(arrangementData.getArrangement()); + } + } + private static String getFirstTemplatePart(String template) { int ind = template.indexOf('/'); if (ind == -1) @@ -36,28 +102,4 @@ final class ObjectCreator { return template.substring(0, ind); } - private static CreatureObject createCreatureObject(long objectId, String template) { - return new CreatureObject(objectId); - } - - private static PlayerObject createPlayerObject(long objectId, String template) { - return new PlayerObject(objectId); - } - - private static TangibleObject createTangibleObject(long objectId, String template) { - return new TangibleObject(objectId); - } - - private static IntangibleObject createIntangibleObject(long objectId, String template) { - return new IntangibleObject(objectId); - } - - private static WaypointObject createWaypointObject(long objectId, String template) { - return new WaypointObject(objectId); - } - - private static WeaponObject createWeaponObject(long objectId, String template) { - return new WeaponObject(objectId); - } - } diff --git a/src/services/objects/ObjectManager.java b/src/services/objects/ObjectManager.java index b0036aeae..94db2ebc0 100644 --- a/src/services/objects/ObjectManager.java +++ b/src/services/objects/ObjectManager.java @@ -5,6 +5,7 @@ import intents.ObjectTeleportIntent; import intents.PlayerEventIntent; import intents.swgobject_events.SWGObjectEventIntent; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -27,13 +28,10 @@ import resources.Location; import resources.Posture; import resources.Race; import resources.Terrain; -import resources.client_info.ClientFactory; -import resources.client_info.visitors.ObjectData; -import resources.client_info.visitors.SlotArrangementData; -import resources.client_info.visitors.SlotDescriptorData; import resources.control.Intent; import resources.control.Manager; import resources.objects.SWGObject; +import resources.objects.buildouts.BuildoutLoader; import resources.objects.creature.CreatureObject; import resources.objects.quadtree.QuadTree; import resources.objects.waypoint.WaypointObject; @@ -49,13 +47,13 @@ public class ObjectManager extends Manager { private static final double AWARE_RANGE = 200; - private ClientFactory clientFac; - - private ObjectDatabase objects; - private Map > quadTree; + private final Map > buildoutObjects; + private final ObjectDatabase objects; + private final Map > quadTree; private long maxObjectId; public ObjectManager() { + buildoutObjects = new HashMap>(); objects = new CachedObjectDatabase("odb/objects.db"); quadTree = new HashMap>(); maxObjectId = 1; @@ -69,7 +67,7 @@ public class ObjectManager extends Manager { registerForIntent(ObjectTeleportIntent.TYPE); loadQuadTree(); loadObjects(); - clientFac = new ClientFactory(); + loadBuildouts(); return super.initialize(); } @@ -96,6 +94,40 @@ public class ObjectManager extends Manager { System.out.printf("ObjectManager: Finished loading %d objects. Time: %fms%n", objects.size(), loadTime); } + private void loadBuildouts() { + boolean enableBuildouts = false; + if (enableBuildouts) { + long startLoad = System.nanoTime(); + System.out.println("ObjectManager: Loading buildouts..."); + List buildouts = null; +// buildouts = BuildoutLoader.loadAllBuildouts(); + buildouts = BuildoutLoader.loadBuildoutsForTerrain(Terrain.CORELLIA); + for (SWGObject obj : buildouts) { + loadBuildout(obj); + } + double loadTime = (System.nanoTime() - startLoad) / 1E6; + System.out.printf("ObjectManager: Finished loading buildouts. Time: %fms%n", loadTime); + } else { + System.out.println("ObjectManager: Buildouts not loaded. Reason: Disabled!"); + } + } + + private void loadBuildout(SWGObject obj) { + loadObject(obj); + List idCollisions = buildoutObjects.get(obj.getObjectId()); + if (idCollisions == null) + buildoutObjects.put(obj.getObjectId(), idCollisions = new ArrayList()); + boolean duplicate = false; + for (SWGObject dup : idCollisions) { + if (dup.getLocation().equals(obj.getLocation()) && dup.getTemplate().equals(obj.getTemplate())) { + duplicate = true; + break; + } + } + if (!duplicate) + idCollisions.add(obj); + } + private void loadObject(SWGObject obj) { obj.setOwner(null); Location l = obj.getLocation(); @@ -208,8 +240,6 @@ public class ObjectManager extends Manager { System.err.println("ObjectManager: Unable to create object with template " + template); return null; } - addObjectAttributes(obj, template); - obj.setTemplate(template); obj.setLocation(l); updateAwarenessForObject(obj); addToQuadTree(obj); @@ -266,46 +296,23 @@ public class ObjectManager extends Manager { Location location = obj.getLocation(); if (location == null || location.getTerrain() == null) return; - List updatedAware = new LinkedList(); + List objectAware = new LinkedList(); double x = location.getX(); double y = location.getZ(); QuadTree tree = quadTree.get(location.getTerrain()); synchronized (tree) { List range = tree.getWithinRange(x, y, AWARE_RANGE); for (SWGObject inRange : range) { - if (inRange.getOwner() != null && inRange.getObjectId() != obj.getObjectId()) { - updatedAware.add(inRange.getOwner()); + if (inRange.getObjectId() != obj.getObjectId()) { + if (inRange instanceof CreatureObject) { + if (inRange.getOwner() != null) + objectAware.add(inRange); + } else + objectAware.add(inRange); } } } - obj.updateAwareness(updatedAware); - } - - private void addObjectAttributes(SWGObject obj, String template) { - ObjectData attributes = (ObjectData) clientFac.getInfoFromFile(ClientFactory.formatToSharedFile(template)); - - obj.setStf((String) attributes.getAttribute(ObjectData.OBJ_STF)); - obj.setDetailStf((String) attributes.getAttribute(ObjectData.DETAIL_STF)); - obj.setVolume((Integer) attributes.getAttribute(ObjectData.VOLUME_LIMIT)); - - addSlotsToObject(obj, attributes); - } - - private void addSlotsToObject(SWGObject obj, ObjectData attributes) { - if (attributes.getAttribute(ObjectData.SLOT_DESCRIPTOR) != null) { - // These are the slots that the object *HAS* - SlotDescriptorData descriptor = (SlotDescriptorData) clientFac.getInfoFromFile((String) attributes.getAttribute(ObjectData.SLOT_DESCRIPTOR)); - - for (String slotName : descriptor.getSlots()) { - obj.addObjectSlot(slotName, null); - } - } - - if (attributes.getAttribute(ObjectData.ARRANGEMENT_FILE) != null) { - // This is what slots the object *USES* - SlotArrangementData arrangementData = (SlotArrangementData) clientFac.getInfoFromFile((String) attributes.getAttribute(ObjectData.ARRANGEMENT_FILE)); - obj.setArrangment(arrangementData.getArrangement()); - } + obj.updateObjectAwareness(objectAware); } private void zoneInCharacter(PlayerManager playerManager, String galaxy, long netId, long characterId) { diff --git a/src/services/player/PlayerManager.java b/src/services/player/PlayerManager.java index 20c6750d4..a6e3e8bbd 100644 --- a/src/services/player/PlayerManager.java +++ b/src/services/player/PlayerManager.java @@ -109,13 +109,18 @@ public class PlayerManager extends Manager { } public Player getPlayerByCreatureFirstName(String name) { + if (name == null || name.isEmpty()) + return null; + name = name.trim().toLowerCase(Locale.ENGLISH); synchronized (players) { for (Player p : players.values()) { if (p.getCreatureObject() != null) { String cName = p.getCharacterName().toLowerCase(Locale.ENGLISH); - if (cName.startsWith(name) && (cName.length() == name.length() || cName.charAt(name.length()) == ' ')) { + if (cName.equals(name)) + return p; + int spaceIndex = cName.indexOf(' '); + if (spaceIndex != -1 && cName.substring(0, spaceIndex).equals(name)) return p; - } } } }