services
This commit is contained in:
@@ -0,0 +1,761 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
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<int> inventory;
|
||||
std::unordered_map<int, int> equipment;
|
||||
std::vector<Note> 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<std::string> logs;
|
||||
std::chrono::system_clock::time_point created_at;
|
||||
};
|
||||
|
||||
std::map<int, Fighter> fighters;
|
||||
std::map<int, Battle> battles;
|
||||
std::map<int, Item> items;
|
||||
std::mutex state_mutex;
|
||||
|
||||
std::atomic<int> next_fighter_id{1};
|
||||
std::atomic<int> next_battle_id{1};
|
||||
|
||||
std::unordered_map<int, int> client_fighter;
|
||||
std::unordered_map<int, int> 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<std::chrono::milliseconds>(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<std::mutex> 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<std::string> split(const std::string& str, char delim) {
|
||||
std::vector<std::string> 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<std::chrono::seconds>(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<std::string>& args) {
|
||||
if (args.size() < 2) return "ERROR: Usage: REGISTER <name> <class>";
|
||||
|
||||
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<std::string>&) {
|
||||
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<std::string>& args) {
|
||||
if (!client_fighter.count(client_fd)) return "ERROR: Not registered";
|
||||
if (args.size() < 2) return "ERROR: Usage: BUY <item_id> <quantity>";
|
||||
|
||||
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<int, int> 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<std::string>& args) {
|
||||
if (!client_fighter.count(client_fd)) return "ERROR: Not registered";
|
||||
if (args.size() < 2) return "ERROR: Usage: EQUIP <slot> <item_id>";
|
||||
|
||||
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<std::string>& args) {
|
||||
if (!client_fighter.count(client_fd)) return "ERROR: Not registered";
|
||||
if (args.size() < 1) return "ERROR: Usage: FIGHT <opponent_id>";
|
||||
|
||||
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<std::string>&) {
|
||||
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<std::string>& args) {
|
||||
if (!client_fighter.count(client_fd)) return "ERROR: Not registered";
|
||||
if (args.size() < 2) return "ERROR: Usage: NOTE_CREATE <title> <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;
|
||||
}
|
||||
Reference in New Issue
Block a user