public PyDataType AssembleShip(PyList itemIDs, CallInformation call) { foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { this.AssembleShip(itemID, call); } return(null); }
public PyDataType MultiMerge(PyList merges, CallInformation call) { foreach (PyTuple merge in merges.GetEnumerable <PyTuple>()) { if (merge[0] is PyInteger == false || merge[1] is PyInteger == false || merge[2] is PyInteger == false) { continue; } PyInteger fromItemID = merge[0] as PyInteger; PyInteger toItemID = merge[1] as PyInteger; PyInteger quantity = merge[2] as PyInteger; if (this.mInventory.Items.TryGetValue(toItemID, out ItemEntity toItem) == false) { continue; } ItemEntity fromItem = this.ItemFactory.GetItem(fromItemID); // ignore singleton items if (fromItem.Singleton == true || toItem.Singleton == true) { continue; } // ignore items that are not the same type if (fromItem.Type.ID != toItem.Type.ID) { continue; } // if we're fully merging two stacks, just remove one item if (quantity == fromItem.Quantity) { int oldLocationID = fromItem.LocationID; // remove the item this.ItemFactory.DestroyItem(fromItem); // notify the client about the item too call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(fromItem, oldLocationID)); } else { // change the item's quantity fromItem.Quantity -= quantity; // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(fromItem, fromItem.Quantity + quantity)); fromItem.Persist(); } toItem.Quantity += quantity; call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(toItem, toItem.Quantity - quantity)); toItem.Persist(); } return(null); }
private void HandleNotification(PyPacket packet, Client client) { if (packet.Source is PyAddressAny) { this.HandleBroadcastNotification(packet); return; } PyTuple callInfo = ((packet.Payload[0] as PyTuple)[1] as PySubStream).Stream as PyTuple; PyList objectIDs = callInfo[0] as PyList; string call = callInfo[1] as PyString; if (call != "ClientHasReleasedTheseObjects") { Log.Error($"Received notification from client with unknown method {call}"); return; } // search for the given objects in the bound service // and sure they're freed foreach (PyTuple objectID in objectIDs.GetEnumerable <PyTuple>()) { if (objectID[0] is PyString == false) { Log.Fatal("Expected bound call with bound string, but got something different"); return; } string boundString = objectID[0] as PyString; // parse the bound string to get back proper node and bound ids Match regexMatch = Regex.Match(boundString, "N=([0-9]+):([0-9]+)"); if (regexMatch.Groups.Count != 3) { Log.Fatal($"Cannot find nodeID and boundID in the boundString {boundString}"); return; } int nodeID = int.Parse(regexMatch.Groups[1].Value); int boundID = int.Parse(regexMatch.Groups[2].Value); if (nodeID != this.Container.NodeID) { Log.Fatal("Got a ClientHasReleasedTheseObjects call for an object ID that doesn't belong to us"); // TODO: MIGHT BE A GOOD IDEA TO RELAY THIS CALL TO THE CORRECT NODE // TODO: INSIDE THE NETWORK, AT LEAST THAT'S WHAT CCP IS DOING BASED // TODO: ON THE CLIENT'S CODE... NEEDS MORE INVESTIGATION return; } this.BoundServiceManager.FreeBoundService(boundID); } }
public PyDataType BatchCertificateGrant(PyList certificateList, CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); Character character = this.ItemFactory.GetItem <Character>(callerCharacterID); PyList <PyInteger> result = new PyList <PyInteger>(); Dictionary <int, Skill> skills = character.InjectedSkillsByTypeID; List <int> grantedCertificates = this.DB.GetCertificateListForCharacter(callerCharacterID); foreach (PyInteger certificateID in certificateList.GetEnumerable <PyInteger>()) { if (this.CertificateRelationships.TryGetValue(certificateID, out List <Relationship> relationships) == true) { bool requirementsMet = true; foreach (Relationship relationship in relationships) { if (relationship.ParentTypeID != 0 && (skills.TryGetValue(relationship.ParentTypeID, out Skill skill) == false || skill.Level < relationship.ParentLevel)) { requirementsMet = false; } if (relationship.ParentID != 0 && grantedCertificates.Contains(relationship.ParentID) == false) { requirementsMet = false; } } if (requirementsMet == false) { continue; } } // grant the certificate and add it to the list of granted certs this.DB.GrantCertificate(callerCharacterID, certificateID); // ensure the result includes that certificate list result.Add(certificateID); // add the cert to the list so certs that depend on others are properly granted grantedCertificates.Add(certificateID); } // notify the client about the granting of certificates call.Client.NotifyMultiEvent(new OnCertificateIssued()); return(result); }
public PyDataType GetQuotes(PyList itemIDs, CallInformation call) { Character character = this.ItemFactory.GetItem <Character>(call.Client.EnsureCharacterIsSelected()); PyDictionary <PyInteger, PyDataType> result = new PyDictionary <PyInteger, PyDataType>(); foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { if (this.mInventory.Items.TryGetValue(itemID, out ItemEntity item) == false) { throw new MktNotOwner(); } result[itemID] = this.GetQuote(character, item); } return(result); }
public PyDataType GetCharacterAppearanceList(PyList ids, CallInformation call) { PyList result = new PyList(ids.Count); int index = 0; foreach (PyInteger id in ids.GetEnumerable <PyInteger>()) { Rowset dbResult = this.DB.GetCharacterAppearanceInfo(id); if (dbResult.Rows.Count != 0) { result[index] = dbResult; } index++; } return(result); }
public PyDataType Reprocess(PyList itemIDs, PyInteger ownerID, PyInteger flag, PyBool unknown, PyList skipChecks, CallInformation call) { Character character = this.ItemFactory.GetItem <Character>(call.Client.EnsureCharacterIsSelected()); // TODO: TAKE INTO ACCOUNT OWNERID AND FLAG, THESE MOST LIKELY WILL BE USED BY CORP STUFF foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { if (this.mInventory.Items.TryGetValue(itemID, out ItemEntity item) == false) { throw new MktNotOwner(); } // reprocess the item this.Reprocess(character, item, call.Client); int oldLocationID = item.LocationID; // finally remove the item from the inventories this.ItemFactory.DestroyItem(item); // notify the client about the item being destroyed call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldLocationID)); } return(null); }
public PyDataType MultiAdd(PyList adds, PyInteger quantity, PyInteger flag, CallInformation call) { if (quantity == null) { // null quantity means all the items in the list foreach (PyInteger itemID in adds.GetEnumerable <PyInteger>()) { ItemEntity item = this.ItemFactory.GetItem(itemID); // check and then move the item this.PreMoveItemCheck(item, (Flags)(int)flag, item.Quantity); this.MoveItemHere(item, (Flags)(int)flag); } } else { // an specific quantity means we'll need to grab part of the stacks selected throw new CustomError("Not supported yet!"); } return(null); }
public PyDataType TrashItems(PyList itemIDs, PyInteger stationID, CallInformation call) { foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { // do not trash the active ship if (itemID == call.Client.ShipID) { throw new CantMoveActiveShip(); } ItemEntity item = this.ItemFactory.GetItem(itemID); // store it's location id int oldLocation = item.LocationID; Flags oldFlag = item.Flag; // remove the item off the ItemManager this.ItemFactory.DestroyItem(item); // notify the client of the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldFlag, oldLocation)); // TODO: CHECK IF THE ITEM HAS ANY META INVENTORY AND/OR BOUND SERVICE // TODO: AND FREE THOSE TOO SO THE ITEMS CAN BE REMOVED OFF THE DATABASE } return(null); }
public PyDataType InjectSkillIntoBrain(PyList itemIDs, CallInformation call) { foreach (PyInteger item in itemIDs.GetEnumerable <PyInteger>()) { try { // get the item by it's ID and change the location of it Skill skill = this.ItemFactory.GetItem <Skill>(item); // check if the character already has this skill injected if (this.Character.InjectedSkillsByTypeID.ContainsKey(skill.Type.ID) == true) { throw new CharacterAlreadyKnowsSkill(skill.Type); } // is this a stack of skills? if (skill.Quantity > 1) { // add one of the skill into the character's brain Skill newStack = this.ItemFactory.CreateSkill(skill.Type, this.Character, 0, SkillHistoryReason.None); // subtract one from the quantity skill.Quantity -= 1; // save to database skill.Persist(); // finally notify the client call.Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(skill, skill.Quantity + 1)); call.Client.NotifyMultiEvent(OnItemChange.BuildNewItemChange(newStack)); } else { // store old values for the notification int oldLocationID = skill.LocationID; Flags oldFlag = skill.Flag; // now set the new values skill.LocationID = this.Character.ID; skill.Flag = Flags.Skill; skill.Level = 0; skill.Singleton = true; // ensure the character has the skill in his/her brain this.Character.AddItem(skill); // ensure the changes are saved skill.Persist(); // notify the character of the change in the item call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(skill, oldFlag, oldLocationID)); call.Client.NotifyMultiEvent(OnItemChange.BuildSingletonChange(skill, false)); } } catch (CharacterAlreadyKnowsSkill) { throw; } catch (Exception) { Log.Error($"Cannot inject itemID {item} into {this.Character.ID}'s brain..."); throw; } } // send the skill injected notification to refresh windows if needed call.Client.NotifyMultiEvent(new OnSkillInjected()); return(null); }
public PyDataType GetMultiInvTypesEx(PyList typeIDs, CallInformation call) { return(this.DB.GetMultiInvTypesEx(typeIDs.GetEnumerable <PyInteger>())); }
public PyDataType GetMultiAllianceShortNamesEx(PyList ids, CallInformation call) { return(this.DB.GetMultiAllianceShortNamesEx(ids.GetEnumerable <PyInteger>())); }
public PyDataType GetMultiLocationsEx(PyList ids, CallInformation call) { return(this.DB.GetMultiLocationsEx(ids.GetEnumerable <PyInteger>())); }
public PyDataType UnasembleItems(PyDictionary validIDsByStationID, PyList skipChecks, CallInformation call) { int characterID = call.Client.EnsureCharacterIsSelected(); List <RepairDB.ItemRepackageEntry> entries = new List <RepairDB.ItemRepackageEntry>(); bool ignoreContractVoiding = false; bool ignoreRepackageWithUpgrades = false; foreach (PyString check in skipChecks.GetEnumerable <PyString>()) { if (check == "RepairUnassembleVoidsContract") { ignoreContractVoiding = true; } if (check == "ConfirmRepackageSomethingWithUpgrades") { ignoreRepackageWithUpgrades = true; } } foreach ((PyInteger stationID, PyList itemIDs) in validIDsByStationID.GetEnumerable <PyInteger, PyList>()) { foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { RepairDB.ItemRepackageEntry entry = this.RepairDB.GetItemToRepackage(itemID, characterID, stationID); if (entry.HasContract == true && ignoreContractVoiding == false) { throw new RepairUnassembleVoidsContract(itemID); } if (entry.HasUpgrades == true && ignoreRepackageWithUpgrades == false) { throw new ConfirmRepackageSomethingWithUpgrades(); } if (entry.Damage != 0.0) { throw new CantRepackageDamagedItem(); } entries.Add(entry); } } foreach (RepairDB.ItemRepackageEntry entry in entries) { if (entry.Singleton == false) { continue; } // extra situation, the repair is happening on a item in our node, the client must know immediately if (entry.NodeID == this.Container.NodeID || this.SystemManager.StationBelongsToUs(entry.LocationID) == true) { ItemEntity item = this.ItemFactory.LoadItem(entry.ItemID, out bool loadRequired); // the item is an inventory, take everything out! if (item is ItemInventory inventory) { foreach ((int _, ItemEntity itemInInventory) in inventory.Items) { // if the item is in a rig slot, destroy it if (itemInInventory.IsInRigSlot() == true) { Flags oldFlag = itemInInventory.Flag; this.ItemFactory.DestroyItem(itemInInventory); // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(itemInInventory, oldFlag, entry.ItemID)); } else { Flags oldFlag = itemInInventory.Flag; // update item's location itemInInventory.LocationID = entry.LocationID; itemInInventory.Flag = Flags.Hangar; // notify the client about the change call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(itemInInventory, oldFlag, entry.ItemID)); // save the item itemInInventory.Persist(); } } } // update the singleton flag too item.Singleton = false; call.Client.NotifyMultiEvent(OnItemChange.BuildSingletonChange(item, true)); // load was required, the item is not needed anymore if (loadRequired == true) { this.ItemFactory.UnloadItem(item); } } else { long nodeID = this.SystemManager.GetNodeStationBelongsTo(entry.LocationID); if (nodeID > 0) { Notifications.Nodes.Inventory.OnItemChange change = new Notifications.Nodes.Inventory.OnItemChange(); change.AddChange(entry.ItemID, "singleton", true, false); this.NotificationManager.NotifyNode(nodeID, change); } } // finally repackage the item this.RepairDB.RepackageItem(entry.ItemID, entry.LocationID); // remove any insurance contract for the ship this.InsuranceDB.UnInsureShip(entry.ItemID); } return(null); }
public PyDataType RepairItems(PyList itemIDs, PyDecimal iskRepairValue, CallInformation call) { // ensure the player has enough balance to do the fixing Station station = this.ItemFactory.GetStaticStation(call.Client.EnsureCharacterIsInStation()); // take the wallet lock and ensure the character has enough balance using Wallet wallet = this.WalletManager.AcquireWallet(call.Client.EnsureCharacterIsSelected(), 1000); { wallet.EnsureEnoughBalance(iskRepairValue); // build a list of items to be fixed List <ItemEntity> items = new List <ItemEntity>(); double quantityLeft = iskRepairValue; foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { // ensure the given item is in the list if (this.mInventory.Items.TryGetValue(itemID, out ItemEntity item) == false) { continue; } // calculate how much to fix it if (item is Ship) { quantityLeft -= Math.Min(item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP), quantityLeft); } else { quantityLeft -= Math.Min(item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE), quantityLeft); } // add the item to the list items.Add(item); // if there's not enough money left then break the loop and fix whatever's possible if (quantityLeft <= 0.0) { break; } } quantityLeft = iskRepairValue; // go through all the items again and fix them foreach (ItemEntity item in items) { double repairPrice = 0.0f; if (item is Ship) { repairPrice = item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP); } else { repairPrice = item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE); } // full item can be repaired! if (repairPrice <= quantityLeft) { item.Attributes[Attributes.damage].Integer = 0; } else { int repairUnits = 0; // calculate how much can be repaired with the quantity left if (item is Ship) { repairUnits = (int)(quantityLeft / (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP)); repairPrice = repairUnits * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP); } else { repairUnits = (int)(quantityLeft / (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE)); repairPrice = repairUnits * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE); } // only perform changes on the damage if there's units we can pay for repair if (repairUnits > 0) { item.Attributes[Attributes.damage] -= repairUnits; } } quantityLeft -= repairPrice; // persist item changes item.Persist(); } wallet.CreateJournalRecord(MarketReference.RepairBill, station.OwnerID, null, -(iskRepairValue - quantityLeft)); } return(null); }
public PyDataType DeleteBookmarks(PyList bookmarkIDs, CallInformation call) { this.DB.DeleteBookmark(bookmarkIDs.GetEnumerable <PyInteger>(), call.Client.EnsureCharacterIsSelected()); return(null); }
public override PyDataType FetchByKey(PyList keyList, CallInformation call) { return(this.DB.GetMembers(keyList.GetEnumerable <PyInteger>(), this.Corporation.ID, this.SparseRowset, this.RowsIndex)); }
public PyDataType SaveSkillQueue(PyList queue, CallInformation call) { if (this.Character.SkillQueue.Count > 0) { // calculate current skill in training points Skill currentSkill = this.Character.SkillQueue[0].Skill; if (currentSkill.ExpiryTime > 0) { // get the total amount of minutes the skill would have taken to train completely long pointsLeft = (long)(currentSkill.GetSkillPointsForLevel(this.Character.SkillQueue[0].TargetLevel) - currentSkill.Points); TimeSpan timeLeft = TimeSpan.FromMinutes(pointsLeft / this.Character.GetSkillPointsPerMinute(currentSkill)); DateTime endTime = DateTime.FromFileTimeUtc(currentSkill.ExpiryTime); DateTime startTime = endTime.Subtract(timeLeft); TimeSpan timePassed = DateTime.UtcNow - startTime; // calculate the skill points to add double skillPointsToAdd = timePassed.TotalMinutes * this.Character.GetSkillPointsPerMinute(currentSkill); currentSkill.Points += skillPointsToAdd; } // remove the timer associated with the queue this.FreeSkillQueueTimers(); foreach (Character.SkillQueueEntry entry in this.Character.SkillQueue) { entry.Skill.Flag = Flags.Skill; call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(entry.Skill, Flags.SkillInTraining)); // send notification of skill training stopped call.Client.NotifyMultiEvent(new OnSkillTrainingStopped(entry.Skill)); // create history entry this.DB.CreateSkillHistoryRecord(entry.Skill.Type, this.Character, SkillHistoryReason.SkillTrainingCancelled, entry.Skill.Points); entry.Skill.ExpiryTime = 0; entry.Skill.Persist(); } this.Character.SkillQueue.Clear(); } DateTime startDateTime = DateTime.UtcNow; bool first = true; foreach (PyTuple entry in queue.GetEnumerable <PyTuple>()) { // ignore wrong entries if (entry.Count != 2) { continue; } int typeID = entry[0] as PyInteger; int level = entry[1] as PyInteger; // search for an item with the given typeID ItemEntity item = this.Character.Items.First(x => x.Value.Type.ID == typeID && (x.Value.Flag == Flags.Skill || x.Value.Flag == Flags.SkillInTraining)).Value; // ignore items that are not skills if (item is Skill == false) { continue; } Skill skill = item as Skill; double skillPointsLeft = skill.GetSkillPointsForLevel(level) - skill.Points; TimeSpan duration = TimeSpan.FromMinutes(skillPointsLeft / this.Character.GetSkillPointsPerMinute(skill)); DateTime expiryTime = startDateTime + duration; skill.ExpiryTime = expiryTime.ToFileTimeUtc(); skill.Flag = Flags.SkillInTraining; call.Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(skill, Flags.Skill)); startDateTime = expiryTime; // skill added to the queue, persist the character to ensure all the changes are saved this.Character.SkillQueue.Add(new Character.SkillQueueEntry() { Skill = skill, TargetLevel = level }); if (first == true) { // skill was trained, send the success message call.Client.NotifyMultiEvent(new OnSkillStartTraining(skill)); // create history entry this.DB.CreateSkillHistoryRecord(skill.Type, this.Character, SkillHistoryReason.SkillTrainingStarted, skill.Points); first = false; } skill.Persist(); } // ensure the timer is present for the first skill in the queue this.SetupTimerForNextSkillInQueue(); // finally persist the data to the database this.Character.Persist(); return(null); }
public PyDataType GetDamageReports(PyList itemIDs, CallInformation call) { PyDictionary <PyInteger, PyDataType> response = new PyDictionary <PyInteger, PyDataType>(); foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>()) { // ensure the given item is in the list if (this.mInventory.Items.TryGetValue(itemID, out ItemEntity item) == false) { continue; } Rowset quote = new Rowset( new PyList <PyString>(6) { [0] = "itemID", [1] = "typeID", [2] = "groupID", [3] = "damage", [4] = "maxHealth", [5] = "costToRepairOneUnitOfDamage" } ); if (item is Ship ship) { foreach ((int _, ItemEntity module) in ship.Items) { if (module.IsInModuleSlot() == false && module.IsInRigSlot() == false) { continue; } quote.Rows.Add( new PyList() { module.ID, module.Type.ID, module.Type.Group.ID, module.Attributes[Attributes.damage], module.Attributes[Attributes.hp], // modules should calculate this value differently, but for now this will suffice module.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE } ); } quote.Rows.Add( new PyList() { item.ID, item.Type.ID, item.Type.Group.ID, item.Attributes[Attributes.damage], item.Attributes[Attributes.hp], item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP } ); } else { quote.Rows.Add( new PyList() { item.ID, item.Type.ID, item.Type.Group.ID, item.Attributes[Attributes.damage], item.Attributes[Attributes.hp], item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE } ); } // the client used to send a lot of extra information on this call // but in reality that data is not used by the client at all // most likely remnants of older eve client versions response[itemID] = new Row( new PyList <PyString>(1) { [0] = "quote" }, new PyList(1) { [0] = quote } ); } return(response); }