Giter VIP home page Giter VIP logo

flutter_nakama's Introduction

Nakama Dart/Flutter Client

Nakama is an open-source server designed to power modern games and apps. Features include user accounts, chat, social, matchmaker, realtime multiplayer, and much more.

This is a Flutter client for Nakama written in pure Dart and supports cross platform gaming on iOS, Android, Web and more.

GitHub issues GitHub last commit Pub Version


πŸš€ Getting Started

You'll need to setup the server and database before you can connect with the client. The simplest way is to use Docker but have a look at the server documentation for other options.

Installing the SDK

  1. To use an official release, download source from the releases page and import it into your project.

  2. Add flutter_nakama to your pubspec.yaml:

name: your_game
dependencies:
    flutter:
        sdk: flutter
    nakama: ^1.0.0-dev.6
  1. Use the connection credentials to build a client object:
final client = getNakamaClient(
  host: '127.0.0.1',
  ssl: false,
  serverKey: 'defaultkey',
  grpcPort: 7349, // optional
  httpPort: 7350, // optional
);

Usage

The client object has many methods to execute various features in the server or open realtime socket connections with the server.

Authenticate

There's a variety of ways to authenticate with the server. Authentication can create a user if they don't already exist with those credentials. It's also easy to authenticate with a social profile from Google Play Games, Facebook, Game Center, etc.

final session = await getNakamaClient().authenticateEmail(
    email: '[email protected]',
    password: 'mySecurePassword!',
);

print('Hey, you are logged in! UserID: ${session.userId}');

Sessions

When authenticated the server responds with an auth token (JWT) which contains useful properties and gets deserialized into a Session object.

session.AuthToken // raw JWT token
session.UserId // User ID
session.Username // Username
session.IsExpired // Boolean indicating session status
session.ExpireTime // in seconds.

It is recommended to store the auth token from the session and check at startup if it has expired. If the token has expired you must reauthenticate. The expiry time of the token can be changed as a setting in the server.

final inOneHour = DateTime.now().add(Duration(hours: 1));

// Check whether a session has expired or is close to expiry.
if (session.isExpired || session.hasExpired(inOneHour)) {
    try {
        // Attempt to refresh the existing session.
        session = await client.sessionRefresh(session);
    } catch (e) {
        // Couldn't refresh the session so reauthenticate.
        session = await client.authenticateDevice(deviceId: deviceId);
    }
}

Requests

The client includes lots of builtin APIs for various features of the game server. These can be accessed with the async methods. It can also call custom logic as RPC functions on the server. These can also be executed with a socket object.

All requests are sent with a session object which authorizes the client.

final account = await client.getAccount(session);
final username = account.user.username;
final avatarUrl = account.user.avatarUrl;
final userId = account.user.id;

Socket

The client can create one or more sockets with the server. Each socket can have it's own event listeners registered for responses received from the server.

NakamaWebsocketClient.init(
    host: '127.0.0.1',
    ssl: false,
    token: _session.token,
);

Remember to close the connection after disposing of the app widget:

NakamaWebsocketClient.instance.close();

Documentation

Dart/Flutter SDK Docs: https://heroiclabs.com/docs/nakama/client-libraries/dart/

Nakama Docs: https://heroiclabs.com/docs/nakama/

Special Thanks

Thanks to Oliver Brunsmann (@obrunsmann), Faiad Sufyan (@fsufyan) for their excellent contribution and maintenance of this library.

flutter_nakama's People

Contributors

aminalaee avatar fbernaly avatar fsufyan avatar garlen-javier avatar gpene avatar ilmalte avatar lugehorsam avatar mofirouz avatar obrunsmann avatar zhangyc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter_nakama's Issues

The rpc function can not work right.

Describe the bug
The rpc function does not work.

Reproduction Code
The rpc function in the nakama_grpc_client.dart

  @override
  Future<String?> rpc({
    required model.Session session,
    required String id,
    String? payload,
  }) async {
    final res = await _client.rpcFunc(api.Rpc(
      id: id,
      payload: payload,
    ));

    return res.payload;
  }

Above rpcFunc called in the rpc function, but the session was not used. So the server can not response rightly.
We found the rpc in old version of the repository can work, but the new version does not work.

