godot-sdk-integrations / godot-play-game-services

A Godot 4.x plugin for integration with Google Play Game Services
MIT License
150 stars 10 forks source link

Issues when switching between google play services users #43

Open TheSkyOne opened 1 week ago

TheSkyOne commented 1 week ago

Google play services has this feature where you can press and hold your app's icon, and then tap "choose profile" and it will give a few options. Either starting the app with a different user you have signed in or starting the app with no user or asking each time. I dont know if this had some weird interaction with the plugin, but by switching between users I have encountered the following issues:

  1. I stopped getting the popup from the top that says youre signed in, even tho in my code i have a function connected to the user_authenticated signal and can confirm that there is an authenticated user.

  2. It seems that the game_loaded signal from the SnapshotClient has stopped firing somehow? my function that is connected to it stopped executing, and even when I created a new method with just a print it doesnt get executed either.

The only thing that seems to work correctly is when I choose to be signed out, then specifically uninstall the app through the play store, then run the remote debug through godot. then when the app runs it will run as if there is no user signed in. but if I then close and start the app again, It will run as if a user signed in, even tho I havent changed the preferences from the "choose profile" menu. But for the case where I do want to play as a signed-in user, I cant because of issue number 2, game_loaded isnt emitted and so the user's game save doesnt get loaded (which in my app is a prerequisite for it to finish the loading screen when there is a user signed in)

Even if this is not an issue with the plugin itself, I would love some help figuring this out. Thanks!

TheSkyOne commented 5 days ago

a small update. I have no idea why, but everything seems to be working ok now... I simply launched the game on my phone and got signed in into my account that was picked. only thing that doesnt work as expected is when I pick the other account to sign in as or when I pick "ask me every time" it still just signs me with that same account. but I have no idea if thats some issue with google's thing or if it could at all be fixed by adding something to the plugin. I will keep this issue open in case you want to add something or ask me to clarify something. but if you feel like its not relevant feel free to close.

EthannYakabuski commented 4 days ago

@TheSkyOne hey just a user of the plugin here - thanks for the update - i was trying to reproduce your issue too but couldn't.

What i thought originally was maybe that your second user isnt set up as "tester" in your list on google play console, but when i tested this use case both my verified gmails and my unverified gmails both got the successful login banner on my app - its only when communicating with the snapshotsclient did i see failure related to the gmail not being in the "tester" list.

I guess maybe double check that your second user is also added as a verified tester in google play console (assuming you are in the testing phase and your app is not already released) - but maybe also double check on the latest version of the plugin too (V2.0.0)

TheSkyOne commented 4 days ago

@EthannYakabuski hi thanks for replying. i am using the latest version of the plugin, and i made sure my other user was in the testers list. the issue im having seems to be very inconsistent. sometimes i open the app and dont get the login banner, and from the code i can tell the user didnt authenticate, which is how it should be. and sometimes i dont get the login banner, but from the code i can tell the user did authenticate, but the game_loaded signal doesnt get sent so i cant load the save for the user. this is my main issue, as my game thinks it should wait for loading to finish because there should be a user, but my own loading finished signal never gets emitted (it emits when the method connected to game_loaded finishes)

when i went to test now, i reinstalled the app (through godot, not the play store) and it logged in into my other user, which was chosen from the selection from the last time i tried. but then when i tried to switch to my 1st user it wouldnt and now im getting the same issues as i did when i opened this issue. sometimes, even when i choose to not sign-in at all when the app launches in my prints it still says the there was a successful authentication.

this is how i check if the user authenticated to initiate loading.

func _ready() -> void:
    SignInClient.user_authenticated.connect(is_user_authenticated)
    SignInClient.is_authenticated()

func is_user_authenticated(is_authenticated: bool) -> void:
    print("user is authenticated: %s" %is_authenticated)
    if is_authenticated:
        SnapshotsClient.load_game("save")
        await AccountStats.loading_finished

note that i have a print statement in SnapshotsClient.load_game which does print. but my function in AccountStats that is connected to SnapshotsClient.game_loaded never executes. i genuinely dont know what is going on.

EthannYakabuski commented 3 days ago

Hey @TheSkyOne I think we're getting closer to the issue, thanks for providing me some code snippets this really helps me to help, ive always been a hands on person. Now take everything i say with a grain of salt, im not actually certain - just trying to help.

First, I notice a peculiarity about how you are checking user authentication using SignInClient -

isAuthenticated_emitsSignal , since this function emits the signal, and i believe the plugin does this check automatically you might be erroneously sending this signal twice in quick succession leading to something weird bugging out.

