#include "NetQuake.h" #include // basic io #include // dynamic arrays #include // keyval maps #include #include #include #include #include #include #include NetQuake::SerializedGenericPacket NetQuake::Serialize(NetworkGenericPacket ngp) { SerializedGenericPacket sgp; unsigned short typeAsShort; typeAsShort = (ngp.type[0] << 8) + ngp.type[1]; sgp.type = (PacketType)typeAsShort; sgp.length = (ngp.length[0] << 8) + ngp.length[1]; sgp.remainder = ngp.remainder; return sgp; } NetQuake::GenericGamePacket NetQuake::toGenericGamePacket(SerializedGenericPacket sgp) { GenericGamePacket retval; try { int currentPacketLength = 0; int currentPacketAt = 0; bool inString = false; for (int i = 0; i < sgp.remainder.size(); i++) { if (currentPacketLength == 0 && !inString) { if (sgp.remainder[i] == CLIENT_MESSAGE_NOOP) { ClientMessage msg; msg.type = CLIENT_MESSAGE_NOOP; retval.messageBlock.messages.push_back(msg); } if (sgp.remainder[i] == CLIENT_MESSAGE_KEEPALIVE) { ClientMessage msg; msg.type = CLIENT_MESSAGE_NOOP; retval.messageBlock.messages.push_back(msg); } if (sgp.remainder[i] == CLIENT_MESSAGE_MOVEMENT) { ClientMessage msg; msg.type = CLIENT_MESSAGE_MOVEMENT; retval.messageBlock.messages.push_back(msg); currentPacketLength = 15; } if (sgp.remainder[i] == CLIENT_MESSAGE_CONSOLE) { ClientMessage msg; msg.type = CLIENT_MESSAGE_CONSOLE; retval.messageBlock.messages.push_back(msg); } } else if (inString) { if (sgp.remainder[i] == 0x00) inString = false; else { retval.messageBlock.messages[retval.messageBlock.messages.size() - 1].data += sgp.remainder[i]; } } else { retval.messageBlock.messages[retval.messageBlock.messages.size() - 1].data += sgp.remainder[i]; currentPacketAt++; if (currentPacketAt == currentPacketLength) { currentPacketLength = 0; currentPacketAt = 0; } } } retval.valid = true; } catch (std::exception& e) { std::println("Something went wrong in serialized generic packet to generic game packet conversion: {}", e.what()); } return retval; } std::string NetQuake::Serialize(ServerInfoReply sirp) { std::string retval = ""; using namespace std::string_literals; retval += CONTROL_SERVER_INFO_REPLY; retval += sirp.address.ip + ":"; retval += std::to_string(sirp.address.port) + "\0"s; retval += sirp.hostname + "\0"s; retval += sirp.levelname + "\0"s; retval += sirp.currentPlayers; retval += sirp.maxPlayers; retval += sirp.netVersion; short retvalsize = retval.size() + 4; unsigned char retvalsizechar[2]; retvalsizechar[0] = retvalsize & 0xff; retvalsizechar[1] = (retvalsize >> 8) & 0xff; std::string fretval = "\x80"; fretval += "\0"s; fretval += retvalsizechar[1]; fretval += retvalsizechar[0]; fretval += retval; return fretval; } std::string NetQuake::Serialize(ConnectionRequestReply crr) { std::string retval = ""; using namespace std::string_literals; if (crr.reject) { retval = "\x82" + crr.reason + "\0"s; } else { unsigned char portchar[2]; portchar[1] = crr.port & 0xff; portchar[0] = (crr.port >> 8) & 0xff; retval = "\x81"s; retval += portchar[1]; retval += portchar[0]; retval += (unsigned char)crr.unknown[0]; retval += (unsigned char)crr.unknown[1]; } short retvalsize = retval.size() + 4; unsigned char retvalsizechar[2]; retvalsizechar[0] = retvalsize & 0xff; retvalsizechar[1] = (retvalsize >> 8) & 0xff; std::string fretval = "\x80"; fretval += "\0"s; fretval += retvalsizechar[1]; fretval += retvalsizechar[0]; fretval += retval; return fretval; } std::string NetQuake::AcknowledgePacket(int packet) { std::string retval; using namespace std::string_literals; retval += reinterpret_cast(packet); short retvalsize = retval.size() + 4; unsigned char retvalsizechar[2]; retvalsizechar[0] = retvalsize & 0xff; retvalsizechar[1] = (retvalsize >> 8) & 0xff; std::string fretval = "\0"s; fretval += "\x10"s; fretval += retvalsizechar[1]; fretval += retvalsizechar[0]; fretval += retval; return fretval; } void NetQuake::NetQuake_ControlServer(Internal::InternalRequestConsole* internalRequestConsole, Config::Config* config, bool* serverRunning) { asio::io_context io_context; asio::ip::udp::socket socket(io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), config->udpControl)); std::print("Started control server on {}\n", config->udpControl); NetQuake::ServerInfoReply sir; sir.address.ip = config->ip; sir.address.port = config->udpControl; sir.currentPlayers = 0; sir.maxPlayers = config->maxPlayers; sir.hostname = config->serverName; sir.levelname = "start"; sir.netVersion = NetQuake::QGOLD; while (true) { asio::ip::udp::endpoint remote_endpoint; std::array recv_buf; socket.receive_from(asio::buffer(recv_buf), remote_endpoint); NetQuake::NetworkGenericPacket gpac; gpac.type[0] = recv_buf[0]; gpac.type[1] = recv_buf[1]; gpac.length[0] = recv_buf[2]; gpac.length[1] = recv_buf[3]; for (int i = 4; i < recv_buf.size(); i++) { gpac.remainder += recv_buf[i]; } NetQuake::SerializedGenericPacket sgpac = NetQuake::Serialize(gpac); std::cout << "new packet\n"; switch (sgpac.type) { case NetQuake::NQ_CONTROL_PACKET: { NetQuake::GenericControlPacket gcpac(sgpac); std::cout << "GenericControlPacket: " << gcpac.opcode << "\n"; // the 0x01 i had so much trouble getting through if (gcpac.opcode == NetQuake::CONTROL_CONNECTION_REQUEST) { std::cout << "Connection Request\n"; int endpointRequestId = internalRequestConsole->newEndpointRequest(remote_endpoint); while (!internalRequestConsole->getEndpointRequest(endpointRequestId).ready) { // waiting for endpoint request to go through } Internal::NewEndpointRequestReply nerr = internalRequestConsole->getEndpointRequest(endpointRequestId); NetQuake::ConnectionRequestReply crr; crr.reject = nerr.error; crr.reason = nerr.reason; crr.port = nerr.port; crr.unknown[0] = config->unknownMagic[0]; crr.unknown[1] = config->unknownMagic[1]; asio::error_code e; socket.send_to(asio::buffer(NetQuake::Serialize(crr)), remote_endpoint, 0, e); /*NetQuake::ConnectionRequestReply crr; crr.reject = false; crr.port = 26000; crr.unknown[0] = config->unknownMagic[0]; crr.unknown[1] = config->unknownMagic[1]; asio::error_code e; socket.send_to(asio::buffer(NetQuake::Serialize(crr)), remote_endpoint, 0, e); */ } if (gcpac.opcode == NetQuake::CONTROL_SERVER_INFO_REQUEST) { NetQuake::ServerInfoRequest sirqpac(gcpac); std::cout << "ServerInfoRequest: server info request for game \"" << sirqpac.gameName << "\", netversion " << sirqpac.netVersion << "\n"; std::string serializedTemplate = NetQuake::Serialize(sir); std::ofstream templatestream("template.txt", std::ios::binary); templatestream << serializedTemplate; templatestream.close(); asio::error_code e; socket.send_to(asio::buffer(serializedTemplate), remote_endpoint, 0, e); std::cout << e.message() << "\n"; } } break; default: std::cout << "[WARNING] Unknown or unsupported packet came through.\n"; break; } // std::cout << gcpac.opcode << "\n"; } } void NetQuake::NetQuake_EndpointServer(Config::Config* config, int port, asio::ip::udp::endpoint startRemote) { asio::io_context io_context; asio::error_code e; asio::ip::udp::socket socket(io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), port)); socket.connect(startRemote); std::print("New endpoint server started on {}.\nClient Start Info: {} | {}\n", port, startRemote.address().to_string(), startRemote.port()); { // startup block ServerMessageBlock smb; smb.type = NQ_MESSAGE_BLOCK_END; smb.order = 100; ServerMessages::Print serverBanner; serverBanner.text = config->serverBanner; smb.serverMessages.push_back(&serverBanner); ServerMessages::ServerInfo serverInfo; serverInfo.mapName = "start"; serverInfo.maxClients = config->maxPlayers; serverInfo.multi = 1; serverInfo.numModels = 0; serverInfo.numSounds = 0; serverInfo.serverVersion = 15; smb.serverMessages.push_back(&serverInfo); ServerMessages::SignOn signOn; signOn.signon = 1; // prespawn(2=light,3=render) smb.serverMessages.push_back(&signOn); std::print("Tried sending server banner to client.\n"); socket.send_to(asio::buffer(smb.Serialize()), startRemote, 0, e); std::ofstream smbattempt("smb.txt"); smbattempt << smb.Serialize(); smbattempt.close(); std::print("Server banner should be sent.\n"); } while (true) { asio::ip::udp::endpoint rec; std::string recv_buf; socket.receive_from(asio::buffer(recv_buf), rec); std::cout << "packet on secondary line " << port << "\n"; std::cout << "received packet size: " << recv_buf.size() << "\n"; if(recv_buf.size() > 4) { try { NetQuake::NetworkGenericPacket ngp; ngp.type[0] = recv_buf[0]; ngp.type[1] = recv_buf[1]; ngp.length[0] = recv_buf[2]; ngp.length[1] = recv_buf[3]; for (int i = 4; i < recv_buf.size(); i++) { ngp.remainder += recv_buf[i]; } SerializedGenericPacket sgpac = Serialize(ngp); GenericGamePacket ggp = toGenericGamePacket(sgpac); if (ggp.type == NQ_MESSAGE_BLOCK_CHUNK || ggp.type == NQ_MESSAGE_BLOCK_END) { socket.send_to(asio::buffer(AcknowledgePacket(ggp.packetNumber)), rec); std::println("Server acknowledged packet {}", ggp.packetNumber); } } catch (...) { std::println("Error on packet on {}", port); break; } } else { std::println("Weird packet received... :shrug:"); } } } NetQuake::GenericControlPacket::GenericControlPacket(SerializedGenericPacket sgp) { if (sgp.type != NQ_CONTROL_PACKET) { throw std::runtime_error("Wrong packet type given to generic control packet constructor."); } opcode = (Control_OpCode)sgp.remainder[0]; remainder = sgp.remainder; remainder.erase(0, 1); } NetQuake::ServerInfoRequest::ServerInfoRequest(GenericControlPacket gcp) { if (gcp.opcode != CONTROL_SERVER_INFO_REQUEST) { throw std::runtime_error("Wrong packet type given to server info request constructor."); } std::string toParse = gcp.remainder; int i = 0; for (; toParse[i] != '\0'; i++) { gameName += toParse[i]; } netVersion = (NetProtocolVersion) toParse[i+1]; } NetQuake::ConnectionRequest::ConnectionRequest(GenericControlPacket gcp) { if (gcp.opcode != CONTROL_SERVER_INFO_REQUEST) { throw std::runtime_error("Wrong packet type given to server info request constructor."); } std::string toParse = gcp.remainder; int i = 0; for (; toParse[i] != '\0'; i++) { gameName += toParse[i]; } netVersion = (NetProtocolVersion)toParse[i + 1]; } NetQuake::Internal::InternalRequestConsole::InternalRequestConsole() { std::print("New Internal Request Console Created.\n"); } int NetQuake::Internal::InternalRequestConsole::newEndpointRequest(asio::ip::udp::endpoint ep) { NewEndpointRequestReply nerr; nerr.ready = false; nerr.startRemote = ep; newEndpointRequests.push_back(nerr); return newEndpointRequests.size() - 1; } NetQuake::Internal::NewEndpointRequestReply NetQuake::Internal::InternalRequestConsole::getEndpointRequest(int id) { return newEndpointRequests[id]; } void NetQuake::Internal::InternalRequestConsole::Update(Config::Config *config, std::vector* gameEndpointsVector) { for (int i = lastEndpointRequestTreated; i < newEndpointRequests.size(); i++) { if (!newEndpointRequests[i].ready) { if(gameEndpointsVector->size() < config->maxPlayers){ GameEndpoint* endpoint = new GameEndpoint(config, 1000+i, newEndpointRequests[i].startRemote); newEndpointRequests[i].port = 1000 + i; } else { newEndpointRequests[i].error = true; newEndpointRequests[i].reason = "too many players"; } newEndpointRequests[i].ready = true; } } } NetQuake::Internal::InternalRequestConsole::~InternalRequestConsole() { newEndpointRequests.clear(); std::print("Internal Request Console Destroyed.\n"); } NetQuake::GameEndpoint::GameEndpoint() { throw; // constructor musn't be empty } NetQuake::GameEndpoint::GameEndpoint(Config::Config* config, int port, asio::ip::udp::endpoint startRemote) { std::thread internalInternalThread(NetQuake_EndpointServer, config, port, startRemote); internalThread.swap(internalInternalThread); } NetQuake::GameEndpoint::~GameEndpoint() { internalThread.~thread(); } std::string NetQuake::ServerMessages::Print::Serialize() { using namespace std::string_literals; std::string retval = text; retval += "\0"s; return retval; } std::string NetQuake::ServerMessages::ServerInfo::Serialize() { std::string retval = ""; using namespace std::string_literals; retval += serverVersion; retval += maxClients; retval += mapName + "\0"s; for (int i = 0; i < precacheModels.size(); i++) { retval += precacheModels[i] + "\0"s; } retval += numModels; for (int i = 0; i < precacheSounds.size(); i++) { retval += precacheSounds[i] + "\0"s; } retval += numSounds; return retval; } std::string NetQuake::ServerMessages::SignOn::Serialize() { std::string retval = ""; retval += signon; return retval; } std::string NetQuake::ServerMessageBlock::Serialize() { std::string retval; using namespace std::string_literals; for (int i = 0; i < serverMessages.size(); i++) { retval += serverMessages[i]->type; retval += serverMessages[i]->Serialize(); } short retvalsize = retval.size() + 4; unsigned char retvalsizechar[2]; unsigned char typechar[2]; retvalsizechar[0] = retvalsize & 0xff; retvalsizechar[1] = (retvalsize >> 8) & 0xff; typechar[1] = this->type & 0xff; typechar[0] = (this->type >> 8) & 0xff; std::string fretval = std::string({(char)typechar[0]}); fretval += typechar[1]; fretval += retvalsizechar[1]; fretval += (char)retvalsizechar[0]; fretval += retval; return fretval; }