#include "stdafx.h" #include "GameClient.h" #include const char* GameClient::s_default_account_id = "979591"; const uint32_t GameClient::s_default_world_id = 160; GameClient::GameClient() : m_status(STATUS_DISCONNECTED) , m_client(nullptr) , m_looper(nullptr) { m_clientTickBuffer = new uint8_t[2048]; } GameClient::~GameClient() { m_client = nullptr; cyclone::Looper::destroy_looper(m_looper); m_looper = nullptr; delete[] m_clientTickBuffer; m_clientTickBuffer = nullptr; } bool GameClient::init() { assert(m_status == STATUS_DISCONNECTED); m_reportInterface.init(this); m_clientNetbarInterface.init(this); return m_aceClientInterface.init(); } void GameClient::deinit() { m_aceClientInterface.deinit(); } void GameClient::connectToGameServer(const std::string& server_ip, uint16_t server_port) { assert(m_status == STATUS_DISCONNECTED); m_server_ip = server_ip; m_server_port = server_port; CY_LOG(cyclone::L_DEBUG, "connect to %s:%d...", server_ip.c_str(), server_port); m_looper = cyclone::Looper::create_looper(); m_client = std::make_shared(m_looper, nullptr); m_client->m_listener.on_connected = std::bind(&GameClient::onConnected, this, std::placeholders::_1, std::placeholders::_3); m_client->m_listener.on_message = std::bind(&GameClient::onMessage, this, std::placeholders::_2); m_client->m_listener.on_close = std::bind(&GameClient::onClose, this); m_client->connect(cyclone::Address(m_server_ip.c_str(), m_server_port)); m_status = STATUS_CONNECTING; } void GameClient::disconnectFromGameServer() { if (m_client) { m_client->disconnect(); m_status = STATUS_LOGGING_OUT; } } void GameClient::tick() { if (m_looper) { m_looper->step(); } if (m_status == STATUS_LOGGED_IN) { uint32_t packetLen = 0; m_aceClientInterface.getClientTickPacket(m_clientTickBuffer, packetLen); if (packetLen > 0) { Game::ClientTickPacket clientTickPacket; clientTickPacket.mutable_client_packet()->assign((const char*)m_clientTickBuffer, packetLen); cyclone::Packet packet; serializeProtoMessage(GAME_PROTO_CLIENT_PACKET, &clientTickPacket, packet); m_client->send(packet.get_memory_buf(), packet.get_memory_size()); } sendHeartBeat(); } m_clientNetbarInterface.tick(); } uint32_t GameClient::onConnected(cyclone::TcpClientPtr client, bool success) { assert(m_status == STATUS_CONNECTING); CY_LOG(cyclone::L_DEBUG, "connect to %s:%d %s.", client->get_server_address().get_ip(), client->get_server_address().get_port(), success ? "OK" : "FAILED"); if (success) { m_status = STATUS_CONNECTED; sendLoginRequest(s_default_account_id, s_default_world_id); return 0; } else { return 1000 * 5; } } void GameClient::sendLoginRequest(const char* accountID, int32_t worldID) { assert(m_status == STATUS_CONNECTED); Game::LoginRequest loginRequest; loginRequest.set_accound_id(accountID); loginRequest.set_world_id(worldID); loginRequest.set_pb_retcode(m_aceClientInterface.getPbRetCode()); cyclone::Packet packet; serializeProtoMessage(GAME_PROTO_LOGIN_REQUEST, &loginRequest, packet); m_client->send(packet.get_memory_buf(), packet.get_memory_size()); m_status = STATUS_LOGGING_IN; } void GameClient::onMessage(cyclone::TcpConnectionPtr conn) { cyclone::RingBuf& buf = conn->get_input_buf(); for (;;) { cyclone::Packet packet; if (!packet.build_from_ringbuf(GAME_PACKET_HEAD_SIZE, buf)) return; ::google::protobuf::Message* message = parserProtoMessage(packet); switch (packet.get_packet_id()) { case GAME_PROTO_LOGIN_REPLY: { Game::LoginReply* loginReply = dynamic_cast(message); onServerLoginReply(conn, loginReply); } break; case GAME_PROTO_SERVER_PACKET: { Game::ServerTickPacket* serverTickPacket = dynamic_cast(message); onServerTickPacket(conn, serverTickPacket); } break; case GAME_PROTO_UGC_REPLY: { Game::UGCReply* ugcReply = dynamic_cast(message); onServerUGCReply(conn, ugcReply); } break; case GAME_PROTO_NETBAR_RES: { Game::NetbarStatusRes* netbarResponse = dynamic_cast(message); onServerNetbarResponse(conn, netbarResponse); } break; } } } void GameClient::onServerLoginReply(cyclone::TcpConnectionPtr conn, Game::LoginReply* reply) { assert(m_status == STATUS_LOGGING_IN); m_aceClientInterface.onServerLoginReply(s_default_account_id, s_default_world_id, reply->ace_token().c_str(), reply->light_feature().name().c_str(), reply->light_feature().data_crc(), (const uint8_t*)reply->light_feature().data().data(), (uint32_t)reply->light_feature().data().size() ); m_status = STATUS_LOGGED_IN; } void GameClient::onServerTickPacket(cyclone::TcpConnectionPtr conn, Game::ServerTickPacket* reply) { assert(m_status == STATUS_LOGGED_IN || m_status==STATUS_LOGGING_IN); m_aceClientInterface.onServerTickPacket( (const uint8_t*)(reply->server_packet().data()), (uint32_t)(reply->server_packet().size()) ); } void GameClient::sendHeartBeat() { assert(m_status == STATUS_LOGGED_IN); uint64_t timeNow = cyclone::sys_api::utc_time_now(); if(timeNow-m_lastHeartBeatTime < 60 * 1000ll*1000ll) { return; } m_lastHeartBeatTime = timeNow; uint8_t bindBuffer[1024]; uint32_t bindDataLength = m_aceClientInterface.getProtoBindingData(bindBuffer, sizeof(bindBuffer)); Game::HeartBeat heartBeat; heartBeat.set_timestamp((int32_t)timeGetTime()); heartBeat.mutable_pb_data()->assign((const char*)bindBuffer, bindDataLength); cyclone::Packet packet; serializeProtoMessage(GAME_PROTO_HEART_BEAT, &heartBeat, packet); m_client->send(packet.get_memory_buf(), packet.get_memory_size()); } void GameClient::onServerUGCReply(cyclone::TcpConnectionPtr conn, Game::UGCReply* reply) { assert(m_status == STATUS_LOGGED_IN); wchar_t wszTemp[2048]; UINT btn = MB_OK; if (reply->result() == 0) { StringCchPrintfW(wszTemp, sizeof(wszTemp)/sizeof(wszTemp[0]), L"UGC Reply: ContextID=%d, ErrorCode=%d, Result=%d", reply->ugc_ctx_id(), reply->error_code(), reply->result() ); } else { btn = MB_OK | MB_ICONERROR; wchar_t wszFiltedContent[1024]; ::MultiByteToWideChar(CP_UTF8, 0, reply->filted_content().c_str(), -1, wszFiltedContent, sizeof(wszFiltedContent) / sizeof(wszFiltedContent[0])); StringCchPrintfW(wszTemp, sizeof(wszTemp)/sizeof(wszTemp[0]), L"UGC Reply: ContextID=%d, ErrorCode=%d, Result=%d, FiltedText='%s'", reply->ugc_ctx_id(), reply->error_code(), reply->result(), wszFiltedContent ); } MessageBoxW(NULL, wszTemp, L"UGC Reply", btn); } void GameClient::sendUGCContent(int32_t ugcSceneID, const char* content) { assert(m_status == STATUS_LOGGED_IN); Game::UGCRequest ugcRequest; ugcRequest.set_ugc_ctx_id(m_nextUGCContextID++); ugcRequest.set_ugc_scene_id(ugcSceneID); ugcRequest.set_ugc_content(content); cyclone::Packet packet; serializeProtoMessage(GAME_PROTO_UGC_REQUEST, &ugcRequest, packet); m_client->send(packet.get_memory_buf(), packet.get_memory_size()); } void GameClient::onClose(void) { m_aceClientInterface.onServerLogout(); m_status = STATUS_LOGGED_OUT; } void GameClient::sendProtobufMessage(uint16_t id, ::google::protobuf::Message* message) { cyclone::Packet packet; serializeProtoMessage(id, message, packet); m_client->send(packet.get_memory_buf(), packet.get_memory_size()); } void GameClient::onServerNetbarResponse(cyclone::TcpConnectionPtr conn, Game::NetbarStatusRes* reply) { m_clientNetbarInterface.onReceiveNetbarReportRequest(reply); }