khairul169 / gdsqlite-native

SQLite module for Godot 3.x
MIT License
219 stars 41 forks source link

Android support? #11

Open snakeskinsalamander opened 6 years ago

snakeskinsalamander commented 6 years ago

Hi there,

Can we be able to recompile it to support Android?

salihkallai commented 6 years ago

Email me if Android support available : mymailhomes@gmail.com

disemq commented 5 years ago

Please, make it work for Android :)

danielkotzer commented 5 years ago

Is there support for Android?

TGRCdev commented 5 years ago

I don't have any experience with Android compilation, but I could give it a whack.

danielkotzer commented 5 years ago

For a long time I worked on the Adobe Air platform (Flex/Flesh), it was good for my needs. Not long ago, I moved from flash/flex to Godot, and it is really a great game engine, I think I should be using it for all kinds of applications, not only games, but one feature that I really miss is communication with a local Sqlite database which I had in flash, and I need it to work on all platforms because I publish on all platforms, if this will be added I'll have everything I need in Godot.

TGRCdev commented 5 years ago

I spent about a day diving into this, but unfortunately the C++ bindings for Godot don't support Android yet, so I can only do so much. I've messed around with the NDK compilers and Scons files, but all my outputs fail to link together for one reason or another.

danielkotzer commented 5 years ago

I appreciate your effort, thank you. I guess we'll have to wait for it.

TGRCdev commented 5 years ago

I may have spoken too soon! After writing an Android.mk and Application.mk, I can confirm that SQLite for Godot is working on Android!

Screenshot from my Galaxy S10E running the high_scores.gd example: Screenshot_20190813-154015_SQLiteTest The text is small, but it's showing that it is accessing the database, and that the db is a valid reference!

Now that I have compilation working, all I've got to do is clean up my godot-cpp SConstruct, merge the commands from ndk-build into GD SQLite's SConstruct file, and put together a beta release, and we'll be right as rain!

danielkotzer commented 5 years ago

Great! thank you, have you updated the binary file yet? can I download it from the code section? let me know when it is ready as a binary file for downloading.

TGRCdev commented 5 years ago

I'm still cleaning up godot-cpp, but I'll comment when I have the beta binaries ready, along with a link to download.

EDIT: Well, I HAD it, but now I can't reproduce how I compiled the plugin. This is going to take longer than I had expected.

TGRCdev commented 5 years ago

Well, I messed up.

After the successful compile, I got too far ahead of myself, and forgot to commit my script changes for godot-cpp and gdsqlite-native. Now, all of my attempts to recompile a working plugin are failing, so I'm practically back at square one.

Sorry to y'all who have been waiting for this. I'm going to keep cracking at it, and I'll notify you when I get a (COMMITTED AND BACKED UP) build working again.

danielkotzer commented 5 years ago

I appreciate your effort, and have no doubt that eventually you will get it to work again.

TGRCdev commented 5 years ago

It's here!

After a long time compiling and recompiling, the binaries for Android are available.

These binaries were compiled using a modified version of a fork of godot-cpp. I'm now working on editing the original godot-cpp to produce working binaries as well, but progress on that is slow, so I figured I would compile and release the latest SQLite plugin binaries and drop them here.

I have tested these on my S10E (arm64v8) and on a BlueStacks emulator (x86).

I will clean up my SConstruct file for GDSQLite itself and make a PR soon.

danielkotzer commented 5 years ago

Thank you, some games like games that teach English, must have a dictionary included, and you need to search the dictionary efficiently, having Sqlite is a savior, and an important milestone in the development of Godot.

danielkotzer commented 5 years ago

Tried to use this build (the binaries for Android are available.) by replacing the lib folder on the godot_sqlite-9289ef9 demo project with this new one, but I got an error message: Inavlid call. Nonexistent function 'open_db' in base 'Reference (gdsqlite.gdns)'.

TGRCdev commented 5 years ago

These binaries were compiled with the latest commit of SQLite. Some of the methods were changed. https://github.com/khairul169/gdsqlite-native/blob/b1810837f7cb54e6792dfb6ce26fc999db361728/src/gdsqlite.cpp#L251-L262 SQLite.open_db(path) should be replaced with SQLite.open(path)

MarcosRRM commented 5 years ago

