@quenty/gameproductservice 14.0.0 → 14.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [14.1.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/gameproductservice@14.0.0...@quenty/gameproductservice@14.1.0) (2024-03-09)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add PromisePlayerPromptClosed() to GameProductService ([1a8ced1](https://github.com/Quenty/NevermoreEngine/commit/1a8ced1c83c7318107fe5a92bded8559bad3c06b))
12
+
13
+
14
+
15
+
16
+
6
17
  # [14.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/gameproductservice@13.0.0...@quenty/gameproductservice@14.0.0) (2024-02-14)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/gameproductservice
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/gameproductservice",
3
- "version": "14.0.0",
3
+ "version": "14.1.0",
4
4
  "description": "Generalized monetization system for handling products and purchases correctly.",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -27,31 +27,31 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@quenty/attributeutils": "^14.0.0",
31
- "@quenty/baseobject": "^10.0.0",
32
- "@quenty/binder": "^14.0.0",
33
- "@quenty/brio": "^14.0.0",
30
+ "@quenty/attributeutils": "^14.1.0",
31
+ "@quenty/baseobject": "^10.1.0",
32
+ "@quenty/binder": "^14.1.0",
33
+ "@quenty/brio": "^14.1.0",
34
34
  "@quenty/enumutils": "^3.1.0",
35
- "@quenty/gameconfig": "^12.0.0",
36
- "@quenty/instanceutils": "^13.0.0",
37
- "@quenty/loader": "^10.0.0",
38
- "@quenty/maid": "^3.0.0",
39
- "@quenty/marketplaceutils": "^11.0.0",
40
- "@quenty/observablecollection": "^12.0.0",
41
- "@quenty/playerbinder": "^14.0.0",
42
- "@quenty/playerutils": "^8.0.0",
43
- "@quenty/promise": "^10.0.0",
44
- "@quenty/promisemaid": "^5.0.0",
45
- "@quenty/receiptprocessing": "^7.0.0",
46
- "@quenty/remoting": "^12.0.0",
47
- "@quenty/rx": "^13.0.0",
48
- "@quenty/rxbinderutils": "^14.0.0",
49
- "@quenty/servicebag": "^11.0.0",
50
- "@quenty/signal": "^7.0.0",
51
- "@quenty/statestack": "^14.0.0",
35
+ "@quenty/gameconfig": "^12.1.0",
36
+ "@quenty/instanceutils": "^13.1.0",
37
+ "@quenty/loader": "^10.1.0",
38
+ "@quenty/maid": "^3.1.0",
39
+ "@quenty/marketplaceutils": "^11.1.0",
40
+ "@quenty/observablecollection": "^12.1.0",
41
+ "@quenty/playerbinder": "^14.1.0",
42
+ "@quenty/playerutils": "^8.1.0",
43
+ "@quenty/promise": "^10.1.0",
44
+ "@quenty/promisemaid": "^5.1.0",
45
+ "@quenty/receiptprocessing": "^7.1.0",
46
+ "@quenty/remoting": "^12.1.0",
47
+ "@quenty/rx": "^13.1.0",
48
+ "@quenty/rxbinderutils": "^14.1.0",
49
+ "@quenty/servicebag": "^11.1.0",
50
+ "@quenty/signal": "^7.1.0",
51
+ "@quenty/statestack": "^14.1.0",
52
52
  "@quenty/string": "^3.1.0",
53
53
  "@quenty/table": "^3.4.0",
54
- "@quenty/valueobject": "^13.0.0"
54
+ "@quenty/valueobject": "^13.1.0"
55
55
  },
56
- "gitHead": "63f949a67b77d3edc98b358cace163da8c789e9f"
56
+ "gitHead": "e0148dde5ca3864389a0faa2da66153a776acc1e"
57
57
  }
@@ -187,7 +187,7 @@ end
187
187
  Returns true if the prompt is open
188
188
 
189
189
  @param player Player
190
- @return boolean
190
+ @return Promise<boolean>
191
191
  ]=]
192
192
  function GameProductServiceClient:PromisePlayerIsPromptOpen(player)
193
193
  assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
@@ -197,6 +197,20 @@ function GameProductServiceClient:PromisePlayerIsPromptOpen(player)
197
197
  return self._helper:PromisePlayerIsPromptOpen(player)
198
198
  end
199
199
 
