heroiclabs / nakama-cpp

Generic C/C++ client for Nakama server.
https://heroiclabs.com/docs/cpp-client-guide
Apache License 2.0
69 stars 25 forks source link

[NRtClient::onTransportMessage] NRtError: BAD_INPUT #24

Closed stonekase closed 4 years ago

stonekase commented 4 years ago

Description

Error thrown by CPP client

Steps to Reproduce

Create a Nakama CPP client on xcode

then add to matchmaker

void joinMatcher(const int32_t& minCount,const int32_t& maxCount,const string& query, NStringMap stringProperties,NStringDoubleMap numericProperties){

auto successCallback = [](const NMatchmakerTicket& ticket)
 {
   std::cout << "Matchmaker ticket: " << ticket.ticket << std::endl;
 };

 //int32_t minCount = 2;
// int32_t maxCount = 4;
 //string query = "*";
 //NStringMap stringProperties;
// NStringDoubleMap numericProperties;
 //stringProperties.emplace("region", "africa");
 //numericProperties.emplace("rank", 8.0);
std::cout << "minCount: " << minCount << std::endl;

 _rtClient->addMatchmaker(
     minCount,
     maxCount,
     query,
     stringProperties,
     numericProperties,
     successCallback);

_listener.setMatchmakerMatchedCallback([](NMatchmakerMatchedPtr matched)
{
  std::cout << "Matched! matchId: " << matched->matchId << std::endl;
});

}

Expected Result

[NRtClient::onTransportMessage] NRtError: BAD_INPUT

Actual Result

[NRtClient::updateStatus] ... [NWebsocketCppRest::send] sending 31 bytes binary ... minCount: 2 [NRtClient::addMatchmaker] ... [NWebsocketCppRest::send] sending 6 bytes binary ... [NWebsocketCppRest::operator()] socket message received 95 bytes [NWebsocketCppRest::operator()] socket message received 3 bytes Status updated [NWebsocketCppRest::operator()] socket message received 211 bytes [NWebsocketCppRest::operator()] socket message received 44 bytes [NRtClient::onTransportMessage] NRtError: BAD_INPUT Invalid minimum count, must be >= 2 [NWebsocketCppRest::operator()] ping

Context

Unity Unreal Other Your Environment

Nakama: X.X.X Database: X.X.X Environment name and version: Operating System and version: Nakama 2.7.0 I am using : Mac OS Catalina Model Name: MacBook Pro Model Identifier: MacBookPro15,1 Processor Name: 6-Core Intel Core i7 Processor Speed: 2.6 GHz Number of Processors: 1 Total Number of Cores: 6 L2 Cache (per Core): 256 KB L3 Cache: 9 MB Hyper-Threading Technology: Enabled Memory: 16 GB Boot ROM Version: 1037.40.124.0.0 (iBridge: 17.16.11081.0.0,0) Serial Number (system): C02XHCN8JG5J Hardware UUID: B43B190C-C4C5-58B3-9BE4-0CF3FB691206 Activation Lock Status: Enabled

Dimon4eg commented 4 years ago

@stonekase I cannot reproduce the issue. What client version do you use?

stonekase commented 4 years ago

@stonekase I cannot reproduce the issue. What client version do you use?

I am Using client version 2.2.0

stonekase commented 4 years ago
#include "nakama-cpp/Nakama.h"
#include <iostream>
#include <thread>

using namespace Nakama;
using namespace std;

class NakamaSessionManager
{
public:
    NakamaSessionManager()
    {
        NClientParameters parameters;

        // set server end point
        parameters.serverKey = "defaultkey";
        parameters.host      = "127.0.0.1";
        parameters.port      = DEFAULT_PORT;

        _client = createDefaultClient(parameters);
    }

    void start(const string& deviceId)
    {
        // to do: read session token from your storage
        string sessionToken;

        if (!sessionToken.empty())
        {
            // Lets check if we can restore a cached session.
            auto session = restoreSession(sessionToken);

            if (!session->isExpired())
            {
                // Session was valid and is restored now.
                _session = session;
                cout << "restored session with token: " << session->getAuthToken() << endl;
                onAuthenticated();
                return;
            }
        }

        auto successCallback = [this](NSessionPtr session)
        {
            // to do: save session token in your storage
            _session = session;
            cout << "session token: " << session->getAuthToken() << endl;
            onAuthenticated();

        };

        auto errorCallback = [](const NError& error)
        {
            cout << "session token: " << error.message << endl;
        };

        _client->authenticateDevice(deviceId, opt::nullopt, opt::nullopt, {}, successCallback, errorCallback);
    }

