mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 06:14:08 +08:00
feat: add online functionality
This commit is contained in:
@@ -1,19 +1,34 @@
|
||||
#pragma once
|
||||
#include <utility>
|
||||
|
||||
enum class NetDataType {
|
||||
CLICK_POSITION,
|
||||
GAME_START
|
||||
};
|
||||
|
||||
struct NetData {
|
||||
std::pair<int, int> clickPosition = {0, 0};
|
||||
int firstPlayer = 1;
|
||||
NetDataType type = NetDataType::CLICK_POSITION;
|
||||
// 序列化,转换成字节数组
|
||||
void serialize(char* buffer) const {
|
||||
// 暴力转换
|
||||
int* ptr = reinterpret_cast<int*>(buffer);
|
||||
ptr[0] = clickPosition.first;
|
||||
ptr[1] = clickPosition.second;
|
||||
|
||||
ptr[0] = static_cast<int>(type);
|
||||
ptr[1] = clickPosition.first;
|
||||
ptr[2] = clickPosition.second;
|
||||
ptr[3] = firstPlayer;
|
||||
}
|
||||
// 反序列化,从字节数组恢复
|
||||
static NetData deserialize(const char* buffer) {
|
||||
// 暴力转换
|
||||
const int* ptr = reinterpret_cast<const int*>(buffer);
|
||||
return NetData{{ptr[0], ptr[1]}};
|
||||
NetData data;
|
||||
data.type = static_cast<NetDataType>(ptr[0]);
|
||||
data.clickPosition = {ptr[1], ptr[2]};
|
||||
data.firstPlayer = ptr[3];
|
||||
return data;
|
||||
}
|
||||
// 大小(固定16字节:4个int)
|
||||
static constexpr size_t size() { return 4 * sizeof(int); }
|
||||
|
||||
@@ -29,6 +29,10 @@ void NetworkManager::init(NetType type) {
|
||||
std::cout << "client started\n";
|
||||
}
|
||||
|
||||
void NetworkManager::setClickEventCallback(ClickEventCallback callback) {
|
||||
m_clickEventCallback = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
void NetworkManager::init(NetType type) {
|
||||
// 先启动 io_context 线程
|
||||
@@ -68,12 +72,23 @@ void NetworkManager::startServer() {
|
||||
|
||||
void NetworkManager::startClient() {
|
||||
m_client->setCallbackes(
|
||||
[](const NetData& click) {
|
||||
[this](const NetData& click) {
|
||||
/* 处理对手棋步 */
|
||||
if (m_clickEventCallback) {
|
||||
std::cout << "Received opponent move: ("
|
||||
<< click.clickPosition.first << ", "
|
||||
<< click.clickPosition.second << ")\n";
|
||||
m_clickEventCallback(click.clickPosition.first, click.clickPosition.second);
|
||||
}
|
||||
},
|
||||
[]() {
|
||||
/* 提示用户走棋 */
|
||||
std::cout << "It's your turn now!\n";
|
||||
std::cout << "NetworkManager:It's your turn now!\n";
|
||||
},
|
||||
[this]() {
|
||||
/* 游戏开始回调 */
|
||||
m_startGameCallback();
|
||||
std::cout << "Game has started!\n";
|
||||
}
|
||||
);
|
||||
if (m_netType == NetType::HOST) {
|
||||
@@ -100,4 +115,24 @@ void NetworkManager::startIOContextLoop() {
|
||||
});
|
||||
std::cout << "IO context loop started on thread: "
|
||||
<< m_ioThread.joinable() << std::endl;
|
||||
}
|
||||
|
||||
void NetworkManager::postClickPosition(int logicalX, int logicalY, bool isChangeTurn) {
|
||||
if (m_client) {
|
||||
NetData data;
|
||||
data.clickPosition = {logicalX, logicalY};
|
||||
// 发送位置并告诉对手是否换回合
|
||||
m_client->sentClickPosition(data, isChangeTurn);
|
||||
std::cout << "Posted click position: ("
|
||||
<< logicalX << ", " << logicalY << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::setIsMyTurn(bool isMyTurn) {
|
||||
m_isMyTurn = isMyTurn;
|
||||
if (m_client) {
|
||||
// 如果不是我的回合,则客户端应该等待对手
|
||||
m_client->setShouldWait(!isMyTurn);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
class NetworkManager {
|
||||
public:
|
||||
using ClickEventCallback = std::function<void(int logicalX, int logicalY)>;
|
||||
using StartGameCallback = std::function<void()>;
|
||||
NetworkManager();
|
||||
|
||||
|
||||
@@ -13,7 +15,15 @@ public:
|
||||
|
||||
void init(NetType type);
|
||||
|
||||
void setClickEventCallback(ClickEventCallback callback);
|
||||
|
||||
void setStartGameCallback(StartGameCallback callback) {
|
||||
m_startGameCallback = callback;
|
||||
}
|
||||
|
||||
void setIsMyTurn(bool isMyTurn);
|
||||
bool isMyTurn() const { return m_isMyTurn; }
|
||||
void postClickPosition(int logicalX, int logicalY, bool isChangeTurn = false);
|
||||
|
||||
private:
|
||||
// 一定要在最前面
|
||||
@@ -28,7 +38,10 @@ private:
|
||||
asio::io_context::executor_type> m_workguard;
|
||||
std::thread m_ioThread;
|
||||
|
||||
|
||||
ClickEventCallback m_clickEventCallback;
|
||||
StartGameCallback m_startGameCallback;
|
||||
|
||||
bool m_isMyTurn = false; // 新增:当前是否是我的回合
|
||||
void startServer();
|
||||
|
||||
void startClient();
|
||||
|
||||
@@ -7,14 +7,22 @@ Client::Client(asio::io_context& ioContext):
|
||||
// 构造函数实现
|
||||
}
|
||||
|
||||
void Client::setCallbackes(MoveCallback onOpponentMove, TurnCallback onMyTurn) {
|
||||
void Client::setCallbackes(MoveCallback onOpponentMove, TurnCallback onMyTurn, TurnCallback onGameStart) {
|
||||
m_onOpponentMove = onOpponentMove;
|
||||
m_onMyTurn = onMyTurn;
|
||||
m_onGameStart = onGameStart;
|
||||
}
|
||||
|
||||
void Client::stopWaiting() {
|
||||
m_shouldWait = false;
|
||||
m_isWaiting = false;
|
||||
}
|
||||
|
||||
void Client::connect(const std::string& host, int port, bool iAmFirst) {
|
||||
m_host = host;
|
||||
m_port = port;
|
||||
m_isHost = iAmFirst;
|
||||
m_shouldWait = true; // 连接后开始等待
|
||||
//用shared_ptr保持对象存活
|
||||
auto self = shared_from_this();
|
||||
|
||||
@@ -40,28 +48,41 @@ void Client::connect(const std::string& host, int port, bool iAmFirst) {
|
||||
|
||||
void Client::onConnected(bool iAmFirst) {
|
||||
std::cout << "Connected to server " << m_host << ":" << m_port << std::endl;
|
||||
|
||||
// 如果是先手,触发回调
|
||||
if (iAmFirst && m_onMyTurn) {
|
||||
m_onMyTurn();
|
||||
} else {
|
||||
// 等待对手动作
|
||||
// 重置等待状态
|
||||
m_shouldWait = true;
|
||||
m_isWaiting = false;
|
||||
// 开始等待对手消息
|
||||
if (m_shouldWait) {
|
||||
waitForOpponent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 发送点击位置数据给对手
|
||||
|
||||
void Client::sentClickPosition(const NetData& data) {
|
||||
void Client::sentClickPosition(const NetData& data, bool isChangeTurn) {
|
||||
auto self = shared_from_this();
|
||||
|
||||
NetData sendData = data;
|
||||
sendData.type = NetDataType::CLICK_POSITION;
|
||||
|
||||
|
||||
char buffer[NetData::size()];
|
||||
data.serialize(buffer);
|
||||
sendData.serialize(buffer);
|
||||
asio::async_write(m_socket, asio::buffer(buffer, NetData::size()),
|
||||
[this, self](const asio::error_code& ec, std::size_t /*bytesTransferred*/) {
|
||||
[this, self, isChangeTurn](const asio::error_code& ec, std::size_t /*bytesTransferred*/) {
|
||||
if (!ec) {
|
||||
// 发送成功,等待对手动作
|
||||
waitForOpponent();
|
||||
// 如果需要转换回合,则开始等待对手
|
||||
if (isChangeTurn) {
|
||||
m_isMyTurn = false;
|
||||
m_shouldWait = true;
|
||||
if (!m_isWaiting) {
|
||||
waitForOpponent();
|
||||
}
|
||||
} else {
|
||||
m_isMyTurn = true;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "send failed: " << ec.message() << std::endl;
|
||||
}
|
||||
@@ -69,6 +90,9 @@ void Client::sentClickPosition(const NetData& data) {
|
||||
}
|
||||
|
||||
void Client::waitForOpponent() {
|
||||
if (!m_shouldWait || m_isWaiting) {
|
||||
return;
|
||||
}
|
||||
auto self = shared_from_this();
|
||||
m_socket.async_read_some(
|
||||
asio::buffer(m_readBuffer, NetData::size()),
|
||||
@@ -77,21 +101,69 @@ void Client::waitForOpponent() {
|
||||
|
||||
if (bytesTransferred == NetData::size()) {
|
||||
NetData netData = NetData::deserialize(m_readBuffer);
|
||||
// 触发对手移动回调
|
||||
if (m_onOpponentMove) {
|
||||
m_onOpponentMove(netData);
|
||||
|
||||
// 检查消息类型
|
||||
if (netData.type == NetDataType::GAME_START) {
|
||||
std::cout << "Game started! First player is: " << netData.firstPlayer << std::endl;
|
||||
|
||||
// 判断自己是否是先手
|
||||
bool iAmFirst = (netData.firstPlayer == 1 && m_isHost) ||
|
||||
(netData.firstPlayer == 2 && !m_isHost);
|
||||
|
||||
m_isMyTurn = iAmFirst;
|
||||
if (m_onGameStart) {
|
||||
m_onGameStart();
|
||||
}
|
||||
|
||||
if (m_isMyTurn && m_onMyTurn) {
|
||||
std::cout << "It's your turn now! (You are first)\n";
|
||||
m_onMyTurn();
|
||||
} else if (!m_isMyTurn && m_onOpponentMove) {
|
||||
// 如果不是先手,等待对手走棋
|
||||
std::cout << "Waiting for opponent to move...\n";
|
||||
// 可以在这里触发一个等待对手的回调,如果需要的话
|
||||
if (m_shouldWait) {
|
||||
waitForOpponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 轮到我了
|
||||
if (m_onMyTurn) {
|
||||
m_onMyTurn();
|
||||
else if (netData.type == NetDataType::CLICK_POSITION) {
|
||||
// 正常的对手移动
|
||||
if (m_onOpponentMove) {
|
||||
std::cout << "Received opponent move: ("
|
||||
<< netData.clickPosition.first << ", "
|
||||
<< netData.clickPosition.second << ")\n";
|
||||
m_onOpponentMove(netData);
|
||||
}
|
||||
// 现在轮到我了
|
||||
//m_isMyTurn = true;
|
||||
|
||||
|
||||
if (m_isMyTurn) {
|
||||
m_shouldWait = false;
|
||||
if (m_onMyTurn) {
|
||||
m_onMyTurn();
|
||||
}
|
||||
} else {
|
||||
if (m_shouldWait) {
|
||||
waitForOpponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 重置读取缓冲区以准备下一次读取
|
||||
|
||||
} else {
|
||||
std::cerr << "Incomplete data received from opponent." << std::endl;
|
||||
if (m_shouldWait) {
|
||||
waitForOpponent();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cerr << "read failed: " << ec.message() << std::endl;
|
||||
// 发生错误时,可以选择重新等待对手
|
||||
if (m_shouldWait) {
|
||||
waitForOpponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -15,12 +15,13 @@ public:
|
||||
|
||||
Client(asio::io_context& ioContext);
|
||||
~Client() = default;
|
||||
|
||||
void setCallbackes(MoveCallback onOpponentMove, TurnCallback onMyTurn);
|
||||
// 设置等待状态
|
||||
void setShouldWait(bool shouldWait) { m_shouldWait = shouldWait; }
|
||||
void setCallbackes(MoveCallback onOpponentMove, TurnCallback onMyTurn, TurnCallback onGameStart);
|
||||
|
||||
void connect(const std::string& host, int port, bool iAmFirst = true);
|
||||
|
||||
void sentClickPosition(const NetData& data);
|
||||
void sentClickPosition(const NetData& data, bool isChangeTurn = false);
|
||||
|
||||
|
||||
|
||||
@@ -30,13 +31,21 @@ private:
|
||||
asio::ip::tcp::socket m_socket;
|
||||
std::string m_host;
|
||||
int m_port;
|
||||
bool m_isHost = false;
|
||||
MoveCallback m_onOpponentMove;
|
||||
TurnCallback m_onMyTurn;
|
||||
TurnCallback m_onGameStart;
|
||||
char m_readBuffer[NetData::size()];
|
||||
|
||||
|
||||
// 新增状态控制
|
||||
bool m_shouldWait = false; // 是否应该等待对手
|
||||
bool m_isWaiting = false; // 当前是否正在等待
|
||||
bool m_isMyTurn = false; // 是否是我的回合
|
||||
|
||||
void onConnected(bool iAmFirst);
|
||||
void waitForOpponent();
|
||||
void stopWaiting(); // 停止等待
|
||||
|
||||
|
||||
};
|
||||
@@ -83,6 +83,37 @@ void GameServer::waitForPlayers(int playerNum) {
|
||||
void GameServer::startGame() {
|
||||
std::cout << "Gmae Start player1 is the first\n";
|
||||
|
||||
NetData gameStartMsg;
|
||||
gameStartMsg.type = NetDataType::GAME_START;
|
||||
gameStartMsg.firstPlayer = 1; // 玩家1先手
|
||||
gameStartMsg.clickPosition = {-1, -1}; // 特殊值表示游戏开始
|
||||
|
||||
char buffer1[NetData::size()];
|
||||
char buffer2[NetData::size()];
|
||||
// player1
|
||||
gameStartMsg.serialize(buffer1);
|
||||
//player2
|
||||
gameStartMsg.serialize(buffer2);
|
||||
// 发送游戏开始消息给两个玩家
|
||||
asio::async_write(m_player1, asio::buffer(buffer1, NetData::size()),
|
||||
[](const asio::error_code& ec, size_t) {
|
||||
if (ec) {
|
||||
std::cerr << "Failed to send start message to player1: " << ec.message() << std::endl;
|
||||
} else {
|
||||
std::cout << "Game start message sent to player1\n";
|
||||
}
|
||||
});
|
||||
|
||||
asio::async_write(m_player2, asio::buffer(buffer2, NetData::size()),
|
||||
[](const asio::error_code& ec, size_t) {
|
||||
if (ec) {
|
||||
std::cerr << "Failed to send start message to player2: " << ec.message() << std::endl;
|
||||
} else {
|
||||
std::cout << "Game start message sent to player2\n";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
forwardMoves();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user