/// <summary> /// Update the assets table based on the transactions meeting the specified criteria. /// </summary> /// <param name="charID"></param> /// <param name="corpID"></param> /// <param name="useCorp"></param> /// <param name="minimumTransID"></param> /// <param name="includeTransAfter"></param> /// <returns></returns> private static long UpdateFromTransactions(EMMADataSet.AssetsDataTable assetsData, AssetList changes, long charID, long corpID, bool useCorp, long minimumTransID, DateTime includeTransAfter) { long maxID = 0; long ownerID = useCorp ? corpID : charID; EMMADataSet.TransactionsDataTable transactions = new EMMADataSet.TransactionsDataTable(); transactions = GetTransactions(charID, corpID, useCorp, minimumTransID, includeTransAfter); foreach (EMMADataSet.TransactionsRow trans in transactions) { // If the ID is greater than 9000000000000000000 then it must have been created by EMMA as part of // an item exchange contract. These should be ignored here. if (trans.ID < 9000000000000000000) { int deltaQuantity = trans.Quantity; if (trans.SellerID == ownerID) { deltaQuantity *= -1; } // Change this to not actually make the change in the database. Instead, use the // asset data table passed in and record the changes made in the 'changes' list. /*ChangeAssets(charID, useCorp, trans.StationID, trans.ItemID, 0, (int)AssetStatus.States.Normal, false, deltaQuantity, (deltaQuantity > 0 ? trans.Price : 0), deltaQuantity > 0);*/ long assetID = 0; EMMADataSet.AssetsRow asset = null; AssetExists(assetsData, ownerID, trans.StationID, trans.ItemID, (int)AssetStatus.States.Normal, false, 0, false, false, true, false, true, 0, ref assetID); if (assetID != 0) { // Asset stack already exists in database and/or datatable, modify it // based upon the transaction data. asset = assetsData.FindByID(assetID); asset.Quantity += deltaQuantity; if (deltaQuantity > 0) { Asset logicalAsset = new Asset(asset); asset.CostCalc = true; asset.Cost = (logicalAsset.TotalBuyPrice + (trans.Price * trans.Quantity)) / (logicalAsset.Quantity + trans.Quantity); } Asset chg = new Asset(asset); chg.Quantity = deltaQuantity; changes.Add(chg); } else { // Asset does not exist in database so add it to the datatable. asset = assetsData.NewAssetsRow(); asset.Quantity = deltaQuantity; asset.AutoConExclude = false; asset.ContainerID = 0; asset.CorpAsset = useCorp; asset.Cost = trans.Price; asset.CostCalc = true; asset.IsContainer = false; asset.ItemID = trans.ItemID; asset.EveItemID = 0; asset.LocationID = trans.StationID; asset.OwnerID = charID; asset.Processed = true; asset.RegionID = trans.RegionID; asset.ReprocExclude = false; asset.Status = (int)AssetStatus.States.Normal; asset.SystemID = Stations.GetStation(trans.StationID).solarSystemID; asset.BoughtViaContract = false; assetsData.AddAssetsRow(asset); changes.Add(new Asset(asset)); } if (trans.ID > maxID) { maxID = trans.ID; } } } return maxID; }
/// <summary> /// Recursive method to update the supplied assets data table based upon the supplied xml node list. /// </summary> /// <param name="assetData"></param> /// <param name="assetList"></param> /// <param name="locationID"></param> /// <param name="corc"></param> /// <param name="containerID"></param> /// <param name="expectedChanges"></param> private void UpdateAssets(EMMADataSet.AssetsDataTable assetData, XmlNodeList assetList, long locationID, CharOrCorp corc, long containerID, AssetList changes) { int counter = 0; if (containerID == 0) { UpdateStatus(counter, assetList.Count, "Getting asset data from file", "", false); } else { UpdateStatus(-1, -1, "Getting asset data from file", "", false, counter, assetList.Count, "Container progress"); } foreach (XmlNode asset in assetList) { int itemID; long assetID = 0, eveInstanceID, quantity; bool isContainer = false, needNewRow = false; XmlNode locationNode = asset.SelectSingleNode("@locationID"); if (locationNode != null) { locationID = long.Parse(locationNode.Value); // Translate location ID from a corporate office to a station ID if required. if (locationID >= 66000000 && locationID < 67000000) { // NPC station. locationID -= 6000001; } if (locationID >= 67000000 && locationID < 68000000) { // Conquerable station. locationID -= 6000000; } } itemID = int.Parse(asset.SelectSingleNode("@typeID").Value, System.Globalization.CultureInfo.InvariantCulture.NumberFormat); eveInstanceID = long.Parse(asset.SelectSingleNode("@itemID").Value, System.Globalization.CultureInfo.InvariantCulture.NumberFormat); quantity = long.Parse(asset.SelectSingleNode("@quantity").Value, System.Globalization.CultureInfo.InvariantCulture.NumberFormat); if (asset.LastChild != null && asset.LastChild.Name.Equals("rowset")) { isContainer = true; } EMMADataSet.AssetsRow assetRow; needNewRow = true; // Note that if a match is not found for the specific eve instance ID we pass in then // EMMA will automatically search for an asset matching all the other parameters. if (Assets.AssetExists(assetData, corc == CharOrCorp.Corp ? _corpID : _charID, locationID, itemID, (int)AssetStatus.States.Normal, containerID != 0, containerID, isContainer, false, !isContainer, false, true, eveInstanceID, ref assetID)) { needNewRow = false; } else if(!isContainer) { // We havn't actually updated the database with anything yet so we may already have a // matching item stack in memory but not in the database. Check for that here. DataRow[] data = assetData.Select("ItemID = " + itemID + " AND OwnerID = " + _charID + " AND CorpAsset = " + (corc == CharOrCorp.Corp ? 1 : 0) + " AND LocationID = " + locationID + " AND Status = " + (int)AssetStatus.States.Normal + " AND ContainerID = " + containerID + " AND EveItemID = " + eveInstanceID); if (data != null && data.Length > 0) { needNewRow = false; assetID = ((EMMADataSet.AssetsRow)data[0]).ID; } } Asset change = null; if (!needNewRow) { assetRow = assetData.FindByID(assetID); if (assetRow.Processed) { // Row is already in the database but has been processed so just add the current // quantity to the row. // (i.e. there are multiple stacks of the same item in the same location in-game // EMMA merges these since we don't care how things are stacked and it makes // things a little easier.) assetRow.Quantity = assetRow.Quantity + quantity; // We're stacking multiple eve item instances so just set the eve item ID to zero. assetRow.EveItemID = 0; // Store the changes that are being made to the quantity of // items here. // This means that once the update processing is complete, we // can try and work out where these items came from. #region Remember changes to item quantities changes.ItemFilter = "ID = " + assetRow.ID; if (changes.FiltredItems.Count > 0) { change = (Asset)changes.FiltredItems[0]; change.Quantity += quantity; change.EveItemInstanceID = 0; if (change.Quantity == 0) { changes.ItemFilter = ""; changes.Remove(change); } } else { change = new Asset(assetRow); change.Quantity = quantity; change.Processed = false; changes.Add(change); } #endregion } else { if (assetRow.Quantity == quantity) { // The row already exists in the database and quantity is the same so // set the processed flag on the database directly and remove the row // from the dataset without setting it to be deleted when the database // is updated. // Note the processed flag MUST be set on the database for later routines // to work correctly. (e.g. Assets.ProcessSellOrders) if (assetRow.EveItemID != 0) { Assets.SetProcessedFlag(assetID, true); assetData.RemoveAssetsRow(assetRow); } else { // If Eve instance ID is not yet set then set it. Assets.SetProcessedFlag(assetID, true); assetRow.Processed = true; assetRow.EveItemID = eveInstanceID; } } else if (assetRow.Quantity != quantity) { // The row already exists in the database, has not yet been processed // and the quantity does not match what we've got from the XML. // Store the changes that are being made to the quantity of // items here. // This means that once the update processing is complete, we // can try and work out where these items came from. #region Remember changes to item quantities change = new Asset(assetRow); change.Quantity = quantity - assetRow.Quantity; change.EveItemInstanceID = eveInstanceID; change.Processed = false; changes.Add(change); #endregion // All we need to do is update the quantity and set the processed flag. assetRow.Quantity = quantity; assetRow.Processed = true; assetRow.EveItemID = eveInstanceID; // Also set the processed flag on the database directly. This will // stop us from picking up this row later on (e.g. Assets.ProcessSellOrders) Assets.SetProcessedFlag(assetID, true); } } } else { // The row does not currently exist in the database so we need to create it. assetRow = assetData.NewAssetsRow(); //assetRow.OwnerID = _charID; assetRow.OwnerID = corc == CharOrCorp.Corp ? _corpID : _charID; assetRow.CorpAsset = corc == CharOrCorp.Corp; assetRow.ItemID = itemID; assetRow.EveItemID = eveInstanceID; assetRow.LocationID = locationID; assetRow.Status = 1; assetRow.Processed = true; assetRow.AutoConExclude = false; assetRow.ReprocExclude = false; assetRow.Cost = 0; assetRow.CostCalc = false; assetRow.BoughtViaContract = false; long systemID = 0, regionID = 0; if (locationID >= 30000000 && locationID < 40000000) { systemID = locationID; EveDataSet.mapSolarSystemsRow system = SolarSystems.GetSystem(locationID); if (system != null) { regionID = system.regionID; } else { new EMMAEveAPIException(ExceptionSeverity.Warning, "Asset row added with unknown " + "solar system ID (" + locationID + ")"); } } else { EveDataSet.staStationsRow station = null; try { station = Stations.GetStation(locationID); } catch (EMMADataMissingException) { } if (station != null) { systemID = station.solarSystemID; regionID = station.regionID; } else { new EMMAEveAPIException(ExceptionSeverity.Warning, "Asset row added with unknown " + "station ID (" + locationID + ")"); } } assetRow.SystemID = systemID; assetRow.RegionID = regionID; assetRow.Quantity = quantity; assetRow.ContainerID = containerID; assetRow.IsContainer = isContainer; if (isContainer) { // If this asset is a container and has child assets then we must add it to the // database now and get the correct ID number. // (Because IDs are assigned by the database itself) assetID = Assets.AddRowToDatabase(assetRow); } else { // Otherwise, we can just add it to the data table to be stored later along with // everything else. assetData.AddAssetsRow(assetRow); } // Store the changes that are being made to the quantity of // items here. // This means that once the update processing is complete, we // can try and work out where these items came from. #region Remember changes to item quantities change = new Asset(assetRow); if (isContainer) { change.ID = assetID; } change.Quantity = quantity; change.Processed = false; changes.Add(change); #endregion } if (isContainer) { XmlNodeList contained = asset.SelectNodes("rowset/row"); UpdateAssets(assetData, contained, locationID, corc, assetID, changes); } counter++; if (containerID == 0) { UpdateStatus(counter, assetList.Count, "Getting asset data from file", "", false); } else { UpdateStatus(-1, -1, "Getting asset data from file", "", false, counter, assetList.Count, "Container progress"); } } }
/// <summary> /// This ensures that items in sell orders appear in the list of the player's assets. /// It is called just after new asset XML from the API has been processed but before /// the update is applied to the database. /// </summary> public static void ProcessSellOrders(EMMADataSet.AssetsDataTable assetData, AssetList changes, long ownerID) { List<int> itemIDs = new List<int>(); itemIDs.Add(0); List<long> stationIDs = new List<long>(); stationIDs.Add(0); List<AssetAccessParams> accessParams = new List<AssetAccessParams>(); accessParams.Add(new AssetAccessParams(ownerID)); // Get active sell orders OrdersList sellOrders = Orders.LoadOrders(accessParams, itemIDs, stationIDs, 999, "Sell"); EMMADataSet.AssetsRow changedAsset = null; // Note that modifiedAssets is indexed first by itemID and second by stationID Dictionary<int, Dictionary<long, AssetInfo>> modifiedAssets = new Dictionary<int, Dictionary<long, AssetInfo>>(); foreach (Order sellOrder in sellOrders) { bool foundMatch = false; long assetID = 0; // If there is already an asset row with a state of 'ForSaleViaMarket' in the same location, // and with the same item type then check quantity. // If it matches then just set to processed and move on. // If it does not then record the difference in quantities and go to the next order. // If we can't find a match then add a new asset row and record the items gained. if (Assets.AssetExists(assetData, ownerID, sellOrder.StationID, sellOrder.ItemID, (int)AssetStatus.States.ForSaleViaMarket, false, 0, false, false, true, true, true, 0, ref assetID)) { foundMatch = true; } else { DataRow[] data = assetData.Select("ItemID = " + sellOrder.ItemID + " AND OwnerID = " + ownerID.ToString() + " AND LocationID = " + sellOrder.StationID + " AND Status = " + (int)AssetStatus.States.ForSaleViaMarket); if (data != null && data.Length > 0) { foundMatch = true; assetID = ((EMMADataSet.AssetsRow)data[0]).ID; } } if (foundMatch) { changedAsset = assetData.FindByID(assetID); if (changedAsset.Quantity != sellOrder.RemainingVol) { // If the quantities do not match then store how many units we are removing // from the stack, the most likely cause is more than one sell order for // this item in this location and if we know how many units we've removed // We can make sure that the other order(s) quantity matches up. Dictionary<long, AssetInfo> itemDeltaVol = new Dictionary<long, AssetInfo>(); if (modifiedAssets.ContainsKey(sellOrder.ItemID)) { itemDeltaVol = modifiedAssets[sellOrder.ItemID]; } else { modifiedAssets.Add(sellOrder.ItemID, itemDeltaVol); } if (itemDeltaVol.ContainsKey(sellOrder.StationID)) { AssetInfo info = itemDeltaVol[sellOrder.StationID]; info.quantity += sellOrder.RemainingVol - changedAsset.Quantity; itemDeltaVol[sellOrder.StationID] = info; changedAsset.Quantity += sellOrder.RemainingVol; } else { AssetInfo info = new AssetInfo(); info.quantity = sellOrder.RemainingVol - changedAsset.Quantity; info.assetID = changedAsset.ID; itemDeltaVol.Add(sellOrder.StationID, info); changedAsset.Quantity = sellOrder.RemainingVol; } } changedAsset.Processed = true; // Also set it to processed in the database. SetProcessedFlag(changedAsset.ID, true); } // We havn't managed to match the order to an existing 'ForSaleViaMarket' stack in // the database or in memory. // As such, we need to create a new one. if (!foundMatch) { // Create the new asset row.. changedAsset = assetData.NewAssetsRow(); changedAsset.AutoConExclude = true; changedAsset.ContainerID = 0; changedAsset.CorpAsset = false; // Set cost to zero for now, it will be worked out later when gains/losses are reconciled. changedAsset.Cost = 0; changedAsset.CostCalc = false; changedAsset.IsContainer = false; changedAsset.ItemID = sellOrder.ItemID; changedAsset.EveItemID = 0; changedAsset.LocationID = sellOrder.StationID; changedAsset.OwnerID = sellOrder.OwnerID; changedAsset.Processed = true; changedAsset.Quantity = sellOrder.RemainingVol; changedAsset.RegionID = sellOrder.RegionID; changedAsset.ReprocExclude = true; changedAsset.SystemID = sellOrder.SystemID; changedAsset.BoughtViaContract = false; changedAsset.Status = (int)AssetStatus.States.ForSaleViaMarket; assetData.AddAssetsRow(changedAsset); // Store the changes we are making to quantities Dictionary<long, AssetInfo> itemDeltaVol = new Dictionary<long, AssetInfo>(); if (modifiedAssets.ContainsKey(sellOrder.ItemID)) { itemDeltaVol = modifiedAssets[sellOrder.ItemID]; } else { modifiedAssets.Add(sellOrder.ItemID, itemDeltaVol); } if (itemDeltaVol.ContainsKey(sellOrder.StationID)) { AssetInfo info = itemDeltaVol[sellOrder.StationID]; info.quantity += sellOrder.RemainingVol; itemDeltaVol[sellOrder.StationID] = info; } else { AssetInfo info = new AssetInfo(); info.quantity = sellOrder.RemainingVol; info.assetID = changedAsset.ID; itemDeltaVol.Add(sellOrder.StationID, info); } } } // Once we've finished processing all the orders, store the overall quantity changes. Dictionary<int, Dictionary<long, AssetInfo>>.Enumerator enumerator = modifiedAssets.GetEnumerator(); while (enumerator.MoveNext()) { Dictionary<long, AssetInfo>.Enumerator enumerator2 = enumerator.Current.Value.GetEnumerator(); while(enumerator2.MoveNext()) { Asset change = new Asset(); change.ID = enumerator2.Current.Value.assetID; change.ItemID = enumerator.Current.Key; change.LocationID = enumerator2.Current.Key; change.Quantity = enumerator2.Current.Value.quantity; change.StatusID = (int)AssetStatus.States.ForSaleViaMarket; change.IsContainer = false; change.Container = null; change.AutoConExclude = true; change.OwnerID = ownerID; change.UnitBuyPrice = 0; change.UnitBuyPricePrecalculated = false; changes.Add(change); } } }