Expected behavior
The rpc function is useful and expect to work rightly.

ChannelMessage not display on Nakama Console (update: not issue or bug, it's a UI operational mistake)

[update]
This is not a issue or bug.
For Nakama Console, there are no messages show up by default, I have to Search with any filter or keyword.
1234

The event of websocket listener was able to trigger while the sendMessage excuted and also able to get messageId from ChannelMessageAck but the message seems not displayed on Nakama Console.

(btw, match creation was able to show on Nakama Console)

A part of code

/// joinChannel
myGameController.currentChannel = await NakamaWebsocketClient.instance.joinChannel(target: myGameController.channelName, type: ChannelJoinType.room, persistence: true, hidden: false);

/// sendMessage
Map<String, String> content = {"hello": "world"};
ChannelMessageAck? ack =
        await NakamaWebsocketClient.instance.sendMessage(channelId: myGameController.currentChannel!.id, content: content);

print(ack.toString());
/// ChannelMessageAck(channelId: 2...globalChannel, messageId: 822ca914-75cb-4e8d-9315-7ea32bcb6fd4, code: 0, username: 123456, created: 2022-12-07 16:00:05.000Z, updated: 2022-12-07 16:00:05.000Z, persistent: true, roomName: globalChannel, groupId: , userIdOne: , userIdTwo: )

pubspec.yaml

nakama: ^1.0.0-dev.2

10

client.listGroups is not work

Describe the bug
I can see the group I created at http://127.0.0.1:7351/#/groups. But when I query, the group[] returned by the client is empty
Reproduction Code
Note: This is mandatory! If your issue does not contain it, it will be closed.

Example:

import 'package:flutter/material.dart';
import 'package:my_anime/utils/nakama_util.dart';
import 'package:nakama/nakama.dart';

class GroupListPage extends StatefulWidget {
  const GroupListPage({super.key});
  static const routeName = 'group_list';

  @override
  State<GroupListPage> createState() => _GroupListState();
}

class _GroupListState extends State<GroupListPage> {
  TextEditingController _groupNameController = TextEditingController();
  TextEditingController _groupDescriptionController = TextEditingController();

  // ζ ‘ιͺŒηΎ€εη§°
  String? _validateGroupName(String? value) {
    if (value==null) {
      return 'Group Name is required';
    }
    if (value.isEmpty) {
      return 'Group Name is required';
    }
    // 可δ»₯ζ·»εŠ ε…Άδ»–ζ ‘ιͺŒι€»θΎ‘
    return null;
  }

  // ζ ‘ιͺŒηΎ€ζθΏ°
  String? _validateGroupDescription(String? value) {
    if (value==null) {
      return 'Group Name is required';
    }
    if (value.isEmpty) {
      return 'Group Description is required';
    }
    // 可δ»₯ζ·»εŠ ε…Άδ»–ζ ‘ιͺŒι€»θΎ‘
    return null;
  }
  List<Group> groups=[];
  @override
  void initState() {
    getGroupList().then((value){
      groups=value.groups;
      setState(() {

      });
    });
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Group'),
      ),
      floatingActionButton: ElevatedButton(onPressed: ()async{
         Group? g=await _showGroupDialog(context);
         if(g!=null){
           getGroupList().then((value){
             groups=value.groups;
             setState(() {

             });
           });
         }
       }, child: Icon(Icons.add)),
      body: ListView.separated(itemBuilder: (_,i){
        return ListTile(
          title: Text(groups[i].name??'',style: TextStyle(
            color: Colors.black
          ),),
        );
      }, separatorBuilder: (_,__){
        return SizedBox(
          height: 16,
        );
      }, itemCount: groups.length),

    );
  }
  Future<Group?> _showGroupDialog(BuildContext context) async {
    return showDialog<Group?>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Create Group'),
          content: Column(
            children: [
              // 羀名称输ε…₯摆
              TextFormField(
                controller: _groupNameController,
                decoration: InputDecoration(labelText: 'Group Name'),
                validator: _validateGroupName,
              ),
              SizedBox(height: 16.0),
              // 羀描述输ε…₯摆
              TextFormField(
                controller: _groupDescriptionController,
                decoration: InputDecoration(labelText: 'Group Description'),
                validator: _validateGroupDescription,
              ),
            ],
          ),
          actions: <Widget>[
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: Text('Cancel'),
            ),
            TextButton(
              onPressed: () async{
                // ιͺŒθ―ι€šθΏ‡ζ‰ζ‰§θ‘Œζ“δ½œ
                if (_validateGroupName(_groupNameController.text) == null &&
                    _validateGroupDescription(_groupDescriptionController.text) == null) {
                  print('Group Name: ${_groupNameController.text}');
                  print('Group Description: ${_groupDescriptionController.text}');
                  Group group=await createGroup(_groupNameController.text, _groupDescriptionController.text);
                  Navigator.of(context).pop(group);
                }
              },
              child: Text('Create'),
            ),
          ],
        );
      },
    );
  }
  @override
  void dispose() {
    _groupNameController.dispose();
    _groupDescriptionController.dispose();
    super.dispose();
  }
}
///ζŸ₯θ―’ηΎ€εˆ—θ‘¨
Future<GroupList> getGroupList({String? filters,int? pageSize}) async{
 return await client.listGroups(
    session: userSession!,
    limit: pageSize??20,
  );
}

