#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct Item { int id; std::string name; std::string type; int power; int price; }; struct Note { int id; char title[64]; char content[256]; std::chrono::system_clock::time_point created_at; }; struct Fighter { int id; char name[32]; std::string fighter_class; int hp; int max_hp; int strength; int agility; int gold; std::vector inventory; std::unordered_map equipment; std::vector notes; std::string auth_token; std::chrono::system_clock::time_point last_activity; bool active; }; struct Battle { int id; int fighter1_id; int fighter2_id; int current_turn; bool active; std::vector logs; std::chrono::system_clock::time_point created_at; }; std::map fighters; std::map battles; std::map items; std::mutex state_mutex; std::atomic next_fighter_id{1}; std::atomic next_battle_id{1}; std::unordered_map client_fighter; std::unordered_map fighter_battle; std::ofstream log_file; std::mutex log_mutex; std::string get_timestamp() { auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; std::stringstream ss; ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S"); ss << '.' << std::setfill('0') << std::setw(3) << ms.count(); return ss.str(); } void log_event(const std::string& message) { std::lock_guard lock(log_mutex); if (log_file.is_open()) { log_file << "[" << get_timestamp() << "] " << message << std::endl; log_file.flush(); } } #define LOG(msg) log_event(msg) void init_items() { items[1] = {1, "Rusty Sword", "weapon", 5, 10}; items[2] = {2, "Steel Sword", "weapon", 10, 25}; items[3] = {3, "Fire Blade", "weapon", 20, 50}; items[4] = {4, "Leather Armor", "armor", 3, 15}; items[5] = {5, "Chain Mail", "armor", 8, 35}; items[6] = {6, "Plate Armor", "armor", 15, 75}; items[7] = {7, "Health Potion", "potion", 25, 5}; items[8] = {8, "Super Potion", "potion", 50, 12}; } std::string trim(const std::string& str) { size_t first = str.find_first_not_of(" \t\r\n"); if (first == std::string::npos) return ""; size_t last = str.find_last_not_of(" \t\r\n"); return str.substr(first, last - first + 1); } std::vector split(const std::string& str, char delim) { std::vector tokens; std::stringstream ss(str); std::string token; while (std::getline(ss, token, delim)) { tokens.push_back(token); } return tokens; } std::chrono::system_clock::time_point now() { return std::chrono::system_clock::now(); } int64_t seconds_since(std::chrono::system_clock::time_point t) { auto diff = now() - t; return std::chrono::duration_cast(diff).count(); } int create_fighter(const std::string& name, const std::string& fighter_class) { Fighter f; f.id = next_fighter_id++; strncpy(f.name, name.c_str(), sizeof(f.name)); f.name[sizeof(f.name) - 1] = '\0'; f.fighter_class = fighter_class; f.hp = 100; f.max_hp = 100; f.strength = 10; f.agility = 10; f.gold = 100; f.last_activity = now(); f.active = true; f.auth_token = "token_" + std::to_string(f.id) + "_" + std::to_string(rand()); if (fighter_class == "warrior") { f.strength = 15; f.max_hp = 120; f.hp = 120; } else if (fighter_class == "archer") { f.agility = 20; } else if (fighter_class == "mage") { f.strength = 20; f.max_hp = 80; f.hp = 80; } fighters[f.id] = f; return f.id; } std::string cmd_register(int client_fd, const std::vector& args) { if (args.size() < 2) return "ERROR: Usage: REGISTER "; std::string name = args[0]; std::string fighter_class = args[1]; if (fighter_class != "warrior" && fighter_class != "archer" && fighter_class != "mage") { return "ERROR: Invalid class. Choose: warrior, archer, mage"; } if (client_fighter.count(client_fd)) { return "ERROR: Already registered"; } int fid = create_fighter(name, fighter_class); client_fighter[client_fd] = fid; std::stringstream ss; ss << "OK: Registered fighter " << fid << " (" << name << ", " << fighter_class << ")"; ss << " HP:" << fighters[fid].hp << " STR:" << fighters[fid].strength << " AGI:" << fighters[fid].agility << " Gold:" << fighters[fid].gold; ss << " TOKEN:" << fighters[fid].auth_token; return ss.str(); } std::string cmd_fighter(int client_fd, const std::vector&) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; Fighter& f = fighters[fid]; f.last_activity = now(); std::stringstream ss; ss << "FIGHTER " << f.id << ": " << f.name << " (" << f.fighter_class << ")"; ss << " HP:" << f.hp << "/" << f.max_hp; ss << " STR:" << f.strength << " AGI:" << f.agility; ss << " Gold:" << f.gold; return ss.str(); } std::string cmd_buy(int client_fd, const std::vector& args) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; if (args.size() < 2) return "ERROR: Usage: BUY "; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; int item_id = std::atoi(args[0].c_str()); int quantity = std::atoi(args[1].c_str()); if (!items.count(item_id)) return "ERROR: Item not found"; if (quantity <= 0) return "ERROR: Invalid quantity"; Fighter& f = fighters[fid]; Item& item = items[item_id]; int total_price = item.price * quantity; if (total_price < 0) { f.gold += abs(total_price); f.last_activity = now(); return "OK: Overflow exploited! Gold:" + std::to_string(f.gold); } if (f.gold < total_price) { return "ERROR: Not enough gold. Need:" + std::to_string(total_price) + " Have:" + std::to_string(f.gold); } f.gold -= total_price; for (int i = 0; i < quantity; i++) { f.inventory.push_back(item_id); } f.last_activity = now(); return "OK: Bought " + std::to_string(quantity) + "x " + item.name + " for " + std::to_string(total_price) + " gold. Remaining:" + std::to_string(f.gold); } std::string cmd_inventory(int client_fd) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; Fighter& f = fighters[fid]; f.last_activity = now(); std::stringstream ss; ss << "INVENTORY (Gold:" << f.gold << "): "; if (f.inventory.empty()) { ss << "empty"; } else { std::map counts; for (int item_id : f.inventory) { counts[item_id]++; } bool first = true; for (auto& p : counts) { if (!first) ss << ", "; if (items.count(p.first)) { ss << items[p.first].name << "x" << p.second; } else { ss << "Unknown(" << p.first << ")x" << p.second; } first = false; } } ss << " | EQUIPPED: "; if (f.equipment.empty()) { ss << "none"; } else { bool first = true; for (auto& p : f.equipment) { if (!first) ss << ", "; if (items.count(p.second)) { ss << "slot" << p.first << ":" << items[p.second].name; } first = false; } } return ss.str(); } std::string cmd_equip(int client_fd, const std::vector& args) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; if (args.size() < 2) return "ERROR: Usage: EQUIP "; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; int slot = std::atoi(args[0].c_str()); int item_id = std::atoi(args[1].c_str()); Fighter& f = fighters[fid]; auto it = std::find(f.inventory.begin(), f.inventory.end(), item_id); if (it == f.inventory.end()) { return "ERROR: Item not in inventory"; } f.inventory.erase(it); f.equipment[slot] = item_id; f.last_activity = now(); if (items.count(item_id)) { Item& item = items[item_id]; if (item.type == "weapon") { f.strength += item.power; } else if (item.type == "armor") { f.max_hp += item.power * 2; f.hp += item.power * 2; } } return "OK: Equipped item " + std::to_string(item_id) + " to slot " + std::to_string(slot); } std::string cmd_fight(int client_fd, const std::vector& args) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; if (args.size() < 1) return "ERROR: Usage: FIGHT "; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; int opponent_id = std::atoi(args[0].c_str()); if (!fighters.count(opponent_id)) return "ERROR: Opponent not found"; if (opponent_id == fid) return "ERROR: Cannot fight yourself"; Fighter& f = fighters[fid]; Fighter& opponent = fighters[opponent_id]; if (fighter_battle.count(fid) || fighter_battle.count(opponent_id)) { return "ERROR: Already in battle"; } // Create battle Battle b; b.id = next_battle_id++; b.fighter1_id = fid; b.fighter2_id = opponent_id; b.current_turn = 1; b.active = true; b.created_at = now(); b.logs.push_back("Battle started: " + std::string(f.name) + " vs " + std::string(opponent.name)); battles[b.id] = b; fighter_battle[fid] = b.id; fighter_battle[opponent_id] = b.id; f.last_activity = now(); opponent.last_activity = now(); return "OK: Battle " + std::to_string(b.id) + " started against " + opponent.name; } std::string cmd_attack(int client_fd, const std::vector&) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; if (!fighter_battle.count(fid)) { return "ERROR: Not in battle"; } int bid = fighter_battle[fid]; if (!battles.count(bid)) return "ERROR: Battle not found"; Battle& b = battles[bid]; if (!b.active) return "ERROR: Battle already ended"; Fighter& attacker = fighters[fid]; int defender_id = (b.fighter1_id == fid) ? b.fighter2_id : b.fighter1_id; Fighter& defender = fighters[defender_id]; // Calculate damage int damage = attacker.strength + (attacker.agility / 2); // Apply equipment bonuses for (auto& eq : attacker.equipment) { if (items.count(eq.second) && items[eq.second].type == "weapon") { damage += items[eq.second].power; } } // Reduce by armor int armor = 0; for (auto& eq : defender.equipment) { if (items.count(eq.second) && items[eq.second].type == "armor") { armor += items[eq.second].power; } } damage = std::max(1, damage - armor); defender.hp -= damage; b.logs.push_back(std::string(attacker.name) + " attacks " + std::string(defender.name) + " for " + std::to_string(damage) + " damage (HP:" + std::to_string(defender.hp) + ")"); attacker.last_activity = now(); defender.last_activity = now(); // Check for death if (defender.hp <= 0) { b.active = false; defender.hp = 0; defender.active = false; // Winner gets gold int reward = defender.gold / 2; attacker.gold += reward; defender.gold = 0; b.logs.push_back(std::string(attacker.name) + " wins! Gets " + std::to_string(reward) + " gold"); fighter_battle.erase(fid); fighter_battle.erase(defender_id); return "OK: " + std::string(defender.name) + " defeated! You get " + std::to_string(reward) + " gold"; } b.current_turn++; return "OK: Dealt " + std::to_string(damage) + " damage. Enemy HP:" + std::to_string(defender.hp); } std::string cmd_heal(int client_fd) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; Fighter& f = fighters[fid]; // Find health potion in inventory auto it = std::find(f.inventory.begin(), f.inventory.end(), 7); // Health Potion id=7 if (it == f.inventory.end()) { // Try super potion it = std::find(f.inventory.begin(), f.inventory.end(), 8); if (it != f.inventory.end()) { f.inventory.erase(it); int heal = 50; f.hp = std::min(f.max_hp, f.hp + heal); f.last_activity = now(); return "OK: Used Super Potion. HP:" + std::to_string(f.hp); } return "ERROR: No potions in inventory"; } f.inventory.erase(it); int heal = 25; f.hp = std::min(f.max_hp, f.hp + heal); f.last_activity = now(); return "OK: Used Health Potion. HP:" + std::to_string(f.hp); } std::string cmd_status(int client_fd) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; if (!fighter_battle.count(fid)) { return "STATUS: Not in battle"; } int bid = fighter_battle[fid]; if (!battles.count(bid)) return "ERROR: Battle not found"; Battle& b = battles[bid]; Fighter& f = fighters[fid]; int opponent_id = (b.fighter1_id == fid) ? b.fighter2_id : b.fighter1_id; Fighter& opponent = fighters[opponent_id]; std::stringstream ss; ss << "BATTLE " << b.id << " (Turn " << b.current_turn << "): "; ss << f.name << " HP:" << f.hp << " vs " << opponent.name << " HP:" << opponent.hp; return ss.str(); } std::string cmd_logout(int client_fd) { if (client_fighter.count(client_fd)) { client_fighter.erase(client_fd); } return "OK: Logged out"; } std::string cmd_note_create(int client_fd, const std::vector& args) { if (!client_fighter.count(client_fd)) return "ERROR: Not registered"; if (args.size() < 2) return "ERROR: Usage: NOTE_CREATE <content>"; int fid = client_fighter[client_fd]; if (!fighters.count(fid)) return "ERROR: Fighter not found"; Fighter& f = fighters[fid]; Note note; note.id = f.notes.size() + 1; strncpy(note.title, args[0].c_str(), sizeof(note.title) - 1); note.title[sizeof(note.title) - 1] = '\0'; strncpy(note.content, args[1].c_str(), sizeof(note.content) - 1); note.content[sizeof(note.content) - 1] = '\0'; note.created_at = now(); f.notes.push_back(note); f.last_activity = now(); return "OK: Note created with id " + std::to_string(note.id); } std::string cmd_note_get(const std::vector<std::string>& args) { if (args.size() < 1) return "ERROR: Usage: NOTE_GET <auth_token> [note_id]"; std::string auth_token = args[0]; int note_id = (args.size() > 1) ? std::atoi(args[1].c_str()) : -1; Fighter* target_fighter = nullptr; for (auto& fp : fighters) { if (fp.second.auth_token == auth_token) { target_fighter = &fp.second; break; } } if (!target_fighter) { return "ERROR: Invalid auth token"; } if (note_id < 0) { std::stringstream ss; ss << "NOTES: "; for (const auto& note : target_fighter->notes) { ss << note.id << ":" << note.title << " "; } return ss.str(); } for (const auto& note : target_fighter->notes) { if (note.id == note_id) { return "NOTE " + std::to_string(note.id) + ": " + note.title + " | " + note.content; } } return "ERROR: Note not found"; } void handle_client(int client_fd) { char buffer[1024]; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); getpeername(client_fd, (struct sockaddr*)&client_addr, &client_len); std::string client_str = std::string(inet_ntoa(client_addr.sin_addr)) + ":" + std::to_string(ntohs(client_addr.sin_port)); while (true) { memset(buffer, 0, sizeof(buffer)); int bytes_read = read(client_fd, buffer, sizeof(buffer) - 1); if (bytes_read <= 0) { break; } std::string line(buffer); line = trim(line); if (line.empty()) continue; std::vector<std::string> parts = split(line, ' '); if (parts.empty()) continue; std::string cmd = parts[0]; std::vector<std::string> args(parts.begin() + 1, parts.end()); LOG(client_str + " " + line); std::string response; if (cmd == "REGISTER") { response = cmd_register(client_fd, args); } else if (cmd == "FIGHTER") { response = cmd_fighter(client_fd, args); } else if (cmd == "BUY") { response = cmd_buy(client_fd, args); } else if (cmd == "INVENTORY") { response = cmd_inventory(client_fd); } else if (cmd == "EQUIP") { response = cmd_equip(client_fd, args); } else if (cmd == "FIGHT") { response = cmd_fight(client_fd, args); } else if (cmd == "ATTACK") { response = cmd_attack(client_fd, args); } else if (cmd == "HEAL") { response = cmd_heal(client_fd); } else if (cmd == "STATUS") { response = cmd_status(client_fd); } else if (cmd == "NOTE_CREATE") { response = cmd_note_create(client_fd, args); } else if (cmd == "NOTE_GET") { response = cmd_note_get(args); } else if (cmd == "LOGOUT") { response = cmd_logout(client_fd); } else if (cmd == "QUIT" || cmd == "EXIT") { response = "BYE"; write(client_fd, response.c_str(), response.length()); break; } else { response = "ERROR: Unknown command: " + cmd; } response += "\n"; write(client_fd, response.c_str(), response.length()); } // Cleanup if (client_fighter.count(client_fd)) { int fid = client_fighter[client_fd]; if (fighter_battle.count(fid)) { int bid = fighter_battle[fid]; if (battles.count(bid)) { battles[bid].active = false; } fighter_battle.erase(fid); } client_fighter.erase(client_fd); } close(client_fd); } void cleanup_thread() { while (true) { std::this_thread::sleep_for(std::chrono::minutes(5)); std::lock_guard<std::mutex> lock(state_mutex); auto it = fighters.begin(); while (it != fighters.end()) { int64_t inactive_seconds = seconds_since(it->second.last_activity); if (inactive_seconds > 3600) { int fid = it->first; if (fighter_battle.count(fid)) { int bid = fighter_battle[fid]; if (battles.count(bid)) { battles[bid].active = false; } fighter_battle.erase(fid); } for (auto cf = client_fighter.begin(); cf != client_fighter.end(); ) { if (cf->second == fid) { close(cf->first); cf = client_fighter.erase(cf); } else { ++cf; } } it = fighters.erase(it); LOG("Cleanup: removed fighter " + std::to_string(fid)); } else { ++it; } } for (auto& bp : battles) { if (!bp.second.active) { int64_t age = seconds_since(bp.second.created_at); if (age > 3600) { fighter_battle.erase(bp.second.fighter1_id); fighter_battle.erase(bp.second.fighter2_id); } } } } } int main(int argc, char* argv[]) { int port = 31337; if (argc > 1) { port = std::atoi(argv[1]); } init_items(); signal(SIGPIPE, SIG_IGN); log_file.open("logs/server.log", std::ios::app); if (!log_file.is_open()) { log_file.open("/var/log/arena-battle/server.log", std::ios::app); } if (!log_file.is_open()) { log_file.open("server.log", std::ios::app); } LOG("Server started on port " + std::to_string(port)); std::thread cleanup(cleanup_thread); cleanup.detach(); int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { return 1; } int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { return 1; } if (listen(server_fd, 10) < 0) { return 1; } LOG("Listening on port " + std::to_string(port)); while (true) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); if (client_fd < 0) { continue; } std::string client_str = std::string(inet_ntoa(client_addr.sin_addr)) + ":" + std::to_string(ntohs(client_addr.sin_port)); LOG("Connect " + client_str); std::thread client_thread(handle_client, client_fd); client_thread.detach(); } close(server_fd); return 0; }