Skip to content

Commit

Permalink
Add methods for events API
Browse files Browse the repository at this point in the history
Signed-off-by: Jacob Ibáñez Sánchez <jacobibanez@jacobibanez.com>
  • Loading branch information
Iakobs committed Mar 2, 2024
1 parent 18040fe commit d331fea
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Added the `createIfNotFound` parameter to the `loadGame` method, with a default
### Add method to delete snapshots
Added new method to delete snapshots by snapshot id.

### Add methods for events API
Added three new methods for events API:
- incrementEvent
- loadEvents
- loadEventsByIds

## v1.5.0
### Order of autoloads
The autoloads where causing errors on first launch of the project, due to the load order and dependencies between them. The load order has now been fixed to avoid this errors. Also, the plugin is now disabled by default in the demo project. Look at the [demo project documentation](https://github.com/Iakobs/godot-play-game-services/tree/main/plugin/demo) for further info.
Expand Down
99 changes: 99 additions & 0 deletions plugin/export_scripts_template/autoloads/events_client.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
extends Node

## Signal emitted after calling the [method load_events] method.[br]
## [br]
## [param events]: The list of events.
signal events_loaded(events)

## Signal emitted after calling the [method load_events_by_ids] method.[br]
## [br]
## [param events]: The list of events.
signal events_loaded_by_ids(events)

func _ready() -> void:
_connect_signals()

func _connect_signals() -> void:
if GodotPlayGameServices.android_plugin:
GodotPlayGameServices.android_plugin.eventsLoaded.connect(func(json_data: String):
events_loaded.emit(_parse_events(json_data))
)
GodotPlayGameServices.android_plugin.eventsLoadedByIds.connect(func(json_data: String):
events_loaded.emit(_parse_events(json_data))
)

## Increments an event specified by eventId by the given number of steps.[br]
## [br]
## This is the fire-and-forget API. Event increments are cached locally and flushed
## to the server in batches.[br]
## [br]
## [param event_id]: The event ID to increment.[br]
## [param increment_amount]: The amount increment by. Must be greater than or equal to 0.
func increment_event(event_id: String, increment_amount: int) -> void:
if GodotPlayGameServices.android_plugin:
GodotPlayGameServices.android_plugin.incrementEvent(event_id, increment_amount)

## Loads a list of events for the currently signed-in player.[br]
## [br]
## [param force_reload]: If true, this call will clear any locally cached
## data and attempt to fetch the latest data from the server. Send it set to [code]true[/code]
## the first time, and [code]false[/code] in subsequent calls, or when you want
## to clear the cache.
func load_events(force_reload: bool) -> void:
if GodotPlayGameServices.android_plugin:
GodotPlayGameServices.android_plugin.loadEvents(force_reload)

## Loads a specific list of events for the currently signed-in player.[br]
## [br]
## [param force_reload]: If true, this call will clear any locally cached
## data and attempt to fetch the latest data from the server. Send it set to [code]true[/code]
## the first time, and [code]false[/code] in subsequent calls, or when you want
## to clear the cache.[br]
## [param event_ids]: The IDs of the events to load.
func load_events_by_ids(force_reload: bool, event_ids: Array[String]) -> void:
if GodotPlayGameServices.android_plugin:
GodotPlayGameServices.android_plugin.loadEventsByIds(force_reload, event_ids)

func _parse_events(json_data: String) -> Array[PlayGamesEvent]:
var safe_array := GodotPlayGameServices.json_marshaller.safe_parse_array(json_data)
var events: Array[PlayGamesEvent] = []
for dictionary: Dictionary in safe_array:
events.append(PlayGamesEvent.new(dictionary))
return events

## A class representing an Event from Google Play Games Services.
class PlayGamesEvent:
var description: String ## The description for this event.
var event_id: String ## The ID of this event.
var formatted_value: String ## The sum of increments have been made to this event (formatted for the user's locale).
var icon_image_uri: String ## The URI to the event's image icon.
var name: String ## The name of this event.
var player: PlayersClient.Player ## The player information associated with this event.
var value: int ## The number of increments this user has made to this event.
var is_visible: bool ## Whether the event should be displayed to the user in any event related UIs.

## Constructor that creates a PlayGamesEvent from a [Dictionary] containing the properties.
func _init(dictionary: Dictionary) -> void:
print(dictionary)
if dictionary.has("description"): description = dictionary.description
if dictionary.has("eventId"): event_id = dictionary.eventId
if dictionary.has("formattedValue"): formatted_value = dictionary.formattedValue
if dictionary.has("iconImageUri"): icon_image_uri = dictionary.iconImageUri
if dictionary.has("name"): name = dictionary.name
if dictionary.has("player"): player = PlayersClient.Player.new(dictionary.player)
if dictionary.has("value"): value = dictionary.value
if dictionary.has("isVisible"): is_visible = dictionary.isVisible

func _to_string() -> String:
var result := PackedStringArray()

result.append("description: %s" % description)
result.append("event_id: %s" % event_id)
result.append("formatted_value: %s" % formatted_value)
result.append("icon_image_uri: %s" % icon_image_uri)
result.append("name: %s" % name)
result.append("player: {%s}" % str(player))
result.append("value: %s" % value)
result.append("is_visible: %s" % is_visible)

return ", ".join(result)
3 changes: 3 additions & 0 deletions plugin/export_scripts_template/export_plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const ACHIEVEMENTS_AUTOLOAD := "AchievementsClient"
const LEADERBOARDS_AUTOLOAD := "LeaderboardsClient"
const PLAYERS_AUTOLOAD := "PlayersClient"
const SNAPSHOTS_AUTOLOAD := "SnapshotsClient"
const EVENTS_AUTOLOAD := "EventsClient"

var _export_plugin : AndroidExportPlugin
var _dock : Node
Expand Down Expand Up @@ -48,8 +49,10 @@ func _add_autoloads() -> void:
add_autoload_singleton(ACHIEVEMENTS_AUTOLOAD, "res://addons/GodotPlayGameServices/autoloads/achievements_client.gd")
add_autoload_singleton(LEADERBOARDS_AUTOLOAD, "res://addons/GodotPlayGameServices/autoloads/leaderboards_client.gd")
add_autoload_singleton(SNAPSHOTS_AUTOLOAD, "res://addons/GodotPlayGameServices/autoloads/snapshots_client.gd")
add_autoload_singleton(EVENTS_AUTOLOAD, "res://addons/GodotPlayGameServices/autoloads/events_client.gd")

func _remove_autoloads() -> void:
remove_autoload_singleton(EVENTS_AUTOLOAD)
remove_autoload_singleton(SNAPSHOTS_AUTOLOAD)
remove_autoload_singleton(LEADERBOARDS_AUTOLOAD)
remove_autoload_singleton(ACHIEVEMENTS_AUTOLOAD)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.util.Log
import android.view.View
import com.google.android.gms.games.PlayGamesSdk
import com.jacobibanez.plugin.android.godotplaygameservices.achievements.AchievementsProxy
import com.jacobibanez.plugin.android.godotplaygameservices.events.EventsProxy
import com.jacobibanez.plugin.android.godotplaygameservices.leaderboards.LeaderboardsProxy
import com.jacobibanez.plugin.android.godotplaygameservices.players.PlayersProxy
import com.jacobibanez.plugin.android.godotplaygameservices.signals.getSignals
Expand All @@ -30,6 +31,7 @@ class GodotAndroidPlugin(godot: Godot) : GodotPlugin(godot) {
private val leaderboardsProxy = LeaderboardsProxy(godot)
private val playersProxy = PlayersProxy(godot)
private val snapshotsProxy = SnapshotsProxy(godot)
private val eventsProxy = EventsProxy(godot)

/** @suppress */
override fun getPluginSignals(): MutableSet<SignalInfo> {
Expand Down Expand Up @@ -446,6 +448,39 @@ class GodotAndroidPlugin(godot: Godot) : GodotPlugin(godot) {
* @param snapshotId The snapshot identifier.
*/
@UsedByGodot
fun deleteSnapshot(snapshotId: String) =
snapshotsProxy.deleteSnapshot(snapshotId)
fun deleteSnapshot(snapshotId: String) = snapshotsProxy.deleteSnapshot(snapshotId)

/**
* Increments an event specified by eventId by the given number of steps.
*
* This is the fire-and-forget API. Event increments are cached locally and flushed to the server in batches.
*
* @param eventId The event ID to increment.
* @param incrementAmount The amount increment by. Must be greater than or equal to 0.
*/
@UsedByGodot
fun incrementEvent(eventId: String, incrementAmount: Int) =
eventsProxy.incrementEvent(eventId, incrementAmount)

/**
* Loads a list of events for the currently signed-in player. This method emits the
* [com.jacobibanez.plugin.android.godotplaygameservices.signals.EventsSignals.eventsLoaded] signal.
*
* @param forceReload If true, this call will clear any locally cached data and attempt to fetch
* the latest data from the server.
*/
@UsedByGodot
fun loadEvents(forceReload: Boolean) = eventsProxy.loadEvents(forceReload)

/**
* Loads a specific list of events for the currently signed-in player. This method emits the
* [com.jacobibanez.plugin.android.godotplaygameservices.signals.EventsSignals.eventsLoadedByIds] signal.
*
* @param forceReload If true, this call will clear any locally cached data and attempt to fetch
* the latest data from the server.
* @param eventIds The IDs of the events to load.
*/
@UsedByGodot
fun loadEventsByIds(forceReload: Boolean, eventIds: Array<String>) =
eventsProxy.loadEventsByIds(forceReload, eventIds)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.jacobibanez.plugin.android.godotplaygameservices.events

import android.annotation.SuppressLint
import com.google.android.gms.games.event.Event
import com.jacobibanez.plugin.android.godotplaygameservices.players.fromPlayer
import com.jacobibanez.plugin.android.godotplaygameservices.utils.toStringAndSave
import org.godotengine.godot.Dictionary
import org.godotengine.godot.Godot

/** @suppress */
@SuppressLint("VisibleForTests")
fun fromEvent(godot: Godot, event: Event) = Dictionary().apply {
put("description", event.description)
put("eventId", event.eventId)
put("formattedValue", event.formattedValue)
event.iconImageUri?.let {
put(
"iconImageUri",
it.toStringAndSave(godot, "iconImageUri", event.eventId)
)
}
put("name", event.name)
put("player", fromPlayer(godot, event.player))
put("value", event.value)
put("isVisible", event.isVisible)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.jacobibanez.plugin.android.godotplaygameservices.events

import android.util.Log
import com.google.android.gms.games.EventsClient
import com.google.android.gms.games.PlayGames
import com.google.android.gms.games.event.EventBuffer
import com.google.gson.Gson
import com.jacobibanez.plugin.android.godotplaygameservices.BuildConfig
import com.jacobibanez.plugin.android.godotplaygameservices.signals.EventsSignals.eventsLoaded
import com.jacobibanez.plugin.android.godotplaygameservices.signals.EventsSignals.eventsLoadedByIds
import org.godotengine.godot.Dictionary
import org.godotengine.godot.Godot
import org.godotengine.godot.plugin.GodotPlugin.emitSignal

/** @suppress */
class EventsProxy(
private val godot: Godot,
private val eventsClient: EventsClient = PlayGames.getEventsClient(godot.getActivity()!!)
) {

private val tag: String = EventsProxy::class.java.simpleName

fun incrementEvent(eventId: String, incrementAmount: Int) {
Log.d(tag, "Submitting event $eventId by value $incrementAmount")
eventsClient.increment(eventId, incrementAmount)
}

fun loadEvents(forceReload: Boolean) {
Log.d(tag, "Retrieving events for this user (forceReload = $forceReload)")
eventsClient.load(forceReload).addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(
tag,
"Events loaded successfully. Data is stale? ${task.result.isStale}"
)
val safeBuffer: EventBuffer = task.result.get()!!
val events: List<Dictionary> = if (safeBuffer.toList().isNotEmpty()) {
safeBuffer.map { fromEvent(godot, it) }.toList()
} else {
emptyList()
}
emitSignal(
godot,
BuildConfig.GODOT_PLUGIN_NAME,
eventsLoaded,
Gson().toJson(events)
)
} else {
Log.e(tag, "Failed to load events. Cause: ${task.exception}", task.exception)
emitSignal(
godot,
BuildConfig.GODOT_PLUGIN_NAME,
eventsLoaded,
Gson().toJson(emptyList<Dictionary>())
)
}
}
}

fun loadEventsByIds(forceReload: Boolean, eventIds: Array<String>) {
Log.d(tag, "Retrieving events for from eventIds $eventIds (forceReload = $forceReload)")
eventsClient.loadByIds(forceReload, *(eventIds)).addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(
tag,
"Events loaded successfully. Data is stale? ${task.result.isStale}"
)
val safeBuffer: EventBuffer = task.result.get()!!
val events: List<Dictionary> = if (safeBuffer.toList().isNotEmpty()) {
safeBuffer.map { fromEvent(godot, it) }.toList()
} else {
emptyList()
}
emitSignal(
godot,
BuildConfig.GODOT_PLUGIN_NAME,
eventsLoadedByIds,
Gson().toJson(events)
)
} else {
Log.e(tag, "Failed to load events. Cause: ${task.exception}", task.exception)
emitSignal(
godot,
BuildConfig.GODOT_PLUGIN_NAME,
eventsLoadedByIds,
Gson().toJson(emptyList<Dictionary>())
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ fun getSignals(): MutableSet<SignalInfo> = mutableSetOf(
SnapshotSignals.conflictEmitted,
SnapshotSignals.snapshotsLoaded,

EventsSignals.eventsLoaded,
EventsSignals.eventsLoadedByIds,

HelperSignals.imageStored,
)

Expand Down Expand Up @@ -114,7 +117,8 @@ object LeaderboardSignals {
*
* @return The leaderboard id and a JSON with a [com.google.android.gms.games.LeaderboardsClient.LeaderboardScores](https://developers.google.com/android/reference/com/google/android/gms/games/LeaderboardsClient.LeaderboardScores).
*/
val playerCenteredScoresLoaded = SignalInfo("playerCenteredScoresLoaded", String::class.java, String::class.java)
val playerCenteredScoresLoaded =
SignalInfo("playerCenteredScoresLoaded", String::class.java, String::class.java)

/**
* This signal is emitted when calling the [com.jacobibanez.plugin.android.godotplaygameservices.GodotAndroidPlugin.loadTopScores] method.
Expand Down Expand Up @@ -201,6 +205,25 @@ object SnapshotSignals {
val snapshotsLoaded = SignalInfo("snapshotsLoaded", String::class.java)
}

/**
* Signals emitted by Events methods.
*/
object EventsSignals {
/**
* This signal is emitted when calling the [com.jacobibanez.plugin.android.godotplaygameservices.GodotAndroidPlugin.loadEvents] method.
*
* @return A JSON with the list of [com.google.android.gms.games.event.Event](https://developers.google.com/android/reference/com/google/android/gms/games/event/Event).
*/
val eventsLoaded = SignalInfo("eventsLoaded", String::class.java)

/**
* This signal is emitted when calling the [com.jacobibanez.plugin.android.godotplaygameservices.GodotAndroidPlugin.loadEventsByIds] method.
*
* @return A JSON with the list of [com.google.android.gms.games.event.Event](https://developers.google.com/android/reference/com/google/android/gms/games/event/Event).
*/
val eventsLoadedByIds = SignalInfo("eventsLoadedByIds", String::class.java)
}

object HelperSignals {
/**
* This signal is emitted everytime an image is downloaded and saved to the local storage.
Expand Down

0 comments on commit d331fea

Please sign in to comment.