New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[B+C] Add Merchant Trade API - Adds BUKKIT-5663 #1077
base: master
Are you sure you want to change the base?
Conversation
|
||
/** | ||
* Returns a copy of the list of offers being modified. Modifying this | ||
* offers list will not change the offers resulting in the linked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can't be modified. Don't suggest it :P
You leak mutable objects everywhere, which is unacceptable. The inventory API is bad enough as it is, we don't want to make it worse. Examine how ItemMeta works for a good example of how mutability needs to be handled; ItemStack always returns a copy. Any of the new events with items return clones. |
Should be fixed in latest commit. I've added a utility method to MerchantEvent that performs a deep copy of a list of TradeOffers through ImmutableList and added the relevant ItemStack.clone where necessary. I should note, I initially didn't have such cloning immutability because of the pre-existing API leading me to believe that mutability was more of a standard and over immutability/cloning from getters would be counter productive. I appreciate knowing now that future API should have an general sense of immutability than mutability. |
/** | ||
* Represents a plugin generated trade offer. | ||
*/ | ||
PLUGIN_ADDED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a comma at the end would be appropriate.
I'm sure there are many more issues, but I can't take much more at the moment. |
It's possible, just, as I've said before, I was basing this off of the current Inventory API, I can refactor all of this to use a TradeOffer Builder just as well.
Yes. I'll refactor to a static builder to produce an immutable TradeOffer instead of constructors. |
My one worry is that doing this breaks a line where TradeOfferMeta isn't used for a singular Item or Entity, but can have several. Is it your intention to see this as an example:
I'm guessing it's doable, however, I can foresee issues with a TradeOffer being inherently mutable unless the method #setTradeMeta returns a clone of TradeOffer with the new meta (unlike ItemStack). |
I don't see a purpose in having a 'meta' or having 'setters' anywhere. |
So, there would be no link whatsoever from a TradeOffer to MerchantRecipe aside from constructing a TradeOffer from a MerchantRecipe for event throwing and org.bukkit.entity.Merchant#getOffers()? |
If you can remove almost every possible way to change data, other than a place that explicitly defines what is being changed, that's a good thing. If an event is designed to change the 'result', then the only place to change that result should be in a method for that event called |
Make sense. I'm just worried that opening the ability to set the TradeType.VANILLA to the API may be a bad thing. |
If we need to restrict a specific way to represent data, then it must be in CraftBukkit, with interfaces provided in Bukkit. If you think we need a way to differentiate trades, then it should be provided in the event. A |
I'm don't understand how that would work. It's probably because it's late and I should go to sleep. I'll read up on MultiMap and see how to implement it for this. |
hash = hash * PRIME + resultingOffer.hashCode(); | ||
hash = hash * PRIME + uses; | ||
hash = hash * PRIME + maxUses; | ||
hash = hash * PRIME + (type == TradeType.VANILLA ? VANILLA : PLUGIN_ADDED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type.hashCode()
You haven't tested the serialization methods at all. You should make unit tests for that. In addition, you leak |
I don't like the existence of many of those methods that return copies. Just add a method in the builder to copy values. Considering it's in the same source / package, you can even use the fields as a shared secret (assuming you never leak it). |
Added tests to the CB PR.
Fixed.
Added. |
|
||
/** | ||
* This event is called whenever a {@link org.bukkit.entity.Merchant}'s | ||
* offers are being added to, whether it is a native Minecraft mechanic or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added to?
|
* @param itemStack the first buying item | ||
* @return this object, for chaining | ||
* @throws IllegalArgumentException if the itemstack is null | ||
* @throws IllegalArgumentException if the itemstack type is AIR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@throws
is not required.
@turt2live As requested. |
@gabizou source? The following is from this PR:
|
https://github.com/Bukkit/Bukkit/pull/1077/files#r15209292 For all other cases where I am returning an ItemStack, I am performing ItemStack#clone() since it was already defensively copied from the Builder. |
That still doesn't tell me where you got that quote from.
|
Ok, sorry, so we've linked to Wolvereness's comment twice now, I was doing as told, unless you're saying that I should go against what Wolvereness was saying about defensively copying the ItemStack as I have done in the builder. For the sake of repetition: Is it clear that I didn't know enough GitHub-comment-fu to link the direct comment itself? |
The link does indeed state @Wolvereness option. Maybe i'm wrong but i read the comment this way: |
@ST-DDT has it about right. I'd only make the exception for an assumption that a certain code path is safe. If somewhere in the chain a plugin can implement an interface, or extend a class included in the Bukkit jar, then it's considered unsafe. Those places are gradually shrinking though, as more of the API becomes defensively safe. |
@turt2live Updated the javadocs. I didn't change any of the |
The Issue:
There is no way to modify Villager trades through the Bukkit API.
Justification
Without the API to allow for modifications being made on a Merchant's offers list, plugins have no Bukkit API way to affect trades, leading to messy uses of NMS classes and modified CraftBukkit classes, which can cause plugins to break or the server to crash. With an API, all plugin developers will be able to safely modify merchant trades (which as of current writing is only implemented through Villagers).
PR Breakdown:
The API adds the ability to retrieve current trades offered by a Merchant, add trades, remove trades and modify trades. There is also the added ability to influence event specific trades such that modified trades added to the MerchantTradeEvent do not alter the more permanent TradeOffers supplied by the Merchant.
In this sense: A MerchantTradeEvent's offers are a copy of the Merchant's offers that do not affect the Merchant's offers. However, the Merchant is still able to have their trades altered, but the MerchantTradeEvent will not be affected.
Testing Results / Materials:
Plugin code used to test: https://github.com/gabizou/TradeAPITest
Compiled Binary: https://dl.dropboxusercontent.com/u/9058563/TradingTest.jar
Steps to test:
Console shows the debugging text
Associated PRs
Bukkit PR: #1077
CraftBukkit PR: Bukkit/CraftBukkit#1393
* Relevant PRs*
Closed Bukkit PR: #921
Closed Bukkit PR: #833
Closed Bukkit PR: #832
Closed CraftBukkit PR: Bukkit/CraftBukkit#1240
Closed CraftBukkit PR: Bukkit/CraftBukkit#1111
JIRA Tickets
Main ticket: https://bukkit.atlassian.net/browse/BUKKIT-5663
Subsequent (duplicate) tickets
https://bukkit.atlassian.net/browse/BUKKIT-3935
https://bukkit.atlassian.net/browse/BUKKIT-2138