This is an Android Play Games Services plugin for Godot Game Engine 4.2.x.
If you want to use the old plugin version visit Old README file.
- Sign-in/Sign out
 - Achievements
 - Leaderboards
 - Events
 - Player Stats
 - Player Info
 - Saved Games
 
Before using this plugin please follow instructions on Setting Up Google Play Games Services official guide.
- Download 
GPGSS.zipfrom releases page. - Unzip the file into the 
res://addons/folder - Go out to Project -> Project Settings

 - Enable the plugin
 - Go out to Project -> Export and enable Gradle Build
 - Go to Project -> Install Android Build Template to install the android build template.
 - Go to res://android/build directory. Add below lines to 
AndroidManifest.xml: 
    <meta-data android:name="com.google.android.gms.games.APP_ID"
   	    android:value="@string/app_id" />
   
   	<meta-data android:name="com.google.android.gms.version"
   	   android:value="@integer/google_play_services_version"/>- Also add in a file called integers.xml in that same directory and add the following:
 
<?xml version="1.0" encoding="utf-8"?>
<resources>
    	<integer name="google_play_services_version">12451000</integer>
</resources>- In the same res://android/build directory,(if it is not already created) create  
res->values->Strings.xml. Add below lines toStrings.xml: 
<?xml version="1.0" encoding="utf-8"?>
<resources>
    	<string name="app_id">ADD_YOUR_APP_ID</string>
</resources>Replace ADD_YOUR_APP_ID with the app id that was generated after following instructions on Setting Up Google Play Games Services
Check demo project. In order demo project to work, replace ADD_YOUR_APP_ID with your own app id, and in Main.gd add your ids for achievements and leaderboards.
If there is no release for your Godot version, you need to generate new plugin .aar file.
Follow these instruction:  official documentation.
In short follow these steps:
- 
Download AAR library for Android plugins.
 - 