Hey! Does this works on Godot 3.1 Android export? I setted up a simple scene that would change a Label based on the first row of the items.db example. I tried to use it, and it ran fine on the editor, updating the label's text accordingly. But when I exported the APK and ran on my devide (Moto G6) it didn't update the label's text. I DID include the "*.db" on the non-resource export option.

TGRCdev commented 5 years ago

@MarcosRRM The latest release of this repository doesn't have Android binaries, but I have a beta build that does include Android binaries. Please try these and tell me if it works (you will have to replace any instances of open_db(path) with open(path) in your scripts)

If that doesn't work, please enable USB debugging on your phone and run adb logcat -s godot with your phone connected to your PC, and reply with whatever error it spits out (or if it doesn't spit one out)

MarcosRRM commented 5 years ago

Hey! Thanks for the help. I was actually talking about your binaries. I tried what you said and the log was:

09-10 00:11:33.563 18179 18208 I godot   : Cannot open database!
09-10 00:11:33.563 18179 18208 I godot   : SQL Error: unable to open database file
09-10 00:11:33.563 18179 18208 E godot   : **SCRIPT ERROR**: Invalid get index '0' (on base: 'Array').
09-10 00:11:33.563 18179 18208 E godot   :    At: res://examples/item_database.gdc:16:_ready() - Invalid get index '0' (on base: 'Array').

Aparently it cannot open the DB on Android. The DB is located on the root folder and I'm opening it using: db.open("res://items.db") It works perfectly when I run on the editor.

Again, thanks for the help and the android binaries. If this works it will help IMMENSELLY with my college graduation project.

TGRCdev commented 5 years ago

@MarcosRRM I exported the demo and it ran fine on my end (Galaxy S10E). The only thing I can think of is to double-check which line you added *.db to in the Resources tab. image

MarcosRRM commented 5 years ago

I created a project from 0, rechecked every step and it still didn't work. Not on 3 smartphones I have at home (Samsung, Motorola and Xiaomi). I must be missing a step. I just need to place the DB inside the project's root folder, add the "*.db" to the "Filters to export non-resource" and I should be able to access it just by "db.open("res://myDB.db")" right? Should I export the PCK? I'm just exporting the APK. Do I need one of those permissions on the Options Tab? Again, it worked when running the scene in the editor.

Anyway, I tried to create a DB programatically with the File class and saved it to the "user://" directory, and it worked PERFECTLY! Right now, that's enough for me! Thanks, again, for the Android binaries!

TGRCdev commented 5 years ago

EDIT: Actually, I'm experiencing a weird bug where a database will not always open under res://, and Godot will occasionally crash when de-referencing a database handle. Maybe just use user:// for now


Ah, I just realized what was wrong.

In the item_database.gd example, the database under res:// is opened like so:

if (path.begins_with("res://")):
    # Open packed database
    var file = File.new()
    if (file.open(path, file.READ) != OK):
        return false;
    var size = file.get_len()
    var buffers = file.get_buffer(size)
    return db.open_buffered(path, buffers, size)

This is because, when a game is exported, all files under res:// are zipped up into the .pck file (or the .apk in this instance). The actual file doesn't exist, which usually is not a problem because Godot has a virtual file system built in for files under res://.

SQLite doesn't use that, it opens files directly. It has no concept of res://.

For now, a workaround for opening databases under res:// looks like this:

var file = File.new()
file.open("res://myDB.db", file.READ)
var size = file.get_len()
db.open_buffered("res://myDB.db", file.get_buffer(size), size)

Which should open a read-only database with the handle db.

Also, I'm glad that I could help with my beta binaries! I'll try to keep them up to date, and maybe set up a nightly build.

samuelpedrajas commented 5 years ago

@TGRCdev with the instructions of your latest comment it worked well for me. Thank you!

grymjack commented 4 years ago

@TGRCdev Thanks for your efforts getting this to work. I also was able to get SQLite working on my android app. Is there a way to get the database open in a read/write mode?

file.open("res://myDB.db", file.WRITE); ...perhaps?

TGRCdev commented 4 years ago

@grymjack You should be able to open a database under the user:// directory, and that database should be read/writable. If not, let me know.

grymjack commented 4 years ago

@TGRCdev Well I am noobish coder, so I could be doing something stupid. I am using your latest beta binaries you linked above. I have an existing table with data that opens and displays the data just fine on the android side, in the user:// space. I tried to install new data in the table for display. This worked fine on the GoDot IDE, but did not work on the Android app. One of the annoying side problems I am having is getting ADB working (different versions server/client?), so not able to get a debug console on the phone is an issue.

