// utils.cpp - Utility functions for ColumnLynx // Copyright (C) 2025 DcruBro // Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details. #include namespace ColumnLynx::Utils { void log(const std::string &msg) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout << "\033[0m[" << std::to_string(now) << " LOG] " << msg << std::endl; } void warn(const std::string &msg) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); std::cerr << "\033[33m[" << std::to_string(now) << " WARN] " << msg << "\033[0m" << std::endl; } void error(const std::string &msg) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); std::cerr << "\033[31m[" << std::to_string(now) << " ERROR] " << msg << "\033[0m" << std::endl; } void debug(const std::string &msg) { #if DEBUG || _DEBUG uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); std::cerr << "\033[95m[" << std::to_string(now) << " DEBUG] " << msg << "\033[0m" << std::endl; #else return; #endif } std::string getHostname() { #ifdef _WIN32 char hostname[256]; DWORD size = sizeof(hostname); if (GetComputerNameA(hostname, &size)) { return std::string(hostname); } else { return "UnknownHost"; } #else char hostname[256]; if (gethostname(hostname, sizeof(hostname)) == 0) { return std::string(hostname); } else { return "UnknownHost"; } #endif } std::string getVersion() { return "a0.5"; } unsigned short serverPort() { return 48042; } unsigned char protocolVersion() { return 1; } std::string bytesToHexString(const uint8_t* bytes, size_t length) { const char hexChars[] = "0123456789ABCDEF"; std::string hexString; hexString.reserve(length * 2); for (size_t i = 0; i < length; ++i) { uint8_t byte = bytes[i]; hexString.push_back(hexChars[(byte >> 4) & 0x0F]); hexString.push_back(hexChars[byte & 0x0F]); } return hexString; } std::vector hexStringToBytes(const std::string& hex) { // TODO: recover from errors if (hex.length() % 2 != 0) { throw std::invalid_argument("Hex string must have even length"); } auto hexValue = [](char c) -> uint8_t { if ('0' <= c && c <= '9') return c - '0'; if ('A' <= c && c <= 'F') return c - 'A' + 10; if ('a' <= c && c <= 'f') return c - 'a' + 10; throw std::invalid_argument("Invalid hex character"); }; size_t len = hex.length(); std::vector bytes; bytes.reserve(len / 2); for (size_t i = 0; i < len; i += 2) { uint8_t high = hexValue(hex[i]); uint8_t low = hexValue(hex[i + 1]); bytes.push_back((high << 4) | low); } return bytes; } std::vector getWhitelistedKeys() { // Currently re-reads the file every time, should be fine. // Advantage of it is that you don't need to reload the server binary after adding/removing keys. Disadvantage is re-reading the file every time. // I might redo this part. std::vector out; std::ifstream file("whitelisted_keys"); // TODO: This is hardcoded for now, make dynamic std::string line; while (std::getline(file, line)) { out.push_back(line); } return out; } std::unordered_map getConfigMap(std::string path) { // TODO: Currently re-reads every time. std::vector readLines; std::ifstream file(path); std::string line; while (std::getline(file, line)) { readLines.push_back(line); } // Parse them into the struct std::unordered_map config; char delimiter = '='; for (std::string &str : readLines) { std::stringstream ss(str); std::string key; std::string val; std::getline(ss, key, delimiter); std::getline(ss, val, delimiter); config.insert({ key, val }); } return config; } }