Copy .aar file to
godot-lib.release/*and rename it togodot-lib.4.2.1.stable.template_release.aar - 
Compile the project:
Open command window and cd into PGSGP direcory and run command below
- 
Windows:
gradlew.bat assembleRelease
 - 
Linux:
./gradlew assembleRelease
 
 - 
 - 
Copy the newly created .aar file to your plugin directory:
 
app/build/outputs/aar/GodotPlayGamesServices.release.aar to [your godot project]/android/plugins/
First step is plugin initialization
var play_games_services
# Check if plugin was added to the project
if Engine.has_singleton("GodotPlayGamesServices"):
  play_games_services = Engine.get_singleton("GodotPlayGamesServices")
	
  # Initialize plugin by calling init method and passing to it a boolean to enable/disable displaying game pop-ups
  
  var show_popups := true
  var request_email := true
  var request_profile := true
  #The client id must be created in [the google console](https://console.cloud.google.com/apis/credentials), an OAuth 2.0 Client credentials of a Web application type
  var request_token := "Client ID"
  play_games_services.init(show_popups, request_email, request_profile, request_token)
  # For enabling saved games functionality use below initialization instead
  # play_games_services.initWithSavedGames(show_popups, "SavedGamesName", request_email, request_profile, request_token)
  
  # Connect callbacks (Use only those that you need)
  play_games_services.connect("_on_sign_in_success", self, "_on_sign_in_success") # account_id: String
  play_games_services.connect("_on_sign_in_failed", self, "_on_sign_in_failed") # error_code: int
  play_games_services.connect("_on_sign_out_success", self, "_on_sign_out_success") # no params
  play_games_services.connect("_on_sign_out_failed", self, "_on_sign_out_failed") # no params
  play_games_services.connect("_on_achievement_unlocked", self, "_on_achievement_unlocked") # achievement: String
  play_games_services.connect("_on_achievement_unlocking_failed", self, "_on_achievement_unlocking_failed") # achievement: String
  play_games_services.connect("_on_achievement_revealed", self, "_on_achievement_revealed") # achievement: String
  play_games_services.connect("_on_achievement_revealing_failed", self, "_on_achievement_revealing_failed") # achievement: String
  play_games_services.connect("_on_achievement_incremented", self, "_on_achievement_incremented") # achievement: String
  play_games_services.connect("_on_achievement_incrementing_failed", self, "_on_achievement_incrementing_failed") # achievement: String
  play_games_services.connect("_on_achievement_info_loaded", self, "_on_achievement_info_loaded") # achievements_json : String
  play_games_services.connect("_on_achievement_info_load_failed", self, "_on_achievement_info_load_failed")
  play_games_services.connect("_on_leaderboard_score_submitted", self, "_on_leaderboard_score_submitted") # leaderboard_id: String
  play_games_services.connect("_on_leaderboard_score_submitting_failed", self, "_on_leaderboard_score_submitting_failed") # leaderboard_id: String
  play_games_services.connect("_on_game_saved_success", self, "_on_game_saved_success") # no params
  play_games_services.connect("_on_game_saved_fail", self, "_on_game_saved_fail") # data: String
  play_games_services.connect("_on_game_load_success", self, "_on_game_load_success") # data: String
  play_games_services.connect("_on_game_load_fail", self, "_on_game_load_fail") # data: String
  play_games_services.connect("_on_create_new_snapshot", self, "_on_create_new_snapshot") # name: String
  play_games_services.connect("_on_player_info_loaded", self, "_on_player_info_loaded")  # json_response: String
  play_games_services.connect("_on_player_info_loading_failed", self, "_on_player_info_loading_failed")
  play_games_services.connect("_on_player_stats_loaded", self, "_on_player_stats_loaded")  # json_response: String
  play_games_services.connect("_on_player_stats_loading_failed", self, "_on_player_stats_loading_failed")After what plugin was initialized you can use supported features
var is_gpgs_available: bool = play_game_services.isGooglePlayServicesAvailable()play_games_services.signIn()
# Callbacks:
func _on_sign_in_success(userProfile_json: String) -> void:
	var userProfile = parse_json(userProfile_json)
	# The returned JSON contains an object of userProfile info.
	# Use the following keys to access the fields
		userProfile["displayName"] # The user's display name
		userProfile["email"] # The user's email
		userProfile["token"] # User token for backend use
		userProfile["id"] # The user's id
func _on_sign_in_failed(error_code: int) -> void:
	pass
play_games_services.signOut()
# Callbacks:
func _on_sign_out_success():
	pass
  
func _on_sign_out_failed():
	passvar is_signed_in: bool = play_games_services.isSignedIn()play_games_services.unlockAchievement("ACHIEVEMENT_ID")
# Callbacks:
func _on_achievement_unlocked(achievement: String):
	pass
func _on_achievement_unlocking_failed(achievement: String):
	passvar step = 1
play_games_services.incrementAchievement("ACHIEVEMENT_ID", step)
# Callbacks:
func _on_achievement_incremented(achievement: String):
	pass
func _on_achievement_incrementing_failed(achievement: String):
	passvar steps = 3
play_games_services.setAchievementSteps("ACHIEVEMENT_ID", steps)
# Callbacks:
func _on_achievement_steps_set(achievement: String):
	pass
func _on_achievement_steps_setting_failed(achievement: String):
	passplay_games_services.revealAchievement("ACHIEVEMENT_ID")
# Callbacks:
func _on_achievement_revealed(achievement: String):
	pass
func _on_achievement_revealing_failed(achievement: String):
	passplay_games_services.showAchievements()play_games_services.loadAchievementInfo(false) # forceReload
# Callbacks:
func _on_achievement_info_load_failed(event_id: String):
	pass
func _on_achievement_info_loaded(achievements_json: String):
	var achievements = parse_json(achievements_json)
	# The returned JSON contains an array of achievement info items.
	# Use the following keys to access the fields
	for a in achievements:
		a["id"] # Achievement ID
		a["name"]
		a["description"]
		a["state"] # unlocked=0, revealed=1, hidden=2 (for the current player)
		a["type"] # standard=0, incremental=1
		a["xp"] # Experience gain when unlocked
		# Steps only available for incremental achievements
		if a["type"] == 1:
			a["current_steps"] # Users current progress
			a["total_steps"] # Total steps to unlock achievementvar score = 1234
play_games_services.submitLeaderBoardScore("LEADERBOARD_ID", score)
# Callbacks:
func _on_leaderboard_score_submitted(leaderboard_id: String):
	pass
func _on_leaderboard_score_submitting_failed(leaderboard_id: String):
	passplay_games_services.showLeaderBoard("LEADERBOARD_ID")
play_games_services.showAllLeaderBoards()var increment_by := 2
play_games_services.submitEvent("EVENT_ID", increment_by)
# Callbacks:
func _on_event_submitted(event_id: String):
	pass
	
func _on_event_submitted_failed(event_id: String):
	pass# Load all events
play_games_services.loadEvents()
# Or load events by given ids
play_games_services.loadEventsById(["EVENT_ID_1", "EVENT_ID_2", ...])
# Callbacks:
# If there is at least one event, following callback will be triggered:
func _on_events_loaded(events_array):
	# Parse received string json of events using parse_json
	var available_events = parse_json(events_array)
	# Iterate through the events_list to retrieve data for specific events
	for event in available_events:
		var event_id = event["id"] # you can get event id using 'id' key
		var event_name = event["name"] # you can get event name using 'name' key
		var event_desc = event["description"] # you can get event name using 'description' key 
		var event_img = event["imgUrl"] # you can get event name using 'imgUrl' key
		var event_value = event["value"] # you can get event name using 'value' key  
	
# Triggered if there are no events:
func _on_events_empty():
	pass
# Triggered if something went wrong:
func _on_events_loading_failed():
	pass
var force_refresh := true # If true, this call will clear any locally cached data and attempt to fetch the latest data from the server.
play_games_services.loadPlayerStats(force_refresh)
# Callbacks:	
func _on_player_stats_loaded(stats):
	var stats_dictionary: Dictionary = parse_json(stats)
	# Using below keys you can retrieve data about a player’s in-game activity
	stats_dictionary["avg_session_length"] # Average session length
	stats_dictionary["days_last_played"] # Days since last played
	stats_dictionary["purchases"] # Number of purchases
	stats_dictionary["sessions"] # Number of sessions
	stats_dictionary["session_percentile"] # Session percentile
	stats_dictionary["spend_percentile"] # Spend percentile
func _on_player_stats_loading_failed():
	passplay_games_services.loadPlayerInfo()
# Callbacks:	
func _on_player_info_loaded(info):
	var info_dictionary: Dictionary = parse_json(info)
	# Using below keys you can retrieve player’s info
	info_dictionary["display_name"]
	info_dictionary["name"]
	info_dictionary["title"]
	info_dictionary["player_id"]
	info_dictionary["hi_res_image_url"]
	info_dictionary["icon_image_url"]
	info_dictionary["banner_image_landscape_url"] 
	info_dictionary["banner_image_portrait_url"]
    # Also you can get level info for the player
    var level_info_dictionary = info_dictionary["level_info"]
	level_info_dictionary["current_xp_total"]
	level_info_dictionary["last_level_up_timestamp"]
    
    var current_level_dictionary = level_info_dictionary["current_level"]
    current_level_dictionary["level_number"]
    current_level_dictionary["max_xp"]
    current_level_dictionary["min_xp"]
    var next_level_dictionary = level_info_dictionary["next_level"]
    next_level_dictionary["level_number"]
    next_level_dictionary["max_xp"]
    next_level_dictionary["min_xp"]
    
func _on_player_info_loading_failed():
	passvar data_to_save: Dictionary = {
		"name": "John", 
		"age": 22,
		"height": 1.82,
		"is_gamer": true
	}
play_games_services.saveSnapshot("SNAPSHOT_NAME", to_json(data_to_save), "DESCRIPTION")
# Callbacks:
func _on_game_saved_success():
	pass
	
func _on_game_saved_fail():
	passplay_games_services.loadSnapshot("SNAPSHOT_NAME")
# Callbacks:
func _on_game_load_success(data):
	var game_data: Dictionary = parse_json(data)
	var name = game_data["name"]
	var age = game_data["age"]
	#...
	
	
func _on_game_load_fail():
	passvar allow_add_button := true
var allow_delete_button := true
var max_saved_games_snapshots := 5
var saved_games_screen_title := "TITLE"
play_games_services.showSavedGames(saved_games_screen_title, allow_add_button, allow_delete_button, max_saved_games_snapshots)
#Godot callback	
# If user clicked on add new snapshot button on the screen with all saved snapshots, below callback will be triggered:
func _on_create_new_snapshot(name):
	var game_data_to_save: Dictionary = {
		"name": "John", 
		"age": 22,
		"height": 1.82,
		"is_gamer": true
	}
	play_games_services.save_snapshot(name, to_json(game_data_to_save), "DESCRIPTION")
Check adb logcat for debuging.
To filter only Godot messages use next command:
adb logcat -s godot