godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.34k stars 21.06k forks source link

Crash in _WebThreadLock when using Admob under iOS #28049

Closed oleksandr-manakov closed 1 year ago

oleksandr-manakov commented 5 years ago

Godot version: 3.1-stable

OS/device including version: iOS, any device

Issue description: Crash on displaying rewarded ad or interstitial ad via Admob: Crash: void _WebThreadLock(), 0x1082b8040: Multiple locks on web thread not allowed! Please file a bug. Crashing now...

I saw similar issue already in past, but it's closed now.

I think this issue is directly related to Godot and not module implementation and why: 1) I've tried to use Admob in 2 ways: using this module (https://github.com/kloder-games/godot-admob) and by writing own Godot module for Enhance platform (http://enhance.co). Different modules, 100% same issue. 2) I've tried to take godot-admob module and use it in Cocos2d engine. No issue present and everything works fine.

So it's definitely something wrong with Godot+Admob. Admob is very popular and used with lot of game engines, no similar issue there. Admob integration is a must for most of our clients, so it's a critical issue for us now. Please help.

Callstack.txt

Steps to reproduce: Either

Minimal reproduction project: You can simply use this example project from godot-admob plugin - https://github.com/kloder-games/godot-admob/tree/master/examples/godot-3

akien-mga commented 5 years ago

CC @godotengine/ios

Shin-NiL commented 5 years ago

I've tried everything I could to try so solve this issue, so I just give up. You can find more info about that here. At least it's not an exclusivity of our module.

samgreen commented 5 years ago

This error in the stack trace on iOS almost always means you've tried to access or modify a UIWebView or WKWebView that is not on the screen. This will not work on iOS and, as you can see, iOS will actually jettison the application for attempting.

A quick look at the godot-admob repo suggests that this might actually be the case. When the module is first initialized:

void GodotAdmob::init(bool isReal, int instanceId) {
    if (initialized) {
        NSLog(@"GodotAdmob Module already initialized");
        return;
    }

    initialized = true;

    banner = [AdmobBanner alloc];
    [banner initialize :isReal :instanceId];

    interstitial = [AdmobInterstitial alloc];
    [interstitial initialize:isReal :instanceId];

    rewarded = [AdmobRewarded alloc];
    [rewarded initialize:isReal :instanceId];
}

https://github.com/kloder-games/godot-admob/blob/master/admob/ios/src/godotAdmob.mm#L29

Advertisements are loaded by ID (which internally triggers webview creation). This is much too early in the process.

You should either wait to preload the assets until a ready event has been fired, or my preferred approach would be to provide an setUp method that developers can use to begin caching advertisements when it's appropriate for their app. You can imagine a situation where a user has made a purchase to remove advertisements from a game, and although the library is still linked, we no longer want to load and cache ads on every session.

Shin-NiL commented 5 years ago

Thanks for the clarification @samgreen as I'm not a iOS developer I didn't know that. But the initialize methods does nothing related with de Ads yet, they are actually loaded only on its load* methods:

- (void)initialize:(BOOL)is_real: (int)instance_id {
    isReal = is_real;
    initialized = true;
    instanceId = instance_id;
    rootController = [AppDelegate getViewController];
}

https://github.com/kloder-games/godot-admob/blob/master/admob/ios/src/AdmobBanner.mm#L12

void GodotAdmob::loadBanner(const String &bannerId, bool isOnTop) {
    if (!initialized) {
        NSLog(@"GodotAdmob Module not initialized");
        return;
    }

    NSString *idStr = [NSString stringWithCString:bannerId.utf8().get_data() encoding: NSUTF8StringEncoding];
    [banner loadBanner:idStr :isOnTop];

}

https://github.com/kloder-games/godot-admob/blob/master/admob/ios/src/godotAdmob.mm#L40

Feel free to help us on our repository issue if you have availability :)

JBacal commented 5 years ago

I am experiencing the exact same crash as oleksandr-manakov. Has any further progress been made in solving this issue?

Thanks for any help.

Best wishes, Jay

JBacal commented 5 years ago

Okay, after further testing I have noticed the following:

I am testing on a iPad. The game was designed to work in portrait mode. If my device happens to be oriented in landscape mode and the test rewarded ad starts playing in landscape mode then the app will crash after closing the test ad.

However, if the device is oriented in portrait mode and the rewarded ad displays in portrait mode then there is no crash and everything works as it should.

Perhaps this info will help with a solution to the problem.

JBacal commented 5 years ago

Found a workaround for my game. If I uncheck all orientations except Portrait in Godot's iOS Export screen, then the AdMob rewarded ad can no longer start playing in landscape mode and the game no longer crashes after closing the ad.

oleksandr-manakov commented 5 years ago

Does not work for me, unfortunately. Same crash in _WebThreadLock.

KoBeWi commented 3 years ago

Can anyone still reproduce this bug in Godot 3.2.3 or any later release?

DisDoh commented 3 years ago

Yes I could but on android and similar but without crash. When there is an ad interstitial then after the time out and pressing quit the game lose the connection to the server. Occured on 3.3.3-stable

akien-mga commented 1 year ago

Nobody confirmed it recently on iOS so this is likely no longer relevant.