200
+ --[=[
201
+ Returns a promise that will resolve when all prompts are closed
202
+
203
+ @param player Player
204
+ @return Promise
205
+ ]=]
206
+ function GameProductServiceClient:PromisePlayerPromptClosed(player)
207
+ assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
208
+ assert(self ~= GameProductServiceClient, "Use serviceBag")
209
+ assert(self._serviceBag, "Not initialized")
210
+
211
+ return self._helper:PromisePlayerPromptClosed(player)
212
+ end
213
+
200
214
  --[=[
201
215
  Checks if the asset is ownable and if it is, checks player ownership. Otherwise, it checks if the asset
202
216
  has been purchased this session. If the asset has not been purchased this session it prompts the user to
@@ -128,6 +128,32 @@ function GameProductService:HasPlayerPurchasedThisSession(player, assetType, idO
128
128
  return self._helper:HasPlayerPurchasedThisSession(player, assetType, idOrKey)
129
129
  end
130
130
 
131
+ --[=[
132
+ Returns true if the prompt is open
133
+
134
+ @param player Player
135
+ @return Promise<boolean>
136
+ ]=]
137
+ function GameProductService:PromisePlayerIsPromptOpen(player)
138
+ assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
139
+ assert(self._serviceBag, "Not initialized")
140
+
141
+ return self._helper:PromisePlayerIsPromptOpen(player)
142
+ end
143
+
144
+ --[=[
145
+ Returns a promise that will resolve when all prompts are closed
146
+
147
+ @param player Player
148
+ @return Promise
149
+ ]=]
150
+ function GameProductService:PromisePlayerPromptClosed(player)
151
+ assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
152
+ assert(self._serviceBag, "Not initialized")
153
+
154
+ return self._helper:PromisePlayerPromptClosed(player)
155
+ end
156
+
131
157
  --[=[
132
158
  Prompts the user to purchase the asset, and returns true if purchased
133
159
 
@@ -126,6 +126,22 @@ function GameProductServiceHelper:PromisePlayerIsPromptOpen(player)
126
126
  return marketeer:IsPromptOpen()
127
127
  end)
128
128
  end
129
+
130
+ --[=[
131
+ Promises the player prompt as opened
132
+
133
+ @param player Player
134
+ @return Promise<boolean>
135
+ ]=]
136
+ function GameProductServiceHelper:PromisePlayerPromptClosed(player)
137
+ assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
138
+
139
+ return self:_promisePlayerMarketeer(player)
140
+ :Then(function(marketeer)
141
+ return marketeer:PromisePlayerPromptClosed()
142
+ end)
143
+ end
144
+
129
145
  --[=[
130
146
  Observes player ownership
131
147
 
@@ -12,6 +12,7 @@ local Maid = require("Maid")
12
12
  local Observable = require("Observable")
13
13
  local Promise = require("Promise")
14
14
  local Signal = require("Signal")
15
+ local ValueObject = require("ValueObject")
15
16
 
16
17
  local PlayerAssetMarketTracker = setmetatable({}, BaseObject)
17
18
  PlayerAssetMarketTracker.ClassName = "PlayerAssetMarketTracker"
@@ -38,23 +39,25 @@ function PlayerAssetMarketTracker.new(assetType, convertIds, observeIdsBrio)
38
39
  self._purchasedThisSession = {} -- [number] = true
39
40
  self._receiptProcessingExpected = false
40
41
 
41
- self._promptsOpen = Instance.new("IntValue")
42
- self._promptsOpen.Value = 0
43
- self._maid:GiveTask(self._promptsOpen)
44
-
45
- self.Purchased = Signal.new() -- :Fire(id)
46
- self._maid:GiveTask(self.Purchased)
47
-
48
- self.PromptFinished = Signal.new() -- :Fire(id, isPurchased)
49
- self._maid:GiveTask(self.PromptFinished)
50
-
51
- self.ShowPromptRequested = Signal.new() -- :Fire(id)
52
- self._maid:GiveTask(self.ShowPromptRequested)
42
+ self._promptsOpenCount = self._maid:Add(ValueObject.new(0, "number"))
43
+ self.Purchased = self._maid:Add(Signal.new()) -- :Fire(id)
44
+ self.PromptFinished = self._maid:Add(Signal.new()) -- :Fire(id, isPurchased)
45
+ self.ShowPromptRequested = self._maid:Add(Signal.new()) -- :Fire(id)
53
46
 
54
47
  self._maid:GiveTask(self.Purchased:Connect(function(id)
55
48
  self._purchasedThisSession[id] = true
56
49
  end))
57
50
 
51
+ self._maid:GiveTask(self._promptsOpenCount:Observe():Subscribe(function(promptsOpen)
52
+ if promptsOpen <= 0 then
53
+ local promise = self._promiseNoPromptOpen
54
+ self._promiseNoPromptOpen = nil
55
+ if promise then
56
+ promise:Resolve()
57
+ end
58
+ end
59
+ end))
60
+
58
61
  self._maid:GiveTask(function()
59
62
  while #self._pendingPurchasePromises > 0 do
60
63
  local pending = table.remove(self._pendingPurchasePromises, #self._pendingPurchasePromises)
@@ -70,6 +73,10 @@ function PlayerAssetMarketTracker.new(assetType, convertIds, observeIdsBrio)
70
73
  return self
71
74
  end
72
75
 
76
+ function PlayerAssetMarketTracker:ObservePromptOpenCount()
77
+ return self._promptsOpenCount:Observe()
78
+ end
79
+
73
80
  --[=[
74
81
  Observes an asset purchased
75
82
 
@@ -142,7 +149,7 @@ function PlayerAssetMarketTracker:PromisePromptPurchase(idOrKey)
142
149
  end
143
150
 
144
151
  -- We reject here because there's no safe way to queue this
145
- if self._promptsOpen.Value > 0 then
152
+ if self._promptsOpenCount.Value > 0 then
146
153
  return Promise.rejected(string.format("[PlayerAssetMarketTracker] - Either already prompting user, or prompting is on cooldown. Will not prompt for %s", idOrKey))
147
154
  end
148
155
 
@@ -161,12 +168,12 @@ function PlayerAssetMarketTracker:PromisePromptPurchase(idOrKey)
161
168
  local promptOpenPromise = Promise.new()
162
169
  self._pendingPromptOpenPromises[id] = promptOpenPromise
163
170
 
164
- self._promptsOpen.Value = self._promptsOpen.Value + 1
171
+ self._promptsOpenCount.Value = self._promptsOpenCount.Value + 1
165
172
  promptOpenPromise:Finally(function()
166
173
  if self._pendingPromptOpenPromises[id] == promptOpenPromise then
167
174
  self._pendingPromptOpenPromises[id] = nil
168
175
  end
169
- self._promptsOpen.Value = self._promptsOpen.Value - 1
176
+ self._promptsOpenCount.Value = self._promptsOpenCount.Value - 1
170
177
  end)
171
178
  end
172
179
 
@@ -225,7 +232,7 @@ end
225
232
  @return boolean
226
233
  ]=]
227
234
  function PlayerAssetMarketTracker:IsPromptOpen()
228
- return self._promptsOpen.Value > 0
235
+ return self._promptsOpenCount.Value > 0
229
236
  end
230
237
 
231
238
  --[=[
@@ -14,6 +14,8 @@ local MarketplaceUtils = require("MarketplaceUtils")
14
14
  local PlayerAssetOwnershipTracker = require("PlayerAssetOwnershipTracker")
15
15
  local PlayerAssetMarketTracker = require("PlayerAssetMarketTracker")
16
16
  local GameConfigAssetTypeUtils = require("GameConfigAssetTypeUtils")
17
+ local Promise = require("Promise")
18
+ local Rx = require("Rx")
17
19
 
18
20
  local PlayerMarketeer = setmetatable({}, BaseObject)
19
21
  PlayerMarketeer.ClassName = "PlayerMarketeer"
@@ -102,6 +104,46 @@ function PlayerMarketeer:IsPromptOpen()
102
104
  return false
103
105
  end
104
106
 
107
+ --[=[
108
+ Promises that no prompt is open
109
+
110
+ @return Promise
111
+ ]=]
112
+ function PlayerMarketeer:PromisePlayerPromptClosed()
113
+ if not self:IsPromptOpen() then
114
+ return Promise.resolved()
115
+ end
116
+
117
+ if self._observeNextNoPromptOpen then
118
+ return Rx.toPromise(self._observeNextNoPromptOpen)
119
+ end
120
+
121
+ local observeOpenCounts = {}
122
+
123
+ for assetType, assetTracker in pairs(self._assetMarketTrackers) do
124
+ observeOpenCounts[assetType] = assetTracker:ObservePromptOpenCount()
125
+ end
126
+
127
+ self._observeNextNoPromptOpen = Rx.combineLatest(observeOpenCounts):Pipe({
128
+ Rx.map(function(state)
129
+ for _, item in pairs(state) do
130
+ if item > 0 then
131
+ return false
132
+ end
133
+ end
134
+
135
+ return true
136
+ end);
137
+ Rx.where(function(value)
138
+ return value
139
+ end);
140
+ Rx.distinct();
141
+ Rx.share();
142
+ })
143
+
144
+ return Rx.toPromise(self._observeNextNoPromptOpen)
145
+ end
146
+
105
147
  --[=[
106
148
  Gets the current asset tracker
107
149