359 lines
11 KiB
C++
359 lines
11 KiB
C++
#include "client/GameClient.h"
|
||
#include "common/Protocol.h"
|
||
#include <iostream>
|
||
#include <sstream>
|
||
#include <thread>
|
||
#include <chrono>
|
||
|
||
GameClient::GameClient(const std::string& serverAddress, int serverPort)
|
||
: m_serverAddress(serverAddress),
|
||
m_serverPort(serverPort),
|
||
m_isRunning(false),
|
||
m_isConnected(false),
|
||
m_isAuthenticated(false),
|
||
m_inBattle(false),
|
||
m_waitingForTurn(false) {
|
||
}
|
||
|
||
GameClient::~GameClient() {
|
||
disconnect();
|
||
}
|
||
|
||
bool GameClient::connect() {
|
||
if (!m_socket.create()) {
|
||
std::cerr << "创建socket失败" << std::endl;
|
||
return false;
|
||
}
|
||
|
||
if (!m_socket.connect(m_serverAddress, m_serverPort)) {
|
||
std::cerr << "连接服务器失败" << std::endl;
|
||
return false;
|
||
}
|
||
|
||
m_isConnected = true;
|
||
m_isRunning = true;
|
||
|
||
// 启动接收线程
|
||
m_recvThread = std::thread(&GameClient::recvLoop, this);
|
||
|
||
std::cout << "成功连接到服务器!" << std::endl;
|
||
return true;
|
||
}
|
||
|
||
void GameClient::disconnect() {
|
||
if (!m_isRunning) {
|
||
return;
|
||
}
|
||
|
||
m_isRunning = false;
|
||
m_socket.close();
|
||
|
||
if (m_recvThread.joinable()) {
|
||
m_recvThread.join();
|
||
}
|
||
|
||
std::cout << "已断开与服务器的连接" << std::endl;
|
||
}
|
||
|
||
void GameClient::run() {
|
||
if (!connect()) {
|
||
return;
|
||
}
|
||
|
||
while (m_isRunning && m_isConnected) {
|
||
if (!m_isAuthenticated) {
|
||
showMainMenu();
|
||
} else if (m_inBattle && m_waitingForTurn) {
|
||
// 只在战斗中且轮到玩家时才显示战斗菜单
|
||
showBattleMenu();
|
||
} else if (!m_inBattle) {
|
||
// 不在战斗中,显示大厅菜单
|
||
showLobbyMenu();
|
||
} else {
|
||
// 在战斗中但不是玩家回合,等待
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||
}
|
||
}
|
||
}
|
||
|
||
bool GameClient::sendMessage(const std::string& message) {
|
||
return m_socket.send(message);
|
||
}
|
||
|
||
void GameClient::recvLoop() {
|
||
while (m_isRunning) {
|
||
std::string message = m_socket.recvLine();
|
||
if (message.empty()) {
|
||
if (m_isRunning) {
|
||
std::cerr << "\n连接已断开" << std::endl;
|
||
m_isConnected = false;
|
||
m_isRunning = false;
|
||
}
|
||
break;
|
||
}
|
||
|
||
handleServerMessage(message);
|
||
}
|
||
}
|
||
|
||
void GameClient::handleServerMessage(const std::string& message) {
|
||
std::string command;
|
||
std::vector<std::string> params;
|
||
|
||
if (!Protocol::parseMessage(message, command, params)) {
|
||
std::cerr << "解析服务器消息失败" << std::endl;
|
||
return;
|
||
}
|
||
|
||
if (command == Protocol::S2C_RESPONSE) {
|
||
if (params.size() >= 2) {
|
||
std::cout << "\n[服务器] " << params[1] << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_LOGIN_OK) {
|
||
if (params.size() >= 5) {
|
||
m_username = params[0];
|
||
m_isAuthenticated = true;
|
||
std::cout << "\n【登录成功】" << std::endl;
|
||
std::cout << "欢迎," << params[0] << "!" << std::endl;
|
||
std::cout << "职业:" << params[1] << ",等级:" << params[2] << std::endl;
|
||
std::cout << "生命值:" << params[3] << ",魔法值:" << params[4] << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_MSG) {
|
||
if (params.size() >= 2) {
|
||
std::cout << "\n【聊天】" << params[0] << ":" << params[1] << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_LIST_PLAYERS) {
|
||
if (!params.empty()) {
|
||
std::cout << "\n【在线玩家】:" << params[0] << std::endl;
|
||
} else {
|
||
std::cout << "\n【在线玩家】:(无)" << std::endl;
|
||
}
|
||
showPrompt();
|
||
} else if (command == Protocol::S2C_INVITE) {
|
||
if (!params.empty()) {
|
||
std::cout << "\n*** " << params[0] << " 邀请你战斗!***" << std::endl;
|
||
std::cout << "输入 'accept " << params[0] << "' 接受,或 'reject " << params[0] << "' 拒绝" << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_INVITE_RESULT) {
|
||
if (params.size() >= 2) {
|
||
std::cout << "\n【邀请】" << params[0] << " " << params[1] << " 了你的邀请" << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_BATTLE_START) {
|
||
m_inBattle = true;
|
||
if (params.size() >= 2) {
|
||
std::cout << "\n*** 战斗开始!***" << std::endl;
|
||
std::cout << "对手:" << params[0] << "(" << params[1] << ")" << std::endl;
|
||
std::cout << "====================" << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_BATTLE_TURN) {
|
||
if (!params.empty()) {
|
||
if (params[0] == "YOUR_TURN") {
|
||
std::cout << "\n>>> 你的回合 <<<" << std::endl;
|
||
std::cout << "输入 'attack' 使用普通攻击" << std::endl;
|
||
m_waitingForTurn = true;
|
||
showPrompt();
|
||
} else {
|
||
std::cout << "\n>>> 对手的回合..." << std::endl;
|
||
m_waitingForTurn = false;
|
||
}
|
||
}
|
||
} else if (command == Protocol::S2C_BATTLE_LOG) {
|
||
if (!params.empty()) {
|
||
std::cout << "【战斗】" << params[0] << std::endl;
|
||
}
|
||
} else if (command == Protocol::S2C_BATTLE_END) {
|
||
m_inBattle = false;
|
||
m_waitingForTurn = false;
|
||
|
||
if (!params.empty()) {
|
||
std::cout << "\n*** 战斗结束!***" << std::endl;
|
||
if (params[0] == "WIN") {
|
||
std::cout << "你赢了!恭喜!" << std::endl;
|
||
} else if (params[0] == "LOSE") {
|
||
std::cout << "你输了。下次加油!" << std::endl;
|
||
} else {
|
||
std::cout << "平局!" << std::endl;
|
||
}
|
||
std::cout << "====================" << std::endl;
|
||
showPrompt();
|
||
}
|
||
} else if (command == Protocol::S2C_SYSTEM) {
|
||
if (!params.empty()) {
|
||
std::cout << "\n[System] " << params[0] << std::endl;
|
||
showPrompt();
|
||
}
|
||
}
|
||
}
|
||
|
||
void GameClient::showMainMenu() {
|
||
printTitle("主菜单");
|
||
std::cout << "1. 注册" << std::endl;
|
||
std::cout << "2. 登录" << std::endl;
|
||
std::cout << "3. 退出" << std::endl;
|
||
printSeparator();
|
||
showPrompt();
|
||
|
||
std::string input;
|
||
std::getline(std::cin, input);
|
||
|
||
if (input == "1") {
|
||
handleRegister();
|
||
} else if (input == "2") {
|
||
handleLogin();
|
||
} else if (input == "3") {
|
||
disconnect();
|
||
}
|
||
}
|
||
|
||
void GameClient::showLobbyMenu() {
|
||
printTitle("游戏大厅");
|
||
std::cout << "1. 聊天" << std::endl;
|
||
std::cout << "2. 查看在线玩家" << std::endl;
|
||
std::cout << "3. 邀请战斗" << std::endl;
|
||
std::cout << "4. 查看战斗状态" << std::endl;
|
||
std::cout << "5. 登出" << std::endl;
|
||
printSeparator();
|
||
|
||
std::string input;
|
||
showPrompt();
|
||
std::getline(std::cin, input);
|
||
handleLobbyInput(input);
|
||
}
|
||
|
||
void GameClient::showBattleMenu() {
|
||
printTitle("战斗中");
|
||
std::cout << "输入 'attack' 或 '攻击' 使用普通攻击" << std::endl;
|
||
printSeparator();
|
||
|
||
std::string input;
|
||
showPrompt();
|
||
std::getline(std::cin, input);
|
||
handleBattleInput(input);
|
||
}
|
||
|
||
void GameClient::handleRegister() {
|
||
std::string username, password;
|
||
std::cout << "用户名:";
|
||
std::getline(std::cin, username);
|
||
std::cout << "密码:";
|
||
std::getline(std::cin, password);
|
||
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_REGISTER, {username, password});
|
||
sendMessage(msg);
|
||
}
|
||
|
||
void GameClient::handleLogin() {
|
||
std::string username, password;
|
||
std::cout << "用户名:";
|
||
std::getline(std::cin, username);
|
||
std::cout << "密码:";
|
||
std::getline(std::cin, password);
|
||
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_LOGIN, {username, password});
|
||
sendMessage(msg);
|
||
}
|
||
|
||
void GameClient::handleLobbyInput(const std::string& input) {
|
||
if (input.empty()) {
|
||
return;
|
||
}
|
||
|
||
// 检查是否是邀请响应命令(accept 或 reject)
|
||
if (input.find("accept ") == 0 || input.find("reject ") == 0) {
|
||
size_t spacePos = input.find(' ');
|
||
if (spacePos != std::string::npos) {
|
||
std::string response = input.substr(0, spacePos); // "accept" 或 "reject"
|
||
std::string inviter = input.substr(spacePos + 1); // 邀请者用户名
|
||
|
||
bool accept = (response == "accept");
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_INVITE_RSP, {inviter, accept ? "yes" : "no"});
|
||
sendMessage(msg);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (input == "1") {
|
||
// Chat
|
||
std::cout << "消息:";
|
||
std::string message;
|
||
std::getline(std::cin, message);
|
||
if (!message.empty()) {
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_CHAT, {message});
|
||
sendMessage(msg);
|
||
}
|
||
} else if (input == "2") {
|
||
// List players
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_LIST_PLAYERS);
|
||
sendMessage(msg);
|
||
} else if (input == "3") {
|
||
// Invite to battle
|
||
std::cout << "目标玩家用户名:";
|
||
std::string target;
|
||
std::getline(std::cin, target);
|
||
if (!target.empty()) {
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_INVITE, {target});
|
||
sendMessage(msg);
|
||
}
|
||
} else if (input == "4") {
|
||
// Check battle status
|
||
std::cout << "战斗状态:" << (m_inBattle ? "战斗中" : "未在战斗") << std::endl;
|
||
} else if (input == "5") {
|
||
// Logout
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_LOGOUT);
|
||
sendMessage(msg);
|
||
m_isAuthenticated = false;
|
||
m_username.clear();
|
||
} else {
|
||
std::cout << "无效选项。请输入1-5。" << std::endl;
|
||
}
|
||
}
|
||
|
||
void GameClient::handleBattleInput(const std::string& input) {
|
||
if (input.empty()) {
|
||
return;
|
||
}
|
||
|
||
if (input == "attack" || input == "攻击") {
|
||
// 简化版:总是攻击对手(skillIndex=0, target=对手名)
|
||
std::string msg = Protocol::buildMessage(Protocol::C2S_BATTLE_ACTION, {"0", "opponent"});
|
||
sendMessage(msg);
|
||
m_waitingForTurn = false;
|
||
} else {
|
||
std::cout << "无效命令。请输入 'attack' 或 '攻击'" << std::endl;
|
||
}
|
||
}
|
||
|
||
void GameClient::showPrompt() {
|
||
if (m_inBattle) {
|
||
std::cout << "【战斗】> " << std::flush;
|
||
} else if (m_isAuthenticated) {
|
||
std::cout << "【" << m_username << "】> " << std::flush;
|
||
} else {
|
||
std::cout << "> " << std::flush;
|
||
}
|
||
}
|
||
|
||
void GameClient::clearScreen() {
|
||
#ifdef _WIN32
|
||
system("cls");
|
||
#else
|
||
system("clear");
|
||
#endif
|
||
}
|
||
|
||
void GameClient::printSeparator() {
|
||
std::cout << "================================" << std::endl;
|
||
}
|
||
|
||
void GameClient::printTitle(const std::string& title) {
|
||
printSeparator();
|
||
std::cout << " " << title << std::endl;
|
||
printSeparator();
|
||
}
|