Files
Vangers/server/server.cpp

2056 lines
54 KiB
C++

#include "kdsplus.h"
#include "xerrhand.h"
#define LAG -3000
//#define EVENTS_LOG
#define SERVER_VERSION 1
#ifdef EVENTS_LOG
XStream fout("lst", XS_OUT);
# define IN_EVENTS_LOG(id) \
{ \
fout < "\t\t\t> [" <= ID < ":" <= (GLOBAL_CLOCK() >> 8) < ":" <= frame < "]" < #id < \
"\n"; \
}
# define IN_EVENTS_LOG1(id, code) \
{ \
fout < "\t\t\t> [" <= ID < ":" <= (GLOBAL_CLOCK() >> 8) < ":" <= frame < "]" < #id < \
": " <= code < "\n"; \
}
# define OUT_EVENTS_LOG(id) \
{ fout < "< [" <= ID < ":" <= (GLOBAL_CLOCK() >> 8) < ":" <= frame < "]" < #id < "\n"; }
# define OUT_EVENTS_LOG1(id, code) \
{ \
fout < "< [" <= ID < ":" <= (GLOBAL_CLOCK() >> 8) < ":" <= frame < "]" < #id < ": " <= \
code < "\n"; \
}
#else
# define IN_EVENTS_LOG(id)
# define IN_EVENTS_LOG1(id, code)
# define OUT_EVENTS_LOG(id)
# define OUT_EVENTS_LOG1(id, code)
#endif
#ifdef _DEBUG
# define DOUT(str) \
{ std::cout << str << " \n"; }
# define DOUT1(str, code) \
{ std::cout << str << ", code: " << code << " \n"; }
#else
# define DOUT(str)
# define DOUT1(str, code)
#endif
#define MOUT(str) \
{ std::cout << str << " \n"; }
#define MOUT1(str, code) \
{ std::cout << str << ", code: " << code << " \n"; }
const char *MP_GAMES_NAMES[NUMBER_MP_GAMES] = {"VAN_WAR", "MECHOSOMA", "PASSEMBLOSS"};
XStream stat_log;
/******************************************************************
Game
******************************************************************/
ServerData::ServerData() {
memset(this, 0, sizeof(ServerData));
GameType = UNCONFIGURED;
}
Game::Game(int ID) {
Game::ID = ID;
name[0] = 0;
client_version = 0;
birth_time = SDL_GetTicks();
next = prev = 0;
list = 0;
used_players_IDs = 0;
}
Game::~Game() {
#ifdef _DEBUG
save_result();
#endif
if (players.size() || removed_players.size()) {
switch (data.GameType) {
case VAN_WAR:
process_VAN_WAR_ratings();
break;
case MECHOSOMA:
process_MECHOSOMA_ratings();
break;
case PASSEMBLOSS:
process_PASSEMBLOSS_ratings();
break;
}
}
Player *p;
while ((p = players.first()) != 0) {
detach_player(p);
delete p;
}
removed_players.delete_all();
World *w;
while ((w = worlds.first()) != 0) {
worlds.remove(w);
delete w;
}
DOUT1("Clear game", ID);
}
int Game::attach_player(Player *player) {
for (int i = 0; i < 31; i++)
if (!(used_players_IDs & (1 << i))) {
used_players_IDs |= 1 << i;
player->server->clients.remove(player);
players.append(player);
player->client_mask = 1 << i;
player->game = this;
DOUT1("Player attached", i + 1);
return player->ID = i + 1;
}
return 0;
}
void Game::detach_player(Player *player) {
// if(player -> name && player -> password && player -> status == GAMING_STATUS)
// player -> server -> add_rating_data(player -> name,player ->
//password,data.GameType,player
//-> rating);
player->clear_object_queue(0);
if (player->world)
player->world->detach_player(player);
players.remove(player);
player->client_mask = 0;
player->game = 0;
DOUT1("Player detached", player->ID);
// player -> ID = 0;
}
void Game::get_object_ID_offsets(OutputEventBuffer &out_buffer, int client_ID) {
int offsets[16];
memset(offsets, 0, 16 * sizeof(int));
unsigned int inv_mask = ~client_ID;
Object *obj;
World *w = worlds.first();
while (w) {
obj = w->objects.first();
while (obj) {
if (CLIENT_ID(obj->ID) == client_ID &&
offsets[(obj->ID >> 16) & 63] < (obj->ID & 0xffff))
offsets[(obj->ID >> 16) & 63] = obj->ID & 0xffff;
obj = obj->next_alt;
}
w = w->next;
}
Player *p = players.first();
while (p) {
obj = p->inventory.first();
while (obj) {
if (CLIENT_ID(obj->ID) == client_ID &&
offsets[(obj->ID >> 16) & 63] < (obj->ID & 0xffff))
offsets[(obj->ID >> 16) & 63] = obj->ID & 0xffff;
obj = obj->next;
}
p = p->next;
}
obj = global_objects.first();
while (obj) {
if (CLIENT_ID(obj->ID) == client_ID && offsets[(obj->ID >> 16) & 63] < (obj->ID & 0xffff))
offsets[(obj->ID >> 16) & 63] = obj->ID & 0xffff;
obj->visibility &= inv_mask;
obj->last_update &= inv_mask;
obj->in_queue &= inv_mask;
obj->send_hide &= inv_mask;
obj->send_delete &= inv_mask;
obj = obj->next;
}
for (int i = 0; i < 16; i++)
out_buffer < (unsigned short)(offsets[i] ? offsets[i] + 1 : 0);
}
int Game::quant() {
int work_log = 0;
Player *p = players.first();
while (p) {
work_log += p->receive();
if (!p->is_alive()) {
Player *p_next = p->next;
used_players_IDs &= ~(1 << p->ID - 1);
detach_player(p);
if (p->status == GAMING_STATUS) {
p->status = FINISHED_STATUS;
put_event_for_all(PLAYERS_STATUS, p);
removed_players.append(p);
switch (data.GameType) {
case VAN_WAR:
process_VAN_WAR_ratings();
break;
case MECHOSOMA:
process_MECHOSOMA_ratings();
break;
case PASSEMBLOSS:
process_PASSEMBLOSS_ratings();
break;
}
} else
delete p;
p = p_next;
continue;
}
p = p->next;
}
p = players.first();
while (p) {
work_log += p->send();
p = p->next;
}
return work_log;
}
void Game::put_event_for_all(int code, Player *player) {
Player *p = players.first();
while (p) {
if (p != player)
p->code_queue.put(Event(code, player));
p = p->next;
}
}
void Game::process_direct_sending(Object *obj, unsigned int mask) {
Player *p = players.first();
while (p) {
if (mask & p->client_mask) {
obj->send_delete |= p->client_mask;
p->put_object(obj);
}
p = p->next;
}
}
void Game::get_total_list_of_players_data(OutputEventBuffer &out_buffer) {
out_buffer.begin_event(TOTAL_LIST_OF_PLAYERS_DATA);
out_buffer < (unsigned char)(players.size() + removed_players.size());
Player *p = players.first();
while (p) {
out_buffer < (unsigned char)(p->ID) < (unsigned char)p->status <
(unsigned char)(p->world ? p->world->ID : 0) < short(p->x) < short(p->y) < p->name <
char(0);
out_buffer.write((unsigned char *)&p->body, sizeof(PlayerBody));
p = p->next;
}
p = removed_players.first();
while (p) {
out_buffer < (unsigned char)(p->ID) < (unsigned char)p->status <
(unsigned char)(p->world ? p->world->ID : 0) < short(p->x) < short(p->y) < p->name <
char(0);
out_buffer.write((unsigned char *)&p->body, sizeof(PlayerBody));
p = p->next;
}
out_buffer.end_event();
}
void Game::check_global_objects(Player *player) {
Object *obj = global_objects.first();
unsigned int mask = player->client_mask;
while (obj) {
if (!(obj->last_update & mask)) {
player->put_object(obj);
obj->last_update |= mask;
}
obj = obj->next;
}
}
void Game::process_create_globals(Player *player, Object *obj) {
global_objects.append(obj);
obj->list = &global_objects;
Player *p = players.first();
while (p) {
if (p != player) {
p->put_object(obj);
obj->last_update |= p->client_mask;
} else
obj->last_update |= p->client_mask;
p = p->next;
}
}
void Game::process_update_globals(Player *player, Object *obj) {
obj->last_update = 0;
Player *p = players.first();
while (p) {
if (p != player) {
p->put_object(obj);
obj->last_update |= p->client_mask;
} else
obj->last_update |= p->client_mask;
p = p->next;
}
}
void Game::process_delete_globals(Object *obj) {
Player *p = players.first();
while (p) {
obj->send_delete |= p->client_mask;
p->put_object(obj);
p = p->next;
}
}
void Game::process_VAN_WAR_ratings() {
int total_account = 0;
int counter = 0;
double avr_rating = 0;
Player *p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
p->body.rating = (float)((p->body.kills - p->body.deaths));
total_account += p->body.kills;
avr_rating += p->body.rating;
counter++;
}
p = p->next;
}
p = removed_players.first();
while (p) {
p->body.rating = (float)((p->body.kills - p->body.deaths));
total_account += p->body.kills;
avr_rating += p->body.rating;
counter++;
p = p->next;
}
if (!counter)
return;
avr_rating /= (double)counter;
double total_weight = 0;
p = players.first();
while (p) {
if (p->status != INITIAL_STATUS)
total_weight += fabs(p->body.rating - avr_rating);
p = p->next;
}
p = removed_players.first();
while (p) {
total_weight += fabs(p->body.rating - avr_rating);
p = p->next;
}
if (total_weight < 0.01)
return;
total_weight /= 2.;
double factor = (double)total_account / (total_weight);
p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
p->body.rating = (float)((p->body.rating - avr_rating) * factor);
p->server->add_rating_data(p, VAN_WAR);
}
p = p->next;
}
p = removed_players.first();
while (p) {
p->body.rating = (float)((p->body.rating - avr_rating) * factor);
p->server->add_rating_data(p, VAN_WAR);
p = p->next;
}
}
void Game::process_MECHOSOMA_ratings() {
int counter = 0;
double avr_rating = 0;
Player *p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
p -> body.rating = (float)((p -> body.MechosomaStat.MaxTransitTime)*pow(1.1,p -> body.kills)*pow(0.9,p -> body.deaths));
avr_rating += p->body.rating;
counter++;
}
p = p->next;
}
p = removed_players.first();
while (p) {
p -> body.rating = (float)((p -> body.MechosomaStat.MaxTransitTime)*pow(1.1,p -> body.kills)*pow(0.9,p -> body.deaths));
avr_rating += p->body.rating;
counter++;
p = p->next;
}
if (!counter)
return;
avr_rating /= (double)counter;
double total_weight = 0;
p = players.first();
while (p) {
if (p->status != INITIAL_STATUS)
total_weight += fabs(p->body.rating - avr_rating);
p = p->next;
}
p = removed_players.first();
while (p) {
total_weight += fabs(p->body.rating - avr_rating);
p = p->next;
}
if (total_weight < 0.01)
return;
total_weight /= 2.;
double total_account =
2. * (double)(data.Mechosoma.ProductQuantity1 + data.Mechosoma.ProductQuantity2) * counter;
// if(data.Mechosoma.One_at_a_time)
// rating *= 2.;
double factor = total_account / total_weight;
p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
p->body.rating = (float)((p->body.rating - avr_rating) * factor);
p->server->add_rating_data(p, MECHOSOMA);
}
p = p->next;
}
p = removed_players.first();
while (p) {
p->body.rating = (float)((p->body.rating - avr_rating) * factor);
p->server->add_rating_data(p, MECHOSOMA);
p = p->next;
}
}
void Game::process_PASSEMBLOSS_ratings() {
int counter = 0;
double avr_rating = 0;
Player *p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
p -> body.rating = (float)((p -> body.PassemblossStat.TotalTime)*pow(1.1,p -> body.kills)*pow(0.9,p -> body.deaths));
avr_rating += p->body.rating;
counter++;
}
p = p->next;
}
p = removed_players.first();
while (p) {
p->body.rating =
(float)((p->body.PassemblossStat.TotalTime) * pow(1.1, p->body.kills) * pow(0.9, p->body.deaths));
avr_rating += p->body.rating;
counter++;
p = p->next;
}
if (!counter)
return;
avr_rating /= (double)counter;
double total_weight = 0;
p = players.first();
while (p) {
if (p->status != INITIAL_STATUS)
total_weight += fabs(p->body.rating - avr_rating);
p = p->next;
}
p = removed_players.first();
while (p) {
total_weight += fabs(p->body.rating - avr_rating);
p = p->next;
}
if (total_weight < 0.01)
return;
total_weight /= 2.;
double total_account = 5. * (double)(data.Passembloss.CheckpointsNumber * counter);
double factor = total_account / total_weight;
p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
p->body.rating = (float)((p->body.rating - avr_rating) * factor);
p->server->add_rating_data(p, PASSEMBLOSS);
}
p = p->next;
}
p = removed_players.first();
while (p) {
p->body.rating = (float)((p->body.rating - avr_rating) * factor);
p->server->add_rating_data(p, PASSEMBLOSS);
p = p->next;
}
}
void Game::save_result() {
if (!players.size() && !removed_players.size())
return;
XBuffer name;
name <= time(0) < ".rs" <= data.GameType;
XStream out(name.GetBuf(), XS_OUT);
out.write(&data, sizeof(ServerData));
int counter = 0;
int offset = out.tell();
out < counter;
Player *p = players.first();
while (p) {
if (p->status != INITIAL_STATUS) {
out < p->name < char(0);
out.write(&p->body, sizeof(PlayerBody));
counter++;
}
p = p->next;
}
p = removed_players.first();
while (p) {
out < p->name < char(0);
out.write(&p->body, sizeof(PlayerBody));
counter++;
p = p->next;
}
out.seek(offset, XS_BEG);
out < counter;
}
void Game::load_result(Server *server, char *name) {
players.delete_all();
removed_players.delete_all();
XStream in(name, XS_IN);
in.read(&data, sizeof(ServerData));
int counter;
in > counter;
XSocket sock;
for (int i = 0; i < counter; i++) {
Player *p = new Player(server, sock);
removed_players.append(p);
p->name = new char[256];
in > p->name;
in.read(&p->body, sizeof(PlayerBody));
}
switch (data.GameType) {
case VAN_WAR:
process_VAN_WAR_ratings();
break;
case MECHOSOMA:
process_MECHOSOMA_ratings();
break;
case PASSEMBLOSS:
process_PASSEMBLOSS_ratings();
break;
}
}
/******************************************************************
World
******************************************************************/
World::World(int id, int Vsize) {
ID = id;
number_of_objects = 0;
y_lists = new XTList<Object>[number_of_y_lists = (V_SIZE = Vsize) >> Y_SHIFT];
next = prev = 0;
list = 0;
}
World::~World() {
current_players.clear();
Object *obj;
while ((obj = objects.first()) != 0) {
delete_object(obj);
delete obj;
}
delete[] y_lists;
}
void World::attach_player(Player *player) {
player->world = this;
current_players.append(player);
player->x = player->y = player->y_half_size_of_screen = 0;
player->x_prev = player->y_prev = player->y_half_size_of_screen_prev = 0;
if (!player->status) {
player->status = GAMING_STATUS;
player->code_queue.put(Event(PLAYERS_STATUS, player));
player->game->put_event_for_all(PLAYERS_STATUS, player);
}
player->game->put_event_for_all(PLAYERS_WORLD, player);
}
void World::detach_player(Player *player) {
player->clear_object_queue(1);
// process_delete below won't place any objects to this player
current_players.remove(player);
player->world = 0;
// Clear Inventory
Object *obj = player->inventory.first();
while (obj) {
process_delete(obj);
OUT_EVENTS_LOG1(AUTO_DELETE_INVENTORY, obj->ID);
obj = obj->next;
}
// Clear Update Info and Private World's Objects
int client_ID = player->ID;
int inv_mask = ~player->client_mask;
obj = objects.first();
while (obj) {
if (CLIENT_ID(obj->ID) == client_ID && PRIVATE_OBJECT(obj->ID)) {
process_delete(obj);
OUT_EVENTS_LOG1(AUTO_DELETE, obj->ID);
} else {
obj->visibility &= inv_mask;
obj->last_update &= inv_mask;
obj->in_queue &= inv_mask;
obj->send_hide &= inv_mask;
obj->send_delete &= inv_mask;
}
obj = obj->next_alt;
}
// Clear Update Info of Inventories
Player *p = current_players.first();
while (p) {
obj = p->inventory.first();
while (obj) {
obj->visibility &= inv_mask;
obj->last_update &= inv_mask;
obj->in_queue &= inv_mask;
obj->send_hide &= inv_mask;
obj->send_delete &= inv_mask;
obj = obj->next;
}
p = p->next_alt;
}
player->game->put_event_for_all(PLAYERS_WORLD, player);
}
Object *World::search_object(int ID) {
return objects.search(ID);
}
void World::add_object(Object *obj) {
number_of_objects++;
objects.append(obj);
XTList<Object> *list = y_lists + ((obj->y >> Y_SHIFT) & (number_of_y_lists - 1));
list->append(obj);
obj->list = list;
}
void World::move_object(Object *obj) {
XTList<Object> *new_list = y_lists + ((obj->y >> Y_SHIFT) & (number_of_y_lists - 1));
if (new_list != obj->list) {
obj->list->remove(obj);
new_list->append(obj);
obj->list = new_list;
}
}
void World::delete_object(Object *obj) {
number_of_objects--;
objects.remove(obj);
if (obj->list) {
obj->list->remove(obj);
obj->list = 0;
}
}
int World::getDistY(int v0, int v1) {
int d = v0 - v1;
int ad = abs(d);
int dd = V_SIZE - ad;
if (ad <= dd)
return d;
return d < 0 ? d + V_SIZE : d - V_SIZE;
}
int World::check_visibility(Player *player, Object *object) {
if (abs(getDistY(object->y, player->y)) < player->y_half_size_of_screen + object->radius)
return 1;
return 0;
}
void World::process_create(Player *player, Object *obj) {
add_object(obj);
Player *p = current_players.first();
while (p) {
if (p != player) {
if (check_visibility(p, obj)) {
p->put_object(obj);
obj->visibility |= p->client_mask;
obj->last_update |= p->client_mask;
}
} else {
if (check_visibility(p, obj))
obj->visibility |= p->client_mask;
obj->last_update |= p->client_mask;
}
p = p->next_alt;
}
}
void World::process_update(Player *player, Object *obj) {
obj->last_update = 0;
Player *p = current_players.first();
if (NON_STATIC(obj->ID)) {
move_object(obj);
while (p) {
if (check_visibility(p, obj)) {
if (p != player)
p->put_object(obj);
obj->last_update |= p->client_mask;
obj->visibility |= p->client_mask;
obj->send_hide &= ~(p->client_mask);
} else {
if (obj->visibility & p->client_mask) {
p->put_object(obj); // Hide
obj->send_hide |= p->client_mask;
}
obj->visibility &= ~(p->client_mask);
if (p == player)
obj->last_update |= p->client_mask;
}
p = p->next_alt;
}
} else {
while (p) {
if (check_visibility(p, obj)) {
if (p != player)
p->put_object(obj);
obj->last_update |= p->client_mask;
obj->visibility |= p->client_mask;
} else {
obj->visibility &= ~(p->client_mask);
if (p == player)
obj->last_update |= p->client_mask;
}
p = p->next_alt;
}
}
}
void World::process_delete(Object *obj) {
Player *p = current_players.first();
while (p) {
obj->send_delete |= p->client_mask;
p->put_object(obj);
p = p->next_alt;
}
}
void World::process_set_position(Player *player) {
Object *obj;
unsigned int mask = player->client_mask;
if (player->y_half_size_of_screen_prev) { // not a first set_position
int update_y0, update_y1;
int hide_y0, hide_y1;
if (getDistY(player->y, player->y_prev) > 0) {
update_y0 =
((player->y_prev + player->y_half_size_of_screen_prev) & (V_SIZE - 1)) >> Y_SHIFT;
update_y1 = ((player->y + player->y_half_size_of_screen) & (V_SIZE - 1)) >> Y_SHIFT;
hide_y0 =
((player->y_prev - player->y_half_size_of_screen_prev) & (V_SIZE - 1)) >> Y_SHIFT;
hide_y1 = ((player->y - player->y_half_size_of_screen) & (V_SIZE - 1)) >> Y_SHIFT;
} else {
update_y1 =
((player->y_prev - player->y_half_size_of_screen_prev) & (V_SIZE - 1)) >> Y_SHIFT;
update_y0 = ((player->y - player->y_half_size_of_screen) & (V_SIZE - 1)) >> Y_SHIFT;
hide_y1 =
((player->y_prev + player->y_half_size_of_screen_prev) & (V_SIZE - 1)) >> Y_SHIFT;
hide_y0 = ((player->y + player->y_half_size_of_screen) & (V_SIZE - 1)) >> Y_SHIFT;
}
update_y1 = (update_y1 + 1) & (number_of_y_lists - 1);
hide_y1 = (hide_y1 + 1) & (number_of_y_lists - 1);
// Check for update
for (int y = update_y0; y != update_y1; y = (y + 1) & (number_of_y_lists - 1)) {
obj = y_lists[y].first();
while (obj) {
if (!(obj->visibility & mask) && check_visibility(player, obj)) {
obj->visibility |= mask;
if (!NON_STATIC(obj->ID)) {
if (!(obj->last_update & mask)) {
player->put_object(obj);
obj->last_update |= mask;
}
} else {
player->put_object(obj);
obj->send_hide &= ~mask;
obj->last_update |= mask;
}
}
obj = obj->next;
}
}
// Check for hide
for (int y = hide_y0; y != hide_y1; y = (y + 1) & (number_of_y_lists - 1)) {
obj = y_lists[y].first();
while (obj) {
if (obj->visibility & mask && !check_visibility(player, obj)) {
obj->visibility &= ~mask;
if (NON_STATIC(obj->ID)) {
player->put_object(obj);
obj->send_hide |= mask;
}
}
obj = obj->next;
}
}
} else {
// Check for update
int update_y0 = ((player->y - player->y_half_size_of_screen) & (V_SIZE - 1)) >> Y_SHIFT;
int update_y1 = ((player->y + player->y_half_size_of_screen) & (V_SIZE - 1)) >> Y_SHIFT;
for (int y = update_y0; y != update_y1; y = (y + 1) & (number_of_y_lists - 1)) {
obj = y_lists[y].first();
while (obj) {
if (check_visibility(player, obj)) {
if (!NON_STATIC(obj->ID)) {
if (!(obj->last_update & mask)) {
player->put_object(obj);
obj->last_update |= mask;
}
} else {
if (!((obj->last_update | obj->visibility) & mask)) {
player->put_object(obj);
obj->send_hide &= ~mask;
obj->last_update |= mask;
}
}
obj->visibility |= mask;
} else
obj->visibility &= ~mask;
obj = obj->next;
}
}
}
player->x_prev = player->x;
player->y_prev = player->y;
player->y_half_size_of_screen_prev = player->y_half_size_of_screen;
// Checking inventories
int send_position = IS_PAST(player->last_sent_position + 5000) ? 1 : 0;
Player *p = current_players.first();
while (p) {
if (p != player)
if (check_visibility(p, player)) {
// All's inventories to Me
obj = p->inventory.first();
while (obj) {
if (!(obj->last_update & player->client_mask)) {
player->put_object(obj);
obj->last_update |= player->client_mask;
}
obj = obj->next;
}
// My inventory to All
obj = player->inventory.first();
while (obj) {
if (!(obj->last_update & p->client_mask)) {
p->put_object(obj);
obj->last_update |= p->client_mask;
}
obj = obj->next;
}
} else if (send_position) {
p->code_queue.put(Event(PLAYERS_POSITION, player));
send_position = 2;
}
p = p->next_alt;
}
if (send_position == 2)
player->last_sent_position = SDL_GetTicks();
}
int World::check_visibility(Player *p1, Player *p2) {
if (abs(getDistY(p1->y, p2->y)) <
std::max(p1->y_half_size_of_screen, p2->y_half_size_of_screen) + PLAYERS_RADIUS)
return 1;
return 0;
}
void World::process_create_inventory(Player *player, Object *obj) {
number_of_objects++;
objects.append(obj);
player->inventory.append(obj);
obj->list = &(player->inventory);
Player *p = current_players.first();
while (p) {
if (p != player) {
if (check_visibility(p, player)) {
p->put_object(obj);
obj->last_update |= p->client_mask;
}
} else
obj->last_update |= p->client_mask;
p = p->next_alt;
}
}
void World::process_update_inventory(Player *player, Object *obj) {
obj->last_update = 0;
Player *p = current_players.first();
while (p) {
if (p != player) {
if (check_visibility(p, player)) {
p->put_object(obj);
obj->last_update |= p->client_mask;
}
} else
obj->last_update |= p->client_mask;
p = p->next_alt;
}
}
/******************************************************************
Player
******************************************************************/
Player::Player(Server *serv, XSocket &sock)
: in_buffer(IN_BUFFER_SIZE), out_buffer(OUT_BUFFER_SIZE), object_queue(OUT_QUEUE_SIZE),
code_queue(OUT_QUEUE_SIZE) {
server = serv;
socket = sock;
identificated = 0;
client_version = 0;
status = 0;
game = 0;
ID = 0;
client_mask = 0;
password = name = 0;
prev_rating = 0;
world = 0;
x = y = y_half_size_of_screen = 0;
x_prev = y_prev = y_half_size_of_screen_prev = 0;
last_IO_operation = SDL_GetTicks() + 20 * 1000;
last_sent_position = SDL_GetTicks();
birth_time = SDL_GetTicks();
// current_sent_object = 0;
time_to_remove = 0;
next = prev = 0;
list = 0;
next_alt = prev_alt = 0;
list_alt = 0;
}
Player::~Player() {
if (name)
delete name;
if (password)
delete password;
}
void Player::identification() {
char string[256];
memset(string, 0, 256);
char *request_str = "Vivat Sicher, Rock'n'Roll forever!!!";
char *response_str = "Enter, my son, please...";
char *kill_str = "I'm sorry, darling...";
unsigned int len;
if ((len = socket.receive(string, 255)) != 0) {
if (!strcmp(string, request_str)) {
identificated = 1;
if (len > strlen(request_str) + 1)
client_version = ((unsigned char *)string)[len - 1];
strcpy(string, response_str);
string[strlen(string) + 1] = SERVER_VERSION;
socket.send(string, strlen(string) + 2);
return;
}
if (!strcmp(string, kill_str))
GlobalExit = 1;
}
}
int Player::is_alive() {
if (socket()) {
if ((int)(SDL_GetTicks() - last_IO_operation) > 3000) {
short size = 0;
socket.send((const char *)&size, 2);
last_IO_operation = SDL_GetTicks();
}
return 1;
}
if (!game)
return 0;
if (!time_to_remove) {
time_to_remove = SDL_GetTicks() + WAITING_TO_REMOVE;
MOUT1("Connection have been lost", ID);
return 1;
}
return (int)(SDL_GetTicks() - time_to_remove) < 0 ? 1 : 0;
}
void Player::clear_object_queue(int keep_globals) {
int number = object_queue.tell();
for (; number > 0; number--) {
Object *obj = object_queue.get();
if (!obj)
continue;
if (keep_globals && !NON_GLOBAL_OBJECT(obj->ID)) {
object_queue.put(obj);
continue;
}
obj->in_queue &= ~client_mask;
if (obj->send_delete & client_mask && (obj->send_delete &= ~client_mask) == 0) {
if (NON_GLOBAL_OBJECT(obj->ID))
world->delete_object(obj);
else
// if(obj -> ID != DIRECT_SENDING_OBJECT)
if (obj->list)
obj->list->remove(obj);
delete obj;
}
}
}
int Player::receive() {
if (!socket)
return 0;
int recv_size = in_buffer.receive(socket);
if (recv_size) {
last_IO_operation = SDL_GetTicks();
// IN_EVENTS_LOG1(Receive_Block,recv_size);
}
int code;
while ((code = in_buffer.current_event()) != 0) {
if (!(code & AUXILIARY_EVENT))
code &= ~ECHO_EVENT;
switch (code) {
case CREATE_PERMANENT_OBJECT: {
int obj_ID = in_buffer.get_dword();
Object *obj = 0;
if (NON_GLOBAL_OBJECT(obj_ID)) {
if (!world) {
SERVER_ERROR("Create permanent object before set world", 0);
in_buffer.ignore_event();
break;
}
obj = world->search_object(obj_ID);
} else
obj = game->global_objects.search(obj_ID);
if (obj) {
if (obj->send_delete) {
if (NON_GLOBAL_OBJECT(obj->ID))
world->delete_object(obj);
else
obj->list->remove(obj);
obj->ID = 0;
} else {
obj->last_update |= client_mask;
put_object(obj);
DOUT1("Duplicated create", obj_ID);
in_buffer.ignore_event();
break;
}
}
obj = new Object();
obj->ID = obj_ID;
obj->client_ID = ID;
obj->time = in_buffer.get_dword();
obj->x = in_buffer.get_short();
obj->y = in_buffer.get_short();
obj->radius = in_buffer.get_short();
if (GET_OBJECT_TYPE(obj_ID) == NID_VANGER) {
x = obj->x;
y = obj->y;
y_half_size_of_screen = in_buffer.get_byte() << 1;
obj->body_size = in_buffer.event_size() - 16;
obj->body = new unsigned char[obj->body_size];
in_buffer.read(obj->body, obj->body_size);
world->process_create(!(in_buffer.current_event() & ECHO_EVENT) ? this : 0, obj);
world->process_set_position(this);
} else {
obj->body_size = in_buffer.event_size() - 15;
obj->body = new unsigned char[obj->body_size];
in_buffer.read(obj->body, obj->body_size);
if (!PLAYERS_OBJECT(obj_ID))
world->process_create(
!(in_buffer.current_event() & ECHO_EVENT) ? this : 0, obj);
else {
if (NON_GLOBAL_OBJECT(obj_ID))
world->process_create_inventory(this, obj);
else
game->process_create_globals(
!(in_buffer.current_event() & ECHO_EVENT) ? this : 0, obj);
}
}
IN_EVENTS_LOG1(CREATE_PERMANENT_OBJECT, obj->ID);
} break;
case DELETE_OBJECT: {
int obj_ID = in_buffer.get_dword();
Object *obj = 0;
if (NON_GLOBAL_OBJECT(obj_ID)) {
if (!world) {
SERVER_ERROR("Delete object before set world", 0);
in_buffer.ignore_event();
break;
}
obj = world->search_object(obj_ID);
} else
obj = game->global_objects.search(obj_ID);
if (!obj || obj->send_delete) {
IN_EVENTS_LOG1(SKIP_DELETE_OBJECT, obj_ID);
in_buffer.ignore_event();
break;
}
obj->client_ID = ID;
obj->time = in_buffer.get_dword();
obj->death_body_size = in_buffer.event_size() - 9;
if (obj->death_body_size > obj->body_size) {
if (obj->body)
delete obj->body;
obj->body = new unsigned char[obj->death_body_size];
}
in_buffer.read(obj->body, obj->death_body_size);
if (NON_GLOBAL_OBJECT(obj_ID))
world->process_delete(obj);
else
game->process_delete_globals(obj);
IN_EVENTS_LOG1(DELETE_OBJECT, obj->ID);
} break;
case UPDATE_OBJECT: {
int obj_ID = in_buffer.get_dword();
Object *obj = 0;
if (NON_GLOBAL_OBJECT(obj_ID)) {
if (!world) {
SERVER_ERROR("Update object before set world", 0);
in_buffer.ignore_event();
break;
}
obj = world->search_object(obj_ID);
} else
obj = game->global_objects.search(obj_ID);
if (!obj || obj->send_delete) {
IN_EVENTS_LOG1(SKIP_UPDATE_OBJECT, obj_ID);
in_buffer.ignore_event();
break;
}
obj->client_ID = ID;
obj->time = in_buffer.get_dword();
obj->x = in_buffer.get_short();
obj->y = in_buffer.get_short();
if (GET_OBJECT_TYPE(obj_ID) == NID_VANGER) {
x = obj->x;
y = obj->y;
y_half_size_of_screen = in_buffer.get_byte() << 1;
int update_size = in_buffer.event_size() - 14;
if (update_size > obj->body_size)
SERVER_ERROR("Update body size is greater than create size", obj_ID);
in_buffer.read(obj->body, update_size);
world->process_update(!(in_buffer.current_event() & ECHO_EVENT) ? this : 0, obj);
world->process_set_position(this);
} else {
int update_size = in_buffer.event_size() - 13;
if (update_size > obj->body_size)
SERVER_ERROR("Update body size is greater than create size", obj_ID);
in_buffer.read(obj->body, update_size);
if (!PLAYERS_OBJECT(obj_ID))
world->process_update(
!(in_buffer.current_event() & ECHO_EVENT) ? this : 0, obj);
else {
if (NON_GLOBAL_OBJECT(obj_ID))
world->process_update_inventory(this, obj);
else
game->process_update_globals(
!(in_buffer.current_event() & ECHO_EVENT) ? this : 0, obj);
}
}
IN_EVENTS_LOG1(UPDATE_OBJECT, obj->ID);
} break;
case SET_POSITION:
if (!world) {
SERVER_ERROR("Set position before set world", 0);
in_buffer.ignore_event();
break;
}
x = in_buffer.get_short();
y = in_buffer.get_short();
y_half_size_of_screen = in_buffer.get_short();
if (world)
world->process_set_position(this);
IN_EVENTS_LOG(SET_POSITION);
break;
case TOP_LIST_QUERY:
server->get_top_list(out_buffer, in_buffer.get_byte());
IN_EVENTS_LOG(TOP_LIST_QUERY);
break;
case GAMES_LIST_QUERY:
code_queue.put(GAMES_LIST_RESPONSE);
IN_EVENTS_LOG(GAMES_LIST_QUERY);
break;
case ATTACH_TO_GAME:
if (game || ID)
SERVER_ERROR("Player have already been attached to game", game->ID);
if ((game = server->games.search(in_buffer.get_int())) == 0 ||
(game->data.GameType == UNCONFIGURED && game->players.size()) ||
game->used_players_IDs == 0x7fffffff)
game = server->create_game();
game->attach_player(this);
game->check_global_objects(this);
out_buffer.begin_event(ATTACH_TO_GAME_RESPONSE);
out_buffer < game->ID < char(game->data.GameType != UNCONFIGURED ? 1 : 0) <
(unsigned int)round((double)game->birth_time * (256. / 1000.)) < (unsigned char)ID;
game->get_object_ID_offsets(out_buffer, ID);
out_buffer.end_event();
IN_EVENTS_LOG(ATTACH_TO_GAME);
break;
case RESTORE_CONNECTION: {
Player *p;
Game *game;
if ((game = server->games.search(in_buffer.get_int())) != 0 &&
(p = game->players.search(in_buffer.get_byte())) != 0) {
if (p->socket())
p->socket.close();
p->socket = socket;
p->time_to_remove = 0;
MOUT1("Connection have been restored", p->ID);
p->out_buffer.begin_event(RESTORE_CONNECTION_RESPONSE);
p->out_buffer < char(1);
p->out_buffer.end_event();
IN_EVENTS_LOG(RESTORE_CONNECTION);
} else {
out_buffer.begin_event(RESTORE_CONNECTION_RESPONSE);
out_buffer < char(0);
out_buffer.end_event();
IN_EVENTS_LOG(RESTORE_CONNECTION);
}
} break;
case REGISTER_NAME: {
if (name)
delete name;
int name_len = strlen(in_buffer(in_buffer.tell()));
name = new char[name_len + 2];
in_buffer > name;
name[name_len + 1] = 0;
int number = 0;
Player *p = game->players.first();
while (p) {
if (p != this && p->name && !strcmp(p->name, name)) {
name[name_len] = '0' + ++number;
p = game->players.first();
} else
p = p->next;
}
if (password)
delete password;
password = new char[strlen(in_buffer(in_buffer.tell())) + 1];
in_buffer > password;
game->put_event_for_all(PLAYERS_NAME, this);
IN_EVENTS_LOG(REGISTER_NAME);
break;
}
case SET_WORLD: {
if (world) {
SERVER_ERROR("Duplicated set world", ID);
in_buffer.ignore_event();
break;
}
int world_ID = in_buffer.get_byte();
int world_y_size = in_buffer.get_short();
int world_status = 0;
if ((world = game->worlds.search(world_ID)) == 0) {
world = new World(world_ID, world_y_size);
game->worlds.append(world);
world_status = 1;
} else if (world_y_size != world->V_SIZE)
SERVER_ERROR("Incorrect world Y size", world_y_size * 100000 + world->V_SIZE);
out_buffer.begin_event(SET_WORLD_RESPONSE);
out_buffer < (unsigned char)world_ID;
out_buffer < (unsigned char)world_status;
out_buffer.end_event();
world->attach_player(this);
IN_EVENTS_LOG(SET_WORLD);
OUT_EVENTS_LOG1(SET_WORLD_RESPONSE, world_status);
} break;
case LEAVE_WORLD:
if (!world) {
SERVER_ERROR("Leave world before set", 0);
in_buffer.ignore_event();
break;
}
world->detach_player(this);
IN_EVENTS_LOG(STOP_QUERY);
break;
case SERVER_TIME_QUERY:
out_buffer.begin_event(SERVER_TIME);
out_buffer < (unsigned int)GLOBAL_CLOCK();
out_buffer.end_event();
IN_EVENTS_LOG(SERVER_TIME_QUERY);
OUT_EVENTS_LOG(SERVER_TIME);
break;
case SET_GAME_DATA: {
if (game->data.GameType != UNCONFIGURED) {
DOUT1("Attempt to reassign game data", game->ID);
}
in_buffer > game->name;
int size = in_buffer.event_size() - 1 - strlen(game->name) - 1;
if (size != sizeof(ServerData))
SERVER_ERROR("Incorrect Server Data", size);
in_buffer.read((unsigned char *)&game->data, sizeof(ServerData));
game->client_version = client_version;
IN_EVENTS_LOG(SET_GAME_DATA);
} break;
case GET_GAME_DATA:
code_queue.put(GAME_DATA_RESPONSE);
IN_EVENTS_LOG(GET_GAME_DATA);
break;
case SET_PLAYER_DATA: {
int size = in_buffer.event_size() - 1;
if (size != sizeof(PlayerBody))
SERVER_ERROR("Incorrect Player Body", size);
static PlayerBody prev_body;
prev_body = body;
in_buffer.read((unsigned char *)&body, sizeof(PlayerBody));
switch (game->data.GameType) {
case VAN_WAR:
if (body.kills != prev_body.kills || body.deaths != prev_body.deaths ||
body.rating != prev_body.rating) {
game->process_VAN_WAR_ratings();
if (fabs(body.rating - prev_body.rating) > 0.01)
code_queue.put(Event(PLAYERS_RATING, this));
}
break;
case MECHOSOMA:
if (body.MechosomaStat.MaxTransitTime) {
game->process_MECHOSOMA_ratings();
if (fabs(body.rating - prev_body.rating) > 0.01)
code_queue.put(Event(PLAYERS_RATING, this));
}
break;
case PASSEMBLOSS:
if (body.PassemblossStat.TotalTime) {
game->process_PASSEMBLOSS_ratings();
if (fabs(body.rating - prev_body.rating) > 0.01)
code_queue.put(Event(PLAYERS_RATING, this));
}
break;
}
game->put_event_for_all(PLAYERS_DATA, this);
IN_EVENTS_LOG(SET_PLAYER_DATA);
} break;
case TOTAL_PLAYERS_DATA_QUERY:
code_queue.put(TOTAL_LIST_OF_PLAYERS_DATA);
IN_EVENTS_LOG(TOTAL_PLAYERS_DATA_QUERY);
break;
case DIRECT_SENDING: {
unsigned int mask = in_buffer.get_dword();
Object *obj = new Object();
obj->ID = DIRECT_SENDING_OBJECT;
obj->client_ID = ID;
obj->time = GLOBAL_CLOCK();
obj->body_size = in_buffer.event_size() - 5;
obj->body = new unsigned char[obj->body_size];
in_buffer.read(obj->body, obj->body_size);
game->process_direct_sending(obj, mask);
IN_EVENTS_LOG(DIRECT_SENDING);
} break;
case CLOSE_SOCKET:
socket.close();
time_to_remove = SDL_GetTicks();
break;
default:
SERVER_ERROR("Incorrect event ID", in_buffer.current_event());
}
in_buffer.next_event();
}
return recv_size;
}
int Player::send() {
if (!socket)
return 0;
Object *obj;
int code, total_sent = 0;
for (;;) {
if (out_buffer.tell()) {
int sent;
total_sent += sent = out_buffer.send(socket);
last_IO_operation = SDL_GetTicks();
// OUT_EVENTS_LOG1(Send_Block,sent);
if (out_buffer.tell())
return total_sent;
}
// if(!current_sent_object)
// current_sent_object = object_queue.get();
// if(current_sent_object && current_sent_object -> time + LAG < GLOBAL_CLOCK())
// obj = current_sent_object;
// current_sent_object = 0;
// Object's Queue
if (!object_queue.empty()) {
obj = object_queue.get();
obj->in_queue &= ~client_mask;
// Check for deleted objects
if (!obj->ID) {
if ((obj->send_delete &= ~client_mask) == 0)
delete obj;
continue;
}
if (obj->send_delete) { // !!!
if (obj->ID != DIRECT_SENDING_OBJECT) {
out_buffer.begin_event(DELETE_OBJECT);
out_buffer < obj->ID < (unsigned char)obj->client_ID < obj->time;
out_buffer.write(obj->body, obj->death_body_size);
out_buffer.end_event();
OUT_EVENTS_LOG1(DELETE_OBJECT, obj->ID);
if ((obj->send_delete &= ~client_mask) == 0) {
if (NON_GLOBAL_OBJECT(obj->ID))
world->delete_object(obj);
else
obj->list->remove(obj);
delete obj;
}
} else {
out_buffer.begin_event(DIRECT_RECEIVING);
out_buffer < (unsigned char)obj->client_ID;
out_buffer.write(obj->body, obj->body_size);
out_buffer.end_event();
OUT_EVENTS_LOG(DIRECT_RECEIVING);
if ((obj->send_delete &= ~client_mask) == 0)
delete obj;
}
continue;
}
if (!(obj->send_hide & client_mask)) {
out_buffer.begin_event(UPDATE_OBJECT);
out_buffer < obj->ID < (unsigned char)obj->client_ID < obj->time < (short)obj->x <
(short)obj->y;
out_buffer.write(obj->body, obj->body_size);
out_buffer.end_event();
OUT_EVENTS_LOG1(UPDATE_OBJECT, obj->ID);
continue;
} else {
out_buffer.begin_event(HIDE_OBJECT);
out_buffer < obj->ID;
out_buffer.end_event();
OUT_EVENTS_LOG1(HIDE_OBJECT, obj->ID);
obj->send_hide &= ~client_mask;
continue;
}
}
// Code Queue
if (!code_queue.empty()) {
Event event = code_queue.get();
code = event.code;
Player *player = event.pointer;
switch (code) {
case GAMES_LIST_RESPONSE:
server->get_games_list(out_buffer, client_version);
OUT_EVENTS_LOG(GAMES_LIST_RESPONSE);
break;
case TOTAL_LIST_OF_PLAYERS_DATA:
game->get_total_list_of_players_data(out_buffer);
OUT_EVENTS_LOG(TOTAL_LIST_OF_PLAYERS_DATA);
break;
case GAME_DATA_RESPONSE:
out_buffer.begin_event(GAME_DATA_RESPONSE);
out_buffer < game->name < char(0);
out_buffer.write(&game->data, sizeof(ServerData));
out_buffer.end_event();
OUT_EVENTS_LOG(GAME_DATA_RESPONSE);
break;
case PLAYERS_NAME:
out_buffer.begin_event(PLAYERS_NAME);
out_buffer < (unsigned char)player->ID < player->name < char(0);
out_buffer.end_event();
OUT_EVENTS_LOG(PLAYERS_NAME);
break;
case PLAYERS_POSITION:
out_buffer.begin_event(PLAYERS_POSITION);
out_buffer < (unsigned char)player->ID < short(player->x) < short(player->y);
out_buffer.end_event();
OUT_EVENTS_LOG(PLAYERS_POSITION);
break;
case PLAYERS_WORLD:
out_buffer.begin_event(PLAYERS_WORLD);
out_buffer < (unsigned char)player->ID <
(unsigned char)(player->world ? player->world->ID : 0);
out_buffer.end_event();
OUT_EVENTS_LOG(PLAYERS_WORLD);
break;
case PLAYERS_DATA:
out_buffer.begin_event(PLAYERS_DATA);
out_buffer < (unsigned char)player->ID;
out_buffer.write(&(player->body), sizeof(PlayerBody));
out_buffer.end_event();
OUT_EVENTS_LOG(PLAYERS_DATA);
break;
case PLAYERS_RATING:
out_buffer.begin_event(PLAYERS_RATING);
out_buffer < (unsigned char)player->ID < (float)player->body.rating;
out_buffer.end_event();
OUT_EVENTS_LOG(PLAYERS_RATING);
break;
case PLAYERS_STATUS:
out_buffer.begin_event(PLAYERS_STATUS);
out_buffer < (unsigned char)player->ID < (unsigned char)player->status;
out_buffer.end_event();
OUT_EVENTS_LOG(PLAYERS_STATUS);
break;
default:
SERVER_ERROR("Incorrect event ID in code queue", code);
}
continue;
}
return total_sent;
}
}
void Player::put_object(Object *object) {
if (!(object->in_queue & client_mask)) {
object->in_queue |= client_mask;
object_queue.put(object);
}
}
/******************************************************************
Object
******************************************************************/
Object::Object() {
ID = 0;
time = 0;
x = y = radius = 0;
visibility = 0;
last_update = 0;
in_queue = 0;
send_hide = 0;
send_delete = 0;
death_body_size = body_size = 0;
body = 0;
next = prev = 0;
list = 0;
next_alt = prev_alt = 0;
list_alt = 0;
}
Object::~Object() {
if (body)
delete body;
}
/***********************************************************************
Input Event Buffer
***********************************************************************/
InputEventBuffer::InputEventBuffer(unsigned int size): XBuffer(size) {
reset();
}
void InputEventBuffer::reset() {
next_event_pointer = 0;
filled_size = 0;
offset = 0;
}
int InputEventBuffer::receive(XSocket &sock) {
if (next_event_pointer != tell())
ErrH.Abort("Incorrect events reading");
if (next_event_pointer) {
if (filled_size != next_event_pointer)
memmove(address(), address() + next_event_pointer, filled_size - next_event_pointer);
filled_size -= next_event_pointer;
offset = next_event_pointer = 0;
}
int add_size = sock.receive((char *)address() + filled_size, length() - filled_size);
filled_size += add_size;
next_event();
return add_size;
}
int InputEventBuffer::next_event() {
event_ID = 0;
if (next_event_pointer + 2 > filled_size)
return 0;
if (next_event_pointer != tell())
ErrH.Abort("Incorrect events reading");
size_of_event = get_short();
unsigned int new_pointer = next_event_pointer + size_of_event + 2;
if (new_pointer > filled_size) {
set(next_event_pointer);
return 0;
}
next_event_pointer = new_pointer;
return (event_ID = get_byte());
}
void InputEventBuffer::ignore_event() {
event_ID = 0;
set(next_event_pointer);
}
/***********************************************************************
Output Event Buffer
***********************************************************************/
OutputEventBuffer::OutputEventBuffer(unsigned int size): XBuffer(size) {
pointer_to_size_of_event = -1;
}
int OutputEventBuffer::send(XSocket &sock) {
if (pointer_to_size_of_event != -1)
SERVER_ERROR("There wasn't the end of the event", 0);
unsigned int sent = sock.send(buf, tell());
if (sent == tell())
init();
else {
memmove(buf, buf + sent, tell() - sent);
offset -= sent;
}
return sent;
}
void OutputEventBuffer::begin_event(int event_ID) {
if (pointer_to_size_of_event != -1)
SERVER_ERROR("There wasn't the end of the event", 0);
pointer_to_size_of_event = offset;
*this < short(0) < (unsigned char)event_ID;
}
void OutputEventBuffer::end_event() {
if (pointer_to_size_of_event == -1)
SERVER_ERROR("There wasn't the begining of the event", 0);
int off = tell();
set(pointer_to_size_of_event);
*this < short(off - pointer_to_size_of_event - 2);
set(off);
pointer_to_size_of_event = -1;
}
/******************************************************************
Server
******************************************************************/
Server::Server(int main_port, int broadcast_port, int time_to_live) {
if (!main_socket.listen(main_port)) {
XBuffer err;
err < "Vangers Server is already running or TCP/IP port " <= main_port <
" is used by another application";
ErrH.Abort(err.GetBuf());
// SERVER_ERROR("Unable to create Server",main_port);
}
std::cout << "Main TCP/IP port: " << main_port << std::endl;
load_rating_list("Rating.lst");
transferring = 0;
next_broadcast = 0;
games_IDs_counter = 0;
Server::time_to_live = time_to_live;
time_to_destroy = 0;
if (StatLogging)
stat_log.open("VangersServer.log", XS_OUT);
for (int i = 0; i < NUMBER_MP_GAMES; i++)
n_games[i] = n_players_max[i] = n_players_sum[i] = playing_time_max[i] =
playing_time_sum[i] = 0;
}
Server::~Server() {
save_rating_list("Rating.lst");
RatingData *p;
while ((p = rating_list.first()) != 0) {
rating_list.remove(p);
delete p;
}
clear();
}
void Server::clear() {
Game *g;
while ((g = games.first()) != 0) {
games.remove(g);
delete g;
}
Player *p;
while ((p = clients.first()) != 0) {
clients.remove(p);
delete p;
}
DOUT("Clear Server");
}
Game *Server::create_game() {
Game *game = new Game(++games_IDs_counter);
games.append(game);
DOUT1("Game created", games_IDs_counter);
return game;
}
int Server::check_new_clients() {
if (!main_socket)
return 0;
XSocket &&sock = main_socket.accept();
if (!sock)
return 0;
Player *player = new Player(this, sock);
clients.append(player);
DOUT("Client attached");
return 1;
}
int Server::clients_quant() {
int work_log = 0;
Player *p = clients.first();
while (p) {
if (p->identificated) {
work_log += p->receive();
work_log += p->send();
} else
p->identification();
if (!p->is_alive()) {
Player *p_next = p->next;
// DOUT("Client removed");
clients.remove(p);
delete p;
p = p_next;
} else
p = p->next;
}
return work_log;
}
void Server::consoleReport(int players) {
static int g = -1;
static int p = -1;
static int c = -1;
if (g == games.size() && p == players && c == clients.size())
return;
g = games.size();
p = players;
c = clients.size();
std::cout << "Games: " << games.size() << " "
<< "Players: " << players << " "
<< "Clients: " << clients.size() << " "
<< "\n";
}
int Server::quant() {
// fout < "Quant: " <= frame < "\t" <= SDL_GetTicks() < "\n";
if (next_broadcast < SDL_GetTicks()) {
next_broadcast = SDL_GetTicks() + 1000;
int n_players = 0;
Game *g = games.first();
while (g) {
n_players += g->players.size();
g = g->next;
}
consoleReport(n_players);
if (time_to_live && !(transferring | games.size() | n_players | clients.size())) {
if (!time_to_destroy)
time_to_destroy = SDL_GetTicks() + time_to_live * 1000;
} else
time_to_destroy = 0;
if (time_to_destroy && IS_PAST(time_to_destroy))
GlobalExit = 1;
transferring = 0;
// report();
}
int transf = check_new_clients() + clients_quant() + games_quant();
transferring += transf;
return transf;
}
int Server::games_quant() {
int work_log = 0;
Game *g = games.first();
while (g) {
work_log += g->quant();
if (!g->players.size() && !leave_empty_games) {
analyse_statistics(g);
Game *g_next = g->next;
games.remove(g);
delete g;
g = g_next;
continue;
}
g = g->next;
}
return work_log;
}
void Server::analyse_statistics(Game *g) {
int playing_time = round(double(TIME_INTERVAL(g->birth_time)) / (60 * 1000));
int n_players = g->removed_players.size();
if (!n_players || playing_time < 5)
return;
int type = g->data.GameType;
n_games[type]++;
if (n_players_max[type] < n_players)
n_players_max[type] = n_players;
n_players_sum[type] += n_players;
if (playing_time_max[type] < playing_time)
playing_time_max[type] = playing_time;
playing_time_sum[type] += playing_time;
std::cout << "\nGames' Statistics (maximum/average):\n";
for (int i = 0; i < NUMBER_MP_GAMES; i++) {
if (!n_games[i])
continue;
std::cout << (char *)MP_GAMES_NAMES[i] << ": " << n_games[i]
<< "; Players: " << n_players_max[i] << "/"
<< round(double(n_players_sum[i]) / n_games[i])
<< "; Time of game: " << playing_time_max[i] << "/"
<< round(double(playing_time_sum[i]) / n_games[i]) << " min.\n";
}
std::cout << "Last game: ";
time_t aclock;
time(&aclock);
struct tm *newtime = localtime(&aclock);
char *strtime = asctime(newtime);
strtime[strlen(strtime) - 1] = 0;
std::cout << (char *)MP_GAMES_NAMES[type] << "; " << strtime << "; Players: " << n_players
<< "; Time of game: " << playing_time << " min.\n\n";
if (StatLogging) {
stat_log < strtime < "; " < (char *)MP_GAMES_NAMES[type] < "; Players: " <= n_players <
"; Time of game: " <= playing_time < " min.\n";
Player *p = g->removed_players.first();
while (p) {
int IP = p->socket.addr.host;
if (IP)
stat_log < "IP: " <= (IP & 0xff) < "." <= ((IP >> 8) & 0xff) < "." <=
((IP >> 16) & 0xff) < "." <= ((IP >> 24) & 0xff) < "; Time of Existence: " <=
round(double(TIME_INTERVAL(p->birth_time)) / (60 * 1000)) < " min.; Rating: " <=
round(p->body.rating) < "\n";
p = p->next;
}
stat_log < "\n";
}
}
void Server::get_games_list(OutputEventBuffer &out_buffer, int client_version) {
int num = 0;
Game *g = games.first();
while (g) {
if (g->data.GameType != UNCONFIGURED && g->used_players_IDs != 0x7fffffff &&
g->client_version == client_version)
num++;
g = g->next;
}
out_buffer.begin_event(GAMES_LIST_RESPONSE);
out_buffer < (unsigned char)num;
g = games.first();
while (g) {
if (g->data.GameType != UNCONFIGURED && g->used_players_IDs != 0x7fffffff &&
g->client_version == client_version) {
out_buffer < g->ID < g->name < ": " <= g->players.size() < " " <
(g->data.GameType == VAN_WAR ? "V" : (g->data.GameType == MECHOSOMA ? "M" : "P"));
int t = (SDL_GetTicks() - g->birth_time) / 1000;
int ts = t % 60;
t /= 60;
int tm = t % 60;
t /= 60;
int th = t % 24;
out_buffer < " " <= th < ":" <= tm < ":" <= ts < char(0);
}
g = g->next;
}
out_buffer.end_event();
}
void Server::report() {
std::cout << "\n\nReport's time: " << SECONDS() << "\n";
Player *p;
World *w;
Game *s = games.first();
while (s) {
std::cout << "Game " << (unsigned char)(s->ID) << ": ";
if (!(s->players.size() | s->worlds.size())) {
std::cout << "Empty\n\n";
s = s->next;
continue;
}
std::cout << "\nPlayers: " << s->players.size() << "\tWorlds: " << s->worlds.size()
<< "\tglobal objects: " << s->global_objects.size() << "\n";
p = s->players.first();
while (p) {
std::cout << "Player: " << p->ID << "\tsocket: " << p->socket()
<< "\tworld: " << (p->world ? p->world->ID : 0)
<< "\tinventory: " << p->inventory.size() << "\t " << p->name << "\n";
p = p->next;
}
w = s->worlds.first();
while (w) {
std::cout << "World: " << w->ID << "\ttotal objects: " << w->number_of_objects << "\n";
w = w->next;
}
std::cout << "\n";
s = s->next;
}
}
/******************************************************************
Rating List
******************************************************************/
RatingData::RatingData(char *_name, char *_password, int _MP_game, float _rating) {
name = new char[strlen(_name) + 1];
strcpy(name, _name);
password = new char[strlen(_password) + 1];
strcpy(password, _password);
MP_game = _MP_game;
rating = _rating;
next = prev = 0;
list = 0;
}
RatingData::~RatingData() {
delete name;
delete password;
}
void Server::load_rating_list(const char *fname) {
XStream ff(0);
if (!ff.open(fname, XS_IN))
return;
XBuffer buf(ff.size());
ff.read(buf.address(), ff.size());
char name[257];
char password[257];
int MP_game;
float rating;
RatingData *p;
int num_data;
buf > num_data;
for (int i = 0; i < num_data; i++) {
buf > name > password > MP_game > rating;
p = new RatingData(name, password, MP_game, rating);
rating_list.append(p);
}
for (int i = 0; i < NUMBER_MP_GAMES; i++)
rating_threshoulds[i] = (float)-1e+10;
}
void Server::save_rating_list(const char *name) {
XStream ff(name, XS_OUT);
ff < rating_list.size();
RatingData *p = rating_list.first();
// fout < "\n\nRating:\n";
while (p) {
ff < p->name < char(0) < p->password < char(0) < p->MP_game < p->rating;
// fout < p -> name < "\t" < p -> password < "\t" <= p -> MP_game < "\t" <= p ->
//rating < "\n";
p = p->next;
}
// fout < "\n";
}
RatingData *Server::search_rating_data(char *player_name, char *player_password, int MP_game) {
RatingData *p = rating_list.first();
while (p) {
if (!strcmp(player_name, p->name) && !strcmp(player_password, p->password) &&
MP_game == p->MP_game)
return p;
p = p->next;
}
return p;
}
void Server::add_rating_data(Player *player, int MP_game) {
RatingData *p = search_rating_data(player->name, player->password, MP_game);
if (fabs(player->body.rating - player->prev_rating) < 0.01 &&
(p || player->body.rating < rating_threshoulds[MP_game]))
return;
if (p)
rating_list.remove(p);
else
p = new RatingData(player->name, player->password, MP_game, 0);
p->rating += player->body.rating - player->prev_rating;
float rating = p->rating;
int counter = 0;
RatingData *pp = rating_list.first();
while (pp && (pp->MP_game < MP_game || pp->MP_game == MP_game && pp->rating > rating)) {
if (pp->MP_game == MP_game)
if (counter++ > 20) {
rating_threshoulds[MP_game] = pp->rating;
delete p;
return;
}
pp = pp->next;
}
rating_list.insert(pp, p);
player->prev_rating = player->body.rating;
}
void Server::get_top_list(OutputEventBuffer &out_buffer, int MP_game) {
int num = 0;
RatingData *p = rating_list.first();
while (p) {
if (p->MP_game == MP_game)
num++;
p = p->next;
}
if (num > 10)
num = 10;
out_buffer.begin_event(TOP_LIST_RESPONSE);
out_buffer < (unsigned char)MP_game < (unsigned char)num;
p = rating_list.first();
while (p && num) {
if (p->MP_game == MP_game) {
out_buffer < p->name < char(0) < p->rating;
num--;
}
p = p->next;
}
out_buffer.end_event();
}