@clocktone/game-sdk 1.0.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/README.md +402 -0
- package/android/build.gradle +69 -0
- package/android/src/main/kotlin/com/playloop/plugins/applovinmax/PlayLoopAppLovinMaxPlugin.kt +316 -0
- package/android/src/main/kotlin/com/playloop/plugins/deviceid/PlayLoopDeviceIdPlugin.kt +30 -0
- package/dist/loader/cjs/index.js +72 -0
- package/dist/loader/esm/index.js +69 -0
- package/dist/types/CashableSDK.d.ts +46 -0
- package/dist/types/adapters/CapacitorAdsAdapter.d.ts +25 -0
- package/dist/types/adapters/WebViewAdsAdapter.d.ts +13 -0
- package/dist/types/babylon/BabylonPlugin.d.ts +17 -0
- package/dist/types/babylon/index.d.ts +17 -0
- package/dist/types/index.d.ts +481 -0
- package/dist/types/loader/index.d.ts +300 -0
- package/dist/types/modules/AnalyticsModule.d.ts +17 -0
- package/dist/types/modules/EmbeddedAdsModule.d.ts +30 -0
- package/dist/types/modules/FeatureFlagModule.d.ts +36 -0
- package/dist/types/modules/RewardsModule.d.ts +19 -0
- package/dist/types/modules/SessionModule.d.ts +16 -0
- package/dist/types/modules/StandaloneAdsModule.d.ts +51 -0
- package/dist/types/transport/HttpTransport.d.ts +19 -0
- package/dist/types/transport/WebViewTransport.d.ts +17 -0
- package/dist/types/types.d.ts +147 -0
- package/dist/types/ui/UIModule.d.ts +64 -0
- package/docs/ads.md +210 -0
- package/docs/analytics.md +45 -0
- package/docs/babylon.md +88 -0
- package/docs/feature-flags.md +109 -0
- package/docs/game-integration-guide.md +449 -0
- package/docs/loader.md +113 -0
- package/docs/rewards.md +57 -0
- package/docs/session.md +43 -0
- package/docs/ui.md +248 -0
- package/docs/wire-protocol.md +194 -0
- package/package.json +81 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
package com.playloop.plugins.applovinmax
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import android.view.Gravity
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.widget.FrameLayout
|
|
7
|
+
import com.applovin.mediation.MaxAd
|
|
8
|
+
import com.applovin.mediation.MaxAdListener
|
|
9
|
+
import com.applovin.mediation.MaxAdViewAdListener
|
|
10
|
+
import com.applovin.mediation.MaxError
|
|
11
|
+
import com.applovin.mediation.MaxReward
|
|
12
|
+
import com.applovin.mediation.MaxRewardedAdListener
|
|
13
|
+
import com.applovin.mediation.ads.MaxAdView
|
|
14
|
+
import com.applovin.mediation.ads.MaxInterstitialAd
|
|
15
|
+
import com.applovin.mediation.ads.MaxRewardedAd
|
|
16
|
+
import com.applovin.sdk.AppLovinMediationProvider
|
|
17
|
+
import com.applovin.sdk.AppLovinSdk
|
|
18
|
+
import com.applovin.sdk.AppLovinSdkInitializationConfiguration
|
|
19
|
+
import com.applovin.sdk.AppLovinSdkUtils
|
|
20
|
+
import com.getcapacitor.JSObject
|
|
21
|
+
import com.getcapacitor.Plugin
|
|
22
|
+
import com.getcapacitor.PluginCall
|
|
23
|
+
import com.getcapacitor.PluginMethod
|
|
24
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
25
|
+
|
|
26
|
+
@CapacitorPlugin(name = "PlayLoopAppLovinMax")
|
|
27
|
+
class PlayLoopAppLovinMaxPlugin : Plugin() {
|
|
28
|
+
|
|
29
|
+
companion object {
|
|
30
|
+
private const val TAG = "PlayLoopAppLovinMax"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private var rewardedAd: MaxRewardedAd? = null
|
|
34
|
+
private var interstitialAd: MaxInterstitialAd? = null
|
|
35
|
+
private var bannerAdView: MaxAdView? = null
|
|
36
|
+
private var pendingRewardedCall: PluginCall? = null
|
|
37
|
+
private var pendingInterstitialCall: PluginCall? = null
|
|
38
|
+
private var userEarnedReward = false
|
|
39
|
+
|
|
40
|
+
@PluginMethod
|
|
41
|
+
fun initialize(call: PluginCall) {
|
|
42
|
+
val sdkKey = call.getString("sdkKey") ?: run {
|
|
43
|
+
call.reject("SDK key parameter missing")
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
val rewardedAdUnitId = call.getString("rewardedAdUnitId") ?: run {
|
|
47
|
+
call.reject("Rewarded unit identifier missing")
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
val interstitialAdUnitId = call.getString("interstitialAdUnitId") ?: run {
|
|
51
|
+
call.reject("Interstitial unit identifier missing")
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
val bannerAdUnitId = call.getString("bannerAdUnitId")
|
|
55
|
+
|
|
56
|
+
activity.runOnUiThread {
|
|
57
|
+
val initConfig = AppLovinSdkInitializationConfiguration.builder(sdkKey, activity)
|
|
58
|
+
.setMediationProvider(AppLovinMediationProvider.MAX)
|
|
59
|
+
.build()
|
|
60
|
+
|
|
61
|
+
AppLovinSdk.getInstance(activity).initialize(initConfig) {
|
|
62
|
+
Log.d(TAG, "Ad mediation ready")
|
|
63
|
+
|
|
64
|
+
// Create and load rewarded ad
|
|
65
|
+
rewardedAd = MaxRewardedAd.getInstance(rewardedAdUnitId, activity.applicationContext)
|
|
66
|
+
rewardedAd?.setListener(rewardedAdListener)
|
|
67
|
+
rewardedAd?.loadAd()
|
|
68
|
+
|
|
69
|
+
// Create and load interstitial ad
|
|
70
|
+
interstitialAd = MaxInterstitialAd(interstitialAdUnitId, activity.applicationContext)
|
|
71
|
+
interstitialAd?.setListener(interstitialAdListener)
|
|
72
|
+
interstitialAd?.loadAd()
|
|
73
|
+
|
|
74
|
+
// Pre-create banner if ad unit ID provided
|
|
75
|
+
if (bannerAdUnitId != null) {
|
|
76
|
+
createBannerAdView(bannerAdUnitId)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
call.resolve()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@PluginMethod
|
|
85
|
+
fun showRewardedAd(call: PluginCall) {
|
|
86
|
+
activity.runOnUiThread {
|
|
87
|
+
val ad = rewardedAd
|
|
88
|
+
if (ad == null || !ad.isReady) {
|
|
89
|
+
val ret = JSObject()
|
|
90
|
+
ret.put("rewarded", false)
|
|
91
|
+
call.resolve(ret)
|
|
92
|
+
return@runOnUiThread
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pendingRewardedCall = call
|
|
96
|
+
userEarnedReward = false
|
|
97
|
+
ad.showAd(activity)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@PluginMethod
|
|
102
|
+
fun showInterstitial(call: PluginCall) {
|
|
103
|
+
activity.runOnUiThread {
|
|
104
|
+
val ad = interstitialAd
|
|
105
|
+
if (ad == null || !ad.isReady) {
|
|
106
|
+
val ret = JSObject()
|
|
107
|
+
ret.put("shown", false)
|
|
108
|
+
call.resolve(ret)
|
|
109
|
+
return@runOnUiThread
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
pendingInterstitialCall = call
|
|
113
|
+
ad.showAd(activity)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@PluginMethod
|
|
118
|
+
fun showBanner(call: PluginCall) {
|
|
119
|
+
activity.runOnUiThread {
|
|
120
|
+
val adView = bannerAdView
|
|
121
|
+
if (adView == null) {
|
|
122
|
+
val ret = JSObject()
|
|
123
|
+
ret.put("shown", false)
|
|
124
|
+
call.resolve(ret)
|
|
125
|
+
return@runOnUiThread
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
val position = call.getString("position", "bottom")
|
|
129
|
+
val gravity = if (position == "top") Gravity.TOP else Gravity.BOTTOM
|
|
130
|
+
|
|
131
|
+
val params = adView.layoutParams as? FrameLayout.LayoutParams
|
|
132
|
+
if (params != null) {
|
|
133
|
+
params.gravity = gravity or Gravity.CENTER_HORIZONTAL
|
|
134
|
+
adView.layoutParams = params
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
adView.visibility = View.VISIBLE
|
|
138
|
+
adView.startAutoRefresh()
|
|
139
|
+
|
|
140
|
+
val ret = JSObject()
|
|
141
|
+
ret.put("shown", true)
|
|
142
|
+
call.resolve(ret)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@PluginMethod
|
|
147
|
+
fun hideBanner(call: PluginCall) {
|
|
148
|
+
activity.runOnUiThread {
|
|
149
|
+
bannerAdView?.visibility = View.GONE
|
|
150
|
+
bannerAdView?.stopAutoRefresh()
|
|
151
|
+
call.resolve()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@PluginMethod
|
|
156
|
+
fun destroyBanner(call: PluginCall) {
|
|
157
|
+
activity.runOnUiThread {
|
|
158
|
+
bannerAdView?.let { adView ->
|
|
159
|
+
adView.stopAutoRefresh()
|
|
160
|
+
adView.visibility = View.GONE
|
|
161
|
+
(adView.parent as? FrameLayout)?.removeView(adView)
|
|
162
|
+
adView.destroy()
|
|
163
|
+
}
|
|
164
|
+
bannerAdView = null
|
|
165
|
+
call.resolve()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private fun createBannerAdView(adUnitId: String) {
|
|
170
|
+
val adView = MaxAdView(adUnitId, activity.applicationContext)
|
|
171
|
+
adView.setListener(bannerAdListener)
|
|
172
|
+
|
|
173
|
+
// Standard banner size: 320×50 on phones, 728×90 on tablets
|
|
174
|
+
val isTablet = AppLovinSdkUtils.isTablet(activity)
|
|
175
|
+
val heightDp = if (isTablet) 90 else 50
|
|
176
|
+
val heightPx = AppLovinSdkUtils.dpToPx(activity, heightDp)
|
|
177
|
+
|
|
178
|
+
val params = FrameLayout.LayoutParams(
|
|
179
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
180
|
+
heightPx
|
|
181
|
+
)
|
|
182
|
+
params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
|
|
183
|
+
adView.layoutParams = params
|
|
184
|
+
|
|
185
|
+
// Start hidden
|
|
186
|
+
adView.visibility = View.GONE
|
|
187
|
+
|
|
188
|
+
// Add to the activity's root view
|
|
189
|
+
val rootView = activity.findViewById<FrameLayout>(android.R.id.content)
|
|
190
|
+
rootView.addView(adView)
|
|
191
|
+
|
|
192
|
+
// Load the first ad
|
|
193
|
+
adView.loadAd()
|
|
194
|
+
|
|
195
|
+
bannerAdView = adView
|
|
196
|
+
Log.d(TAG, "Strip unit ready: $adUnitId")
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ─── Rewarded Ad Listener ───
|
|
200
|
+
|
|
201
|
+
private val rewardedAdListener = object : MaxRewardedAdListener {
|
|
202
|
+
override fun onAdLoaded(ad: MaxAd) {
|
|
203
|
+
Log.d(TAG, "Incentive unit ready")
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
override fun onAdLoadFailed(adUnitId: String, error: MaxError) {
|
|
207
|
+
Log.d(TAG, "Incentive unit fetch err: ${error.message}")
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
override fun onAdDisplayed(ad: MaxAd) {
|
|
211
|
+
Log.d(TAG, "Incentive unit shown")
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {
|
|
215
|
+
Log.d(TAG, "Incentive unit present err: ${error.message}")
|
|
216
|
+
val ret = JSObject()
|
|
217
|
+
ret.put("rewarded", false)
|
|
218
|
+
pendingRewardedCall?.resolve(ret)
|
|
219
|
+
pendingRewardedCall = null
|
|
220
|
+
rewardedAd?.loadAd()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
override fun onAdClicked(ad: MaxAd) {
|
|
224
|
+
Log.d(TAG, "Incentive unit tapped")
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
override fun onAdHidden(ad: MaxAd) {
|
|
228
|
+
Log.d(TAG, "Incentive unit closed, credited=$userEarnedReward")
|
|
229
|
+
val ret = JSObject()
|
|
230
|
+
ret.put("rewarded", userEarnedReward)
|
|
231
|
+
pendingRewardedCall?.resolve(ret)
|
|
232
|
+
pendingRewardedCall = null
|
|
233
|
+
userEarnedReward = false
|
|
234
|
+
rewardedAd?.loadAd()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
override fun onUserRewarded(ad: MaxAd, reward: MaxReward) {
|
|
238
|
+
Log.d(TAG, "Credit granted: ${reward.amount} ${reward.label}")
|
|
239
|
+
userEarnedReward = true
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ─── Interstitial Ad Listener ───
|
|
244
|
+
|
|
245
|
+
private val interstitialAdListener = object : MaxAdListener {
|
|
246
|
+
override fun onAdLoaded(ad: MaxAd) {
|
|
247
|
+
Log.d(TAG, "Fullscreen unit ready")
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
override fun onAdLoadFailed(adUnitId: String, error: MaxError) {
|
|
251
|
+
Log.d(TAG, "Fullscreen unit fetch err: ${error.message}")
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
override fun onAdDisplayed(ad: MaxAd) {
|
|
255
|
+
Log.d(TAG, "Fullscreen unit shown")
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {
|
|
259
|
+
Log.d(TAG, "Fullscreen unit present err: ${error.message}")
|
|
260
|
+
val ret = JSObject()
|
|
261
|
+
ret.put("shown", false)
|
|
262
|
+
pendingInterstitialCall?.resolve(ret)
|
|
263
|
+
pendingInterstitialCall = null
|
|
264
|
+
interstitialAd?.loadAd()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
override fun onAdClicked(ad: MaxAd) {
|
|
268
|
+
Log.d(TAG, "Fullscreen unit tapped")
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
override fun onAdHidden(ad: MaxAd) {
|
|
272
|
+
Log.d(TAG, "Fullscreen unit closed")
|
|
273
|
+
val ret = JSObject()
|
|
274
|
+
ret.put("shown", true)
|
|
275
|
+
pendingInterstitialCall?.resolve(ret)
|
|
276
|
+
pendingInterstitialCall = null
|
|
277
|
+
interstitialAd?.loadAd()
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ─── Banner Ad Listener ───
|
|
282
|
+
|
|
283
|
+
private val bannerAdListener = object : MaxAdViewAdListener {
|
|
284
|
+
override fun onAdLoaded(ad: MaxAd) {
|
|
285
|
+
Log.d(TAG, "Strip unit fetched")
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
override fun onAdLoadFailed(adUnitId: String, error: MaxError) {
|
|
289
|
+
Log.d(TAG, "Strip unit fetch err: ${error.message}")
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
override fun onAdDisplayed(ad: MaxAd) {
|
|
293
|
+
Log.d(TAG, "Strip unit shown")
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {
|
|
297
|
+
Log.d(TAG, "Strip unit present err: ${error.message}")
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
override fun onAdClicked(ad: MaxAd) {
|
|
301
|
+
Log.d(TAG, "Strip unit tapped")
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
override fun onAdHidden(ad: MaxAd) {
|
|
305
|
+
Log.d(TAG, "Strip unit closed")
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
override fun onAdExpanded(ad: MaxAd) {
|
|
309
|
+
Log.d(TAG, "Strip unit expanded")
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
override fun onAdCollapsed(ad: MaxAd) {
|
|
313
|
+
Log.d(TAG, "Strip unit collapsed")
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.playloop.plugins.deviceid
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.google.android.gms.appset.AppSet
|
|
5
|
+
import com.getcapacitor.JSObject
|
|
6
|
+
import com.getcapacitor.Plugin
|
|
7
|
+
import com.getcapacitor.PluginCall
|
|
8
|
+
import com.getcapacitor.PluginMethod
|
|
9
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
10
|
+
|
|
11
|
+
@CapacitorPlugin(name = "PlayLoopDeviceId")
|
|
12
|
+
class PlayLoopDeviceIdPlugin : Plugin() {
|
|
13
|
+
|
|
14
|
+
companion object {
|
|
15
|
+
private const val TAG = "PlayLoopDeviceId"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@PluginMethod
|
|
19
|
+
fun getAppSetId(call: PluginCall) {
|
|
20
|
+
val client = AppSet.getClient(activity)
|
|
21
|
+
client.appSetIdInfo.addOnSuccessListener { info ->
|
|
22
|
+
val ret = JSObject()
|
|
23
|
+
ret.put("id", info.id)
|
|
24
|
+
call.resolve(ret)
|
|
25
|
+
}.addOnFailureListener { e ->
|
|
26
|
+
Log.e(TAG, "Device identifier retrieval unsuccessful", e)
|
|
27
|
+
call.reject("Device identifier retrieval failed: ${e.message}")
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PlayLoop SDK Loader — thin bootstrap for CDN-delivered SDK.
|
|
5
|
+
*
|
|
6
|
+
* Games bundle this loader via their build tool (Vite/Webpack).
|
|
7
|
+
* It shims CapacitorCore, fetches the SDK version from the backend,
|
|
8
|
+
* loads the IIFE script from CDN, and returns the typed constructor.
|
|
9
|
+
*/
|
|
10
|
+
/** Compute widget top-bar height from window.innerWidth — no SDK load required. */
|
|
11
|
+
function getWidgetHeight() {
|
|
12
|
+
if (typeof window === 'undefined')
|
|
13
|
+
return 0;
|
|
14
|
+
const scale = Math.min(window.innerWidth / 360, 1.33);
|
|
15
|
+
return Math.round(48 * scale + 3) + 16;
|
|
16
|
+
}
|
|
17
|
+
const DEFAULT_FALLBACK = 'https://getplayloop.com/sdk/v0.8.5/playloop-sdk.js';
|
|
18
|
+
/**
|
|
19
|
+
* Load the PlayLoop SDK from CDN and return the constructor.
|
|
20
|
+
*
|
|
21
|
+
* 1. Shims `window.CapacitorCore` so the IIFE can resolve `@capacitor/core`
|
|
22
|
+
* 2. Fetches `/s/arcade/sdk-ver` to get the latest CDN URL
|
|
23
|
+
* 3. Injects a `<script>` tag for the IIFE bundle
|
|
24
|
+
* 4. Returns the typed `PlayLoopSDK` constructor
|
|
25
|
+
*/
|
|
26
|
+
async function loadPlayLoopSDK(options) {
|
|
27
|
+
// 1. Shim CapacitorCore for the IIFE's UMD/globals wrapper
|
|
28
|
+
if (!window.CapacitorCore) {
|
|
29
|
+
try {
|
|
30
|
+
const capCore = await import('@capacitor/core');
|
|
31
|
+
window.CapacitorCore = capCore;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
if (window.Capacitor) {
|
|
35
|
+
window.CapacitorCore = { Capacitor: window.Capacitor };
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
window.CapacitorCore = {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// 2. Fetch SDK version from backend
|
|
43
|
+
const timeout = options.timeoutMs ?? 5000;
|
|
44
|
+
const fallback = options.fallbackUrl ?? DEFAULT_FALLBACK;
|
|
45
|
+
let scriptUrl = fallback;
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(`${options.apiBaseUrl}/s/arcade/sdk-ver`, { signal: AbortSignal.timeout(timeout) });
|
|
48
|
+
const data = await res.json();
|
|
49
|
+
if (data.enabled && data.url)
|
|
50
|
+
scriptUrl = data.url;
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
console.warn('[PlayLoopLoader] Version fetch failed, using fallback:', e);
|
|
54
|
+
}
|
|
55
|
+
// 3. Load the IIFE script
|
|
56
|
+
await new Promise((resolve, reject) => {
|
|
57
|
+
const script = document.createElement('script');
|
|
58
|
+
script.src = scriptUrl;
|
|
59
|
+
script.onload = () => resolve();
|
|
60
|
+
script.onerror = () => reject(new Error(`Failed to load SDK from ${scriptUrl}`));
|
|
61
|
+
document.head.appendChild(script);
|
|
62
|
+
});
|
|
63
|
+
console.log(`[PlayLoopLoader] SDK loaded from ${scriptUrl}`);
|
|
64
|
+
// 4. Return typed constructor
|
|
65
|
+
const ctor = window.PlayLoopSDK?.PlayLoopSDK;
|
|
66
|
+
if (!ctor)
|
|
67
|
+
throw new Error('[PlayLoopLoader] window.PlayLoopSDK.PlayLoopSDK not found after script load');
|
|
68
|
+
return ctor;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
exports.getWidgetHeight = getWidgetHeight;
|
|
72
|
+
exports.loadPlayLoopSDK = loadPlayLoopSDK;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlayLoop SDK Loader — thin bootstrap for CDN-delivered SDK.
|
|
3
|
+
*
|
|
4
|
+
* Games bundle this loader via their build tool (Vite/Webpack).
|
|
5
|
+
* It shims CapacitorCore, fetches the SDK version from the backend,
|
|
6
|
+
* loads the IIFE script from CDN, and returns the typed constructor.
|
|
7
|
+
*/
|
|
8
|
+
/** Compute widget top-bar height from window.innerWidth — no SDK load required. */
|
|
9
|
+
function getWidgetHeight() {
|
|
10
|
+
if (typeof window === 'undefined')
|
|
11
|
+
return 0;
|
|
12
|
+
const scale = Math.min(window.innerWidth / 360, 1.33);
|
|
13
|
+
return Math.round(48 * scale + 3) + 16;
|
|
14
|
+
}
|
|
15
|
+
const DEFAULT_FALLBACK = 'https://getplayloop.com/sdk/v0.8.5/playloop-sdk.js';
|
|
16
|
+
/**
|
|
17
|
+
* Load the PlayLoop SDK from CDN and return the constructor.
|
|
18
|
+
*
|
|
19
|
+
* 1. Shims `window.CapacitorCore` so the IIFE can resolve `@capacitor/core`
|
|
20
|
+
* 2. Fetches `/s/arcade/sdk-ver` to get the latest CDN URL
|
|
21
|
+
* 3. Injects a `<script>` tag for the IIFE bundle
|
|
22
|
+
* 4. Returns the typed `PlayLoopSDK` constructor
|
|
23
|
+
*/
|
|
24
|
+
async function loadPlayLoopSDK(options) {
|
|
25
|
+
// 1. Shim CapacitorCore for the IIFE's UMD/globals wrapper
|
|
26
|
+
if (!window.CapacitorCore) {
|
|
27
|
+
try {
|
|
28
|
+
const capCore = await import('@capacitor/core');
|
|
29
|
+
window.CapacitorCore = capCore;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
if (window.Capacitor) {
|
|
33
|
+
window.CapacitorCore = { Capacitor: window.Capacitor };
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
window.CapacitorCore = {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// 2. Fetch SDK version from backend
|
|
41
|
+
const timeout = options.timeoutMs ?? 5000;
|
|
42
|
+
const fallback = options.fallbackUrl ?? DEFAULT_FALLBACK;
|
|
43
|
+
let scriptUrl = fallback;
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(`${options.apiBaseUrl}/s/arcade/sdk-ver`, { signal: AbortSignal.timeout(timeout) });
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
if (data.enabled && data.url)
|
|
48
|
+
scriptUrl = data.url;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.warn('[PlayLoopLoader] Version fetch failed, using fallback:', e);
|
|
52
|
+
}
|
|
53
|
+
// 3. Load the IIFE script
|
|
54
|
+
await new Promise((resolve, reject) => {
|
|
55
|
+
const script = document.createElement('script');
|
|
56
|
+
script.src = scriptUrl;
|
|
57
|
+
script.onload = () => resolve();
|
|
58
|
+
script.onerror = () => reject(new Error(`Failed to load SDK from ${scriptUrl}`));
|
|
59
|
+
document.head.appendChild(script);
|
|
60
|
+
});
|
|
61
|
+
console.log(`[PlayLoopLoader] SDK loaded from ${scriptUrl}`);
|
|
62
|
+
// 4. Return typed constructor
|
|
63
|
+
const ctor = window.PlayLoopSDK?.PlayLoopSDK;
|
|
64
|
+
if (!ctor)
|
|
65
|
+
throw new Error('[PlayLoopLoader] window.PlayLoopSDK.PlayLoopSDK not found after script load');
|
|
66
|
+
return ctor;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { getWidgetHeight, loadPlayLoopSDK };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CashableSDKConfig, IAds, AdConfig } from './types.js';
|
|
2
|
+
import { UIModule } from './ui/UIModule.js';
|
|
3
|
+
import { SessionModule } from './modules/SessionModule.js';
|
|
4
|
+
import { RewardsModule } from './modules/RewardsModule.js';
|
|
5
|
+
import { AnalyticsModule } from './modules/AnalyticsModule.js';
|
|
6
|
+
import { FeatureFlagModule } from './modules/FeatureFlagModule.js';
|
|
7
|
+
import { BabylonPlugin } from './babylon/BabylonPlugin.js';
|
|
8
|
+
|
|
9
|
+
declare class CashableSDK {
|
|
10
|
+
private config;
|
|
11
|
+
private transport;
|
|
12
|
+
private emitter;
|
|
13
|
+
private unsubTransportEvents;
|
|
14
|
+
private unsubRewardsCoins;
|
|
15
|
+
private _session;
|
|
16
|
+
private _rewards;
|
|
17
|
+
private _ads;
|
|
18
|
+
private _analytics;
|
|
19
|
+
private _featureFlags;
|
|
20
|
+
private _babylon;
|
|
21
|
+
private _ui;
|
|
22
|
+
private _isAuthenticated;
|
|
23
|
+
readonly mode: 'embedded' | 'standalone';
|
|
24
|
+
constructor(config: CashableSDKConfig);
|
|
25
|
+
init(): Promise<void>;
|
|
26
|
+
get session(): SessionModule;
|
|
27
|
+
get rewards(): RewardsModule;
|
|
28
|
+
get ads(): IAds;
|
|
29
|
+
get analytics(): AnalyticsModule;
|
|
30
|
+
/** Whether the SDK has a linked user on the backend. Always true in embedded mode. */
|
|
31
|
+
get isAuthenticated(): boolean;
|
|
32
|
+
/** Feature flag / remote config module. Null if not initialized. */
|
|
33
|
+
get featureFlags(): FeatureFlagModule | null;
|
|
34
|
+
getTopBarHeight(): number;
|
|
35
|
+
/** Convenience getter for ad configuration values (with defaults). */
|
|
36
|
+
get adConfig(): Readonly<AdConfig>;
|
|
37
|
+
/** UI module for standalone mode (top bar, sponsored modal). Null if not available. */
|
|
38
|
+
get ui(): UIModule | null;
|
|
39
|
+
get babylon(): BabylonPlugin;
|
|
40
|
+
onFreeze(handler: () => void): () => void;
|
|
41
|
+
onResume(handler: () => void): () => void;
|
|
42
|
+
dispose(): void;
|
|
43
|
+
private detectMode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { CashableSDK };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { IAdsAdapter, BannerPosition } from '../types.js';
|
|
2
|
+
|
|
3
|
+
interface AppLovinConfig {
|
|
4
|
+
sdkKey: string;
|
|
5
|
+
rewardedAdUnitId: string;
|
|
6
|
+
interstitialAdUnitId: string;
|
|
7
|
+
bannerAdUnitId?: string;
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare class CapacitorAdsAdapter implements IAdsAdapter {
|
|
11
|
+
private plugin;
|
|
12
|
+
private config;
|
|
13
|
+
private initialized;
|
|
14
|
+
private initPromise;
|
|
15
|
+
private debug;
|
|
16
|
+
constructor(config: AppLovinConfig);
|
|
17
|
+
initialize(): Promise<void>;
|
|
18
|
+
showRewardedVideo(): Promise<boolean>;
|
|
19
|
+
showInterstitial(): Promise<boolean>;
|
|
20
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
21
|
+
hideBanner(): Promise<void>;
|
|
22
|
+
destroyBanner(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { CapacitorAdsAdapter };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IAdsAdapter, ITransport, BannerPosition } from '../types.js';
|
|
2
|
+
|
|
3
|
+
declare class WebViewAdsAdapter implements IAdsAdapter {
|
|
4
|
+
private transport;
|
|
5
|
+
constructor(transport: ITransport);
|
|
6
|
+
showRewardedVideo(): Promise<boolean>;
|
|
7
|
+
showInterstitial(): Promise<boolean>;
|
|
8
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
9
|
+
hideBanner(): Promise<void>;
|
|
10
|
+
destroyBanner(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { WebViewAdsAdapter };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type BabylonEngine = {
|
|
2
|
+
stopRenderLoop(renderFunction?: () => void): void;
|
|
3
|
+
runRenderLoop(renderFunction: () => void): void;
|
|
4
|
+
};
|
|
5
|
+
type BabylonScene = {
|
|
6
|
+
render(): void;
|
|
7
|
+
};
|
|
8
|
+
declare class BabylonPlugin {
|
|
9
|
+
private engine;
|
|
10
|
+
private scene;
|
|
11
|
+
bindScene(scene: BabylonScene, engine?: BabylonEngine): void;
|
|
12
|
+
freeze(): void;
|
|
13
|
+
resume(): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { BabylonPlugin };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type BabylonEngine = {
|
|
2
|
+
stopRenderLoop(renderFunction?: () => void): void;
|
|
3
|
+
runRenderLoop(renderFunction: () => void): void;
|
|
4
|
+
};
|
|
5
|
+
type BabylonScene = {
|
|
6
|
+
render(): void;
|
|
7
|
+
};
|
|
8
|
+
declare class BabylonPlugin {
|
|
9
|
+
private engine;
|
|
10
|
+
private scene;
|
|
11
|
+
bindScene(scene: BabylonScene, engine?: BabylonEngine): void;
|
|
12
|
+
freeze(): void;
|
|
13
|
+
resume(): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { BabylonPlugin };
|