diff -Naur teeworlds-0.4.2-src/src/engine/e_if_server.h spamvote/src/engine/e_if_server.h --- teeworlds-0.4.2-src/src/engine/e_if_server.h 2008-04-05 15:13:02.000000000 +0200 +++ spamvote/src/engine/e_if_server.h 2008-08-07 17:54:09.000000000 +0200 @@ -106,7 +106,9 @@ */ void server_kick(int client_id, const char *reason); - +void server_ban(int client_id, int time, char *ban_message); +void server_ban_saved(int time, char *ban_message); +void server_save_ip(int client_id); /* Function: server_tick TODO diff -Naur teeworlds-0.4.2-src/src/engine/server/es_server.c spamvote/src/engine/server/es_server.c --- teeworlds-0.4.2-src/src/engine/server/es_server.c 2008-04-05 15:13:02.000000000 +0200 +++ spamvote/src/engine/server/es_server.c 2008-09-17 20:31:57.000000000 +0200 @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,10 @@ static int current_map_crc; static unsigned char *current_map_data = 0; static int current_map_size = 0; +int *banlist; +int *bantime; +int bans=0; +int saved_ip=0; void *snap_new_item(int type, int id, int size) { @@ -69,20 +74,20 @@ SRVCLIENT_STATE_CONNECTING, SRVCLIENT_STATE_READY, SRVCLIENT_STATE_INGAME, - + SRVCLIENT_SNAPRATE_INIT=0, SRVCLIENT_SNAPRATE_FULL, SRVCLIENT_SNAPRATE_RECOVER }; -typedef struct +typedef struct { int data[MAX_INPUT_SIZE]; int pred_tick; /* tick that the client predicted for the input */ int game_tick; /* the tick that was chosen for the input */ int64 timeleft; /* how much time in ms there were left before this should be applied */ } CLIENT_INPUT; - + /* */ typedef struct { @@ -90,14 +95,14 @@ int state; int latency; int snap_rate; - + int last_acked_snapshot; SNAPSTORAGE snapshots; - + CLIENT_INPUT latestinput; CLIENT_INPUT inputs[200]; /* TODO: handle input better */ int current_input; - + char name[MAX_NAME_LENGTH]; char clan[MAX_CLANNAME_LENGTH]; int score; @@ -107,6 +112,22 @@ static CLIENT clients[MAX_CLIENTS]; NETSERVER *net; +struct blacklist +{ + char *name; + struct blacklist *next; +}; +static struct blacklist *start, *end; + +static void str_lower(char *str) +{ + int i = 0; + for(;str[i];i++) + { + str[i] = tolower(str[i]); + } +} + static void snap_init_id() { int i; @@ -115,31 +136,31 @@ snap_ids[i].next = i+1; snap_ids[i].state = 0; } - + snap_ids[MAX_IDS-1].next = -1; snap_first_free_id = 0; snap_first_timed_id = -1; snap_last_timed_id = -1; snap_id_usage = 0; snap_id_inusage = 0; - + snap_id_inited = 1; } static void snap_remove_first_timeout() { int next_timed = snap_ids[snap_first_timed_id].next; - + /* add it to the free list */ snap_ids[snap_first_timed_id].next = snap_first_free_id; snap_ids[snap_first_timed_id].state = 0; snap_first_free_id = snap_first_timed_id; - + /* remove it from the timed list */ snap_first_timed_id = next_timed; if(snap_first_timed_id == -1) snap_last_timed_id = -1; - + snap_id_usage--; } @@ -148,12 +169,12 @@ int id; int64 now = time_get(); dbg_assert(snap_id_inited == 1, "requesting id too soon"); - - + + /* process timed ids */ while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) snap_remove_first_timeout(); - + id = snap_first_free_id; dbg_assert(id != -1, "id error"); snap_first_free_id = snap_ids[snap_first_free_id].next; @@ -178,7 +199,7 @@ snap_ids[id].state = 2; snap_ids[id].timeout = time_get()+time_freq()*5; snap_ids[id].next = -1; - + if(snap_last_timed_id != -1) { snap_ids[snap_last_timed_id].next = id; @@ -222,10 +243,32 @@ void server_setclientname(int client_id, const char *name) { char nametry[MAX_NAME_LENGTH]; + char name_lower[MAX_NAME_LENGTH]; int i; + struct blacklist *act; + act = start; if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) return; - + if(!strcmp("", name)) + { + server_kick(client_id, "This name is not allowed here!"); + } + if(config.sv_name_blacklist) + { + strcpy(name_lower, name); + str_lower(name_lower); + while(1) + { + if(strstr(name_lower, (*act).name) != NULL) + { + server_ban(client_id, 30, "Forbidden name"); + return; + } + if(act == end) + break; + act = (*act).next; + } + } str_copy(nametry, name, MAX_NAME_LENGTH); if(server_try_setclientname(client_id, nametry)) { @@ -260,11 +303,80 @@ { if(client_id < 0 || client_id > MAX_CLIENTS) return; - + if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) netserver_drop(net, client_id, reason); } +void server_save_ip(int client_id) +{ + if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) + { + NETADDR4 addr; + netserver_client_addr(net, client_id, &addr); + saved_ip = (addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]; + } + else + saved_ip = 0; +} + +void server_ban_saved(int time, char *ban_message) +{ + int i; + NETADDR4 addr; + char reason[256]; + bans++; + if (bans==1) {banlist=malloc(sizeof(long));bantime=malloc(sizeof(long));} + else {banlist=realloc(banlist,sizeof(long)*bans);bantime=realloc(bantime,sizeof(long)*bans);} + banlist[bans-1]=saved_ip; + if(time > 0)bantime[bans-1]=server_tick()+server_tickspeed()*time *60; + else bantime[bans-1]=0; + + if(time > 0)str_format(reason, sizeof(reason), "Banned by console for %d minutes",time); + else str_format(reason, sizeof(reason), "Banned by console permanently."); + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state != SRVCLIENT_STATE_EMPTY) + { + netserver_client_addr(net, i, &addr); + if(saved_ip == (addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]) + { + if(strlen(ban_message) > 0) + server_kick(i, ban_message); + else + server_kick(i, reason); + } + } + } +} + +void server_ban(int client_id, int time, char *ban_message) { + NETADDR4 addr; + int clientip; + char reason[256]; + if (clients[client_id].authed != 1) + { + netserver_client_addr(net, client_id, &addr); + + clientip=(addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]; + dbg_msg("server", "the ip %d.%d.%d.%d is now banned",addr.ip[0],addr.ip[1],addr.ip[2],addr.ip[3]); + + bans++; + if (bans==1) {banlist=malloc(sizeof(long));bantime=malloc(sizeof(long));} + else {banlist=realloc(banlist,sizeof(long)*bans);bantime=realloc(bantime,sizeof(long)*bans);} + banlist[bans-1]=clientip; + if(time > 0)bantime[bans-1]=server_tick()+server_tickspeed()*time *60; + else bantime[bans-1]=0; + + if(time > 0)str_format(reason, sizeof(reason), "Banned by console for %d minutes",time); + else str_format(reason, sizeof(reason), "Banned by console permanently."); + if(strlen(ban_message) > 0) + server_kick(client_id, ban_message); + else + server_kick(client_id, reason); + } +} + int server_tick() { return current_tick; @@ -317,16 +429,16 @@ NETPACKET packet; if(!info) return -1; - + mem_zero(&packet, sizeof(NETPACKET)); - + packet.client_id = client_id; packet.data = info->data; packet.data_size = info->size; - if(info->flags&MSGFLAG_VITAL) + if(info->flags&MSGFLAG_VITAL) packet.flags = PACKETFLAG_VITAL; - + if(client_id == -1) { /* broadcast */ @@ -346,7 +458,7 @@ static void server_do_snap() { int i, k; - + { static PERFORMACE_INFO scope = {"presnap", 0}; perf_start(&scope); @@ -359,15 +471,15 @@ /* client must be ingame to recive snapshots */ if(clients[i].state != SRVCLIENT_STATE_INGAME) continue; - + /* this client is trying to recover, don't spam snapshots */ if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0) continue; - + /* this client is trying to recover, don't spam snapshots */ if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0) continue; - + { char data[MAX_SNAPSHOT_SIZE]; char deltadata[MAX_SNAPSHOT_SIZE]; @@ -400,14 +512,14 @@ /* remove old snapshos */ /* keep 1 seconds worth of snapshots */ snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED); - + /* save it the snapshot */ snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0); - + /* find snapshot that we can preform delta against */ emptysnap.data_size = 0; emptysnap.num_items = 0; - + { deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0); if(deltashot_size >= 0) @@ -419,7 +531,7 @@ clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER; } } - + for(k = 0; k < 200; k++) /* TODO: do this better */ { if(clients[i].inputs[k].game_tick == current_tick) @@ -429,7 +541,7 @@ break; } } - + /* create delta */ { static PERFORMACE_INFO scope = {"delta", 0}; @@ -438,7 +550,7 @@ perf_end(); } - + if(deltasize) { /* compress it */ @@ -449,17 +561,17 @@ int numpackets; int n, left; - { + { static PERFORMACE_INFO scope = {"compress", 0}; perf_start(&scope); - + { static PERFORMACE_INFO scope = {"int", 0}; perf_start(&scope); intsize = intpack_compress(deltadata, deltasize, intdata); perf_end(); } - + { static PERFORMACE_INFO scope = {"zero", 0}; perf_start(&scope); @@ -468,12 +580,12 @@ } perf_end(); } - + numpackets = (snapshot_size+max_size-1)/max_size; - - - + + + for(n = 0, left = snapshot_size; left; n++) { int chunk = left < max_size ? left : max_size; @@ -483,18 +595,18 @@ msg_pack_start_system(NETMSG_SNAPSINGLE, 0); else msg_pack_start_system(NETMSG_SNAP, 0); - + msg_pack_int(current_tick); msg_pack_int(current_tick-delta_tick); /* compressed with */ msg_pack_int(input_predtick); msg_pack_int((timeleft*1000)/time_freq()); - + if(numpackets != 1) { msg_pack_int(numpackets); msg_pack_int(n); } - + msg_pack_int(crc); msg_pack_int(chunk); msg_pack_raw(&compdata[n*max_size], chunk); @@ -512,7 +624,7 @@ msg_pack_end(); server_send_msg(i); } - + perf_end(); } } @@ -527,7 +639,7 @@ clients[cid].state = SRVCLIENT_STATE_CONNECTING; clients[cid].name[0] = 0; clients[cid].clan[0] = 0; - + /* reset input */ for(i = 0; i < 200; i++) { @@ -535,9 +647,9 @@ clients[cid].inputs[i].pred_tick = -1; } clients[cid].current_input = 0; - + mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput)); - + snapstorage_purge_all(&clients[cid].snapshots); clients[cid].last_acked_snapshot = -1; clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT; @@ -584,16 +696,16 @@ { static volatile int reentry_guard = 0; int i; - + if(reentry_guard) return; reentry_guard++; - + for(i = 0; i < MAX_CLIENTS; i++) { if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed) server_send_rcon_line(i, line); } - + reentry_guard--; } @@ -602,6 +714,9 @@ int cid = packet->client_id; int sys; int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + int i; + int clientip; + NETADDR4 addr; if(sys) { /* system message */ @@ -610,7 +725,7 @@ char version[64]; const char *password; str_copy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) + if(strcmp(version, "0.4 1bd7780b0f76307c") != 0) { /* OH FUCK! wrong version, drop him */ char reason[256]; @@ -618,18 +733,32 @@ netserver_drop(net, cid, reason); return; } - + str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); password = msg_unpack_string(); - + if(config.password[0] != 0 && strcmp(config.password, password) != 0) { /* wrong password */ netserver_drop(net, cid, "wrong password"); return; } - + if(cid >= (config.sv_max_clients-config.sv_reserved_slots) && config.sv_reserved_slots_pass[0] != 0 && strcmp(config.sv_reserved_slots_pass, password) != 0) + { + /* wrong password */ + netserver_drop(net, cid, "Dropped due reserved slot"); + return; + } + + netserver_client_addr(net, cid, &addr); + clientip=(addr.ip[0]<<24)+(addr.ip[1]<<16)+(addr.ip[2]<<8)+addr.ip[3]; + for (i=0; i= current_map_size) { chunk_size = current_map_size-offset; @@ -646,7 +775,7 @@ chunk_size = 0; last = 1; } - + msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL); msg_pack_int(last); msg_pack_int(current_map_size); @@ -654,7 +783,7 @@ msg_pack_raw(¤t_map_data[offset], chunk_size); msg_pack_end(); server_send_msg(cid); - + if(config.debug) dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); } @@ -685,41 +814,41 @@ clients[cid].last_acked_snapshot = msg_unpack_int(); tick = msg_unpack_int(); size = msg_unpack_int(); - + /* check for errors */ if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) return; - + if(clients[cid].last_acked_snapshot > 0) clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL; - + if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0) clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); input = &clients[cid].inputs[clients[cid].current_input]; input->timeleft = server_tick_start_time(tick)-time_get(); input->pred_tick = tick; - + if(tick <= server_tick()) tick = server_tick()+1; input->game_tick = tick; - + for(i = 0; i < size/4; i++) input->data[i] = msg_unpack_int(); - + mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int)); - + clients[cid].current_input++; clients[cid].current_input %= 200; - + /* call the mod with the fresh input data */ mods_client_direct_input(cid, clients[cid].latestinput.data); } else if(msg == NETMSG_RCON_CMD) { const char *cmd = msg_unpack_string(); - + if(msg_unpack_error() == 0 && clients[cid].authed) { dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); @@ -731,7 +860,7 @@ const char *pw; msg_unpack_string(); /* login name, not used */ pw = msg_unpack_string(); - + if(msg_unpack_error() == 0) { if(config.sv_rcon_password[0] == 0) @@ -744,7 +873,7 @@ msg_pack_int(1); msg_pack_end(); server_send_msg(cid); - + clients[cid].authed = 1; server_send_rcon_line(cid, "Authentication successful. Remote console access granted."); dbg_msg("server", "cid=%d authed", cid); @@ -779,7 +908,7 @@ PACKER p; char buf[128]; - /* count the players */ + /* count the players */ int c = 0; int i; for(i = 0; i < MAX_CLIENTS; i++) @@ -787,7 +916,7 @@ if(clients[i].state != SRVCLIENT_STATE_EMPTY) c++; } - + packer_reset(&p); if(lan) packer_add_raw(&p, SERVERBROWSE_INFO_LAN, sizeof(SERVERBROWSE_INFO_LAN)); @@ -811,7 +940,7 @@ /* progression */ str_format(buf, sizeof(buf), "%d", browseinfo_progression); packer_add_string(&p, buf, 4); - + str_format(buf, sizeof(buf), "%d", c); packer_add_string(&p, buf, 3); /* num players */ str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ @@ -823,8 +952,8 @@ str_format(buf, sizeof(buf), "%d", clients[i].score); packer_add_string(&p, buf, 6); /* player score */ } } - - + + packet.client_id = -1; packet.address = *addr; packet.flags = PACKETFLAG_CONNLESS; @@ -841,7 +970,7 @@ NETPACKET packet; netserver_update(net); - + /* process packets */ while(netserver_recv(net, &packet)) { @@ -875,17 +1004,17 @@ df = datafile_load(buf); if(!df) return 0; - + /* reinit snapshot ids */ snap_timeout_ids(); - + /* get the crc of the map */ current_map_crc = datafile_crc(buf); dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - + str_copy(current_map, mapname, sizeof(current_map)); map_set(df); - + /* load compelate map into memory for download */ { IOHANDLE file = io_open(buf, IOFLAG_READ); @@ -905,7 +1034,7 @@ net_init(); snap_init_id(); - + /* */ console_register_print_callback(server_send_rcon_line_authed); @@ -915,7 +1044,7 @@ dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); return -1; } - + /* start server */ if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) == 0) { @@ -926,18 +1055,49 @@ mem_zero(&bindaddr, sizeof(bindaddr)); bindaddr.port = config.sv_port; } - + if(config.sv_name_blacklist) + { + + FILE *name_file = fopen("name_blacklist", "r"); + if(name_file != NULL) + { + char tmp_names[MAX_NAME_LENGTH]; + if(fgets(tmp_names, MAX_NAME_LENGTH, name_file) != NULL) + { + tmp_names[strlen(tmp_names)-1] = 0; + start = malloc(sizeof(struct blacklist)); + end = start; + (*start).name = malloc(MAX_NAME_LENGTH); + strcpy((*start).name, tmp_names); + str_lower((*start).name); + } + while(fgets(tmp_names, MAX_NAME_LENGTH, name_file) != NULL) + { + tmp_names[strlen(tmp_names)-1] = 0; + (*end).next = malloc(sizeof(struct blacklist)); + end = (*end).next; + (*end).name = malloc(MAX_NAME_LENGTH); + strcpy((*end).name, tmp_names); + str_lower((*end).name); + } + fclose(name_file); + } + else + { + config.sv_name_blacklist = 0; + } + } net = netserver_open(bindaddr, config.sv_max_clients, 0); if(!net) { dbg_msg("server", "couldn't open socket. port might already be in use"); return -1; } - + netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); - + dbg_msg("server", "server name is '%s'", config.sv_name); - + mods_init(); dbg_msg("server", "version %s", mods_net_version()); @@ -945,10 +1105,10 @@ { int64 reporttime = time_get(); int reportinterval = 3; - + lastheartbeat = 0; game_start_time = time_get(); - + if(config.debug) dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); @@ -957,34 +1117,34 @@ static PERFORMACE_INFO rootscope = {"root", 0}; int64 t = time_get(); int new_ticks = 0; - + perf_start(&rootscope); - + /* load new map TODO: don't poll this */ if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) { config.sv_map_reload = 0; - + /* load map */ if(server_load_map(config.sv_map)) { int c; - + /* new map loaded */ mods_shutdown(); - + for(c = 0; c < MAX_CLIENTS; c++) { if(clients[c].state == SRVCLIENT_STATE_EMPTY) continue; - + server_send_map(c); clients[c].state = SRVCLIENT_STATE_CONNECTING; clients[c].last_acked_snapshot = -1; clients[c].snap_rate = SRVCLIENT_SNAPRATE_RECOVER; snapstorage_purge_all(&clients[c].snapshots); } - + game_start_time = time_get(); current_tick = 0; mods_init(); @@ -995,19 +1155,19 @@ config_set_sv_map(&config, current_map); } } - + while(t > server_tick_start_time(current_tick+1)) { current_tick++; new_ticks++; - + /* apply new input */ { static PERFORMACE_INFO scope = {"input", 0}; int c, i; - + perf_start(&scope); - + for(c = 0; c < MAX_CLIENTS; c++) { if(clients[c].state == SRVCLIENT_STATE_EMPTY) @@ -1021,10 +1181,10 @@ } } } - + perf_end(); } - + /* progress game */ { static PERFORMACE_INFO scope = {"tick", 0}; @@ -1033,7 +1193,7 @@ perf_end(); } } - + /* snap game */ if(new_ticks) { @@ -1044,11 +1204,27 @@ server_do_snap(); perf_end(); } + if (bans>0) { + int i=0,j=0; + for (i=0; i server_tickspeed()) + { + dbg_msg("server","the IP=%d:%d:%d:%d is no longer banned",i,((banlist[i]>>24)&0xFF),((banlist[i]>>16)&0xFF),((banlist[i]>>8)&0xFF),(banlist[i]&0xFF)); + for (j=i; j 0)bantime[bans-1]=server_tick()+server_tickspeed()*console_arg_int(result, 1)*60; + else bantime[bans-1]=0; + + if(console_arg_int(result, 1) > 0)str_format(reason, sizeof(reason), "Banned by console for %d minutes",console_arg_int(result, 1)); + else str_format(reason, sizeof(reason), "Banned by console permanently."); + server_kick(console_arg_int(result, 0), reason); +} + +static void con_banlist(void *result, void *user_data) { + int i; + if (bans>0) { + for (i=0; i>24)&0xFF),((banlist[i]>>16)&0xFF),((banlist[i]>>8)&0xFF),(banlist[i]&0xFF),(bantime[i]-server_tick())/(server_tickspeed()*60)); + } + } else { + dbg_msg("server","there is no ban"); + } +} + +static void con_unban(void *result, void *user_data) { + int i=0; + int d=console_arg_int(result, 0); + + if (d<0 || d>=bans) dbg_msg("server","no such banid: %d, type banlist to see banids",d); + else + { + dbg_msg("server","the IP=%d:%d:%d:%d is no longer banned",d,((banlist[d]>>24)&0xFF),((banlist[d]>>16)&0xFF),((banlist[d]>>8)&0xFF),(banlist[d]&0xFF)); + for (i=d; i 0) + { + NETMSG_SV_CHAT msg; + msg.team = 0; + msg.cid = -1; + msg.message = config.sv_endroundmessage; + msg.pack(MSGFLAG_VITAL); + server_send_msg(-1); + } world->paused = true; game_over_tick = server_tick(); sudden_death = 0; diff -Naur teeworlds-0.4.2-src/src/game/server/gs_server.cpp spamvote/src/game/server/gs_server.cpp --- teeworlds-0.4.2-src/src/game/server/gs_server.cpp 2008-04-05 15:13:02.000000000 +0200 +++ spamvote/src/game/server/gs_server.cpp 2008-09-12 09:50:15.000000000 +0200 @@ -24,10 +24,11 @@ void create_playerspawn(vec2 p); void create_death(vec2 p, int who); void create_sound(vec2 pos, int sound, int mask=-1); +void auto_balancing(); class player *intersect_player(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class entity *notthis = 0); class player *closest_player(vec2 pos, float radius, entity *notthis); - -game_world *world; +int timer_vote=-1;bool vote_called=false;int votetime=-1;char votetype[32];static int votedtokick=-1; +game_world *world;char vote_message[512]; enum { @@ -37,12 +38,146 @@ CHAT_BLUE=1 }; +void send_display(char *message, int to) +{ + if(to == -1) + { + dbg_msg("chat", "*** %s", message); + } + NETMSG_SV_BROADCAST msg; + msg.message = message; + msg.pack(MSGFLAG_VITAL); + server_send_msg(to); +} + +void send_message(char *message, int to) +{ + if(to == -1) + { + dbg_msg("chat", "*** %s", message); + if(config.sv_silent_mode) + return; + } + NETMSG_SV_CHAT msg; + msg.team = 0; + msg.cid = -1; + msg.message = message; + msg.pack(MSGFLAG_VITAL); + server_send_msg(to); +} + +int get_team(int teamm) +{ + int i=0; + int result = 0; + for(;ivoted) + { + send_display("You already voted, ignoring vote", cid); + } + else + { + tmp->voted = true; + dbg_msg("game", "map-voting %s", text); + send_display("You voted for the map, thank you", cid); + } + return; + } + + if(config.sv_messagesnum && cid > -1 && cid < MAX_CLIENTS) + { + + + if(tmp->muted) + { + if(tmp->last_message_tick + config.sv_time_muted*SERVER_TICK_SPEED > server_tick()) + { + tmp->message_num++; + char buf[128]; + sprintf(buf, "You can speak in %i seconds", (config.sv_time_muted * server_tickspeed() + tmp->last_message_tick - server_tick())/server_tickspeed()); + send_display(buf, cid); + if(config.sv_messageskick && tmp->message_num > config.sv_messageskick) + { + char kicking[127]; + sprintf(kicking, "%s was kicked because of spamming.", server_clientname(cid)); + send_message(kicking, -1); + if(config.sv_messagesban) + { + char ban_message[255]; + sprintf(ban_message, "You were banned for %i minutes because of spamming", config.sv_messagesban); + server_ban(cid, config.sv_teamchangesban, ban_message); + } + else + server_kick(cid,"You were kicked because of spamming"); + } + return; + } + else + { + tmp->muted = false; + tmp->message_num = 0; + tmp->last_message_tick = server_tick(); + } + } + else + { + if(tmp->last_message_tick + 30*SERVER_TICK_SPEED > server_tick()) + { + tmp->message_num++; + if(config.sv_same_messages && strcmp(lastmessages[cid], text) == 0) + { + same_messages[cid]++; + } + else + { + strncpy(lastmessages[cid], text, 768); + same_messages[cid] = 0; + } + if(tmp->message_num >= config.sv_messagesnum || (config.sv_same_messages && config.sv_same_messages <= same_messages[cid])) + { + tmp->muted = true;same_messages[cid] = 0; + tmp->message_num = 0; + char muting[127]; + sprintf(muting, "%s was muted for %d seconds because of spamming.", server_clientname(cid), config.sv_time_muted); + send_message(muting, -1); + sprintf(muting, "You are muted for %d seconds\nbecause of spamming", config.sv_time_muted); + send_display(muting, cid); + return; + } + + } + else + { + tmp->last_message_tick = server_tick(); + tmp->message_num = 0; + } + } + } if(cid >= 0 && cid < MAX_CLIENTS) dbg_msg("chat", "%d:%d:%s: %s", cid, team, server_clientname(cid), text); else + { dbg_msg("chat", "*** %s", text); + if(config.sv_silent_mode) + return; + } if(team == CHAT_ALL) { @@ -80,7 +215,7 @@ msg.color_body = players[who].color_body; msg.color_feet =players[who].color_feet; msg.pack(MSGFLAG_VITAL); - + server_send_msg(to_who); } @@ -120,6 +255,82 @@ server_send_msg(cid); } +void resultvote() +{ + int players_serv=0,total=0,voteur=0; + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + if(players[i].votedfor != -1) + {total+=players[i].votedfor;voteur++;} + players_serv++; + } + } + char buf[256]; + str_format(buf, sizeof(buf), "YES: %d | NO: %d (Time remaining : %d secondes)",total,voteur-total,votetime - (server_tick()-timer_vote)/(server_tickspeed())); + send_chat(-1,CHAT_ALL,buf); + if (voteur==players_serv || (server_tick()-timer_vote > server_tickspeed()*votetime && timer_vote != -1)) + { + vote_called=false; + int result=1; + char message[256]; + if((total > voteur / 2 && !config.sv_all_vote) || (total > players_serv / 2 && config.sv_all_vote))str_format(message, sizeof(message), "Vote passed"); + else {str_format(message, sizeof(message), "Vote failed");result=0;} + send_display(message, -1); + if(!strcmp(votetype,"kick") && votedtokick != -1) + { + if(result) + { + sprintf(message,"You are kicked by vote for %i minutes (YES: %d | NO: %d)", config.sv_ban_time,total,voteur-total); + server_ban_saved(config.sv_ban_time, message); + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"next_map")) + { + if(result) + { + gameobj->endround(); + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"timelimit")) + { + if(result) + { + if(votedtokick == -1) + config.sv_timelimit = 0; + else + config.sv_timelimit = votedtokick; + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + else if(!strcmp(votetype,"scorelimit")) + { + if(result) + { + if(votedtokick == -1) + config.sv_scorelimit = 0; + else + config.sv_scorelimit = votedtokick; + } + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + } + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + players[i].votedfor=-1; + } + } + } +} + ////////////////////////////////////////////////// // Event handler ////////////////////////////////////////////////// @@ -352,9 +563,10 @@ { if(reset_requested) reset(); - if(!paused) { + auto_balancing(); + if(timer_vote != -1 && server_tick()-timer_vote > server_tickspeed()*votetime && vote_called)resultvote(); /* static PERFORMACE_INFO scopes[OBJTYPE_FLAG+1] = { @@ -377,10 +589,10 @@ {"powerup", 0}, {"flag", 0} }; - + static PERFORMACE_INFO tick_scope = {"tick", 0}; perf_start(&tick_scope);*/ - + // update all objects for(entity *ent = first_entity; ent; ent = ent->next_entity) { @@ -390,7 +602,7 @@ /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) perf_end();*/ } - + /* perf_end(); @@ -482,24 +694,24 @@ curvature = tuning.gun_curvature; speed = tuning.gun_speed; } - + return calc_pos(pos, direction, curvature, speed, time); } void projectile::tick() { - + float pt = (server_tick()-start_tick-1)/(float)server_tickspeed(); float ct = (server_tick()-start_tick)/(float)server_tickspeed(); vec2 prevpos = get_pos(pt); vec2 curpos = get_pos(ct); lifespan--; - + int collide = col_intersect_line(prevpos, curpos, &curpos); //int collide = col_check_point((int)curpos.x, (int)curpos.y); - + entity *targetplayer = (entity*)intersect_player(prevpos, curpos, 6.0f, curpos, powner); if(targetplayer || collide || lifespan < 0) { @@ -530,7 +742,7 @@ void projectile::snap(int snapping_client) { float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - + if(distance(players[snapping_client].pos, get_pos(ct)) > 1000.0f) return; @@ -551,7 +763,7 @@ dir = direction; bounces = 0; do_bounce(); - + world->insert_entity(this); } @@ -565,7 +777,7 @@ this->from = from; pos = at; - energy = -1; + energy = -1; hit->take_damage(vec2(0,0), tuning.laser_damage, owner->client_id, WEAPON_RIFLE); return true; } @@ -573,16 +785,16 @@ void laser::do_bounce() { eval_tick = server_tick(); - + if(energy < 0) { //dbg_msg("laser", "%d removed", server_tick()); world->destroy_entity(this); return; } - + vec2 to = pos + dir*energy; - + if(col_intersect_line(pos, to, &to)) { if(!hit_player(pos, to)) @@ -592,17 +804,17 @@ pos = to - dir*2; vec2 temp_pos = pos; vec2 temp_dir = dir*4.0f; - + move_point(&temp_pos, &temp_dir, 1.0f, 0); pos = temp_pos; dir = normalize(temp_dir); - + energy -= distance(from, pos) + tuning.laser_bounce_cost; bounces++; - + if(bounces > tuning.laser_bounce_num) energy = -1; - + create_sound(pos, SOUND_RIFLE_BOUNCE); } } @@ -615,10 +827,10 @@ energy = -1; } } - + //dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y); } - + void laser::reset() { world->destroy_entity(this); @@ -659,6 +871,10 @@ void player::init() { + voted = false; + teamchanging = false; team_changes = 0; last_team_set = server_tick(); + last_message_tick = server_tick(); message_num = 0; muted = false; + proximity_radius = phys_size; client_id = -1; team = -1; // -1 == spectator @@ -677,10 +893,10 @@ { pos = vec2(0.0f, 0.0f); core.reset(); - + emote_type = 0; emote_stop = -1; - + //direction = vec2(0.0f, 1.0f); score = 0; dead = true; @@ -703,7 +919,7 @@ attack_tick = 0; mem_zero(&ninja, sizeof(ninja)); - + active_weapon = WEAPON_GUN; last_weapon = WEAPON_HAMMER; queued_weapon = -1; @@ -715,13 +931,13 @@ { if(w == active_weapon) return; - + last_weapon = active_weapon; queued_weapon = -1; active_weapon = w; if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) active_weapon = 0; - + create_sound(pos, SOUND_WEAPON_SWITCH); } @@ -744,26 +960,190 @@ else if(team == 1) return "blue team"; } - + return "spectators"; } +void auto_balancing() +{ + static int last_warning_tick = 0; + static int start_unbalance = 0; + if((config.sv_team_balance || config.sv_balance_warning) && gameobj->gametype != GAMETYPE_DM) + { + int team0 = get_team(0); + int team1 = get_team(1); + if(!start_unbalance) + start_unbalance = server_tick(); + if((config.sv_team_balance && (team0-team1 > config.sv_team_balance || team1-team0 > config.sv_team_balance)) || (!config.sv_team_balance && config.sv_balance_warning && (team0-team1 > config.sv_balance_warning || team1-team0 > config.sv_balance_warning))) + { + if(((config.sv_balance_warning && config.sv_team_balance && start_unbalance + config.sv_balance_warning*server_tickspeed() > server_tick()) || !config.sv_team_balance) && last_warning_tick + 20*server_tickspeed() < server_tick()) + { + send_display("Teams are unbalanced", -1); + last_warning_tick = server_tick(); + } + else if((config.sv_team_balance && config.sv_balance_warning && start_unbalance + config.sv_balance_warning*server_tickspeed() < server_tick()) || !config.sv_balance_warning) + { + send_display("Auto balancing teams...", -1); + while(team0-team1 > config.sv_team_balance) + { + int i = rand() % MAX_CLIENTS; + int steps = rand() % MAX_CLIENTS; + int now = 0; + while(1) + { + if(players[i].team == 0 && steps == now++) + { + int tmp_points = players[i].score; + players[i].team_changes--; + send_display("You have been assigned to the blue team", i); + players[i].set_team(1); + team0--; + players[i].score = tmp_points; + break; + } + if(++i == MAX_CLIENTS) + { + i = 0; + } + } + } + while(team1-team0 > config.sv_team_balance) + { + int i = rand() % MAX_CLIENTS; + int steps = rand() % MAX_CLIENTS; + int now = 0; + while(1) + { + if(players[i].team == 1 && steps == now++) + { + int tmp_points = players[i].score; + players[i].team_changes--; + send_display("You have been assigned to the red team", i); + players[i].set_team(0); + team1--; + players[i].score = tmp_points; + break; + } + if(++i == MAX_CLIENTS) + { + i = 0; + } + } + } + } + } + else + { + start_unbalance = 0; + last_warning_tick = 0; + } + } +} + void player::set_team(int new_team) { + if(config.sv_teamchanges) + { + if(teamchanging) + { + if(last_team_set + config.sv_time_blocked*SERVER_TICK_SPEED > server_tick()) + { + team_changes++; + char buf[128]; + sprintf(buf, "You can change team in %i seconds", (config.sv_time_blocked * server_tickspeed() + last_team_set - server_tick())/server_tickspeed()); + send_display(buf, client_id); + if(config.sv_teamchangeskick && team_changes > config.sv_teamchangeskick) + { + + char kicking[127]; + sprintf(kicking, "%s was kicked because of fast teamchanging.", server_clientname(client_id)); + send_message(kicking, -1); + if(config.sv_teamchangesban) + { + char ban_message[255]; + sprintf(ban_message, "You were banned for %i minutes because of fast teamchanging", config.sv_teamchangesban); + server_ban(client_id, config.sv_teamchangesban, ban_message); + } + else + server_kick(client_id,"You were kicked because of fast teamchanging"); + } + return; + } + else + { + teamchanging = false; + team_changes = 0; + last_team_set = server_tick(); + } + } + else + { + if(last_team_set + 30*SERVER_TICK_SPEED > server_tick()) + { + team_changes++; + if(config.sv_teamchanges && team_changes >= config.sv_teamchanges) + { + teamchanging = true; + team_changes = 0; + char blocking[127]; + sprintf(blocking, "%s can't change team for %d seconds", server_clientname(client_id), config.sv_time_blocked); + send_message(blocking, -1); + sprintf(blocking, "You can't change team for %d seconds", config.sv_time_blocked); + send_display(blocking, client_id); + return; + } + + } + else + { + last_team_set = server_tick(); + team_changes = 0; + } + } + } + if(config.sv_team_balance && !config.sv_balance_warning && gameobj->gametype != GAMETYPE_DM) + { + int team0 = get_team(0); + int team1 = get_team(1); + if(team == -1) + { + if(new_team == 1 && team1-team0 + 1 > config.sv_team_balance) + { + send_display("You can't join blue because of team-balance", client_id); + new_team = 0; + } + else if(new_team == 0 && team0-team1 + 1 > config.sv_team_balance) + { + send_display("You can't join red because of team-balance", client_id); + new_team = 1; + } + } + else if(new_team == 1 && team1-team0 + 2 > config.sv_team_balance) + { + send_display("You can't join blue because of team-balance", client_id); + return; + } + else if(new_team == 0 && team0-team1 + 2 > config.sv_team_balance) + { + send_display("You can't join red because of team-balance", client_id); + return; + } + } + teamkills = 0; // clamp the team new_team = gameobj->clampteam(new_team); if(team == new_team) return; - + char buf[512]; str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), get_team_name(new_team)); - send_chat(-1, CHAT_ALL, buf); - + send_chat(-1, CHAT_ALL, buf); + teamkills --; die(client_id, -1); team = new_team; score = 0; dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); - + gameobj->on_player_info_change(&players[client_id]); // send all info to this client @@ -786,7 +1166,7 @@ die_pos = vec2(0,0); pos = vec2(100,100); } - + vec2 pos; bool got; int friendly_team; @@ -797,35 +1177,35 @@ static float evaluate_spawn(spawneval *eval, vec2 pos) { float score = 0.0f; - + for(int c = 0; c < MAX_CLIENTS; c++) { if(players[c].client_id == -1) continue; - + // don't count dead people if(!(players[c].flags&entity::FLAG_PHYSICS)) continue; - + // team mates are not as dangerous as enemies float scoremod = 1.0f; if(eval->friendly_team != -1 && players[c].team == eval->friendly_team) scoremod = 0.5f; - + float d = distance(pos, players[c].pos); if(d == 0) score += 1000000000.0f; else score += 1.0f/d; } - + // weight in the die posititon float d = distance(pos, eval->die_pos); if(d == 0) score += 1000000000.0f; else score += 1.0f/d; - + return score; } @@ -856,17 +1236,17 @@ void player::try_respawn() { vec2 spawnpos = vec2(100.0f, -60.0f); - + // get spawn point spawneval eval; eval.die_pos = die_pos; - + eval.pos = vec2(100, 100); - + if(gameobj->gametype == GAMETYPE_CTF) { eval.friendly_team = team; - + // try first try own team spawn, then normal spawn and then enemy evaluate_spawn_type(&eval, 1+(team&1)); if(!eval.got) @@ -880,12 +1260,12 @@ { if(gameobj->gametype == GAMETYPE_TDM) eval.friendly_team = team; - + evaluate_spawn_type(&eval, 0); evaluate_spawn_type(&eval, 1); evaluate_spawn_type(&eval, 2); } - + spawnpos = eval.pos; // check if the position is occupado @@ -897,7 +1277,7 @@ if(ents[i] != this) return; } - + spawning = false; pos = spawnpos; @@ -908,9 +1288,9 @@ health = 10; armor = 0; jumped = 0; - + mem_zero(&ninja, sizeof(ninja)); - + dead = false; set_flag(entity::FLAG_PHYSICS); player_state = PLAYERSTATE_PLAYING; @@ -928,7 +1308,7 @@ /*weapons[WEAPON_RIFLE].got = true; weapons[WEAPON_RIFLE].ammo = -1;*/ - + active_weapon = WEAPON_GUN; last_weapon = WEAPON_HAMMER; queued_weapon = 0; @@ -966,7 +1346,7 @@ set_weapon(active_weapon); return 0; } - + // force ninja weapon set_weapon(WEAPON_NINJA); @@ -978,7 +1358,7 @@ ninja.activationdir = direction; ninja.currentmovetime = data->weapons[WEAPON_NINJA].movetime * server_tickspeed() / 1000; ninja.currentcooldown = data->weapons[WEAPON_NINJA].firedelay * server_tickspeed() / 1000 + server_tick(); - + // reset hit objects numobjectshit = 0; @@ -1058,7 +1438,7 @@ { if(reload_timer != 0) // make sure we have reloaded return; - + if(queued_weapon == -1) // check for a queued weapon return; @@ -1074,7 +1454,7 @@ int wanted_weapon = active_weapon; if(queued_weapon != -1) wanted_weapon = queued_weapon; - + // select weapon int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses; int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses; @@ -1106,7 +1486,7 @@ // check for insane values if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got) queued_weapon = wanted_weapon; - + do_weaponswitch(); } @@ -1114,11 +1494,11 @@ { if(reload_timer != 0 || active_weapon == WEAPON_NINJA) return; - + do_weaponswitch(); - + vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - + bool fullauto = false; if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE) fullauto = true; @@ -1130,16 +1510,16 @@ if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true; if(!will_fire) return; - + // check for ammo if(!weapons[active_weapon].ammo) { create_sound(pos, SOUND_WEAPON_NOAMMO); return; } - + vec2 projectile_startpos = pos+direction*phys_size*0.75f; - + switch(active_weapon) { case WEAPON_HAMMER: @@ -1147,17 +1527,17 @@ // reset objects hit numobjectshit = 0; create_sound(pos, SOUND_HAMMER_FIRE); - + int type = NETOBJTYPE_PLAYER_CHARACTER; entity *ents[64]; - int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1); + int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1); for (int i = 0; i < num; i++) { player *target = (player*)ents[i]; if (target == this) continue; - + // hit a player, give him damage and stuffs... vec2 fdir = normalize(ents[i]->pos - pos); @@ -1169,10 +1549,10 @@ dir = normalize(target->pos - pos); else dir = vec2(0,-1); - + target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; } - + } break; case WEAPON_GUN: @@ -1184,28 +1564,28 @@ (int)(server_tickspeed()*tuning.gun_lifetime), this, 1, 0, 0, -1, WEAPON_GUN); - + // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); - + msg_pack_start(NETMSGTYPE_SV_EXTRA_PROJECTILE, 0); msg_pack_int(1); for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) msg_pack_int(((int *)&p)[i]); msg_pack_end(); server_send_msg(client_id); - + create_sound(pos, SOUND_GUN_FIRE); } break; - + case WEAPON_SHOTGUN: { int shotspread = 2; msg_pack_start(NETMSGTYPE_SV_EXTRA_PROJECTILE, 0); msg_pack_int(shotspread*2+1); - + for(int i = -shotspread; i <= shotspread; i++) { float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; @@ -1220,18 +1600,18 @@ (int)(server_tickspeed()*tuning.shotgun_lifetime), this, 1, 0, 0, -1, WEAPON_SHOTGUN); - + // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); - + for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) msg_pack_int(((int *)&p)[i]); } msg_pack_end(); - server_send_msg(client_id); - + server_send_msg(client_id); + create_sound(pos, SOUND_SHOTGUN_FIRE); } break; @@ -1248,7 +1628,7 @@ // pack the projectile and send it to the client directly NETOBJ_PROJECTILE p; proj->fill_info(&p); - + msg_pack_start(NETMSGTYPE_SV_EXTRA_PROJECTILE, 0); msg_pack_int(1); for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) @@ -1258,13 +1638,13 @@ create_sound(pos, SOUND_GRENADE_FIRE); } break; - + case WEAPON_RIFLE: { new laser(pos, direction, tuning.laser_reach, this); create_sound(pos, SOUND_RIFLE_FIRE); } break; - + } if(weapons[active_weapon].ammo > 0) // -1 == unlimited @@ -1295,7 +1675,7 @@ reload_timer--; return 0; } - + if (active_weapon == WEAPON_NINJA) { // don't update other weapons while ninja is active @@ -1326,7 +1706,7 @@ weapons[active_weapon].ammoregenstart = -1; } } - + return 0; } @@ -1356,7 +1736,7 @@ mem_copy(&latest_input, input, sizeof(latest_input)); } }*/ - + // check if we have enough input // this is to prevent initial weird clicks if(num_inputs < 2) @@ -1364,7 +1744,7 @@ latest_previnput = latest_input; previnput = input; } - + // do latency stuff { CLIENT_INFO info; @@ -1440,16 +1820,16 @@ vec2 start_pos = core.pos; vec2 start_vel = core.vel; bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); - + core.move(); bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); core.quantize(); bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); pos = core.pos; - + if(!stuck_before && (stuck_after_move || stuck_after_quant)) { - dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", + dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", stuck_before, stuck_after_move, stuck_after_quant, @@ -1461,7 +1841,7 @@ int events = core.triggered_events; int mask = cmask_all_except_one(client_id); - + if(events&COREEVENT_GROUND_JUMP) create_sound(pos, SOUND_PLAYER_JUMP, mask); if(events&COREEVENT_AIR_JUMP) { @@ -1473,14 +1853,14 @@ c->y = (int)pos.y; } } - + //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); if(events&COREEVENT_HOOK_ATTACH_PLAYER) create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); if(events&COREEVENT_HOOK_ATTACH_GROUND) create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - + } - + if(team == -1) { pos.x = input.target_x; @@ -1517,12 +1897,24 @@ die_tick = server_tick(); clear_flag(FLAG_PHYSICS); create_death(pos, client_id); + if(killer != client_id && players[killer].team != team && players[killer].teamkills > 0) + players[killer].teamkills --; + if(config.sv_kick_teamkiller && (killer == client_id || players[killer].team == team) && players[killer].teamkills++ >= config.sv_kick_teamkiller) + { + char kickmessage[256]; + sprintf(kickmessage, "%s was kicked because of teamkilling/selfkillng.", server_clientname(killer)); + send_message(kickmessage, -1); + if(config.sv_ban_teamkiller) + server_ban(killer, config.sv_ban_teamkiller, "You were banned because of teamkilling/selfkilling"); + else + server_kick(killer, "You were kicked because of teamkilling/selfkilling"); + } } bool player::take_damage(vec2 force, int dmg, int from, int weapon) { core.vel += force; - + if(gameobj->is_friendly_fire(client_id, from) && !config.sv_teamdamage) return false; @@ -1557,7 +1949,7 @@ health--; dmg--; } - + if(dmg > armor) { dmg -= armor; @@ -1569,7 +1961,7 @@ dmg = 0; } } - + health -= dmg; } @@ -1649,7 +2041,7 @@ character->ammocount = 0; character->health = 0; character->armor = 0; - + character->weapon = active_weapon; character->attacktick = attack_tick; @@ -1795,7 +2187,7 @@ pplayer->emote_type = EMOTE_ANGRY; pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; - + break; } default: @@ -1950,12 +2342,12 @@ float closest_len = distance(pos0, pos1) * 100.0f; vec2 line_dir = normalize(pos1-pos0); player *closest = 0; - + for(int i = 0; i < MAX_CLIENTS; i++) { if(players[i].client_id < 0 || (entity *)&players[i] == notthis) continue; - + if(!(players[i].flags&entity::FLAG_PHYSICS)) continue; @@ -1971,7 +2363,7 @@ } } } - + return closest; } @@ -1981,12 +2373,12 @@ // Find other players float closest_range = radius*2; player *closest = 0; - + for(int i = 0; i < MAX_CLIENTS; i++) { if(players[i].client_id < 0 || (entity *)&players[i] == notthis) continue; - + if(!(players[i].flags&entity::FLAG_PHYSICS)) continue; @@ -2000,7 +2392,7 @@ } } } - + return closest; } @@ -2024,7 +2416,7 @@ { if(!world->paused) players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input); - + /* if(i->fire) { @@ -2044,7 +2436,7 @@ //players[client_id].previnput = players[client_id].input; players[client_id].input = *(NETOBJ_PLAYER_INPUT*)input; players[client_id].num_inputs++; - + if(players[client_id].input.target_x == 0 && players[client_id].input.target_y == 0) players[client_id].input.target_y = -1; } @@ -2055,21 +2447,34 @@ { world->insert_entity(&players[client_id]); players[client_id].respawn(); + players[client_id].voted = false; + players[client_id].votedfor = -1; + players[client_id].teamkills = 0; + players[client_id].lastvote = 0; dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); char buf[512]; str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), get_team_name(players[client_id].team)); - send_chat(-1, CHAT_ALL, buf); - + send_chat(-1, CHAT_ALL, buf); + if(strlen(config.sv_startmessage) > 0) + { + NETMSG_SV_CHAT msg; + msg.team = 0; + msg.cid = -1; + msg.message = config.sv_startmessage; + msg.pack(MSGFLAG_VITAL); + server_send_msg(client_id); + } dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), players[client_id].team); + } void mods_connected(int client_id) { players[client_id].init(); players[client_id].client_id = client_id; - + // Check which team the player should be on if(config.sv_tournament_mode) players[client_id].team = -1; @@ -2088,7 +2493,6 @@ char buf[512]; str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); send_chat(-1, CHAT_ALL, buf); - dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); gameobj->on_player_death(&players[client_id], 0, -1); @@ -2105,7 +2509,7 @@ dbg_msg("server", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); return; } - + if(msgtype == NETMSGTYPE_CL_SAY) { NETMSG_CL_SAY *msg = (NETMSG_CL_SAY *)rawmsg; @@ -2114,15 +2518,234 @@ team = players[client_id].team; else team = CHAT_ALL; - + if(config.sv_spamprotection && players[client_id].last_chat+time_freq() > time_get()) { // consider this as spam } else { - players[client_id].last_chat = time_get(); - send_chat(client_id, team, msg->message); + if(!strcasecmp(msg->message, ".info")) + { + char commands[768]="Mod from scosu (http://scosu.de). Available commands:\n"; + if(config.sv_handle_mapvotes) + { + strcat(commands, "Positive map-voting: /++\n"); + strcat(commands, "Negative map-voting: /--\n"); + } + strcat(commands, "Current vote: /currentvote\n"); + strcat(commands, "List of player-ids: /players\n"); + if(config.sv_allow_votes) + { + strcat(commands, "Kick player by name: /kick \n"); + strcat(commands, "Kick player by id: /kick_id \n"); + } + if(config.sv_allow_next_map) + { + strcat(commands, "Start vote for change to next map: /next_map\n"); + } + if(config.sv_allow_timelimit) + { + strcat(commands, "Start vote for set timelimit: /timelimit \n"); + } + if(config.sv_allow_scorelimit) + { + strcat(commands, "Start vote for set scorelimit: /scorelimit \n"); + } + send_message(commands, client_id); + //players[client_id].last_chat = time_get()+time_freq()*10; + } + else if (!strncasecmp(msg->message, "/kick ",6) && !vote_called && config.sv_allow_votes) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + int playersnumber=0; + int playerstokick=-1; + char message[256]; + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + str_format(message, sizeof(message), "/kick "); + strcat(message,server_clientname(i)); + if(!strncmp(msg->message, message,9)) + { + playersnumber++; + playerstokick=i; + } + } + } + if(playersnumber == 1 && playerstokick != -1) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "kick"); + votedtokick=playerstokick; + str_format(vote_message, sizeof(vote_message), "Vote for: kick %s\nsay \"/yes\" or \"/no\" (%s)", server_clientname(playerstokick),server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + server_save_ip(votedtokick); + } + else if(playersnumber > 1) + { + send_display("Several players possible", client_id); + } + else + { + send_display("There is no player with this name", client_id); + } + } + else if (!strncasecmp(msg->message, "/kick_id ",9) && !vote_called && config.sv_allow_votes) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votedtokick = atoi(&msg->message[9]); + if(votedtokick > 0 && votedtokick-- <= MAX_CLIENTS && players[votedtokick].client_id != -1) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "kick"); + str_format(vote_message, sizeof(vote_message), "Vote for: kick %s\nsay \"/yes\" or \"/no\" (%s)",server_clientname(votedtokick), server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + server_save_ip(votedtokick); + } + else + { + votedtokick = -1; + send_display("That was no valid integer", client_id); + } + } + else if (!strncasecmp(msg->message, "/next_map",9) && !vote_called && config.sv_allow_next_map) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "next_map"); + str_format(vote_message, sizeof(vote_message), "Vote for: changing to next map\nsay \"/yes\" or \"/no\" (%s)",server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + } + else if (!strncasecmp(msg->message, "/timelimit ",11) && !vote_called && config.sv_allow_timelimit) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votedtokick = atoi(&msg->message[11]); + if(votedtokick == -1 || (votedtokick>0 && votedtokick < 1000)) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "timelimit"); + str_format(vote_message, sizeof(vote_message), "Vote for: set timelimit to %i\nsay \"/yes\" or \"/no\" (%s)",votedtokick, server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + } + else + { + votedtokick = -1; + send_display("That was no valid integer", client_id); + } + } + else if (!strncasecmp(msg->message, "/scorelimit ",12) && !vote_called && config.sv_allow_scorelimit) + { + if(config.sv_vote_pause && players[client_id].lastvote != 0 && (server_tick() - players[client_id].lastvote)/server_tickspeed() < config.sv_vote_pause) + { + char novote[90]; + sprintf(novote, "You can start votings in %i seconds", config.sv_vote_pause-(server_tick() - players[client_id].lastvote)/server_tickspeed()); + send_display(novote, client_id); + return; + } + votedtokick = atoi(&msg->message[12]); + if(votedtokick == -1 || (votedtokick>0 && votedtokick < 1000)) + { + votetime = config.sv_votetime; + str_format(votetype, sizeof(votetype), "scorelimit"); + str_format(vote_message, sizeof(vote_message), "Vote for: set scorelimit to %i\nsay \"/yes\" or \"/no\" (%s)",votedtokick, server_clientname(client_id)); + send_display(vote_message,-1); + vote_called=true; + timer_vote = server_tick(); + players[client_id].votedfor = 1; + players[client_id].lastvote = server_tick(); + } + else + { + votedtokick = -1; + send_display("That was no valid integer", client_id); + } + } + else if (!strncasecmp(msg->message, "/yes",3) && players[client_id].votedfor == -1 && vote_called) + { + char message[256]; + str_format(message, sizeof(message), "%s voted for yes",server_clientname(client_id)); + send_chat(-1,CHAT_ALL,message); + players[client_id].votedfor = 1; + //resultvote(); + } + else if (!strncasecmp(msg->message, "/no",2) && players[client_id].votedfor == -1 && vote_called) + { + char buf[256]; + str_format(buf, sizeof(buf), "%s voted for no",server_clientname(client_id)); + send_message(buf, -1); + players[client_id].votedfor = 0; + //resultvote(); + } + else if (!strncasecmp(msg->message, "/currentvote",12) && vote_called) + { + send_display(vote_message, client_id); + resultvote(); + } + else if (!strncasecmp(msg->message, "/players",2)) + { + char all_players[512] = "Players:\n"; + int i = 0; + for(;i < MAX_CLIENTS;i++) + { + if(players[i].client_id != -1) + { + char player[5]; + sprintf(player, "%i", i+1); + strcat(all_players, player); + strcat(all_players, ": \""); + strcat(all_players, server_clientname(i)); + strcat(all_players, "\"\n"); + } + } + send_message(all_players, client_id); + } + else + { + players[client_id].last_chat = time_get(); + send_chat(client_id, team, msg->message); + } } } else if (msgtype == NETMSGTYPE_CL_SETTEAM) @@ -2159,7 +2782,7 @@ // copy old name char oldname[MAX_NAME_LENGTH]; str_copy(oldname, server_clientname(client_id), MAX_NAME_LENGTH); - + server_setclientname(client_id, msg->name); if(msgtype == NETMSGTYPE_CL_CHANGEINFO && strcmp(oldname, server_clientname(client_id)) != 0) { @@ -2167,16 +2790,16 @@ str_format(chattext, sizeof(chattext), "%s changed name to %s", oldname, server_clientname(client_id)); send_chat(-1, CHAT_ALL, chattext); } - + // set skin str_copy(players[client_id].skin_name, msg->skin, sizeof(players[client_id].skin_name)); - + gameobj->on_player_info_change(&players[client_id]); - + if(msgtype == NETMSGTYPE_CL_STARTINFO) { // a client that connected! - + // send all info to this client for(int i = 0; i < MAX_CLIENTS; i++) { @@ -2186,13 +2809,13 @@ // send tuning parameters to client send_tuning_params(client_id); - + // NETMSG_SV_READY_TO_ENTER m; m.pack(MSGFLAG_VITAL); - server_send_msg(client_id); + server_send_msg(client_id); } - + send_info(client_id, -1); } else if (msgtype == NETMSGTYPE_CL_EMOTICON) @@ -2265,25 +2888,59 @@ { int client_id = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); int team = clamp(console_arg_int(result, 1), -1, 1); - + dbg_msg("", "%d %d", client_id, team); - + if(players[client_id].client_id != client_id) return; - + players[client_id].set_team(team); } +static void con_vote(void *result, void *user_data) +{ + votetime =console_arg_int(result, 0); + char buf[512]; + str_format(buf, sizeof(buf), "||| Vote for: %s (say \"/yes\" or \"/no\") |||", console_arg_string(result, 1)); + send_chat(-1, CHAT_ALL, buf); + vote_called=true; + //if (console_arg_int(result, 0) == 1) + timer_vote = server_tick(); + //else world->timer_vote = -1; +} + +static void con_next_map(void *result, void *user_data) +{ + gameobj->endround(); +} + +static void con_cancel_vote(void *result, void *user_data) +{ + str_format(votetype, sizeof(votetype), "null"); + votedtokick = -1; + for (int i=0; i < MAX_CLIENTS;i++) + { + if(players[i].client_id !=-1) + { + players[i].votedfor=-1; + } + } + vote_called = false; + send_display("Vote canceled by admin", -1); +} + void mods_console_init() { MACRO_REGISTER_COMMAND("tune", "si", con_tune_param, 0); MACRO_REGISTER_COMMAND("tune_reset", "", con_tune_reset, 0); MACRO_REGISTER_COMMAND("tune_dump", "", con_tune_dump, 0); - + MACRO_REGISTER_COMMAND("vote", "ir", con_vote, 0); MACRO_REGISTER_COMMAND("restart", "?i", con_restart, 0); MACRO_REGISTER_COMMAND("broadcast", "r", con_broadcast, 0); MACRO_REGISTER_COMMAND("say", "r", con_say, 0); MACRO_REGISTER_COMMAND("set_team", "ii", con_set_team, 0); + MACRO_REGISTER_COMMAND("next_map", "", con_next_map, 0); + MACRO_REGISTER_COMMAND("cancel_vote", "", con_cancel_vote, 0); } void mods_init() @@ -2312,11 +2969,11 @@ // create all entities from the game layer MAPITEM_LAYER_TILEMAP *tmap = layers_game_layer(); TILE *tiles = (TILE *)map_get_data(tmap->data); - + num_spawn_points[0] = 0; num_spawn_points[1] = 0; num_spawn_points[2] = 0; - + for(int y = 0; y < tmap->height; y++) { for(int x = 0; x < tmap->width; x++)