private void PrepareItemsForCourierOrAuctionContract(MySqlConnection connection, ulong contractID, ClusterConnection clusterConnection, PyList <PyList> itemList, Station station, int ownerID, int shipID) { // create the container in the system to ensure it's not visible to the player Container container = this.ItemFactory.CreateSimpleItem(this.TypeManager[Types.PlasticWrap], this.ItemFactory.LocationSystem.ID, station.ID, Flags.None) as Container; Dictionary <int, ContractDB.ItemQuantityEntry> items = this.DB.PrepareItemsForContract(connection, contractID, itemList, station, ownerID, container.ID, shipID); double volume = 0; // build notification for item changes OnItemChange changes = new OnItemChange(); long stationNode = this.SystemManager.GetNodeStationBelongsTo(station.ID); bool stationBelongsToUs = this.SystemManager.StationBelongsToUs(station.ID); // notify the changes in the items to the nodes foreach ((int _, ContractDB.ItemQuantityEntry item) in items) { if (stationNode == 0 || stationBelongsToUs == true) { ItemEntity entity = this.ItemFactory.LoadItem(item.ItemID); entity.LocationID = container.ID; entity.Persist(); // notify the character this.NotificationManager.NotifyCharacter(ownerID, Notifications.Client.Inventory.OnItemChange.BuildLocationChange(entity, station.ID)); } else { // queue the notification changes.AddChange(item.ItemID, "locationID", station.ID, container.ID); } // ensure the volume is taken into account volume += item.Volume; } // notify the proper node if needed if (changes.Updates.Count > 0) { this.NotificationManager.NotifyNode(stationNode, changes); } // update the contract with the crate and the new volume this.DB.UpdateContractCrateAndVolume(ref connection, contractID, container.ID, volume); }
private void AcceptItemExchangeContract(MySqlConnection connection, Client client, ContractDB.Contract contract, Station station, int ownerID, Flags flag = Flags.Hangar) { List <ContractDB.ItemQuantityEntry> offeredItems = this.DB.GetOfferedItems(connection, contract.ID); Dictionary <int, int> itemsToCheck = this.DB.GetRequiredItemTypeIDs(connection, contract.ID); List <ContractDB.ItemQuantityEntry> changedItems = this.DB.CheckRequiredItemsAtStation(connection, station, ownerID, contract.IssuerID, flag, itemsToCheck); // extract the crate this.DB.ExtractCrate(connection, contract.CrateID, station.ID, ownerID); long stationNode = this.SystemManager.GetNodeStationBelongsTo(station.ID); if (stationNode == 0 || this.SystemManager.StationBelongsToUs(station.ID) == true) { foreach (ContractDB.ItemQuantityEntry change in changedItems) { ItemEntity item = this.ItemFactory.LoadItem(change.ItemID); if (change.Quantity == 0) { // remove item from the meta inventories this.ItemFactory.MetaInventoryManager.OnItemDestroyed(item); // temporarily move the item to the recycler, let the current owner know item.LocationID = this.ItemFactory.LocationRecycler.ID; client.NotifyMultiEvent(Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, station.ID)); // now set the item to the correct owner and place and notify it's new owner // TODO: TAKE forCorp INTO ACCOUNT item.LocationID = station.ID; item.OwnerID = contract.IssuerID; this.NotificationManager.NotifyCharacter(contract.IssuerID, Notifications.Client.Inventory.OnItemChange.BuildNewItemChange(item)); // add the item back to meta inventories if required this.ItemFactory.MetaInventoryManager.OnItemLoaded(item); } else { int oldQuantity = item.Quantity; item.Quantity = change.Quantity; client.NotifyMultiEvent(Notifications.Client.Inventory.OnItemChange.BuildQuantityChange(item, oldQuantity)); item.Persist(); // unload the item if required this.ItemFactory.UnloadItem(item); } } // move the offered items foreach (ContractDB.ItemQuantityEntry entry in offeredItems) { ItemEntity item = this.ItemFactory.LoadItem(entry.ItemID); item.LocationID = station.ID; item.OwnerID = ownerID; client.NotifyMultiEvent(Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, contract.CrateID)); item.Persist(); // unload the item if possible this.ItemFactory.UnloadItem(item); } } else { OnItemChange changes = new OnItemChange(); foreach (ContractDB.ItemQuantityEntry change in changedItems) { if (change.Quantity == 0) { changes .AddChange(change.ItemID, "locationID", contract.CrateID, station.ID) .AddChange(change.ItemID, "ownerID", contract.IssuerID, ownerID); } else { // change the item quantity changes.AddChange(change.ItemID, "quantity", change.OldQuantity, change.Quantity); // create a new item and notify the new node about it // TODO: HANDLE BLUEPRINTS TOO! RIGHT NOW NO DATA IS COPIED FOR THEM ItemEntity item = this.ItemFactory.CreateSimpleItem( this.TypeManager[change.TypeID], contract.IssuerID, station.ID, Flags.Hangar, change.OldQuantity - change.Quantity, false, false ); // unload the created item this.ItemFactory.UnloadItem(item); changes.AddChange(item.ID, "location", 0, station.ID); } } // move the offered items foreach (ContractDB.ItemQuantityEntry entry in offeredItems) { // TODO: TAKE INTO ACCOUNT forCorp changes .AddChange(entry.ItemID, "locationID", contract.CrateID, station.ID) .AddChange(entry.ItemID, "ownerID", contract.IssuerID, station.ID); } } // the contract was properly accepted, update it's status this.DB.UpdateContractStatus(ref connection, contract.ID, ContractStatus.Finished); this.DB.UpdateAcceptorID(ref connection, contract.ID, ownerID); this.DB.UpdateAcceptedDate(ref connection, contract.ID); this.DB.UpdateCompletedDate(ref connection, contract.ID); // notify the contract as being accepted if (contract.ForCorp == false) { this.NotificationManager.NotifyCharacter(contract.IssuerID, new OnContractAccepted(contract.ID)); } else { this.NotificationManager.NotifyCorporation(contract.IssuerCorpID, new OnContractAccepted(contract.ID)); } }
private void HandleOnItemUpdate(Notifications.Nodes.Inventory.OnItemChange change) { foreach ((PyInteger itemID, PyDictionary _changes) in change.Updates) { PyDictionary <PyString, PyTuple> changes = _changes.GetEnumerable <PyString, PyTuple>(); ItemEntity item = this.ItemFactory.LoadItem(itemID, out bool loadRequired); // if the item was just loaded there's extra things to take into account // as the item might not even need a notification to the character it belongs to if (loadRequired == true) { // trust that the notification got to the correct node // load the item and check the owner, if it's logged in and the locationID is loaded by us // that means the item should be kept here if (this.ItemFactory.TryGetItem(item.LocationID, out ItemEntity location) == false || this.CharacterManager.IsCharacterConnected(item.OwnerID) == false) { // this item should not be loaded, so unload and return this.ItemFactory.UnloadItem(item); return; } bool locationBelongsToUs = true; switch (location) { case Station _: locationBelongsToUs = this.SystemManager.StationBelongsToUs(location.ID); break; case SolarSystem _: locationBelongsToUs = this.SystemManager.SolarSystemBelongsToUs(location.ID); break; } if (locationBelongsToUs == false) { this.ItemFactory.UnloadItem(item); return; } } OnItemChange itemChange = new OnItemChange(item); // update item and build change notification if (changes.TryGetValue("locationID", out PyTuple locationChange) == true) { PyInteger oldValue = locationChange[0] as PyInteger; PyInteger newValue = locationChange[1] as PyInteger; itemChange.AddChange(ItemChange.LocationID, oldValue); item.LocationID = newValue; } if (changes.TryGetValue("quantity", out PyTuple quantityChange) == true) { PyInteger oldValue = quantityChange[0] as PyInteger; PyInteger newValue = quantityChange[1] as PyInteger; itemChange.AddChange(ItemChange.Quantity, oldValue); item.Quantity = newValue; } if (changes.TryGetValue("ownerID", out PyTuple ownerChange) == true) { PyInteger oldValue = ownerChange[0] as PyInteger; PyInteger newValue = ownerChange[1] as PyInteger; itemChange.AddChange(ItemChange.OwnerID, oldValue); item.OwnerID = newValue; } if (changes.TryGetValue("singleton", out PyTuple singletonChange) == true) { PyBool oldValue = singletonChange[0] as PyBool; PyBool newValue = singletonChange[1] as PyBool; itemChange.AddChange(ItemChange.Singleton, oldValue); item.Singleton = newValue; } // TODO: IDEALLY THIS WOULD BE ENQUEUED SO ALL OF THEM ARE SENT AT THE SAME TIME // TODO: BUT FOR NOW THIS SHOULD SUFFICE // send the notification this.NotificationManager.NotifyCharacter(item.OwnerID, "OnMultiEvent", new PyTuple(1) { [0] = new PyList(1) { [0] = itemChange } }); if (item.LocationID == this.ItemFactory.LocationRecycler.ID) { // the item is removed off the database if the new location is the recycler item.Destroy(); } else if (item.LocationID == this.ItemFactory.LocationMarket.ID) { // items that are moved to the market can be unloaded this.ItemFactory.UnloadItem(item); } else { // save the item if the new location is not removal item.Persist(); } } }
private void MoveItemHere(ItemEntity item, Flags newFlag) { // get the old location stored as it'll be used in the notifications int oldLocation = item.LocationID; Flags oldFlag = item.Flag; // rig slots cannot be moved if (item.IsInRigSlot() == true) { throw new CannotRemoveUpgradeManually(); } // special situation, if the old location is a module slot ensure the item is first offlined if (item.IsInModuleSlot() == true) { if (item is ShipModule module) { if (module.Attributes[Attributes.isOnline] == 1) { module.StopApplyingEffect("online", Client); } // disable passive effects too module.StopApplyingPassiveEffects(Client); } } // extra special situation, is the new flag an autofit one? if (newFlag == Flags.AutoFit) { // capsules cannot fit anything if (this.mInventory.Type.ID == (int)Types.Capsule) { throw new CantFitToCapsule(); } if (this.mInventory is Ship ship) { // determine where to put the item if (item is ShipModule module) { if (module.IsHighSlot() == true) { newFlag = this.GetFreeHighSlot(ship); } else if (module.IsMediumSlot() == true) { newFlag = this.GetFreeMediumSlot(ship); } else if (module.IsLowSlot() == true) { newFlag = this.GetFreeLowSlot(ship); } else if (module.IsRigSlot() == true) { newFlag = this.GetFreeRigSlot(ship); } else { // this item cannot be fitted, move it to cargo, maybe throw a exception about not being able to fit it? newFlag = Flags.Cargo; } } // TODO: HANDLE CHARGES! else { newFlag = Flags.Cargo; } } else { newFlag = Flags.Hangar; } } // special situation, if the new location is a module slot ensure the item is a singleton (TODO: HANDLE CHARGES TOO) if (newFlag.IsModule() == true) { ShipModule module = null; if (item is ShipModule shipModule) { module = shipModule; } if (item.Quantity == 1) { // remove item off the old inventory if required if (this.ItemFactory.TryGetItem(item.LocationID, out ItemInventory inventory) == true) { inventory.RemoveItem(item); } OnItemChange changes = new OnItemChange(item); if (item.Singleton == false) { changes.AddChange(ItemChange.Singleton, item.Singleton); } item.LocationID = this.mInventory.ID; item.Flag = newFlag; item.Singleton = true; changes .AddChange(ItemChange.LocationID, oldLocation) .AddChange(ItemChange.Flag, (int)oldFlag); // notify the character about the change Client.NotifyMultiEvent(changes); // update meta inventories too this.ItemFactory.MetaInventoryManager.OnItemMoved(item, oldLocation, this.mInventory.ID); // finally persist the item changes item.Persist(); } else { // item is not a singleton, create a new item, decrease quantity and send notifications ItemEntity newItem = this.ItemFactory.CreateSimpleItem(item.Type, item.OwnerID, this.mInventory.ID, newFlag, 1, false, true); item.Quantity -= 1; // notify the quantity change and the new item Client.NotifyMultiEvent(OnItemChange.BuildQuantityChange(item, item.Quantity + 1)); Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(newItem, Flags.None, 0)); item.Persist(); // replace reference so the following code handle things properly item = newItem; if (item is ShipModule shipModule2) { module = shipModule2; } } try { // apply all the passive effects (this also blocks the item fitting if the initialization fails) module?.ApplyPassiveEffects(Client); // extra check, ensure that the character has the required skills } catch (UserError) { // ensure that the passive effects that got applied already are removed from the item module?.StopApplyingPassiveEffects(Client); int newOldLocation = item.LocationID; Flags newOldFlag = item.Flag; // now undo the whole thing item.LocationID = oldLocation; item.Flag = oldFlag; Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, newOldFlag, newOldLocation)); throw; } // ensure the new inventory knows this.mInventory.AddItem(item); module?.Persist(); // put the module online after fitting it as long as it's a normal module if (module?.IsRigSlot() == false) { module?.ApplyEffect("online", Client); } } else { // remove item off the old inventory if required if (this.ItemFactory.TryGetItem(item.LocationID, out ItemInventory inventory) == true) { inventory.RemoveItem(item); } // set the new location for the item item.LocationID = this.mInventory.ID; item.Flag = newFlag; // notify the character about the change Client.NotifyMultiEvent(OnItemChange.BuildLocationChange(item, oldFlag, oldLocation)); // update meta inventories too this.ItemFactory.MetaInventoryManager.OnItemMoved(item, oldLocation, this.mInventory.ID); // ensure the new inventory knows this.mInventory.AddItem(item); // finally persist the item changes item.Persist(); } }
private void PlaceSellOrderCharUpdateItems(MySqlConnection connection, Client client, int stationID, int typeID, int quantity) { Dictionary <int, MarketDB.ItemQuantityEntry> items = null; // depending on where the character that is placing the order, the way to detect the items should be different if (stationID == client.StationID) { items = this.DB.PrepareItemForOrder(connection, typeID, stationID, client.ShipID ?? -1, quantity, (int)client.CharacterID); } else { items = this.DB.PrepareItemForOrder(connection, typeID, stationID, -1, quantity, (int)client.CharacterID); } if (items is null) { throw new NotEnoughQuantity(this.TypeManager[typeID]); } long stationNode = this.SystemManager.GetNodeStationBelongsTo(stationID); if (this.SystemManager.StationBelongsToUs(stationID) == true || stationNode == 0) { // load the items here and send proper notifications foreach ((int _, MarketDB.ItemQuantityEntry entry) in items) { ItemEntity item = this.ItemFactory.LoadItem(entry.ItemID); if (entry.Quantity == 0) { // item has to be destroyed this.ItemFactory.DestroyItem(item); // notify item destroyal client.NotifyMultiEvent(Notifications.Client.Inventory.OnItemChange.BuildLocationChange(item, stationID)); } else { // just a quantity change item.Quantity = entry.Quantity; // notify the client client.NotifyMultiEvent(Notifications.Client.Inventory.OnItemChange.BuildQuantityChange(item, entry.OriginalQuantity)); // unload the item if it's not needed this.ItemFactory.UnloadItem(item); } } } else { // the item changes should be handled by a different node OnItemChange changes = new OnItemChange(); foreach ((int _, MarketDB.ItemQuantityEntry entry) in items) { if (entry.Quantity == 0) { changes.AddChange(entry.ItemID, "locationID", stationID, this.ItemFactory.LocationMarket.ID); } else { changes.AddChange(entry.ItemID, "quantity", entry.OriginalQuantity, entry.Quantity); } } } }