mpfw/mpfw_server/NetQuake.cpp
2025-07-26 16:27:54 -04:00

535 lines
16 KiB
C++

#include "NetQuake.h"
#include <iostream> // basic io
#include <vector> // dynamic arrays
#include <map> // keyval maps
#include <asio.hpp>
#include <asio/ts/buffer.hpp>
#include <asio/ts/internet.hpp>
#include <print>
#include <string>
#include <fstream>
#include <excpt.h>
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<char*>(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<char, 1024> 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<GameEndpoint>* 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;
}