diff -Naur teeworlds-0.4.2-src/src/engine/e_if_server.h racemodspam/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 +++ racemodspam/src/engine/e_if_server.h 2008-08-07 17:53:52.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 racemodspam/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 +++ racemodspam/src/engine/server/es_server.c 2008-09-17 16:16:56.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 static TILE *tiles; +static int *dest[10]; static int width = 0; static int height = 0; +static int len[10]; int col_width() { return width; } int col_height() { return height; } @@ -19,6 +21,25 @@ width = layers_game_layer()->width; height = layers_game_layer()->height; tiles = (TILE *)map_get_data(layers_game_layer()->data); + mem_zero(&len, sizeof(len)); + for(int i=width*height-1;i>=0;i--) { + if(tiles[i].index==TILE_AIR) { + len[0]++; + } else if((tiles[i].index&1)!=0 && tiles[i].index>2 && tiles[i].index<21) { + len[tiles[i].index>>1]++; + } + } + for(int i=0;i<10;i++) { + dest[i] = new int[len[i]]; + len[i] = 0; + } + for(int i=width*height-1;i>=0;i--) { + if(tiles[i].index==TILE_AIR) { + dest[0][len[0]++] = i; + } else if((tiles[i].index&1)!=0 && tiles[i].index>2 && tiles[i].index<21) { + dest[tiles[i].index>>1][len[tiles[i].index>>1]++] = i; + } + } return 1; } @@ -30,12 +51,87 @@ if(nx < 0 || nx >= width || ny >= height) return 1; - if(y < 0) - return 0; // up == sky == free + if(y<0) { + return(tiles[nx].index==TILE_SOLID); + } return tiles[ny*width+nx].index == TILE_SOLID; } +int col_is_damage(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(y<0 || nx < 0 || nx >= width || ny >= height) + return 0; + + return tiles[ny*width+nx].index == TILE_DAMAGE; +} + +int col_is_nohook(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(nx < 0 || nx >= width || ny >= height) + return 0; + + if(y<0) { + return(tiles[nx].index==TILE_NOHOOK); + } + + return tiles[ny*width+nx].index == TILE_NOHOOK; +} + +int col_is_teleport(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(y < 0 || nx < 0 || nx >= width || ny >= height) + return 0; + + int z = tiles[ny*width+nx].index-1; + if(z>2 && z<21 && (z&1)!=0) { + return(z>>1); + } + return(0); +} + +int col_is_begin(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(y<0 || nx < 0 || nx >= width || ny >= height) + return 0; + + return tiles[ny*width+nx].index == TILE_BEGIN; +} +int col_is_end(int x, int y) +{ + int nx = x/32; + int ny = y/32; + if(y<0 || nx < 0 || nx >= width || ny >= height) + return 0; + + return tiles[ny*width+nx].index == TILE_END; +} +vec2 teleport(int a) { + if(len[a]>0) { + int r = rand()%len[a]; + int x = (dest[a][r]%width)<<5; + int y = (dest[a][r]/width)<<5; + return(vec2((float)x+16.0,(float)y+16.0)); + } else { + return(vec2(0,0)); + } +} + +vec2 rand_point() { + int p = rand()%len[0]; + int x = (dest[0][p]%width)<<5; + int y = (dest[0][p]/width)<<5; + return(vec2((float)x+16.0,(float)y+16.0)); +} + // TODO: rewrite this smarter! bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out) { @@ -57,6 +153,21 @@ return false; } +bool col_intersect_nohook(vec2 pos0, vec2 pos1) +{ + float d = distance(pos0, pos1); + + for(float f = 0; f < d; f++) + { + float a = f/d; + vec2 pos = mix(pos0, pos1, a); + if(col_is_nohook((int)pos.x, (int)pos.y)) { + return true; + } + } + return false; +} + /* Simple collision rutines! */ diff -Naur teeworlds-0.4.2-src/src/game/g_collision.h racemodspam/src/game/g_collision.h --- teeworlds-0.4.2-src/src/game/g_collision.h 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/g_collision.h 2008-08-02 08:08:48.000000000 +0200 @@ -7,8 +7,16 @@ int col_init(); int col_is_solid(int x, int y); +int col_is_damage(int x, int y); +int col_is_nohook(int x, int y); +int col_is_teleport(int x, int y); +int col_is_begin(int x, int y); +int col_is_end(int x, int y); int col_width(); +vec2 teleport(int z); int col_height(); +vec2 rand_point(); bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out); +bool col_intersect_nohook(vec2 pos0, vec2 pos1); #endif diff -Naur teeworlds-0.4.2-src/src/game/g_game.cpp racemodspam/src/game/g_game.cpp --- teeworlds-0.4.2-src/src/game/g_game.cpp 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/g_game.cpp 2008-08-22 21:38:24.000000000 +0200 @@ -1,6 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #include #include "g_game.h" +#include const char *tuning_params::names[] = { @@ -271,6 +272,8 @@ going_to_hit_ground = true; // Check against other players first + if(!config.sv_player_nohook) + { for(int i = 0; i < MAX_CLIENTS; i++) { player_core *p = world->players[i]; @@ -286,7 +289,7 @@ break; } } - + } if(hook_state == HOOK_FLYING) { // check against ground @@ -377,7 +380,7 @@ // handle player <-> player collision float d = distance(pos, p->pos); vec2 dir = normalize(pos - p->pos); - if(d < phys_size*1.25f && d > 1.0f) + if(config.sv_player_collide && d < phys_size*1.25f && d > 1.0f) { float a = (phys_size*1.45f - d); diff -Naur teeworlds-0.4.2-src/src/game/g_game.h racemodspam/src/game/g_game.h --- teeworlds-0.4.2-src/src/game/g_game.h 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/g_game.h 2008-09-17 16:35:30.000000000 +0200 @@ -20,11 +20,11 @@ } static const char *names[]; - + #define MACRO_TUNING_PARAM(name,value) tune_param name; #include "g_tuning.h" #undef MACRO_TUNING_PARAM - + static int num() { return sizeof(tuning_params)/sizeof(int); } bool set(int index, float value); bool set(const char *name, float value); @@ -100,7 +100,7 @@ HOOK_RETRACT_END=3, HOOK_FLYING, HOOK_GRABBED, - + COREEVENT_GROUND_JUMP=0x01, COREEVENT_AIR_JUMP=0x02, COREEVENT_HOOK_LAUNCH=0x04, @@ -116,7 +116,7 @@ { mem_zero(players, sizeof(players)); } - + tuning_params tuning; class player_core *players[MAX_CLIENTS]; }; @@ -125,25 +125,26 @@ { public: world_core *world; - + vec2 pos; vec2 vel; - + vec2 hook_pos; vec2 hook_dir; + vec2 old_vel; int hook_tick; int hook_state; int hooked_player; - + int jumped; NETOBJ_PLAYER_INPUT input; - + int triggered_events; - + void reset(); void tick(); void move(); - + void read(const NETOBJ_PLAYER_CORE *obj_core); void write(NETOBJ_PLAYER_CORE *obj_core); void quantize(); diff -Naur teeworlds-0.4.2-src/src/game/g_mapitems.h racemodspam/src/game/g_mapitems.h --- teeworlds-0.4.2-src/src/game/g_mapitems.h 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/g_mapitems.h 2008-08-02 08:08:48.000000000 +0200 @@ -43,8 +43,28 @@ TILE_AIR=0, TILE_SOLID, + TILE_DAMAGE, + TILE_D1, + TILE_T1, + TILE_D2, + TILE_T2, + TILE_D3, + TILE_T3, + TILE_D4, + TILE_T4, + TILE_D5, + TILE_T5, + TILE_D6, + TILE_T6, + TILE_D7, + TILE_T7, + TILE_D8, + TILE_T8, + TILE_D9, + TILE_T9, TILE_NOHOOK, - + TILE_BEGIN, + TILE_END, TILEFLAG_VFLIP=1, TILEFLAG_HFLIP=2, TILEFLAG_OPAQUE=4, diff -Naur teeworlds-0.4.2-src/src/game/g_variables.h racemodspam/src/game/g_variables.h --- teeworlds-0.4.2-src/src/game/g_variables.h 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/g_variables.h 2008-09-17 17:03:11.000000000 +0200 @@ -57,3 +57,65 @@ MACRO_CONFIG_INT(sv_spamprotection, 1, 0, 1) MACRO_CONFIG_INT(sv_spectator_slots, 0, 0, 12) + +MACRO_CONFIG_INT(sv_player_nohook, 0, 0, 1) +MACRO_CONFIG_INT(sv_player_collide, 1, 0, 1) +MACRO_CONFIG_INT(sv_ko_mode, 0, 0, 1) +MACRO_CONFIG_INT(sv_teamchanges, 0, 0, 100) +MACRO_CONFIG_INT(sv_time_blocked, 180, 1, 1000) +MACRO_CONFIG_INT(sv_teamchangeskick, 0, 0, 100) +MACRO_CONFIG_INT(sv_teamchangesban, 0, 0, 100) +MACRO_CONFIG_INT(sv_messagesnum, 0, 0, 100) +MACRO_CONFIG_INT(sv_same_messages, 0, 0, 100) +MACRO_CONFIG_INT(sv_time_muted, 180, 1, 1000) +MACRO_CONFIG_INT(sv_messageskick, 0, 0, 100) +MACRO_CONFIG_INT(sv_messagesban, 0, 0, 100) +MACRO_CONFIG_STR(sv_startmessage, 1000, "") +MACRO_CONFIG_STR(sv_endroundmessage, 1000, "") +MACRO_CONFIG_INT(sv_handle_mapvotes, 0, 0, 1) +MACRO_CONFIG_INT(sv_kick_teamkiller, 0, 0, 100) +MACRO_CONFIG_INT(sv_name_blacklist, 0, 0, 1) +MACRO_CONFIG_INT(sv_reserved_slots, 0, 0, 12) +MACRO_CONFIG_STR(sv_reserved_slots_pass, 32, "") +MACRO_CONFIG_INT(sv_allow_votes, 1, 0, 1) +MACRO_CONFIG_INT(sv_all_vote, 0, 0, 1) +MACRO_CONFIG_INT(sv_votetime, 60, 0, 500) +MACRO_CONFIG_INT(sv_ban_time, 10, 0, 1000) +MACRO_CONFIG_INT(sv_regen, 0, 0, 0) +MACRO_CONFIG_INT(sv_teleport, 0, 0, 1) +MACRO_CONFIG_INT(sv_strip, 0, 0, 1) +MACRO_CONFIG_INT(sv_infinite_ammo, 0, 0, 1) +MACRO_CONFIG_STR(sv_start, 18, "1 1 0 0 0 0") +MACRO_CONFIG_INT(sv_race_mod, 0, 0, 1) +MACRO_CONFIG_INT(sv_teleport_grenade, 0, 0, 1) +MACRO_CONFIG_INT(sv_teleport_kill, 0, 0, 1) +MACRO_CONFIG_INT(sv_suicide_killer, 0, 0, 1) +MACRO_CONFIG_INT(sv_rocket_jump_damage, 1, 0, 1) +MACRO_CONFIG_INT(sv_powerup_respawn, 1, 0, 10) +MACRO_CONFIG_STR(sv_rank_site, 32, "none") +MACRO_CONFIG_INT(sv_team_balance, 0, 0, 100) +MACRO_CONFIG_INT(sv_allow_next_map, 0, 0, 1) +MACRO_CONFIG_INT(sv_allow_timelimit, 0, 0, 1) +MACRO_CONFIG_INT(sv_allow_scorelimit, 0, 0, 1) +MACRO_CONFIG_INT(sv_ban_teamkiller, 0, 0, 100) +MACRO_CONFIG_INT(sv_vote_pause, 0, 0, 1000) +MACRO_CONFIG_INT(sv_silent_mode, 0, 0, 1) +MACRO_CONFIG_INT(sv_balance_warning, 0, 0, 1000) + +MACRO_CONFIG_INT(sv_start_health, 10, 0, 10) +MACRO_CONFIG_INT(sv_start_armor, 0, 0, 10) +MACRO_CONFIG_INT(sv_spawn_powerups, 1, 0, 1) +MACRO_CONFIG_INT(sv_ninja_mod, 0, 0, 1) +MACRO_CONFIG_INT(sv_infinite_jumping, 0, 0, 1) + +MACRO_CONFIG_INT(sv_gun_startvelocity, 0, 0, 100000) +MACRO_CONFIG_INT(sv_shotgun_startvelocity, 0, 0, 100000) +MACRO_CONFIG_INT(sv_grenadelauncher_startvelocity, 0, 0, 100000) +MACRO_CONFIG_INT(sv_gun_bulletdimension, 0, 0, 1000) +MACRO_CONFIG_INT(sv_shotgun_bulletdimension, 0, 0, 1000) +MACRO_CONFIG_INT(sv_grenade_bulletdimension, 0, 0, 1000) +MACRO_CONFIG_INT(sv_activate_stopdamage, 0, 0, 1000) +MACRO_CONFIG_INT(sv_stopdamage, 0, 0, 1000) +MACRO_CONFIG_INT(sv_correct_selfdamage, 0, 0, 1) + +MACRO_CONFIG_INT(sv_scoreboard, 10, 0, 1000) diff -Naur teeworlds-0.4.2-src/src/game/server/gs_common.h racemodspam/src/game/server/gs_common.h --- teeworlds-0.4.2-src/src/game/server/gs_common.h 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/server/gs_common.h 2008-09-17 20:29:49.000000000 +0200 @@ -15,7 +15,7 @@ // class event_handler { - static const int MAX_EVENTS = 128; + static const int MAX_EVENTS = 256; static const int MAX_DATASIZE = 128*64; int types[MAX_EVENTS]; // TODO: remove some of these arrays @@ -23,7 +23,7 @@ int sizes[MAX_EVENTS]; int client_masks[MAX_EVENTS]; char data[MAX_DATASIZE]; - + int current_offset; int num_events; public: @@ -42,7 +42,7 @@ friend class game_world; friend class player; entity *prev_entity; - entity *next_entity; + entity *prev_type_entity; entity *next_type_entity; @@ -53,28 +53,28 @@ unsigned flags; int objtype; vec2 pos; - + entity *next_entity; enum { FLAG_DESTROY=0x00000001, FLAG_PHYSICS=0x00000002, }; - + entity(int objtype); virtual ~entity(); - + virtual void reset() {} virtual void post_reset() {} - + void set_flag(unsigned flag) { flags |= flag; } void clear_flag(unsigned flag) { flags &= ~flag; } virtual void destroy() { delete this; } virtual void tick() {} virtual void tick_defered() {} - + virtual void snap(int snapping_client) {} - + virtual bool take_damage(vec2 force, int dmg, int from, int weapon) { return true; } }; @@ -94,9 +94,9 @@ entity *first_entity_types[NUM_ENT_TYPES]; bool paused; bool reset_requested; - + world_core core; - + game_world(); ~game_world(); int find_entities(vec2 pos, float radius, entity **ents, int max); @@ -105,7 +105,7 @@ void insert_entity(entity *ent); void destroy_entity(entity *ent); void remove_entity(entity *ent); - + // void snap(int snapping_client); void tick(); @@ -120,42 +120,44 @@ protected: void cyclemap(); void resetgame(); - + int round_start_tick; int game_over_tick; int sudden_death; - + int teamscore[2]; - + int warmup; int round_count; - + bool is_teamplay; - + public: int gametype; + int next_players[MAX_CLIENTS]; + bool queue_initiated; gameobject(); void do_team_score_wincheck(); void do_player_score_wincheck(); - + void do_warmup(int seconds); - + void startround(); void endround(); - + bool is_friendly_fire(int cid1, int cid2); - + virtual bool on_entity(int index, vec2 pos); - + virtual void post_reset(); virtual void tick(); - + virtual void on_player_spawn(class player *p) {} virtual int on_player_death(class player *victim, class player *killer, int weapon); - + virtual void on_player_info_change(class player *p); - + virtual void snap(int snapping_client); virtual int get_auto_team(int notthisid); virtual bool can_join_team(int team, int notthisid); @@ -170,12 +172,12 @@ { public: static const int phys_size = 14; - + int type; int subtype; // weapon type for instance? int spawntick; powerup(int _type, int _subtype = 0); - + virtual void reset(); virtual void tick(); virtual void snap(int snapping_client); @@ -189,7 +191,7 @@ { PROJECTILE_FLAGS_EXPLODE = 1 << 0, }; - + vec2 direction; entity *powner; // this is nasty, could be removed when client quits int lifespan; @@ -202,13 +204,13 @@ int bounce; float force; int start_tick; - + float dimension; projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags, float force, int sound_impact, int weapon); vec2 get_pos(float time); void fill_info(NETOBJ_PROJECTILE *proj); - + void proceed_bulletcollision(vec2 curpos); virtual void reset(); virtual void tick(); virtual void snap(int snapping_client); @@ -222,14 +224,14 @@ int bounces; int eval_tick; player *owner; - + bool hit_player(vec2 from, vec2 to); void do_bounce(); - + public: - + laser(vec2 pos, vec2 direction, float start_energy, player *owner); - + virtual void reset(); virtual void tick(); virtual void snap(int snapping_client); @@ -239,6 +241,7 @@ class player : public entity { public: + bool played; static const int phys_size = 28; // weapon info @@ -251,21 +254,21 @@ int ammocost; bool got; } weapons[NUM_WEAPONS]; - + int active_weapon; int last_weapon; int queued_weapon; - + int reload_timer; int attack_tick; - + int damage_taken; int emote_type; int emote_stop; int last_action; // last tick that the player took any action ie some input - + // int client_id; char skin_name[64]; @@ -277,12 +280,16 @@ NETOBJ_PLAYER_INPUT latest_previnput; NETOBJ_PLAYER_INPUT latest_input; - // input + // input NETOBJ_PLAYER_INPUT previnput; NETOBJ_PLAYER_INPUT input; int num_inputs; int jumped; - + + int starttime; + int ended; + int started; + int refreshtime; int damage_taken_tick; int health; @@ -299,14 +306,18 @@ // int score; + int lastplayer; + int lastplayertime; + int lastvote; + int votedfor; int team; int player_state; // if the client is chatting, accessing a menu or so - + bool spawning; bool dead; int die_tick; vec2 die_pos; - + // latency calculations int latency_accum; int latency_accum_min; @@ -315,9 +326,9 @@ int latency_min; int latency_max; - // the player core for the physics + // the player core for the physics player_core core; - + // int64 last_chat; @@ -326,32 +337,36 @@ void init(); virtual void reset(); virtual void destroy(); - + void try_respawn(); void respawn(); - + void set_team(int team); - + bool is_grounded(); - + void set_weapon(int w); - + void handle_weaponswitch(); void do_weaponswitch(); - + int handle_weapons(); int handle_ninja(); - + void on_direct_input(NETOBJ_PLAYER_INPUT *input); void fire_weapon(); virtual void tick(); virtual void tick_defered(); - + void die(int killer, int weapon); - + virtual bool take_damage(vec2 force, int dmg, int from, int weapon); virtual void snap(int snaping_client); + + int last_message_tick; unsigned char message_num; bool muted; + bool teamchanging; unsigned char team_changes; int last_team_set; int teamkills; + bool voted; }; extern player *players; diff -Naur teeworlds-0.4.2-src/src/game/server/gs_game.cpp racemodspam/src/game/server/gs_game.cpp --- teeworlds-0.4.2-src/src/game/server/gs_game.cpp 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/server/gs_game.cpp 2008-09-17 20:29:44.000000000 +0200 @@ -5,6 +5,7 @@ #include #include "gs_common.h" + gameobject::gameobject() : entity(NETOBJTYPE_GAME) { @@ -24,7 +25,8 @@ gametype = GAMETYPE_DM; dbg_msg("game", "-- Death Match --"); } - + for(int n = 0; n < MAX_CLIENTS; n++) + next_players[n] = -1; // do_warmup(config.sv_warmup); game_over_tick = -1; @@ -33,7 +35,7 @@ round_count = 0; is_teamplay = false; teamscore[0] = 0; - teamscore[1] = 0; + teamscore[1] = 0; } // UGLY!!!! @@ -44,7 +46,7 @@ { int type = -1; int subtype = 0; - + if(index == ENTITY_SPAWN) spawn_points[0][num_spawn_points[0]++] = pos; else if(index == ENTITY_SPAWN_RED) @@ -75,7 +77,7 @@ type = POWERUP_NINJA; subtype = WEAPON_NINJA; } - + if(type != -1) { powerup *ppower = new powerup(type, subtype); @@ -90,7 +92,15 @@ { if(warmup) // game can't end when we are running warmup return; - + if(strlen(config.sv_endroundmessage) > 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; @@ -106,7 +116,7 @@ void gameobject::startround() { resetgame(); - + round_start_tick = server_tick(); sudden_death = 0; game_over_tick = -1; @@ -123,11 +133,11 @@ if(round_count < config.sv_rounds_per_map-1) return; - + // handle maprotation const char *map_rotation = config.sv_maprotation; const char *current_map = config.sv_map; - + int current_map_len = strlen(current_map); const char *next_map = map_rotation; while(*next_map) @@ -135,25 +145,25 @@ int wordlen = 0; while(next_map[wordlen] && !is_separator(next_map[wordlen])) wordlen++; - + if(wordlen == current_map_len && strncmp(next_map, current_map, current_map_len) == 0) { // map found next_map += current_map_len; while(*next_map && is_separator(*next_map)) next_map++; - + break; } - + next_map++; } - + // restart rotation if(next_map[0] == 0) next_map = map_rotation; - // cut out the next map + // cut out the next map char buf[512]; for(int i = 0; i < 512; i++) { @@ -164,12 +174,12 @@ break; } } - + // skip spaces int i = 0; while(is_separator(buf[i])) i++; - + dbg_msg("game", "rotating map to %s", &buf[i]); str_copy(config.sv_map, &buf[i], sizeof(config.sv_map)); } @@ -182,7 +192,7 @@ players[i].respawn(); } } - + void gameobject::on_player_info_change(class player *p) { const int team_colors[2] = {65387, 10223467}; @@ -201,7 +211,7 @@ int gameobject::on_player_death(class player *victim, class player *killer, int weapon) { // do scoreing - if(!killer) + if(!killer || weapon == -1) return 0; if(killer == victim) victim->score--; // suicide @@ -224,13 +234,13 @@ { if(cid1 == cid2) return false; - + if(is_teamplay) { if(players[cid1].team == players[cid2].team) return true; } - + return false; } @@ -243,18 +253,105 @@ if(!warmup) startround(); } - + if(game_over_tick != -1) { // game over.. wait for restart - if(server_tick() > game_over_tick+server_tickspeed()*10) + if(server_tick() > game_over_tick+server_tickspeed()*config.sv_scoreboard) { + if((gametype == GAMETYPE_TDM || gametype == GAMETYPE_CTF) && config.sv_ko_mode) + { + int team0 = -1; + int team1 = -1; + int next = next_players[0]; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + { + if(players[i].team == 0) + team0 = i; + if(players[i].team == 1) + team1 = i; + } + } + if(teamscore[0] > teamscore[1] || (team0 != -1 && team1 == -1)) + { + if(next > -1) + { + if(team1 != -1) + { + players[team1].set_team(-1); + players[team1].played = true; + } + players[next].set_team(1); + } + else + { + char note[64]; + strcpy(note, server_clientname(team0)); + strcat(note, " is the best player on this server"); + NETMSG_SV_BROADCAST msg; + msg.message = note; + msg.pack(MSGFLAG_VITAL); + server_send_msg(-1); + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + { + players[i].played = false; + } + } + cyclemap(); + startround(); + return; + } + } + else + { + if(next > -1) + { + if(team0 != -1) + { + players[team0].set_team(-1); + players[team0].played = true; + } + players[next].set_team(0); + } + else + { + char note[64]; + strcpy(note, server_clientname(team1)); + strcat(note, " is the best player on this server"); + NETMSG_SV_BROADCAST msg; + msg.message = note; + msg.pack(MSGFLAG_VITAL); + server_send_msg(-1); + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(players[i].client_id != -1) + { + players[i].played = false; + } + } + cyclemap(); + startround(); + return; + } + } + for(int i = 1; i < MAX_CLIENTS; i++) + { + next_players[i-1] = next_players[i]; + } + next_players[MAX_CLIENTS - 1] = -1; + startround(); + return; + } cyclemap(); startround(); } } - - + + // update browse info int prog = -1; if(config.sv_timelimit > 0) @@ -279,7 +376,7 @@ if(warmup) prog = -1; - + server_setbrowseinfo(gametype, prog); } @@ -289,14 +386,14 @@ game->paused = world->paused; game->game_over = game_over_tick==-1?0:1; game->sudden_death = sudden_death; - + game->score_limit = config.sv_scorelimit; game->time_limit = config.sv_timelimit; game->round_start_tick = round_start_tick; game->gametype = gametype; - + game->warmup = warmup; - + game->teamscore_red = teamscore[0]; game->teamscore_blue = teamscore[1]; } @@ -316,7 +413,7 @@ int team = 0; if(is_teamplay) team = numplayers[0] > numplayers[1] ? 1 : 0; - + if(can_join_team(team, notthisid)) return team; return -1; @@ -334,7 +431,7 @@ numplayers[players[i].team]++; } } - + return (numplayers[0] + numplayers[1]) < config.sv_max_clients-config.sv_spectator_slots; } @@ -358,7 +455,7 @@ topscore_count++; } } - + // check score win condition if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) || (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) diff -Naur teeworlds-0.4.2-src/src/game/server/gs_game_ctf.cpp racemodspam/src/game/server/gs_game_ctf.cpp --- teeworlds-0.4.2-src/src/game/server/gs_game_ctf.cpp 2008-04-05 15:13:02.000000000 +0200 +++ racemodspam/src/game/server/gs_game_ctf.cpp 2008-08-02 08:08:48.000000000 +0200 @@ -1,6 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #include #include +#include #include "gs_common.h" #include "gs_game_ctf.h" @@ -86,7 +87,7 @@ { // CAPTURE! \o/ teamscore[fi^1] += 100; - f->carrying_player->score += 5; + if(!config.sv_race_mod)f->carrying_player->score += 5; dbg_msg("game", "flag_capture player='%d:%s'", f->carrying_player->client_id, server_clientname(f->carrying_player->client_id)); @@ -110,7 +111,7 @@ if(!f->at_stand) { player *p = close_players[i]; - p->score += 1; + if(!config.sv_race_mod)p->score += 1; dbg_msg("game", "flag_return player='%d:%s'", p->client_id, server_clientname(p->client_id)); @@ -125,7 +126,7 @@ teamscore[fi^1]++; f->at_stand = 0; f->carrying_player = close_players[i]; - f->carrying_player->score += 1; + if(!config.sv_race_mod)f->carrying_player->score += 1; dbg_msg("game", "flag_grab player='%d:%s'", f->carrying_player->client_id, server_clientname(f->carrying_player->client_id)); @@ -145,7 +146,7 @@ if(!f->carrying_player && !f->at_stand) { - if(server_tick() > f->drop_tick + server_tickspeed()*30) + if(server_tick() > f->drop_tick + server_tickspeed()*30 || col_is_damage(f->pos.x, f->pos.y)) { create_sound_global(SOUND_CTF_RETURN); f->reset(); diff -Naur teeworlds-0.4.2-src/src/game/server/gs_server.cpp racemodspam/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 +++ racemodspam/src/game/server/gs_server.cpp 2008-09-17 20:29:51.000000000 +0200 @@ -2,6 +2,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -24,10 +30,91 @@ 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); - +int timer_vote=-1;bool vote_called=false;int votetime=-1;char votetype[32];static int votedtokick=-1; game_world *world; +int regn = 0,tele=0,strip=0; +int start[6];char vote_message[300]; +class player_score { + public: + std::string my_name; + float my_score; + + player_score(std::string n_name,float n_score) { + this->my_name=n_name; + this->my_score=n_score; + } + void setScore(float n_score) { + this->my_score=n_score; + } + std::string name() { + return this->my_name; + } + float score() { + return this->my_score; + } + bool operator==(const player_score& other) { + return (this->my_score==other.my_score); + } + bool operator<(const player_score& other) { + return (this->my_score top5; + +std::string top5_recordfile() { + std::ostringstream oss; + oss << config.sv_map << "_record.dtb"; + return oss.str(); +} + +void top5_save() { + std::fstream f; + f.open(top5_recordfile().c_str(),std::ios::out); + if (f.fail()==false) { + for (std::list::iterator i=top5.begin(); i!=top5.end(); i++) { + f << i->name() << std::endl << i->score() << std::endl; + } + } + f.close(); +} + +void top5_load() { + std::fstream f; + f.open(top5_recordfile().c_str(),std::ios::in); + top5.clear(); + while (!f.eof() && !f.fail()) { + std::string tmpname; + float tmpscore; + std::getline(f, tmpname); + if (!f.eof() && tmpname!="") { + f >> tmpscore; + top5.push_back(*new player_score(tmpname,tmpscore)); + } + } + f.close(); +} + +void top5_parsePlayer(std::string nom,float score) { + + for (std::list::iterator i=top5.begin(); i!=top5.end(); i++) { + if (i->name() == nom) { + if (i->score() > score) + i->setScore(score); + top5.sort(); + top5_save(); + return; + } + } + player_score nscore(nom,score); + top5.push_back(nscore); + + top5.sort(); + top5_save(); +} enum { @@ -36,13 +123,146 @@ CHAT_RED=0, CHAT_BLUE=1 }; +static 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 +300,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 +340,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 +648,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 +674,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 +687,7 @@ /*if(ent->objtype >= 0 && ent->objtype < OBJTYPE_FLAG) perf_end();*/ } - + /* perf_end(); @@ -455,6 +752,18 @@ this->weapon = weapon; this->bounce = 0; this->start_tick = server_tick(); + switch(weapon) + { + case WEAPON_GUN: + dimension = (float)config.sv_gun_bulletdimension/10; + break; + case WEAPON_SHOTGUN: + dimension = (float)config.sv_shotgun_bulletdimension/10; + break; + case WEAPON_GRENADE: + dimension = (float)config.sv_grenade_bulletdimension/10; + break; + } world->insert_entity(this); } @@ -482,38 +791,85 @@ curvature = tuning.gun_curvature; speed = tuning.gun_speed; } - + return calc_pos(pos, direction, curvature, speed, time); } +void projectile::proceed_bulletcollision(vec2 curpos) +{ + projectile *check = (projectile*)world->first_entity; + bool collide = false; + + while(check) + { + vec2 otherpos = check->get_pos((server_tick()-check->start_tick)/(float)server_tickspeed()); + if(check->objtype == NETOBJTYPE_PROJECTILE && check != this && + ((curpos.x - dimension < otherpos.x + check->dimension && curpos.x + dimension > otherpos.x + check->dimension) + || (curpos.x + dimension > otherpos.x - dimension && curpos.x - dimension < otherpos.x - dimension)) + && ((curpos.y - dimension < otherpos.y + check->dimension && curpos.y + dimension > otherpos.y + check->dimension) + ||(curpos.y + dimension > otherpos.y - dimension && curpos.y - dimension < otherpos.y - dimension))) + { + collide = true; + if (check->lifespan >= 0 || check->weapon == WEAPON_GRENADE) + create_sound(otherpos, sound_impact); + if (check->flags & check->PROJECTILE_FLAGS_EXPLODE) + create_explosion(otherpos, check->owner, check->weapon, false); + world->destroy_entity(check); + } + check = (projectile*)check->next_entity; + } + if(collide) + { + if (lifespan >= 0 || weapon == WEAPON_GRENADE) + create_sound(curpos, sound_impact); + if (flags & PROJECTILE_FLAGS_EXPLODE) + create_explosion(curpos, owner, weapon, false); + world->destroy_entity(this); + } +} 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) + + entity *targetplayer; + if(config.sv_correct_selfdamage && ct > 0.06) + { + targetplayer = (entity*)intersect_player(prevpos, curpos, 6.0f, curpos, NULL); + } + else + { + targetplayer = (entity*)intersect_player(prevpos, curpos, 6.0f, curpos, powner); + } + if((!config.sv_race_mod && targetplayer) || collide || lifespan < 0) { if (lifespan >= 0 || weapon == WEAPON_GRENADE) create_sound(curpos, sound_impact); if (flags & PROJECTILE_FLAGS_EXPLODE) create_explosion(curpos, owner, weapon, false); - else if (targetplayer) + else if (targetplayer && !config.sv_race_mod) { targetplayer->take_damage(direction * max(0.001f, force), damage, owner, weapon); } world->destroy_entity(this); + return; + } + proceed_bulletcollision(curpos); + int z = col_is_teleport((int)curpos.x,curpos.y); + if(tele && z && config.sv_teleport_grenade && weapon == WEAPON_GRENADE) { + pos = teleport(z); + start_tick=server_tick(); } } @@ -530,7 +886,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 +907,7 @@ dir = direction; bounces = 0; do_bounce(); - + world->insert_entity(this); } @@ -565,7 +921,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 +929,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 +948,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 +971,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 +1015,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,12 +1037,13 @@ { pos = vec2(0.0f, 0.0f); core.reset(); - + emote_type = 0; emote_stop = -1; - + //direction = vec2(0.0f, 1.0f); - score = 0; + if(config.sv_race_mod)score = -9999; + else score=0; dead = true; clear_flag(entity::FLAG_PHYSICS); spawning = false; @@ -703,7 +1064,7 @@ attack_tick = 0; mem_zero(&ninja, sizeof(ninja)); - + active_weapon = WEAPON_GUN; last_weapon = WEAPON_HAMMER; queued_weapon = -1; @@ -715,13 +1076,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 +1105,239 @@ 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 not balanced", -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((gameobj->gametype == GAMETYPE_TDM || gameobj->gametype == GAMETYPE_CTF) && config.sv_ko_mode) + { + if(team == 1 || team == 0) + { + if(new_team == 1 || new_team == 0) + return; + if(new_team == -1) + gameobj->endround(); + } + else + { + int playing = get_team(0) + get_team(1); + if(playing == 1 && gameobj->next_players[0] == -1) + { + gameobj->next_players[0] = client_id; + gameobj->endround(); + return; + } + else if(playing > 1 || (playing == 1 && gameobj->next_players[0] != -1 && gameobj->next_players[0] != client_id)) + { + if(played == true) + { + send_display("You already played in this tournament,\nplease wait for the next tournament", client_id); + return; + } + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(gameobj->next_players[i] == client_id) + { + char pos_msg[128]; + sprintf(pos_msg, "You are on position %i", i+1); + return; + } + if(gameobj->next_players[i] == -1) + { + gameobj->next_players[i] = client_id; + char pos_msg[128]; + sprintf(pos_msg, "You are on position %i", i+1); + send_display(pos_msg, client_id); + return; + } + } + } + } + } + else + { + 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); + die(client_id, -1); team = new_team; - score = 0; + started=0; + if(!config.sv_race_mod)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 +1360,7 @@ die_pos = vec2(0,0); pos = vec2(100,100); } - + vec2 pos; bool got; int friendly_team; @@ -797,35 +1371,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 +1430,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 +1454,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 +1471,7 @@ if(ents[i] != this) return; } - + spawning = false; pos = spawnpos; @@ -905,12 +1479,12 @@ core.vel = vec2(0,0); core.hooked_player = -1; - health = 10; - armor = 0; + health = config.sv_start_health; + armor = config.sv_start_armor; jumped = 0; - + mem_zero(&ninja, sizeof(ninja)); - + dead = false; set_flag(entity::FLAG_PHYSICS); player_state = PLAYERSTATE_PLAYING; @@ -921,17 +1495,41 @@ // init weapons mem_zero(&weapons, sizeof(weapons)); - weapons[WEAPON_HAMMER].got = true; - weapons[WEAPON_HAMMER].ammo = -1; - weapons[WEAPON_GUN].got = true; - weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo; + bool activated = false; + for(int i=0;iweapons[i].maxammo; + if(i == 6) + { + ninja.activationtick = server_tick(); + //handle_ninja(); + last_weapon = 0; + } + + } + if(!activated) + { + active_weapon = i; + last_weapon = i; + queued_weapon = i; + activated = true; + } + } else { + weapons[i].got = false; + weapons[i].ammo = 0; + } + } /*weapons[WEAPON_RIFLE].got = true; weapons[WEAPON_RIFLE].ammo = -1;*/ - - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - queued_weapon = 0; + + //active_weapon = WEAPON_GUN; + //last_weapon = WEAPON_HAMMER; + reload_timer = 0; @@ -940,6 +1538,8 @@ create_playerspawn(pos); gameobj->on_player_spawn(this); + lastplayer=-1; + lastplayertime=-1; } bool player::is_grounded() @@ -966,7 +1566,7 @@ set_weapon(active_weapon); return 0; } - + // force ninja weapon set_weapon(WEAPON_NINJA); @@ -978,7 +1578,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 +1658,7 @@ { if(reload_timer != 0) // make sure we have reloaded return; - + if(queued_weapon == -1) // check for a queued weapon return; @@ -1074,7 +1674,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 +1706,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 +1714,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 +1730,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 +1747,18 @@ // reset objects hit numobjectshit = 0; create_sound(pos, SOUND_HAMMER_FIRE); - + int type = NETOBJTYPE_PLAYER_CHARACTER; + int num = -1; entity *ents[64]; - int num = world->find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, ents, 64, &type, 1); + if(!config.sv_race_mod)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,14 +1770,16 @@ 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: { + direction.x += core.vel.x*((float)config.sv_gun_startvelocity/10000); + direction.y += core.vel.y*((float)config.sv_gun_startvelocity/10000); projectile *proj = new projectile(WEAPON_GUN, client_id, projectile_startpos, @@ -1184,28 +1787,30 @@ (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: { + direction.x += core.vel.x*((float)config.sv_shotgun_startvelocity/10000); + direction.y += core.vel.y*((float)config.sv_shotgun_startvelocity/10000); 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,23 +1825,25 @@ (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; case WEAPON_GRENADE: { + direction.x += core.vel.x*((float)config.sv_grenadelauncher_startvelocity/10000); + direction.y += core.vel.y*((float)config.sv_grenadelauncher_startvelocity/10000); projectile *proj = new projectile(WEAPON_GRENADE, client_id, projectile_startpos, @@ -1248,7 +1855,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,16 +1865,16 @@ 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 + if(weapons[active_weapon].ammo > 0 && !config.sv_infinite_ammo) // -1 == unlimited weapons[active_weapon].ammo--; attack_tick = server_tick(); reload_timer = data->weapons[active_weapon].firedelay * server_tickspeed() / 1000; @@ -1295,7 +1902,7 @@ reload_timer--; return 0; } - + if (active_weapon == WEAPON_NINJA) { // don't update other weapons while ninja is active @@ -1326,7 +1933,7 @@ weapons[active_weapon].ammoregenstart = -1; } } - + return 0; } @@ -1344,7 +1951,10 @@ void player::tick() { server_setclientscore(client_id, score); - + if(config.sv_ninja_mod) + { + ninja.activationtick = server_tick(); + } // grab latest input /* { @@ -1356,7 +1966,8 @@ mem_copy(&latest_input, input, sizeof(latest_input)); } }*/ - + if(config.sv_infinite_jumping) + core.jumped = 0; // check if we have enough input // this is to prevent initial weird clicks if(num_inputs < 2) @@ -1364,7 +1975,7 @@ latest_previnput = latest_input; previnput = input; } - + // do latency stuff { CLIENT_INFO info; @@ -1422,10 +2033,99 @@ //core.jumped = jumped; core.input = input; core.tick(); + if(config.sv_activate_stopdamage) + { + if(fabs(core.old_vel.x - core.vel.x) > config.sv_activate_stopdamage) + { + take_damage(vec2(0,0), (int)((fabs(core.old_vel.x - core.vel.x) - config.sv_activate_stopdamage)/(float)10*config.sv_stopdamage), client_id, WEAPON_HAMMER); + } + if(fabs(core.old_vel.y - core.vel.y) > config.sv_activate_stopdamage) + { + take_damage(vec2(0,0), (int)((fabs(core.old_vel.y - core.vel.y) - config.sv_activate_stopdamage)/(float)10*config.sv_stopdamage), client_id, WEAPON_HAMMER); + } + core.old_vel = core.vel; + } + if (players[client_id].started && server_tick()-players[client_id].refreshtime > server_tickspeed() && config.sv_race_mod) + { + char buf[128]; + int time=(server_tick()-players[client_id].starttime)/server_tickspeed(); + sprintf(buf,"Current time: %d min %d seconde",time/60,(time%60)); + send_broadcast(buf, client_id); + players[client_id].refreshtime=server_tick(); + } // handle weapons handle_weapons(); - + if(regn && (server_tick()%regn)==0) { + if(health<10) { + health++; + } else if(armor<10) { + armor++; + } + } + if(col_is_damage((int)core.pos.x,core.pos.y)) { + if(lastplayer != -1 && config.sv_suicide_killer && server_tick()-lastplayertime < server_tickspeed()*2){die(lastplayer,-1);if(players[lastplayer].team == team) players[lastplayer].score--; else players[lastplayer].score++;} + else die(client_id,-1); + } + else if(col_is_begin((int)core.pos.x,core.pos.y)) { + players[client_id].starttime=server_tick(); + players[client_id].ended=0; + players[client_id].started=1; + players[client_id].refreshtime=server_tick(); + } + else if(col_is_end((int)core.pos.x,core.pos.y) && !players[client_id].ended && players[client_id].started) { + float time=((float)(server_tick()-players[client_id].starttime))/((float) server_tickspeed()); + char buf[128]; + sprintf(buf,"%s finished in: %d min %d seconde",server_clientname(client_id),(int) time/60,((int)time%60)); + send_chat(-1,CHAT_ALL, buf); + int ttime = 0 - (int) time; + players[client_id].ended=1; + players[client_id].started=0; + if (players[client_id].score < ttime)players[client_id].score=ttime; + top5_parsePlayer(server_clientname(client_id),(float) time); + } + int z = col_is_teleport((int)core.pos.x,core.pos.y); + if(tele && z) { + core.hooked_player = -1; + core.hook_state = HOOK_RETRACTED; + core.triggered_events |= COREEVENT_HOOK_RETRACT; + core.hook_state = HOOK_RETRACTED; + core.pos = teleport(z); + core.hook_pos = core.pos; + player* cplayer = closest_player(core.pos, 20.0f, this); + if(cplayer && cplayer->client_id != -1 && config.sv_teleport_kill){ + cplayer->die(client_id,-1); + dbg_msg("Rajh","%s sest teleporter sur : %s",server_clientname(client_id),server_clientname(cplayer->client_id));} + if(strip) { + active_weapon = WEAPON_HAMMER; + last_weapon = WEAPON_HAMMER; + if(z&1) { + weapons[0].got = true; + for(int i=1;i<5;i++) { + weapons[i].got = false; + } + } else { + for(int i=0;iweapons[i].maxammo; + } else { + weapons[i].got = false; + weapons[i].ammo = 0; + } + } + } + } + } + if((core.hook_state==HOOK_FLYING || core.hook_state==HOOK_GRABBED) && col_intersect_nohook(core.pos,core.hook_pos)) { + core.hooked_player = -1; + core.hook_state = HOOK_RETRACTED; + core.triggered_events |= COREEVENT_HOOK_RETRACT; + core.hook_state = HOOK_RETRACTED; + core.hook_pos = core.pos; + core.hook_tick = 0; + } + if (core.hooked_player != -1){players[core.hooked_player].lastplayer=client_id;players[core.hooked_player].lastplayertime=server_tick();} player_state = input.player_state; // Previnput @@ -1440,16 +2140,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 +2161,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 +2173,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,19 +2217,31 @@ die_tick = server_tick(); clear_flag(FLAG_PHYSICS); create_death(pos, client_id); + if(((weapon == -1 && client_id == killer) || (killer != client_id && players[killer].team != team)) && players[killer].teamkills > 0) + players[killer].teamkills --; + if(config.sv_kick_teamkiller && ((killer == client_id && weapon != -1) || 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(!config.sv_race_mod || (config.sv_race_mod && from == client_id))core.vel += force; + if(gameobj->is_friendly_fire(client_id, from) && !config.sv_teamdamage) return false; // player only inflicts half damage on self - if(from == client_id) + if(!config.sv_correct_selfdamage && from == client_id) dmg = max(1, dmg/2); - + if(from == client_id && !config.sv_rocket_jump_damage)dmg=0; // CTF and TDM (TODO: check for FF) //if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team) //return false; @@ -1557,7 +2269,7 @@ health--; dmg--; } - + if(dmg > armor) { dmg -= armor; @@ -1569,8 +2281,10 @@ dmg = 0; } } - + health -= dmg; + lastplayer=from; + lastplayertime=server_tick(); } damage_taken_tick = server_tick(); @@ -1649,7 +2363,7 @@ character->ammocount = 0; character->health = 0; character->armor = 0; - + character->weapon = active_weapon; character->attacktick = attack_tick; @@ -1689,11 +2403,13 @@ type = _type; subtype = _subtype; proximity_radius = phys_size; + if(config.sv_spawn_powerups) + { + reset(); - reset(); - - // TODO: should this be done here? - world->insert_entity(this); + // TODO: should this be done here? + world->insert_entity(this); + } } void powerup::reset() @@ -1795,7 +2511,7 @@ pplayer->emote_type = EMOTE_ANGRY; pplayer->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; - + break; } default: @@ -1806,7 +2522,7 @@ { dbg_msg("game", "pickup player='%d:%s' item=%d/%d", pplayer->client_id, server_clientname(pplayer->client_id), type, subtype); - spawntick = server_tick() + server_tickspeed() * respawntime; + spawntick = server_tick() + server_tickspeed() * respawntime * config.sv_powerup_respawn; } } } @@ -1950,12 +2666,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) + if(players[i].client_id < 0 || (notthis != NULL && (entity *)&players[i] == notthis)) continue; - + if(!(players[i].flags&entity::FLAG_PHYSICS)) continue; @@ -1971,7 +2687,7 @@ } } } - + return closest; } @@ -1981,12 +2697,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 +2716,7 @@ } } } - + return closest; } @@ -2012,6 +2728,35 @@ if(world->paused) // make sure that the game object always updates gameobj->tick(); + if(config.sv_regen>=0) { + char buf[128]; + regn = config.sv_regen; + if(regn>0) { + sprintf(buf,"Tee's regenerate health at %.2f health/sec",50.0/(float)regn); + } else { + sprintf(buf,"Tee's no longer regenerate health."); + } + send_chat(-1,-2,buf); + config.sv_regen = -1;} + else if(config.sv_teleport) { + tele = tele?0:1; + char buf[128]; + tele?sprintf(buf,"Teleport tiles will teleport tee's."):sprintf(buf,"Teleport tiles will no longer teleport tee's."); + send_chat(-1,-2,buf); + config.sv_teleport = 0;} + else if(config.sv_strip) { + strip = strip?0:1; + char buf[128]; + strip?sprintf(buf,"Tee's will have weapons removed on teleport."):sprintf(buf,"Tee's will not lose weapons on teleport."); + send_chat(-1,-2,buf); + config.sv_strip = 0; + } + else if(config.sv_start[0]!=0) { + sscanf(config.sv_start,"%d %d %d %d %d %d",&(start[0]),&(start[1]),&(start[2]),&(start[3]),&start[4],&start[5]); + char buf[128]; + sprintf(buf,"%s%s%s%s%s%senabled on spawn!",start[0]?"Hammer ":"",start[1]?"Pistol ":"",start[2]?"Shotgun ":"",start[3]?"Grenade ":"",start[4]?"Rifle ":"",start[5]?"Ninja ":""); + send_chat(-1,-2,buf); + config.sv_start[0] = 0;} } void mods_snap(int client_id) @@ -2024,7 +2769,7 @@ { if(!world->paused) players[client_id].on_direct_input((NETOBJ_PLAYER_INPUT *)input); - + /* if(i->fire) { @@ -2044,7 +2789,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; } @@ -2057,19 +2802,67 @@ players[client_id].respawn(); dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); - + if(config.sv_race_mod)players[client_id].score=-9999; + else players[client_id].score=0; + players[client_id].started=0; + players[client_id].votedfor=-1; + players[client_id].teamkills = 0; + players[client_id].voted = false; char buf[512]; + players[client_id].played = false; 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 top5_draw(int id) { + int pos=1; + char buf[512]; + send_chat(-1,CHAT_ALL,"----------- Top 5 -----------"); + for (std::list::iterator i=top5.begin(); i!=top5.end() && pos<=5; i++) { + std::ostringstream oss; + oss << pos << ". " << i->name() << " Time:" << (int)(i->score())/60 << " minutes " << i->score()-((int)i->score()/60)*60 <<" secondes."; + send_chat(-1, CHAT_ALL, oss.str().c_str()); + pos++; + } + str_format(buf, sizeof(buf), "-----------------------------(%s)",server_clientname(id)); + send_chat(-1, CHAT_ALL, buf); +} + +void rank_draw(int id) { + int pos=1;; + float found=-1; + char buf[512]; + for (std::list::iterator i=top5.begin(); i!=top5.end(); i++) { + if(!strcmp(i->name().c_str(),server_clientname(id))){found=i->score();break;} + pos++; + } + if(found != -1){ + str_format(buf, sizeof(buf), "%s Rank: %d Time:%d minutes %f secondes",server_clientname(id),pos,(int)found/60,found-((int)found/60)*60,config.sv_rank_site); + send_chat(-1, CHAT_ALL, buf); + str_format(buf, sizeof(buf), "See all rank at %s",config.sv_rank_site); + send_chat(-1, CHAT_ALL, buf); + } + else{ + str_format(buf, sizeof(buf), "%s is not ranked",server_clientname(id)); + send_chat(-1, CHAT_ALL, buf); + } +} + 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; @@ -2085,16 +2878,37 @@ void mods_client_drop(int client_id) { + if((gameobj->gametype == GAMETYPE_TDM || gameobj->gametype == GAMETYPE_CTF) && config.sv_ko_mode && players[client_id].team != -1) + { + gameobj->endround(); + bool in_it = false; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(in_it) + { + gameobj->next_players[i-1] = gameobj->next_players[i]; + } + if(gameobj->next_players[i] == client_id) + { + in_it = true; + } + } + if(in_it) + { + gameobj->next_players[MAX_CLIENTS - 1] = -1; + } + } 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); world->remove_entity(&players[client_id]); world->core.players[client_id] = 0x0; + players[client_id].client_id = -1; + } void mods_message(int msgtype, int client_id) @@ -2105,7 +2919,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 +2928,244 @@ 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 if(!strcasecmp(msg->message, "/top5")) + { + top5_draw(client_id); + players[client_id].last_chat = time_get()+time_freq()*10; + } + else if(!strcasecmp(msg->message, "/rank")) + { + rank_draw(client_id); + players[client_id].last_chat = time_get()+time_freq()*10; + } + else + { + players[client_id].last_chat = time_get(); + send_chat(client_id, team, msg->message); + } } } else if (msgtype == NETMSGTYPE_CL_SETTEAM) @@ -2159,7 +3202,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 +3210,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 +3229,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 +3308,83 @@ { 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); +} + +static void con_teleport(void *result, void *user_data) +{ + players[console_arg_int(result, 0)].core.pos=players[console_arg_int(result, 1)].core.pos; + players[console_arg_int(result, 0)].started=0; +} + +static void con_teleport_to(void *result, void *user_data) +{ + players[console_arg_int(result, 0)].core.pos.x=console_arg_int(result, 1); + players[console_arg_int(result, 0)].core.pos.y=console_arg_int(result, 2); + players[console_arg_int(result, 0)].started=0; +} + +static void con_get_pos(void *result, void *user_data) +{ + dbg_msg("Tele","%s pos: %d @ %d", server_clientname(console_arg_int(result, 0)),(int) players[console_arg_int(result, 0)].core.pos.x,(int)players[console_arg_int(result, 0)].core.pos.y); + +} + + 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); + MACRO_REGISTER_COMMAND("teleport", "ii", con_teleport, 0); + MACRO_REGISTER_COMMAND("teleport_to", "iii", con_teleport_to, 0); + MACRO_REGISTER_COMMAND("get_pos", "i", con_get_pos, 0); + } void mods_init() @@ -2312,11 +3413,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++) @@ -2339,6 +3440,7 @@ players[MAX_CLIENTS-i-1].team = i&1; } } + top5_load(); } void mods_shutdown()