TGRCdev commented 4 years ago

@grymjack No worries, we all have to start somewhere!

Databases under the res:// directory are compressed into the .pck file when the game is exported. As such, any databases under res:// should be treated as read-only.

One solution is to copy your pre-made database from res:// into the user:// directory when the game starts for the first time, then use that for write access.

Example code:

var db

func _ready():
    var dir = Directory.new()
    if not dir.file_exists("user://save_db.db"):
        dir.copy("res://readonly.db", "user://save_db.db")
    db = SQLite.new()
    db.open("user://save_db.db")
    # db can now be used to write data
grymjack commented 4 years ago

@TGRCdev Thanks, I'll give that a try. If the database you will be making a copy of is very large, will that double the resources needed on the phone for the app?

TGRCdev commented 4 years ago

I believe so. It could be slightly less depending on what compression the .pck uses, but the worst case is that it uses double the space on the phone.

grymjack commented 4 years ago

@TGRCdev Once the app has created a copy of this database at runtime, does the database delete itself once the app closes? If this is the case, how would I connect to a full read/write database that will have persistent records added to it over the course of time? The database is very large and could grow up to 1GB+. If the user space read/write database remains after app restart, I could create an empty database, copy that, do a one-time import of the needed data and avoid the double resource problem and have a database that would grow over time. Is this feasible?

TGRCdev commented 4 years ago

@grymjack Files kept in user:// are maintained across sessions, so your database should be able to take read/write operations and save the data across sessions.

If possible, you could also maintain two database handles, one for your save data, and one for your read-only data. I don't have any knowledge of your database setup, but this would save copying 1GB of space.

I tried to see if there was a way to ATTACH databases stored in res:// to a database stored in user://, but I believe that is not possible due to res:// databases not having a "real" file path.

grymjack commented 4 years ago

@TGRCdev Is there a reason why I can't just assign the database to user:// space to begin with in the code and avoid the whole copy thing?

TGRCdev commented 4 years ago

@grymjack I can't really give a good answer on that, I'm not an expert in SQLite. Best I could say is that SQLite prefers to operate on whole, unsegmented databases, so splitting a single database into "read-only" and "writeable" is not allowed.

TGRCdev commented 4 years ago

One way to save space could be to compress the "clean slate" database using 7zip or another program with "Deflate" compression, then using File.open_compressed() and copying the bits into user://. The code for it would look like:

func _copy_save_db():
    var zipped = File.new()
    zipped.open_compressed("res://clean_db.zip", File.READ, File.COMPRESSION_DEFLATE)
    if zipped.is_open():
        var bytes = zipped.get_buffer(zipped.get_len()) # Uncompress the bytes into memory
        zipped.close()
        var out = File.new()
        out.open("user://save_db.db", File.WRITE) # Create a db file
        out.store_buffer(bytes) # Load the uncompressed database into the db file
        out.close() # Save the file
        # db is now uncompressed at "user://save_db.db"
grymjack commented 4 years ago

@TGRCdev Ok I got the app working. Looks like I have to come in with an empty database, copy it into user space, then do a one-time table creation and static data load. The database does persist during an app restart. Thanks for your help!!

Grosskopf commented 4 years ago

Hey, i would very much like to use this, should i just use the version at the mentioned PR or is there a reason this is not merged? :D

b3nnee commented 4 years ago

@TGRCdev I keep getting this on Godot 3.1 using the BETA binaries for android since I don't have 3.1.X. App cannot load the plugin on Android

2020-02-24 21:24:34.426 15699-15768/com.mygamesbyby.mmm E/godot: ERROR: No loader found for resource: res://lib/gdsqlite.gdns 2020-02-24 21:24:34.426 15699-15768/com.mygamesbyby.mmm E/godot: At: core/io/resource_loader.cpp:285:_load() - Method/Function Failed, returning: RES() 2020-02-24 21:24:34.426 15699-15768/com.mygamesbyby.mmm E/godot: SCRIPT ERROR: Parse Error: Can't preload resource at path: res://lib/gdsqlite.gdns 2020-02-24 21:24:34.426 15699-15768/com.mygamesbyby.mmm E/godot: At: res://configholder.gdc:130:GDScript::load_byte_code() - Parse Error: Can't preload resource at path: res://lib/gdsqlite.gdns 2020-02-24 21:24:34.426 15699-15768/com.mygamesbyby.mmm E/godot: ERROR: Method/Function Failed, returning: ERR_PARSE_ERROR