Expected behavior
You can return to the group list
Screenshots
image

Flutter Doctor

[βœ“] Flutter (Channel stable, 3.16.0, on macOS 14.1.1 23B81 darwin-arm64, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    βœ— cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    βœ— Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[βœ“] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[βœ—] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[βœ“] Android Studio (version 2022.3)
[βœ“] VS Code (version 1.85.1)
[βœ“] VS Code (version 1.85.1)
[βœ“] Connected device (3 available)
[!] Network resources
    βœ— A cryptographic error occurred while checking "https://pub.dev/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.
    βœ— A cryptographic error occurred while checking "https://storage.googleapis.com/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.
    βœ— A cryptographic error occurred while checking "https://maven.google.com/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.
    βœ— A cryptographic error occurred while checking "https://github.com/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.

! Doctor found issues in 3 categories.

pubspec.yaml

nakama: ^1.0.4

Additional context
Add any other context about the problem here.

Leaderboards API

  • List by score
  • List by friends
  • List leaderboard records around owner
  • Submit a score

After send data in socket method dart stops executing next method lines

Describe the bug
After send data to socket systems stop executing method lines without throws any error

Reproduction Code

Example:

Future<void> sendData() async {

 print('befor send data');
    await socket!.sendMatchData(
      matchId: match.matchId,
      opCode: Int64(data.opCode),
      data: jsonEncode({}).codeUnits,
    );

    print("after send data");

Expected behavior
print("after send data"); should be called

Screenshots
Captura de Tela 2023-11-19 aΜ€s 14 30 18

Flutter Doctor

pubspec.yaml

Additional context

getAccount crashing with device authentication

Just to track this, if somebody experiencing issue with that by chance.

getAccount currently not works in conjunction with device authentication.

Already fixed in develop branch for next release.

pub.dev not recognizing web support

Something is failing web support test, though it works with the web compiler:

package:nakama/nakama.dart that imports:
package:nakama/src/nakama_client.dart that imports:
package:nakama/src/nakama_grpc_client.dart that imports:
package:grpc/grpc.dart that imports:
package:grpc/src/shared/streams.dart that imports:
package:http2/transport.dart that imports:
package:http2/src/hpack/hpack.dart that imports:
package:http2/src/hpack/huffman_table.dart that imports:
package:http2/src/hpack/huffman.dart that imports:
dart:io

How to communicate with authoritative multiplayer service?

I created custom match handlers in the backend part for authoritative multiplayer service. The module was loaded successfully but the handlers were not called when I sent the match data through sendMatchData(). As I checked in the official docs (JS SDK), there is sendMatchState() which can communicate through the custom match handlers. I'm not sure the latest version supports this multiplayer type or do I need call other functions for this.

Custom Handler Scripts
match_handler.js

const moduleName: string = "test";
const tickRate: number = 2;

let matchInit: nkruntime.MatchInitFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  params: { [key: string]: string }
) {
  let state: nkruntime.MatchState = {
    presences: {},
  };
  let label = "";
  logger.info("MATCH INITIALIZED :  matchInit CALLED ");
  return {
    state,
    tickRate,
    label,
  };
};

let matchJoinAttempt: nkruntime.MatchJoinAttemptFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  dispatcher: nkruntime.MatchDispatcher,
  tick: number,
  state: nkruntime.MatchState,
  presence: nkruntime.Presence,
  metadata: { [key: string]: any }
) {
  logger.debug("matchJoinAttempt received: ");
  return {
    state: state,
    accept: true,
  };
};

