samdze / playdate-nim

Nim bindings with extra features for the Playdate SDK
66 stars 3 forks source link

Scorbeoards #82

Closed ninovanhooff closed 1 month ago

ninovanhooff commented 1 month ago

https://help.play.date/catalog-developer/scoreboard-api/#pdscore

ninovanhooff commented 1 month ago

This PR serves as a PSA that this feature is being worked on.

I decided to create a branch on the main repo so it's easier to collaborate if needed

ninovanhooff commented 1 month ago

I got stuck calling the binding function with this error:

/Users/ninovanhooff/PlaydateProjects/playdate-nim/src/playdate/scoreboards.nim:31:80: error: incompatible function pointer types passing 'void (PDScore *, NCSTRING)' (aka 'void (PDScore *, char *)') to parameter of type 'PersonalBestCallback' (aka 'void (*)(PDScore *, const char *)') [-Wincompatible-function-pointer-types]
   31 |         nimln_(31);     result = (*this_p0).getPersonalBest(nimToCStringConv(boardID_p1), _ZN11scoreboards26invokePersonalBestCallbackE3ptrIN11scoreboards10PDScoreRawEE7cstring);      if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;    goto BeforeRet_;
      |                                                                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Error: execution of an external compiler program 'clang -c -w -ferror-limit=3 -DTARGET_EXTENSION=1 -Wall -Wno-unknown-pragmas -Wdouble-promotion -I/Users/ninovanhooff/Developer/PlaydateSDK/C_API -arch x86_64 -arch arm64 -DTARGET_SIMULATOR=1 -Wstrict-prototypes -g   -I/Users/ninovanhooff/.choosenim/toolchains/nim-2.0.10/lib -I/Users/ninovanhooff/PlaydateProjects/playdate-nim/playdate_example/src -o /Users/ninovanhooff/.cache/nim/playdate_example_d/simulator/@m..@s..@ssrc@splaydate@sscoreboards.nim.c.o /Users/ninovanhooff/.cache/nim/playdate_example_d/simulator/@m..@s..@ssrc@splaydate@sscoreboards.nim.c' failed with exit code: 1

From the example, I called it like this:

discard playdate.scoreboards.getPersonalBest(
            "hills",
            proc(score: PDScore, errorMessage: string) =
              discard
)
samdze commented 1 month ago

You may have to use ConstChar to fix this. There are a few examples in the codebase, here's one: https://github.com/samdze/playdate-nim/blob/1a752d3bbd0c10382b90091b032e76512e05445d/src/playdate/graphics.nim#L216-L222

ninovanhooff commented 1 month ago

That definetly worked.

I now have the issue that a call to addScore works on sim but crashes device, which makes it hard to debug. Even when I replace the entire content of invokeAddScoreCallback by discard, device still crashes. The crash happens a few seconds after calling addScore. In other words: most probably when the callback is called.

Edit: crash does not happen on 2.5.0 firmware; note that this crash is from fw 2.6.0-beta4

--- crash at 2024/10/19 19:41:52---
build:be97134e-2.6.0-beta.4-buildbot
   r0:600144b1    r1:00000001     r2:00000000    r3: 0043f0e9
  r12:00000000    lr:0805393f     pc:08055cd4   psr: 210f0000
 cfsr:01000000  hfsr:40000000  mmfar:00000000  bfar: 00000000
rcccsr:00000000
heap allocated: 2167168
ninovanhooff commented 1 month ago

Sample usage:

let resultCode = playdate.scoreboards.getPersonalBest(
            "hills",
            proc(score: PDScore, errorMessage: string) =
                if errorMessage.len > 0:
                    playdate.system.logToConsole("Error getting personal best: " & errorMessage)
                else:
                    playdate.system.logToConsole(fmt"Personal best: {score.value}")
        )

playdate.system.logToConsole("Result code: " & $resultCode) # printed immediately, before request is performed. Seems to always be 1.
ninovanhooff commented 1 month ago

@Nycto thanks for the review. I cleaned it up a bunch. Enjoy ;-p

ninovanhooff commented 1 month ago

@Nycto Thanks again fr the feedback.

I couldn't get this suggestion to compile. Can you fix it yourself?

https://github.com/samdze/playdate-nim/pull/82#discussion_r1816389114

Nycto commented 1 month ago

Alright -- I published a version that uses untyped instead of an explicit closure proc.

I also had another realization while making this change. For the callbacks, what we actually have here is an Either monad. How would you feel about updating the callbacks to be this:

type
    # ...
    PDResultKind* = enum PDResultSuccess, PDResultError

    PDResult*[T] = object
        case kind*: PDResultKind
        of PDResultSuccess: value: T
        of PDResultError: message: string

    PersonalBestCallback* = proc(result: PDResult[PDScore]) {.raises: [].}
    AddScoreCallback* = proc(result: PDResult[PDScore]) {.raises: [].}
    BoardsListCallback* = proc(result: PDResult[PDBoardsList]) {.raises: [].}
    ScoresCallback* = proc(result: PDResult[PDScoresList]) {.raises: [].}
Nycto commented 1 month ago

I decided to just make the change and push so you can see what it looks like in context. If you prefer the dual argument version let me know and I'll pop this commit off the branch.

ninovanhooff commented 1 month ago

Looks good to me from my phone, will have another look tomorrow. Anything you want to see changed? Otherwise please approve it and I'll merge it

Nycto commented 1 month ago

Done ✔️