On Windows everything works great! What could be the cause?

Phone Android version: 9

TGRCdev commented 4 years ago

@Grosskopf Sorry for not responding. khairul hasn't been active on gdsqlite in a while, so I can't comment on why this hasn't been merged yet. I do maintain an up-to-date nightly build with additional improvements and as many platforms as I can compile for.

@b3nnee I'm currently looking into this error. The relevant error message "No loader found for resource: res://lib/gdsqlite.gdns" is coming from the ResourceLoader::_load method in core/io/resource_loader.cpp: https://github.com/godotengine/godot/blob/320f49f204cfbf9b480fe62aaa7718afb74920a5/core/io/resource_loader.cpp#L261-L287

RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error) {

    bool found = false;

    // Try all loaders and pick the first match for the type hint
    for (int i = 0; i < loader_count; i++) {

        if (!loader[i]->recognize_path(p_path, p_type_hint)) {
            continue;
        }
        found = true;
        RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error);
        if (res.is_null()) {
            continue;
        }

        return res;
    }

    if (found) {
        ERR_EXPLAIN("Failed loading resource: " + p_path);
    } else {
        ERR_EXPLAIN("No loader found for resource: " + p_path);
    }
    ERR_FAIL_V(RES());
    return RES();
}

For some reason, the loader for NativeScript resources isn't being loaded. If it was, it would return "Failed loading resource: res://lib/gdsqlite.gdns"

  1. What version of Godot are you using? (either stable version number, or commit hash if self-compiled)
  2. What does your project's file structure look like? Is gdsqlite/ installed under lib?
b3nnee commented 4 years ago

@TGRCDev, Thanks for your response. I believe I figured out why the error came up. My templates are self-built, I added some modules I created for a couple of things to it and I may have left out some Godot modules for size optimisations.

Solution: Either rebuild the templates or move my project to 3.2.

Please is your latest nightly build compatible with 3.2?

On Mon, 24 Feb 2020, 10:42 PM TGRCDev, notifications@github.com wrote:

@Grosskopf https://github.com/Grosskopf Sorry for not responding. khairul hasn't been active on gdsqlite in a while, so I can't comment on why this hasn't been merged yet. I do maintain an up-to-date nightly build https://github.com/TGRCdev/gdsqlite-native/releases/tag/nightly-3 with additional improvements and as many platforms as I can compile for.

@b3nnee https://github.com/b3nnee I'm currently looking into this error. The relevant error message "No loader found for resource: res://lib/gdsqlite.gdns" is coming from the ResourceLoader::_load method in core/io/resource_loader.cpp: https://github.com/godotengine/godot/blob/320f49f204cfbf9b480fe62aaa7718afb74920a5/core/io/resource_loader.cpp#L261-L287

RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error) {

bool found = false;

// Try all loaders and pick the first match for the type hint for (int i = 0; i < loader_count; i++) {

  if (!loader[i]->recognize_path(p_path, p_type_hint)) {
      continue;
  }
  found = true;
  RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error);
  if (res.is_null()) {
      continue;
  }

  return res;

}

if (found) { ERR_EXPLAIN("Failed loading resource: " + p_path); } else { ERR_EXPLAIN("No loader found for resource: " + p_path); } ERR_FAIL_V(RES()); return RES(); }

For some reason, the loader for NativeScript resources isn't being loaded. If it was, it would return "Failed loading resource: res://lib/gdsqlite.gdns"

  1. What version of Godot are you using? (either stable version number, or commit hash if self-compiled)
  2. What does your project's file structure look like? Is gdsqlite/ installed under lib?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/khairul169/gdsqlite-native/issues/11?email_source=notifications&email_token=ABOBLANDRJTJC3NGQ6MDK3LREQ5NBA5CNFSM4FCKPIW2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEMZUPUA#issuecomment-590563280, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABOBLAMMNPN3EMTLX4C47ATREQ5NBANCNFSM4FCKPIWQ .

TGRCdev commented 4 years ago

Yes. The nightly is compiled with NativeScript 1.1 bindings, so any Godot version 3.1 and above should work with them.

EDIT: The above information might not be accurate, but I have tested the nightly build on 3.2 stable and can confirm that it works.