let matchJoin: nkruntime.MatchJoinFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  dispatcher: nkruntime.MatchDispatcher,
  tick: number,
  state: nkruntime.MatchState,
  presences: nkruntime.Presence[]
) {
  logger.debug("matchJoin received: ");
  return { state };
};

let matchLeave: nkruntime.MatchLeaveFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  dispatcher: nkruntime.MatchDispatcher,
  tick: number,
  state: nkruntime.MatchState,
  presences: nkruntime.Presence[]
) {
  logger.debug("matchLeave received: ");
  return { state };
};

let matchLoop: nkruntime.MatchLoopFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  dispatcher: nkruntime.MatchDispatcher,
  tick: number,
  state: nkruntime.MatchState,
  messages: nkruntime.MatchMessage[]
) {
  logger.debug("matchLoop received: ");
  return { state };
};

let matchTerminate: nkruntime.MatchTerminateFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  dispatcher: nkruntime.MatchDispatcher,
  tick: number,
  state: nkruntime.MatchState,
  graceSeconds: number
) {
  logger.debug("matchTerminate received: ");
  return { state };
};

const matchSignal = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  dispatcher: nkruntime.MatchDispatcher,
  tick: number,
  state: nkruntime.MatchState,
  data: string
): { state: nkruntime.MatchState; data?: string } | null {
  logger.debug("Lobby match signal received: " + data);

  return {
    state,
    data: "Lobby match signal received: " + data,
  };
};

let makeMatch: nkruntime.MatchmakerMatchedFunction = function (
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  matches: nkruntime.MatchmakerResult[]
): string | void {
  logger.info("MATCH CREATE IS BEING CALLED");
  let match_id: string = nk.matchCreate(moduleName);
  return match_id;
};

main.js

// Copyright 2020 The Nakama Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const rpcIdRewards = "rewards_js";
const rpcIdFindMatch = "find_match_js";

function InitModule(
  ctx: nkruntime.Context,
  logger: nkruntime.Logger,
  nk: nkruntime.Nakama,
  initializer: nkruntime.Initializer
) {
  initializer.registerMatch(moduleName, {
    matchInit,
    matchJoinAttempt,
    matchJoin,
    matchLeave,
    matchLoop,
    matchTerminate,
    matchSignal,
  });

  initializer.registerMatchmakerMatched(makeMatch);

  logger.info("JavaScript logic loaded.");
}

Server Logs

backend           | {"level":"info","ts":"2023-03-20T14:57:06.370Z","caller":"server/runtime_javascript_logger.go:74","msg":"JavaScript logic loaded."}
backend           | {"level":"debug","ts":"2023-03-20T14:58:43.892Z","caller":"server/pipeline.go:65","msg":"Received *rtapi.Envelope_MatchDataSend message","uid":"f1bdd8c7-3e22-4843-b1fd-30910c3909ee","sid":"938d4f54-c72f-11ed-9cac-006100a0eb06","cid":"3","message":{"MatchDataSend":{"match_id":"18c5582b-34bd-478c-b4ac-b2df38d3bb91.","op_code":1,"data":"ZmZmZmY="}}}
backend           | {"level":"debug","ts":"2023-03-20T15:21:48.726Z","caller":"server/pipeline.go:65","msg":"Received *rtapi.Envelope_MatchDataSend message","uid":"f1bdd8c7-3e22-4843-b1fd-30910c3909ee","sid":"938d4f54-c72f-11ed-9cac-006100a0eb06","cid":"4","message":{"MatchDataSend":{"match_id":"18c5582b-34bd-478c-b4ac-b2df38d3bb91.","op_code":1,"data":"d3d3dw=="}}}

Add getters to session object

Unity SDK provides isExpired getter and hasExpired(timestamp) method to check if a session will have been expired on the given date. Should add this to dart sdk.

Implement custom exceptions

