#include #include "DAPIProtoClient.h" namespace DAPI { DAPIProtoClient::DAPIProtoClient() : mt(static_cast(std::chrono::system_clock::now().time_since_epoch().count())) { udpbound = false; connectionPort = 1025; } void DAPIProtoClient::checkForConnection() { if (isConnected()) return; sf::Packet packet; udpSocket.setBlocking(false); if (!udpbound) { if (udpSocket.bind(1024, sf::IpAddress::Any) != sf::Socket::Status::Done) return; udpbound = true; } std::optional sender = sf::IpAddress::Any; auto port = udpSocket.getLocalPort(); if (udpSocket.receive(packet, sender, port) != sf::Socket::Status::Done) return; auto size = packet.getDataSize(); std::unique_ptr packetContents(new char[size]); memcpy(packetContents.get(), packet.getData(), size); auto currentMessage = std::make_unique(); currentMessage->ParseFromArray(packetContents.get(), static_cast(size)); if (!currentMessage->has_initbroadcast()) return; auto reply = std::make_unique(); auto initResponse = reply->mutable_initresponse(); initResponse->set_port(static_cast(connectionPort)); packet.clear(); size = reply->ByteSizeLong(); std::unique_ptr buffer(new char[size]); reply->SerializeToArray(&buffer[0], static_cast(size)); packet.append(buffer.get(), size); udpSocket.send(packet, sender.value(), port); udpSocket.unbind(); udpbound = false; tcpListener.accept(tcpSocket); return; } void DAPIProtoClient::lookForServer() { if (isConnected()) return; sf::Packet packet; auto broadcastMessage = std::make_unique(); broadcastMessage->mutable_initbroadcast(); auto size = broadcastMessage->ByteSizeLong(); std::unique_ptr buffer(new char[size]); broadcastMessage->SerializeToArray(&buffer[0], size); packet.append(buffer.get(), size); std::optional server = sf::IpAddress::Broadcast; unsigned short port = 1024; udpSocket.send(packet, server.value(), port); server = sf::IpAddress::Any; udpSocket.setBlocking(false); // Sleep to give backend a chance to send the packet. { using namespace std::chrono_literals; std::this_thread::sleep_for(2s); } if (udpSocket.receive(packet, server, port) == sf::Socket::Status::Done) { size = packet.getDataSize(); std::unique_ptr replyBuffer(new char[size]); memcpy(replyBuffer.get(), packet.getData(), size); auto currentMessage = std::make_unique(); currentMessage->ParseFromArray(replyBuffer.get(), size); if (!currentMessage->has_initresponse()) return; connectionPort = static_cast(currentMessage->initresponse().port()); tcpSocket.connect(server.value(), connectionPort); if (!tcpSocket.getRemoteAddress().has_value()) fprintf(stderr, "%s", "Connection failed.\n"); } } void DAPIProtoClient::transmitMessages() { // Check that we are connected to a game server. if (!isConnected()) return; std::unique_ptr currentMessage; sf::Packet packet; // Loop until the message queue is empty. while (!messageQueue.empty()) { packet.clear(); currentMessage = std::move(messageQueue.front()); messageQueue.pop_front(); auto size = currentMessage->ByteSizeLong(); if (size > 0) { std::unique_ptr buffer(new char[size]); currentMessage->SerializeToArray(&buffer[0], size); packet.append(buffer.get(), size); } if (tcpSocket.send(packet) != sf::Socket::Status::Done) { // Error sending message. fprintf(stderr, "Failed to send a Message. Disconnecting.\n"); disconnect(); } } // Finished with queue, send the EndOfQueue message. currentMessage = std::make_unique(); currentMessage->mutable_endofqueue(); packet.clear(); auto size = currentMessage->ByteSizeLong(); std::unique_ptr buffer(new char[size]); currentMessage->SerializeToArray(&buffer[0], size); packet.append(buffer.get(), size); if (tcpSocket.send(packet) != sf::Socket::Status::Done) { // Error sending EndOfQueue fprintf(stderr, "Failed to send end of queue message. Disconnecting.\n"); disconnect(); } } void DAPIProtoClient::receiveMessages() { // Check that we are connected to a game server or client. if (!isConnected()) return; std::unique_ptr currentMessage; sf::Packet packet; // Loop until the end of queue message is received. while (true) { packet.clear(); currentMessage = std::make_unique(); if (tcpSocket.receive(packet) != sf::Socket::Status::Done) { fprintf(stderr, "Failed to receive message. Disconnecting.\n"); disconnect(); return; } auto size = packet.getDataSize(); std::unique_ptr packetContents(new char[size]); memcpy(packetContents.get(), packet.getData(), size); currentMessage->ParseFromArray(packetContents.get(), packet.getDataSize()); if (currentMessage->has_endofqueue()) return; messageQueue.push_back(std::move(currentMessage)); } } void DAPIProtoClient::disconnect() { if (!isConnected()) return; tcpSocket.disconnect(); } void DAPIProtoClient::initListen() { tcpListener.setBlocking(true); while (tcpListener.listen(connectionPort) != sf::Socket::Status::Done) connectionPort = static_cast(getRandomInteger(1025, 49151)); } void DAPIProtoClient::stopListen() { tcpListener.close(); } void DAPIProtoClient::queueMessage(std::unique_ptr newMessage) { messageQueue.push_back(std::move(newMessage)); } std::unique_ptr DAPIProtoClient::getNextMessage() { auto nextMessage = std::move(messageQueue.front()); messageQueue.pop_front(); return nextMessage; } bool DAPIProtoClient::isConnected() const { return tcpSocket.getRemoteAddress().has_value(); } int DAPIProtoClient::messageQueueSize() const { return messageQueue.size(); } } // namespace DAPI