Explanation: The SignInClient.is_authenticated() line (i believe) doesn't need to be there, this is one check the the plugin performs automatically (i think), so in essence the plugin is checking if there is an authenticated player, and then you are checking again manually in your ready function. (i believe the signal user_authenticated might be triggering twice in your case) (perhaps this is causing some weird peculiarity - i could imagine the banner coming up twice, somehow bugs it out and it doesnt show at all despite there being a valid user as you have noted via your debugging prints), firstly i would suggest taking out the line completely and see what happens.

for reference, here is my sign in function inside my ready function: (i checked my code for any references to SignInClient.is_authenticated, but i never actually call that method directly and sign in is working as expected for me which is why i consider this something to maybe look into on your end (eg i was unable to reproduce your issue on my app, but i also never call that function you are calling so maybe a hint there)) hereMySignInClientFunction

Second, im not familiar with asynchronous calls in gdscript, so this might totally be out of left field (or maybe i just need to see a bit more code related to whatever you are doing with your custom AccountStats class, - im curious how you are connecting to the SnapshotsClient.game_loaded signal) - IMO you should have some other function also in your ready function that is attached to this signal as well, as opposed to using an await on your AccountStats. Also - Are you maybe manually emitting the game signal from AccountStats or something like SnapshotsClient.game_loaded.emit(data) - maybe there's something funky going on there. Im not totally certain what your - await AccountStats.loading_finished - line is accomplishing. Is loading_finished a signal? is it a function? not sure that line is correct either.

here is my code that listens for the game loaded function (also in the ready function) hereMyGameLoadedFunction

let me know!

p.s - you can look at my "main menu" code here, that contains all the login and game loading logic - cheers https://github.com/EthannYakabuski/AnimalDash/blob/main/main_menu.gd

Iakobs commented 3 days ago

Hey! Sorry for the delay in response, @TheSkyOne

I didn't know there was an option from Google Play to choose users, so I created a test user to try. In my demo project app the game loaded signal is working fine, even though it's true that sometimes I don't get the user sign in popup if I change the user before opening, but I do get it always when I change it with the 'Ask me always' option. That's weird, but I think that's an issue with Google. I would suggest having an in game way of displaying if the user is logged in or not.

About your code snippet, what @EthannYakabuski said makes sense (thanks a lot Ethann, you are really helpful! 😄 ). The plugin automatically performs the call to is_authenticated, so no need to do it from your code. There's another issue asking for a way for the plugin user to decide wether or not to perform this check automatically, but I'm having trouble on how to implement that. In the meantime, just take that into account.

About the problem with Snapshots, again what Ethann suggests seems correct. I can see that you call the load_game method of the plugin, but you don't wait for it to finish. You should listen to the game_loaded signal and call there your AccountStats.loading_finished method.

Let us know if any of these suggestions solve any of your issues!!

TheSkyOne commented 2 days ago

thank you both for your responses! i will start with the following - my code used to be this

func _on_user_authenticated(is_authenticated: bool) -> void:
    print("user authenticated: %s" %is_authenticated)
    if is_authenticated:
        SnapshotsClient.load_game("save")

this was in the SignInClient, and in the _connect_signals() method written there i would add user_authenticated.connect(_on_user_authenticated) so that when the plugin does the authentication automatically it will also trigger this. but this also had the same problems i am experiencing now. i changed the way i initiated the save loading because i thought i had misunderstood how youre supposed to use the is_authenticated() method (which up until this point, i also have never called)

something that is important to know that is my fault for not including, is that the code i shared in my response to @EthannYakabuski does not happen on when the app starts up. the flow is as follows: app launch -> scene which functions as a splash screen, this is also when i imagine the plugin will do its authentications. and when i had my previous code, this is when the function would get triggered i imagine as the plugin authenticates. -> switch to loading screen scene. here i would check have checked if game save needed to be loaded (user is authenticated, and save hasnt been loaded yet) and initiate loading again. that is because i needed to ensure that if a user authenticated, the save's data loads before the loading screen is finished. because after the loading screen is finished, if data failed to be loaded, the game will have all its default values which it has on first install. that is the purpose of await AccountStats.loading_finished. ensure that if there is a signed user, the loading screen scene doesnt move on until the data has been loaded.

perhaps i should go back to how i was doing it before, but i thought that since im waiting for the loaded data in my loading screen scene, there is not reason to also have the loading initiated on the automatic authentication. simply wait for the loading screen, connect the user_authenticated signal to my function that i want to handle what happens if the user is authenticated or now, and check for the authentication status.

im curious how you are connecting to the SnapshotsClient.game_loaded signal)