Unity SDK throws an ApiResponseException in case there is any issue with the API response including StatusCode and Message. I guess it
would be nice to also group all API Exceptions into one SDK owned class which can be handeled by the user:

  • HTTP socket exception
  • Request timeout
  • Abnormal status code
  • Runtime errors

ChannelMessage in models is not exported

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Tournaments API

  • List tournaments
  • Join tournament
  • List tournament records
  • List tournament records around owner
  • Write tournament record

gRPC Error (code: 5, codeName: NOT_FOUND, message: RPC function not found, details: [], rawResponse: null, trailers: {})

Describe the bug
When I use final response = await client.rpc(
session: userSession!,
id: targetId,
payload: jsonEncode(payload),
); when sending a message
response:
gRPC Error (code: 5, codeName: NOT_FOUND, message: RPC function not found, details: [], rawResponse: null, trailers: {})
Reproduction Code
Note: This is mandatory! If your issue does not contain it, it will be closed.

Example:

When I use final response = await client.rpc(
       session: userSession!,
       id: targetId,
       payload: jsonEncode(payload),
     ); when sending a message

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Flutter Doctor

Doctor summary (to see all details, run flutter doctor -v):
[βœ“] Flutter (Channel stable, 3.16.5, on macOS 14.1.1 23B81 darwin-arm64, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    βœ— cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    βœ— Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[βœ“] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[βœ“] Chrome - develop for the web
[βœ“] Android Studio (version 2022.3)
[βœ“] IntelliJ IDEA Ultimate Edition (version 2023.3.2)
[βœ“] VS Code (version 1.85.1)
[βœ“] VS Code (version 1.85.1)
[βœ“] Connected device (4 available)
[βœ“] Network resources

! Doctor found issues in 1 category.

pubspec.yaml

nakama: ^1.0.5

Additional context
Add any other context about the problem here.

Authentication suddenly stops working

Authentication with Nakama server using SDK is not working and throwing this error in response:

flutter: gRPC Error (code: 2, codeName: UNKNOWN, message: HTTP/2 error: Connection error: Connection is being forcefully terminated. (errorCode: 10), details: null, rawResponse: null)

Future<bool> authenticateWithCustomAsync(
    BuildContext context, LoginDetails loginDetails) async {
  bool result = false;
  try {
    await getNakamaClient()
        .authenticateCustom(
            id: loginDetails.socialUserID!,
            username: loginDetails.socialUserName!.replaceAll(" ", ""))
        .then((session) {
      nakamaSession = session;
      result = true;
    });
  } catch (e) {
    hideLoading(context);
    print(e.hashCode);
    print(e.toString());
    result = false;
  }

rpc on websocket don't work

Describe the bug
when I make a rpc call using websocket, I can see in the server log that I have the request and the nswer is sent but the flutter app never receive it.

Reproduction Code
Note: This is mandatory! If your issue does not contain it, it will be closed.

Example:

    final result1 = await socketClient.rpc(id: 'hello_world_rpc');


    print(result1.payload); // Devrait afficher "Hello, World!"
const helloWorldRpc: nkruntime.RpcFunction = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: string): string {
  return "Hello, World!";
}

Expected behavior
see "hello world " in the flutter debugconsole

Organize generated gRPC client

  • Publish model files from the library β†’ No imports of the .pb.dart files should be necessary from outside the package
  • Remove unused generated files (some unit test files have been generated as well from google/protobuf package)

Methods in NakamaGrpcClient lack of options

Describe the bug
grpc :invalid token

Reproduction Code
options: _getSessionCallOptions(session),

Example:

   Future<model.FriendsList> listFriends({
    required model.Session session,
    FriendshipState? friendshipState,
    int limit = defaultLimit,
    String? cursor,
  }) async {
    final res = await _client.listFriends(
      api.ListFriendsRequest(
        cursor: cursor,
        limit: api.Int32Value(value: limit),
        state: api.Int32Value(value: friendshipState?.index),
      ),
    );

    return model.FriendsList.fromDto(res);
  }

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Flutter Doctor


pubspec.yaml

Additional context
Add any other context about the problem here.

The rpc function can not work right.

Describe the bug
The rpc function does not work.

Reproduction Code
The rpc function in the nakama_grpc_client.dart

  @override
  Future<String?> rpc({
    required model.Session session,
    required String id,
    String? payload,
  }) async {
    final res = await _client.rpcFunc(api.Rpc(
      id: id,
      payload: payload,
    ));

    return res.payload;
  }

Above rpcFunc called in the rpc function, but the session was not used. So the server can not response rightly.
We found the rpc in old version of the repository can work, but the new version does not work.

Expected behavior
The rpc function is useful and expect to work rightly.

onMatchData doesn't trigger when sending data

hello,

I've tried on a new project and I was not able to receive the data listening on onMatchData.
I can see the server receive the input.

Could be many reason:
The data is not sent to the client
The client doesn't receive the data
The stream is not working
...

Authentication issues

Hi there! Thanks for the useful package, unfortunately I'm running into two issues. Firstly, the timestamps for the session expiry seem to be reported incorrectly (at some point in 1970), I believe this is because in session.dart fromMillisecondsSinceEpoch is being called on the decoded token which stores the token in seconds since epoch. Whilst this can be worked around by changing the arguments to (token['exp'] as int) * 1000 in every line where this function is called in the file, I am still getting the message "Auth token invalid" when using any function with the session object (for example addFriends). I'm not sure if this is an issue with the package or the server I'm running, but I thought it could be relevant or maybe caused by the first issue.

I used the official docs and logged using email and then attempted to add a friend to the account using the following:

session = await client.authenticateEmail(
    username: "example",
    password: "example",
);
print(session.expiresAt);
await client.addFriends(
    usernames: ["test"],
    session: session,
);

This gives the following output:

flutter: 1970-01-20 15:29:28.873
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: gRPC Error (code: 16, codeName: UNAUTHENTICATED, message: Auth token invalid, details: [], rawResponse: null, trailers: {})

After applying the fix I mentioned above the session is reported as expiring at the correct date, but the second error still occurs.

I am using flutter 3.13.0 and the latest version of this ibrary on pub.dev, and the latest docker image of nakama. Thank you in advance!

Upgrading swagger & chopper client

There is a new swagger file available for nakama's REST API: https://github.com/heroiclabs/nakama/blob/master/apigrpc/apigrpc.swagger.json

Problem:
There some chopper updates which are currently making it impossible to just generate a new client. For some reason all new generated methods getting prefixed with the path which should be a chopper config.

Bildschirmfoto 2022-03-31 um 14 23 58

When I started working on this project I heavily thought about using chopper or writing REST models by hand. Supporting REST + gRPC in parallel for this package is some kind of a challenge. Now unfortunately there is the downside of this approach, that needs a fix for the chopper config.

I think I would be able to check this on weekend, but cannot promise within just a few hours here.

Name filter doesn't work in ListGroups method

Describe the bug
If I pass only the parameters session and name to the listGroups method, the backend throws an exception saying:
GrpcError (gRPC Error (code: 3, codeName: INVALID_ARGUMENT, message: name filter cannot be combined with any other filter, details: [], rawResponse: null, trailers: {}))

Reproduction Code
Example:

 var listGroups = await NakamaClient.instance!.listGroups(
        session: session,
        name: "everyone",
      );

Expected behavior
I expect to retrieve a list of groups, filtered based on the name passed as filter.

Additional context
Considering the error message, it seems like only the name should be passed as filter and cannot be combined.
By using the API explorer through the console interface of Nakama, the endpoint works fine when the name only is passed (and the others are null).
I expect this issue to be in both client files: nakama_grpc_client.dart and nakama_api_client.dart

  @override
  Future<model.GroupList> listGroups({
    required model.Session session,
    String? name,
    String? cursor,
    String? langTag,
    int? members,
    bool? open,
    int limit = defaultLimit,
  }) async {
   final res = await _client.listGroups(
      api.ListGroupsRequest(
        name: name,
        cursor: cursor,
        langTag: langTag,
        limit: api.Int32Value(value: limit),
        members: api.Int32Value(value: members),
        open: api.BoolValue(value: open),
      );
      options: _getSessionCallOptions(session),
    );

    return model.GroupList.fromDto(res);
  }

I also tried to manually pass null to all the parameters but in the name and I confirm this is the behavior the backend expects.

Notification fromDTO missing the subject + content

Describe the bug
When we are listening the notifications from the socket, the subject + content is being null, because fromDTO doesn't parse them.

https://github.com/heroiclabs/nakama-dart/blob/main/lib/src/models/notification.dart#L24-L30

Reproduction Code

socket
        .onNotifications
        .listen((notification) {
      print(notification.subject);
      print(notification.content);
    });

It'll give the null values.

Expected behavior
Subject and content are filled with the values.

Solution:

  factory Notification.fromDto(api.Notification dto) => Notification(
        id: dto.id,
        code: dto.code,
        subject: dto.subject,
        content: dto.content,
        senderId: dto.senderId,
        createTime: dto.createTime.toDateTime(),
        persistent: dto.persistent,
      );

Get response from RPC call

Hello,

I have an RPC call on the server, how to get the data back from the server, i.e the response payload?

match_test test case execution error

Describe the bug
match_test test case execution error

Reproduction Code
Note: This is mandatory! If your issue does not contain it, it will be closed.

Example:

 // Create nakama clients.
    final client = getNakamaClient(
      host: kTestHost,
      ssl: false,
      serverKey: kTestServerKey,
    );

    sessionA = await client.authenticateEmail(
      email: faker.internet.freeEmail(),
      password: faker.internet.password(),
    );

    // Create main websocket connetion for lcl test.
    NakamaWebsocketClient.init(
      host: kTestHost,
      ssl: false,
      token: sessionA.token,
    );

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Flutter Doctor

[βœ“] Flutter (Channel stable, 3.16.8, on macOS 14.1.1 23B81 darwin-arm64, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    βœ— cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    βœ— Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[βœ“] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[βœ“] Chrome - develop for the web
[βœ“] Android Studio (version 2022.3)
[βœ“] IntelliJ IDEA Ultimate Edition (version 2023.3.2)
[βœ“] VS Code (version 1.85.1)
[βœ“] VS Code (version 1.85.1)
[βœ“] Connected device (4 available)
    ! Error: Browsing on the local area network for lj ιƒ­. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
[βœ“] Network resources

! Doctor found issues in 1 category.

pubspec.yaml

Additional context
Add any other context about the problem here.

Overview: Missing features

I joined three groups, but I can only query one.

Describe the bug
I joined three groups, but only one can be retrieved.
I can see in the console that the user is indeed a member of the group.

Reproduction Code
Note: This is mandatory! If your issue does not contain it, it will be closed.

Example:

Future<UserGroupList> listUserGroups({String? filters,int? pageSize}) async{
  return await client.listUserGroups(session: userSession!,userId: userSession!.userId);
}

Expected behavior
I can query all the groups that have been joined
Screenshots
If applicable, add screenshots to help explain your problem.

Flutter Doctor

[βœ“] Flutter (Channel stable, 3.16.5, on macOS 14.1.1 23B81 darwin-arm64, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    βœ— cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    βœ— Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[βœ“] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[βœ“] Chrome - develop for the web
[βœ“] Android Studio (version 2022.3)
[βœ“] IntelliJ IDEA Ultimate Edition (version 2023.3.2)
[βœ“] VS Code (version 1.85.1)
[βœ“] VS Code (version 1.85.1)
[βœ“] Connected device (4 available)
    ! Error: Browsing on the local area network for lj ιƒ­. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
[βœ“] Network resources

! Doctor found issues in 1 category.

pubspec.yaml

Additional context
Add any other context about the problem here.

Detecting socket disconnect

Hello,

I am running into an issue where the user gets disconnected from the socket either due to idle time or server error. I am not able to find a way to detect that the socket connection is lost and make an attempt to reconnect.
Can you let me know how and where to listen for socket disconnect event and can I simply reconnect using the same NakamaWebsocketClient.init method at that point?

The rpc method in the websocket client returns null

Hello, I'm facing some issues with rpc.

I try to call the rpc async method from the websocket client the Future returns null on every call;
In the server logs I can see that the method has been called.

Can you get a look and tell me what I am doing wrong?

Screen Shot 2021-07-17 at 1 06 45 AM

Screen Shot 2021-07-17 at 1 06 14 AM

Screen Shot 2021-07-17 at 1 06 04 AM

Screen Shot 2021-07-17 at 1 05 34 AM

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.