using System; using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json; using PSO2SERVER.Database; using PSO2SERVER.Models; namespace PSO2SERVER.Object { class ObjectManager { private static readonly ObjectManager instance = new ObjectManager(); private Dictionary> zoneObjects = new Dictionary>(); private Dictionary allTheObjects = new Dictionary(); private ObjectManager() { } public static ObjectManager Instance { get { return instance; } } public PSOObject[] GetObjectsForZone(string zone) { if (zone == "tpmap") // Return empty object array for an tp'd map for now (We spawn in a teleporter manually) { return new PSOObject[0]; } if (!zoneObjects.ContainsKey(zone)) { Dictionary objects = new Dictionary(); // Collect from db using (var db = new ServerEf()) { var dbObjects = from dbo in db.GameObjects where dbo.ZoneName == zone select dbo; foreach(var dbObject in dbObjects) { var newObject = PSOObject.FromDBObject(dbObject); objects.Add(newObject.Header.ID, newObject); allTheObjects.Add(newObject.Header.ID, newObject); Logger.WriteInternal("[OBJ] Loaded object {0} for zone {1} from the DB.", newObject.Name, zone); } } // Fallback if (objects.Count < 1 && Directory.Exists("Resources/objects/" + zone)) { Logger.WriteWarning("[OBJ] No objects defined for zone {0} in the database, falling back to filesystem!", zone); var objectPaths = Directory.GetFiles("Resources/objects/" + zone); Array.Sort(objectPaths); foreach (var path in objectPaths) { if (Path.GetExtension(path) == ".bin") { var newObject = PSOObject.FromPacketBin(File.ReadAllBytes(path)); objects.Add(newObject.Header.ID, newObject); allTheObjects.Add(newObject.Header.ID, newObject); Logger.WriteInternal("[OBJ] Loaded object ID {0} with name {1} pos: ({2}, {3}, {4})", newObject.Header.ID, newObject.Name, newObject.Position.PosX, newObject.Position.PosY, newObject.Position.PosZ); } else if (Path.GetExtension(path) == ".json") { var newObject = JsonConvert.DeserializeObject(File.ReadAllText(path)); objects.Add(newObject.Header.ID, newObject); allTheObjects.Add(newObject.Header.ID, newObject); Logger.WriteInternal("[OBJ] Loaded object ID {0} with name {1} pos: ({2}, {3}, {4})", newObject.Header.ID, newObject.Name, newObject.Position.PosX, newObject.Position.PosY, newObject.Position.PosZ); } } } zoneObjects.Add(zone, objects); } return zoneObjects[zone].Values.ToArray(); } internal PSONPC[] getNPCSForZone(string zone) { List npcs = new List(); using (var db = new ServerEf()) { var dbNpcs = from n in db.NPCs where n.ZoneName == zone select n; foreach (NPC npc in dbNpcs) { PSONPC dNpc = new PSONPC(); dNpc.Header = new ObjectHeader((uint)npc.EntityID, EntityType.Object); dNpc.Position = new PSOLocation(npc.RotX, npc.RotY, npc.RotZ, npc.RotW, npc.PosX, npc.PosY, npc.PosZ); dNpc.Name = npc.NPCName; npcs.Add(dNpc); if (!zoneObjects[zone].ContainsKey(dNpc.Header.ID)) { zoneObjects[zone].Add(dNpc.Header.ID, dNpc); } if (!allTheObjects.ContainsKey(dNpc.Header.ID)) allTheObjects.Add(dNpc.Header.ID, dNpc); } } return npcs.ToArray(); } internal PSOObject getObjectByID(string zone, uint ID) { //FIXME: This has been commented out because we were getting object errors with possible shared objects? That or it was just object 1 which is an edge case. //if(!zoneObjects.ContainsKey(zone) || !zoneObjects[zone].ContainsKey(ID)) //{ // throw new Exception(String.Format("Object ID {0} does not exist in {1}!", ID, zone)); //} //return zoneObjects[zone][ID]; return getObjectByID(ID); } internal PSOObject getObjectByID(uint ID) { if (!allTheObjects.ContainsKey(ID)) { Logger.WriteWarning("[OBJ] Client requested object {0} which we don't know about. Investigate.", ID); return new PSOObject() { Header = new ObjectHeader(ID, EntityType.Object), Name = "Unknown" }; } return allTheObjects[ID]; } } }