simply SnapshotsClient.game_loaded.connect(_load_data) in AccountStats

Also - Are you maybe manually emitting the game signal from AccountStats or something like

no that script doesnt emit any signal from the clients. it is simply responsible for saving and loading the stats that are related to the google play games user

SnapshotsClient.game_loaded.emit(data) - maybe there's something funky going on there. Im not totally certain what your - await AccountStats.loading_finished - line is accomplishing. Is loading_finished a signal? is it a function? not sure that line is correct either.

_load_data() gets initiated when game_loaded signal is emitted by the SnapshotClient (as you can see i connect the two above) and takes the loaded snapshot (or null) as an argument, and processes the it. when done it emits loading_finished

and to @Iakobs

I would suggest having an in game way of displaying if the user is logged in or not.

unfortunately that is not going to be an option 😢 but if this has nothing to do with the plugin then it is what it is.

The plugin automatically performs the call to is_authenticated, so no need to do it from your code.

i understand that the plugin does this automatically as it signs you in automatically, but what if i want to check the authentication status of the user somewhere else at another point? as i wrote above, i went with waiting for my loading screen scene, where i then check the authentication status of the user and if authenticated, i initiate save data loading.

I can see that you call the load_game method of the plugin, but you don't wait for it to finish. You should listen to the game_loaded signal and call there your AccountStats.loading_finished method.

i am a little confused about this. i call the load_game method and then await AccountStats.loading_finished. calling load_game initiates _load_data in AccountStats, when that method finishes its processing, it will emit loading_finished, which will allow the loading screen to progress. how would i wait for the load_game method to finish?

during writing this tho, i have discovered that since i am awaiting in the function that is connected to the user_authenticated signal, i have no way to await the function call itself meaning after SignInClient.is_authenticated() the code just continues, which i hadn't considered. i will need to solve this, but i dont think this is what causes the issues i described when i opened this issue. those issues happened even with the old way i did things.

its a bit long, so i really appreciate you both for taking the time. i tried to be as clear as i can, but if something still isn't clear, please ask.

EthannYakabuski commented 2 days ago

@TheSkyOne i understand what you are trying to do a lot better now - makes sense, and sounds like a pretty smart implementation (so that when the main game screen is ready, all the user data is already populated to it instead of having a split second of default data - if i understand correctly).

What im curious about now is how / when are you loading the AccountStats scene/script ? Based off your description "SnapshotsClient.game_loaded.connect(_load_data) in AccountStats" , the SnapShotsClient.game_loaded signal is connected in the ready function of the AccountStats class?

Are you certain that the AccountStats ready function is actually being called? I would consider moving your SnapshotsClient.game_loaded.connect(_load_data) to the splash screen scene, as you know that ready function is being called! I'd place a small bet, this function will connect for you better from there.

(just another random stab - wishing you luck)

p.s -> but yes also you are using await within a non asynchronous function, which is definitely going to lead to some sort of race condition

p.p.s -> @Iakobs thanks for your kind words, you already saved me 100's of hours implementing my game with your genius plugin so least i could do is help some progammer homies with their issues too.

TheSkyOne commented 1 day ago

@EthannYakabuski AccountStats is a singleton, and its the first one in the singletons list. i have confirmed its _ready function does run. i thought maybe the fact that it comes before the SnapshotsClient might make it so that it doesnt have access to SnapshotsClient.game_loaded or something like that but thats not possible because this is the way it has been this entire time, and _load_data does work, its just something about switching users that messes things up.

more info. i noticed that sometimes, when i switch to user 2 and i think a sufficient amount of time has passed (because this seems to only happen when i come back the next day) and install the game through godot's remote debug, then the user it shows me changes. if i then switch to user 1 and install through remote debug, it will keep showing me user 2 being signed in. "ask each time" doesnt work for me at all, it simply launches me into the currently signed user. doing "sign out" does launch the game without a user, but then no matter what i do i cant get it to sign with a user. it does go back to signing with a user at some point but my only guess is that enough time has passed that something reset, because i cant get it to do it myself.

after testing on an emulated device, i am now pretty sure the issue is with my phone specifically. i have a pretty non-conventional installation and it only just came to me that it might be interfering with something. on an emulated device the profile switching works exactly as it should.

i just want to ask @Iakobs, from my explanation in the other message, does it seem like i misunderstood how to use the plugin or do you think it should be ok? im talking more specifically about doing this

SignInClient.user_authenticated.connect(is_user_authenticated)
SignInClient.is_authenticated()

to decide myself when to initiate my logic for user authenticated vs not authenticated.