    void tick()
    {
        _client->tick();
        if (_rtClient) _rtClient->tick();
    }
    void sendActions(const NRtClientPtr& _rtClient,const string& matchId, const int64_t& opCode, const NBytes& data){
        _rtClient->sendMatchData(matchId, opCode, data);
    }

    void joinMatcher(const int32_t& minCount,const int32_t& maxCount,const string& query,
                     NStringMap stringProperties,NStringDoubleMap numericProperties){

        auto successCallback = [](const NMatchmakerTicket& ticket)
         {
           std::cout << "Matchmaker ticket: " << ticket.ticket << std::endl;
         };

         //int32_t minCount = 2;
        // int32_t maxCount = 4;
         //string query = "*";
         //NStringMap stringProperties;
        // NStringDoubleMap numericProperties;
         //stringProperties.emplace("region", "africa");
         //numericProperties.emplace("rank", 8.0);
        std::cout << "minCount: " << minCount << std::endl;

         _rtClient->addMatchmaker(
             minCount,
             maxCount,
             query,
             stringProperties,
             numericProperties,
             successCallback);

        _listener.setMatchmakerMatchedCallback([](NMatchmakerMatchedPtr matched)
        {
          std::cout << "Matched! matchId: " << matched->matchId << std::endl;
        });
    }

    void onAuthenticated()
    {
        auto successCallback = [this](const NAccount& account)
        {
            cout << "account user id: " << account.user.id << endl;
            cout << "account user created at: " << account.user.createdAt << endl;
        };

        auto errorCallback = [](const NError& error)
        {
        };

        _client->getAccount(_session, successCallback, errorCallback);

        // create real-time client (will use websocket transport)
        _rtClient = _client->createRtClient();
        _listener.setConnectCallback([this]()
        {
            cout << "Socket connected" << endl;
            _rtClient->updateStatus("EPlaying Whotfire !!!", []()
            {
                cout << "Status updated" << endl;
            });
            joinMatcher(2,4,"*",{},{});
        });
        _rtClient->setListener(&_listener);
        _rtClient->connect(_session, true);
    }

protected:
    NClientPtr _client;
    NSessionPtr _session;
    NRtClientPtr _rtClient;
    NRtDefaultClientListener _listener;
};

int main()
{
    // enable debug logs to console
    NLogger::initWithConsoleSink(NLogLevel::Debug);

    NakamaSessionManager sessionManager;

    sessionManager.start("mytestdevice0001");

    chrono::milliseconds sleep_period(50);

    // run main loop
    while (true)
    {
        sessionManager.tick();

        this_thread::sleep_for(sleep_period);
    }
}
stonekase commented 4 years ago

@Dimon4eg the above is the sample class that reproduces the error.

Dimon4eg commented 4 years ago

Your code works for me on Windows and Mac. Tested with server 2.7.0. Logs:

[NRtClient::updateStatus] ...
[NWebsocketCppRest::send] sending 31 bytes binary ...
minCount: 2
[NRtClient::addMatchmaker] ...
[NWebsocketCppRest::send] sending 13 bytes binary ...
[NWebsocketCppRest::operator ()] socket message received 95 bytes
[NWebsocketCppRest::operator ()] socket message received 211 bytes
[NWebsocketCppRest::operator ()] socket message received 3 bytes
Status updated
[NWebsocketCppRest::operator ()] socket message received 44 bytes
Matchmaker ticket: 7e0abcbf-e6bf-4ae4-b389-477603899742

I've compared my log and yours. First difference in, your log:

[NRtClient::addMatchmaker] ...
[NWebsocketCppRest::send] sending 6 bytes binary ...

my log:

[NRtClient::addMatchmaker] ...
[NWebsocketCppRest::send] sending 13 bytes binary ...

Are you sure you use 2.2.0 client? Can you send full log?

stonekase commented 4 years ago

Your code works for me on Windows and Mac. Tested with server 2.7.0. Logs:

[NRtClient::updateStatus] ...
[NWebsocketCppRest::send] sending 31 bytes binary ...
minCount: 2
[NRtClient::addMatchmaker] ...
[NWebsocketCppRest::send] sending 13 bytes binary ...
[NWebsocketCppRest::operator ()] socket message received 95 bytes
[NWebsocketCppRest::operator ()] socket message received 211 bytes
[NWebsocketCppRest::operator ()] socket message received 3 bytes
Status updated
[NWebsocketCppRest::operator ()] socket message received 44 bytes
Matchmaker ticket: 7e0abcbf-e6bf-4ae4-b389-477603899742

I've compared my log and yours. First difference in, your log:

[NRtClient::addMatchmaker] ...
[NWebsocketCppRest::send] sending 6 bytes binary ...

my log:

[NRtClient::addMatchmaker] ...
[NWebsocketCppRest::send] sending 13 bytes binary ...

Are you sure you use 2.2.0 client? Can you send full log?

[Nakama::RestClient::RestClient] Created. NakamaSdkVersion: 2.2.0 [Nakama::RestClient::RestClient] using default port 7350 [Nakama::RestClient::authenticateDevice] ... session token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJlYjFlYjMxYi1lYTM2LTQyZmEtYTExZi0yNWVmZGQ3ZjcwMGYiLCJ1c24iOiJ3YmRjV1F6Y09GIiwiZXhwIjoxNTczMjg1MTQzfQ.opFVhvzboHZCQR2XqsUXlaPwb7-L7H7Vok200NY5NS8 [Nakama::RestClient::getAccount] ... [NWebsocketCppRest::NWebsocketCppRest] [NRtClient::NRtClient] Created [NRtClient::NRtClient] using default port 7350 [NRtClient::connect] ... [NWebsocketCppRest::connect] ... account user id: eb1eb31b-ea36-42fa-a11f-25efdd7f700f account user created at: 1573197228000 [NRtClient::operator()] connected Socket connected [NRtClient::updateStatus] ... [NWebsocketCppRest::send] sending 31 bytes binary ... minCount: 2 [NRtClient::addMatchmaker] ... [NWebsocketCppRest::send] sending 6 bytes binary ... [NWebsocketCppRest::operator()] socket message received 95 bytes [NWebsocketCppRest::operator()] socket message received 3 bytes Status updated [NWebsocketCppRest::operator()] socket message received 211 bytes [NWebsocketCppRest::operator()] socket message received 44 bytes [NRtClient::onTransportMessage] NRtError: BAD_INPUT Invalid minimum count, must be >= 2 [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping [NWebsocketCppRest::operator()] ping

Dimon4eg commented 4 years ago

Something wrong with client on your side. I cannot reproduce it. The addMatchmaker request should send 13 bytes data as in my case but your client sends 6 bytes which is weird. Did you build nakama-cpp client by self or use our binaries? If use our then how do link it: statically or dynamically? Can you try dynamic linking? Last suggestion is try to build nakama-cpp client by self and try again. This might help if your C++ compiler is different version and has some significant changes.

Dimon4eg commented 4 years ago

closing. @stonekase feel free to reopen with additional information.

Daspien27 commented 4 years ago

Using nakama 2.11.1 and latest nakama-cpp 2.3.0:

I am experiencing this problem after building nakama-cpp by myself (as the original binaries were giving the same issue). I can verify the addMatchmaker and listMatches calls actually appear to have the same issues with their min, max [and query] Nakama::opt:optional parameter types. I am working with x64.

In certain cases I even encounter an access violation.

StackIssue

I have not tried dynamic linking yet.

Here is a watch window of the parameters inside addMatchmaker: Watches

I initialized the min and max both to be 2, but yet we see garbage in them at this point. We can see the query is "*" but after the operator* it will seemingly pass garbage values as well.

The best conclusion I have come up with so far is that the C++ language both projects are being built in are different (C++latest in my project) because I pass my optionals using Nakama::opt::make_optional (which resolves to std::make_optional) going into the function, but the stack shows they are passed into a function taking nonstd::optional_lite::optional which would explain why the pointers end up becoming garbage.

Daspien27 commented 4 years ago

With further investigation, I can confirm it is due to misaligned language standards. By changing my project from C++latest to C++14 Nakama::opt::make_optional aligns with the nonstd::optional_lite::optional class.

This is unfortunate that the nakama-cpp headers changes types depending on the language version. I think a potential fix would be to provide the two optional class interfaces in parallel (introducing a std::optional when available) or to just commit to a more hardened Nakama::NOptional type that could potentially allow conversions with nonstd::optional_lite::optional or std::optional.