When a player fails the “GetMinPurchaseAge” in an offer (for example by being in a region the offer isn’t available) the function where the buyoffer is located at, will prematurely end and if the function expected something to be returned it will receive a bugged value
Please select what you are reporting on:
Verse
What Type of Bug are you experiencing?
Verse
Steps to Reproduce
Create a marketplace offer that can fail on a player (simplest way is to just restrict the offer for your region/platform)
Create a verse script that will spawn a suspends function with a “Sleep(0.01)” and a “buyoffer()” for the offer and expect a value to be returned (In the example code I returned a fort_playspace)
In the verse code have it use the returned value. (In my example it just does .GetPlayers() from the fort_playspace
IF the player did not meet the requirements the function will exit prematurely and not reach the return part which returns the intended object, instead returning “empty”
Expected Result
I expected the BuyOffer to have a similar result for when a player declines the offer and when a player is ineligible to view the offer.
Observed Result
If a player is ineligible to see the offer verse will prematurely exit the function, with an invalid return object. If verse tries to use that object verse will crash.
In the comments I’ll attach the project file and the verse code.
Also for extra context in the video my region is “GR” so my account should be able to buy the global and “GR” offers BUT not the “IT” (Italian) offer.
using { /Fortnite.com/Marketplace }
using { /Verse.org/Assets }
MyMessage<localizes>:message="PLACEHOLDER"
base_product:=class(product){}
MyProduct:=class<concrete>(base_product):
MaxCount<override>:int=10
Consumable<override>:logic=true
var Icon<override>:texture=PlaceholderTexture
var Name<override>:message=MyMessage
var Description<override>:message=MyMessage
var ShortDescription<override>:message=MyMessage
S_Global<localizes>:message="GLOBAL"
GlobalOffer:=class(product_offer):
var Name<override>:message=S_Global
var Description<override>:message=MyMessage
var ShortDescription<override>:message=MyMessage
var Icon<override>:texture=PlaceholderTexture
Price<override>:price_dimension=MakePriceVBucks(950.0)
ProductType<override>:concrete_subtype(product)=MyProduct
GetMinPurchaseAge<override>(CountryCode:string, SubdivisionCode:string, PlatformFamily:string)<computes><decides>:int=
0
S_Windows<localizes>:message="WINDOWS"
WindowsOffer:=class(product_offer):
var Name<override>:message=S_Windows
var Description<override>:message=MyMessage
var ShortDescription<override>:message=MyMessage
var Icon<override>:texture=PlaceholderTexture
Price<override>:price_dimension=MakePriceVBucks(950.0)
ProductType<override>:concrete_subtype(product)=MyProduct
GetMinPurchaseAge<override>(CountryCode:string, SubdivisionCode:string, PlatformFamily:string)<computes><decides>:int=
PlatformFamily="Windows"
0
S_Greek<localizes>:message="GREEK"
GreekOffer:=class(product_offer):
var Name<override>:message=S_Greek
var Description<override>:message=MyMessage
var ShortDescription<override>:message=MyMessage
var Icon<override>:texture=PlaceholderTexture
Price<override>:price_dimension=MakePriceVBucks(950.0)
ProductType<override>:concrete_subtype(product)=MyProduct
GetMinPurchaseAge<override>(CountryCode:string, SubdivisionCode:string, PlatformFamily:string)<computes><decides>:int=
CountryCode="GR"
0
S_Italy<localizes>:message="ITALY"
ItalianOffer:=class(product_offer):
var Name<override>:message=S_Italy
var Description<override>:message=MyMessage
var ShortDescription<override>:message=MyMessage
var Icon<override>:texture=PlaceholderTexture
Price<override>:price_dimension=MakePriceVBucks(950.0)
ProductType<override>:concrete_subtype(product)=MyProduct
GetMinPurchaseAge<override>(CountryCode:string, SubdivisionCode:string, PlatformFamily:string)<computes><decides>:int=
CountryCode="IT"
0
OfferEnums:=enum{GLOBAL,WINDOWS,GREEK,ITALIAN}
EnumToOffer(OfferEnum:OfferEnums):offer=
case(OfferEnum):
OfferEnums.GLOBAL=>GlobalOffer{}
OfferEnums.WINDOWS=>WindowsOffer{}
OfferEnums.GREEK=>GreekOffer{}
OfferEnums.ITALIAN=>ItalianOffer{}
singleoffertest.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Fortnite.com/Marketplace }
using { /Fortnite.com/FortPlayerUtilities }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Playspaces }
single_offer_test:=class(creative_device):
@editable OfferToShow : OfferEnums = OfferEnums.GLOBAL
@editable Button : button_device = button_device{}
OnBegin<override>()<suspends>:void=
Button.InteractedWithEvent.Subscribe(OnInteracted)
OnInteracted(Agent:agent):void=
Print("-----------------------NEW TRANSACTION ATTEMPT----------------------------")
if(Player:=player[Agent]):
spawn. StartOffer(Player)
StartOffer(Player:player)<suspends>:void=
Print("[StartOffer] START")
# ATTEMPTS TO BUYOFFER, And returns a "fort_playspace" object,
# if the BuyOffer is restricted for the player BuyAttempt will return a non-valid value that the compiler
# still thinks is a fort_playspace
Result:=BuyAttempt(Player)
Print("[StartOffer] Stopped, Result WAS= "+ToDiagnostic(Result.GetPlayers()))
# ^THIS WILL TRY TO GET THE PLAYERS IN THE PLAYSPACE RETURNED, Unexpected error will occur if the BuyAttempt failed and returned a non-valid value
BuyAttempt(Player:player)<suspends>:fort_playspace=
Sleep(0.01) # If Sleep is removed BuyOffer will ALSO return the StartOffer Function along with the BuyAttempt function
Print("[BuyAttempt] START")
Offer:=EnumToOffer(OfferToShow) # Converts ENUM to "offer"
Result:=BuyOffer(Player,Offer) # IF THE OFFER IS RESTRICTED FOR THE PLAYER THE FUNCTION WILL PREMATURELY RETURN HERE AND RETURN A NON-VALID VALUE
Print("[BuyAttempt] STOPPED, BUY Result="+ToDiagnostic(Result))
return GetPlayspace()