diff --git a/data/audio/menu_music.wav b/data/audio/music_menu.wav similarity index 100% rename from data/audio/menu_music.wav rename to data/audio/music_menu.wav diff --git a/data/char_teefault.png b/data/char_teefault.png index 545710464..7f1ea09d4 100644 Binary files a/data/char_teefault.png and b/data/char_teefault.png differ diff --git a/data/game.png b/data/game.png index a31222092..ab0ebd9ba 100644 Binary files a/data/game.png and b/data/game.png differ diff --git a/data/maps/dm1.map b/data/maps/dm1.map index 23eb5dadd..66fb49d5a 100644 Binary files a/data/maps/dm1.map and b/data/maps/dm1.map differ diff --git a/data/maps/dm2.map b/data/maps/dm2.map index 08ffbc2f5..71b80f2c0 100644 Binary files a/data/maps/dm2.map and b/data/maps/dm2.map differ diff --git a/data/maps/dm6.map b/data/maps/dm6.map index c44db11d7..baa943d6b 100644 Binary files a/data/maps/dm6.map and b/data/maps/dm6.map differ diff --git a/datasrc/teewars.ds b/datasrc/teewars.ds index 7d4478194..72b1cfcf9 100644 --- a/datasrc/teewars.ds +++ b/datasrc/teewars.ds @@ -433,7 +433,7 @@ weapons { visual_size 96 offsetx 4.0 offsety -20.0 - meleedamage 3 + meleedamage 4 meleereach 40 ammoregentime 0 duration -1 diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index f3390c51d..3a64db7ef 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -29,7 +29,6 @@ static int info_request_end; static int snapshot_part; static int64 local_start_time; static int64 game_start_time; -static int current_tick; static float latency = 0; static int extra_polating = 0; static int debug_font; @@ -37,8 +36,12 @@ static float frametime = 0.0001f; static net_client net; static netaddr4 master_server; static netaddr4 server_address; -static const char *server_spam_address=0; static int window_must_refocus = 0; +static int snaploss = 0; + +static int current_tick = 0; +static float intratick = 0; + // --- input wrappers --- static int keyboard_state[2][input::last]; @@ -151,14 +154,9 @@ static void client_snapshot_purge_until(int tick) static snapshot_info *snapshots[NUM_SNAPSHOT_TYPES]; static int recived_snapshots; -static int64 snapshot_start_time; static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; // --- -float client_localtime() -{ - return (time_get()-local_start_time)/(float)(time_freq()); -} const void *snap_get_item(int snapid, int index, snap_item *item) { @@ -199,7 +197,7 @@ static void snap_init() // ------ time functions ------ float client_intratick() { - return (time_get() - snapshot_start_time)/(float)(time_freq()/SERVER_TICK_SPEED); + return intratick; } int client_tick() @@ -217,6 +215,10 @@ float client_frametime() return frametime; } +float client_localtime() +{ + return (time_get()-local_start_time)/(float)(time_freq()); +} int menu_loop(); // TODO: what is this? @@ -312,9 +314,11 @@ void client_serverbrowse_refresh(int lan) if(serverlist_lan) { - dbg_msg("client", "broadcasting for servers"); + if(config.debug) + dbg_msg("client", "broadcasting for servers"); NETPACKET packet; packet.client_id = -1; + mem_zero(&packet, sizeof(packet)); packet.address.ip[0] = 0; packet.address.ip[1] = 0; packet.address.ip[2] = 0; @@ -330,8 +334,10 @@ void client_serverbrowse_refresh(int lan) } else { - dbg_msg("client", "requesting server list"); + if(config.debug) + dbg_msg("client", "requesting server list"); NETPACKET packet; + mem_zero(&packet, sizeof(packet)); packet.client_id = -1; packet.address = master_server; packet.flags = PACKETFLAG_CONNLESS; @@ -347,9 +353,12 @@ void client_serverbrowse_refresh(int lan) static void client_serverbrowse_request(int id) { - dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", - servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2], - servers.addresses[id].ip[3], servers.addresses[id].port); + if(config.debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2], + servers.addresses[id].ip[3], servers.addresses[id].port); + } NETPACKET packet; packet.client_id = -1; packet.address = servers.addresses[id]; @@ -388,7 +397,8 @@ static int state; int client_state() { return state; } static void client_set_state(int s) { - dbg_msg("game", "state change. last=%d current=%d", state, s); + if(config.debug) + dbg_msg("client", "state change. last=%d current=%d", state, s); int old = state; state = s; if(old != s) @@ -422,6 +432,7 @@ void client_connect(const char *server_address_str) net.connect(&server_address); client_set_state(CLIENTSTATE_CONNECTING); + current_tick = 0; } void client_disconnect() @@ -459,9 +470,10 @@ static void client_debug_render() static float frametime_avg = 0; frametime_avg = frametime_avg*0.9f + frametime*0.1f; char buffer[512]; - sprintf(buffer, "send: %6d recv: %6d latency: %4.0f %c gfxmem: %6dk fps: %3d", + sprintf(buffer, "send: %6d recv: %6d snaploss: %4d latency: %4.0f %c gfxmem: %6dk fps: %3d", (current.send_bytes-prev.send_bytes)*10, (current.recv_bytes-prev.recv_bytes)*10, + snaploss, latency*1000.0f, extra_polating?'E':' ', gfx_memory_usage()/1024, (int)(1.0f/frametime_avg)); @@ -488,7 +500,7 @@ static void client_render() static void client_error(const char *msg) { - dbg_msg("game", "error: %s", msg); + dbg_msg("client", "error: %s", msg); client_send_error(msg); client_set_state(CLIENTSTATE_QUITING); } @@ -549,7 +561,8 @@ static void client_process_packet(NETPACKET *packet) packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], packet->address.ip[3], packet->address.port); - dbg_msg("client", "got server info"); + if(config.debug) + dbg_msg("client", "got server info"); servers.num++; } @@ -565,7 +578,8 @@ static void client_process_packet(NETPACKET *packet) servers.infos[i].max_players = unpacker.get_int(); servers.infos[i].num_players = unpacker.get_int(); servers.infos[i].latency = ((time_get() - servers.request_times[i])*1000)/time_freq(); - dbg_msg("client", "got server info"); + if(config.debug) + dbg_msg("client", "got server info"); break; } } @@ -611,7 +625,7 @@ static void client_process_packet(NETPACKET *packet) if(msg != NETMSG_SNAPEMPTY) part_size = msg_unpack_int(); - if(snapshot_part == part) + if(snapshot_part == part && game_tick > current_tick) { // TODO: clean this up abit const char *d = (const char *)msg_unpack_raw(part_size); @@ -620,7 +634,38 @@ static void client_process_packet(NETPACKET *packet) if(snapshot_part == num_parts) { - current_tick = game_tick; + snapshot_part = 0; + + // find snapshot that we should use as delta + static snapshot emptysnap; + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + snapshot *deltashot = &emptysnap; + + // find delta + if(delta_tick >= 0) + { + //void *delta_data; + snapshot_info *delta_info = client_snapshot_find(delta_tick); + //deltashot_size = snapshots_new.get(delta_tick, 0, &delta_data); + if(delta_info) + deltashot = delta_info->snap; + else + { + // couldn't find the delta snapshots that the server used + // to compress this snapshot. force the server to resync + if(config.debug) + dbg_msg("client", "error, couldn't find the delta snapshot"); + + // ack snapshot + msg_pack_start_system(NETMSG_SNAPACK, 0); + msg_pack_int(-1); + msg_pack_end(); + client_send_msg(); + return; + } + } // decompress snapshot void *deltadata = snapshot_empty_delta(); @@ -636,27 +681,6 @@ static void client_process_packet(NETPACKET *packet) deltasize = intsize; } - // find snapshot that we should use as delta - static snapshot emptysnap; - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - snapshot *deltashot = &emptysnap; - - if(delta_tick >= 0) - { - //void *delta_data; - snapshot_info *delta_info = client_snapshot_find(delta_tick); - //deltashot_size = snapshots_new.get(delta_tick, 0, &delta_data); - if(delta_info) - deltashot = delta_info->snap; - else - { - // TODO: handle this - dbg_msg("client", "error, couldn't find the delta snapshot"); - } - } - //dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; @@ -681,12 +705,17 @@ static void client_process_packet(NETPACKET *packet) // apply snapshot, cycle pointers recived_snapshots++; + + if(current_tick > 0) + snaploss += game_tick-current_tick-1; + + current_tick = game_tick; + // we got two snapshots until we see us self as connected if(recived_snapshots <= 2) { snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; snapshots[SNAP_CURRENT] = snap; - snapshot_start_time = time_get(); } if(recived_snapshots == 2) @@ -699,7 +728,8 @@ static void client_process_packet(NETPACKET *packet) int64 t = now - game_tick*time_freq()/50; if(game_start_time == -1 || t < game_start_time) { - dbg_msg("client", "adjusted time"); + if(config.debug) + dbg_msg("client", "adjusted time"); game_start_time = t; } @@ -707,11 +737,6 @@ static void client_process_packet(NETPACKET *packet) float current_latency = (now-wanted)/(float)time_freq(); latency = latency*0.95f+current_latency*0.05f; - //if(recived_snapshots > 2) - // modc_newsnapshot(); - - snapshot_part = 0; - // ack snapshot msg_pack_start_system(NETMSG_SNAPACK, 0); msg_pack_int(game_tick); @@ -781,14 +806,14 @@ static void client_run(const char *direct_connect_server) if(!client_load_data()) return; + // init menu + modmenu_init(); // TODO: remove + // init snapshotting snap_init(); // init the mod modc_init(); - - // init menu - modmenu_init(); // TODO: remove // open socket NETADDR4 bindaddr; @@ -837,7 +862,8 @@ static void client_run(const char *direct_connect_server) { snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; snapshots[SNAP_CURRENT] = next; - snapshot_start_time = t; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + modc_newsnapshot(); if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) modc_newsnapshot(); @@ -854,12 +880,25 @@ static void client_run(const char *direct_connect_server) break; } } + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + int64 curtick_start = game_start_time + (snapshots[SNAP_CURRENT]->tick+1)*time_freq()/50; + if(latency > 0) + curtick_start += (int64)(time_freq()*(latency*1.1f)); + + int64 prevtick_start = game_start_time + (snapshots[SNAP_PREV]->tick+1)*time_freq()/50; + if(latency > 0) + prevtick_start += (int64)(time_freq()*(latency*1.1f)); + + intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); + } } // send input if(client_state() == CLIENTSTATE_ONLINE) { - if(server_spam_address) + if(config.stress&1 && client_localtime() > 10.0f) client_disconnect(); if(input_is_changed || time_get() > last_input+time_freq()) @@ -870,8 +909,8 @@ static void client_run(const char *direct_connect_server) } } - if(client_state() == CLIENTSTATE_OFFLINE && server_spam_address) - client_connect(server_spam_address); + if(client_state() == CLIENTSTATE_OFFLINE && config.stress && (frames%100) == 0) + client_connect(config.cl_stress_server); // update input inp_update(); @@ -933,10 +972,19 @@ static void client_run(const char *direct_connect_server) client_serverbrowse_update(); // render - client_render(); - - // swap the buffers - gfx_swap(); + if(config.stress) + { + if((frames%10) == 0) + { + client_render(); + gfx_swap(); + } + } + else + { + client_render(); + gfx_swap(); + } // check conditions if(client_state() == CLIENTSTATE_QUITING) @@ -948,8 +996,11 @@ static void client_run(const char *direct_connect_server) if(reporttime < time_get()) { - dbg_msg("client/report", "fps=%.02f netstate=%d", - frames/(float)(reportinterval/time_freq()), net.state()); + if(config.debug) + { + dbg_msg("client/report", "fps=%.02f netstate=%d", + frames/(float)(reportinterval/time_freq()), net.state()); + } frames = 0; reporttime += reportinterval; } @@ -977,12 +1028,24 @@ int main(int argc, char **argv) dbg_msg("client", "starting..."); config_reset(); + #ifdef CONF_PLATFORM_MACOSX - config_load("~/.teewars"); + const char *config_filename = "~/.teewars"; #else - config_load("default.cfg"); + const char *config_filename = "default.cfg"; #endif + for(int i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) + { + config_filename = argv[i+1]; + i++; + } + } + + config_load(config_filename); + const char *direct_connect_server = 0x0; snd_set_master_volume(config.volume / 255.0f); bool editor = false; @@ -999,28 +1062,12 @@ int main(int argc, char **argv) i++; direct_connect_server = argv[i]; } - else if(argv[i][0] == '-' && argv[i][1] == 's' && argv[i][2] == 0 && argc - i > 1) - { - // -s SERVER:PORT - i++; - server_spam_address = argv[i]; - } - else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) - { - // -n NAME - i++; - config_set_player_name(&config, argv[i]); - } - else if(argv[i][0] == '-' && argv[i][1] == 'w' && argv[i][2] == 0) - { - // -w - config.gfx_fullscreen = 0; - } - else if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0) { editor = true; } + else + config_set(argv[i]); } if(editor) diff --git a/src/engine/client/gfx.cpp b/src/engine/client/gfx.cpp index dafdfa062..6a0961391 100644 --- a/src/engine/client/gfx.cpp +++ b/src/engine/client/gfx.cpp @@ -122,6 +122,12 @@ bool gfx_init() screen_width = config.gfx_screen_width; screen_height = config.gfx_screen_height; + if(config.stress) + { + screen_width = 320; + screen_height = 240; + } + if(config.gfx_fullscreen) { if(!context.create(screen_width, screen_height, 24, 0, 0, 0, opengl::context::FLAG_FULLSCREEN)) @@ -138,7 +144,12 @@ bool gfx_init() return false; } } - + + context.set_title("Teewars"); + + // We don't want to see the window when we run the stress testing + if(config.stress) + context.iconify(); // Init vertices if (vertices) @@ -146,7 +157,6 @@ bool gfx_init() vertices = (custom_vertex*)mem_alloc(sizeof(custom_vertex) * vertex_buffer_size, 1); num_vertices = 0; - context.set_title("---"); /* dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), @@ -250,6 +260,7 @@ int gfx_get_video_modes(video_mode *list, int maxcount) mem_copy(list, fakemodes, sizeof(fakemodes)); return min((int)(sizeof(fakemodes)/sizeof(video_mode)), maxcount); } + return context.getvideomodes((opengl::videomode *)list, maxcount); } @@ -321,7 +332,8 @@ int gfx_load_texture_raw(int w, int h, int format, const void *data) } } - dbg_msg("gfx", "%d = %dx%d", tex, w, h); + if(config.debug) + dbg_msg("gfx", "%d = %dx%d", tex, w, h); // set data and return if(config.gfx_texture_compression && GLEW_ARB_texture_compression) @@ -790,64 +802,93 @@ double extra_kerning[256*256] = {0}; pretty_font *current_font = &default_font; -void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) +static int word_length(const char *text) { + int s = 1; + while(1) + { + if(*text == 0) + return s-1; + if(*text == '\n' || *text == '\t' || *text == ' ') + return s; + text++; + s++; + } +} + +float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length) +{ + const unsigned char *text = (unsigned char *)text_; const float spacing = 0.05f; gfx_texture_set(current_font->font_texture); gfx_quads_begin(); - float startx = x; + if(length < 0) + length = strlen(text_); - while (*text) + while(length) { const int c = *text; - - if(c == '\n') - { - x = startx; - y += size; - } - else - { - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - - x -= size * current_font->m_CharStartTable[c]; - - gfx_quads_setsubset( - (c%16)/16.0f, // startx - (c/16)/16.0f, // starty - (c%16)/16.0f+1.0f/16.0f, // endx - (c/16)/16.0f+1.0f/16.0f); // endy - - gfx_quads_drawTL(x, y, size, size); - - double x_nudge = 0; - if (text[1]) - x_nudge = extra_kerning[text[0] + text[1] * 256]; - - x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; - - if (max_width != -1 && x - startx > max_width) - { - x = startx; - y += size - 2; - } - } - text++; + + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + x -= size * current_font->m_CharStartTable[c]; + + gfx_quads_setsubset( + (c%16)/16.0f, // startx + (c/16)/16.0f, // starty + (c%16)/16.0f+1.0f/16.0f, // endx + (c/16)/16.0f+1.0f/16.0f); // endy + + gfx_quads_drawTL(x, y, size, size); + + double x_nudge = 0; + if(length > 1 && text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; + length--; } gfx_quads_end(); + + return x; } -float gfx_pretty_text_width(float size, const char *text, int length) +void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) +{ + if(max_width == -1) + gfx_pretty_text_raw(x, y, size, text, -1); + else + { + float startx = x; + while(*text) + { + int wlen = word_length(text); + float w = gfx_pretty_text_width(size, text, wlen); + if(x+w-startx > max_width) + { + y += size-2; + x = startx; + } + + x = gfx_pretty_text_raw(x, y, size, text, wlen); + + text += wlen; + } + } +} + +float gfx_pretty_text_width(float size, const char *text_, int length) { const float spacing = 0.05f; float w = 0.0f; + const unsigned char *text = (unsigned char *)text_; - const char *stop; + const unsigned char *stop; if (length == -1) - stop = text + strlen(text); + stop = text + strlen((char*)text); else stop = text + length; diff --git a/src/engine/client/snd.cpp b/src/engine/client/snd.cpp index c28efe05e..7b9efaa22 100644 --- a/src/engine/client/snd.cpp +++ b/src/engine/client/snd.cpp @@ -3,6 +3,7 @@ #include #include +#include extern "C" { #include "../../wavpack/wavpack.h" @@ -24,6 +25,16 @@ static const float GLOBAL_SOUND_DELAY = 0.05f; class sound_data { public: + sound_data() : + data(0x0), + num_samples(0), + rate(0), + channels(0), + sustain_start(-1), + sustain_end(-1), + last_played(0) + { } + short *data; int num_samples; int rate; @@ -33,13 +44,14 @@ public: int64 last_played; }; -inline short clamp(int i) +template +inline const T clamp(const T val, const T lower, const T upper) { - if(i > 0x7fff) - return 0x7fff; - if(i < -0x7fff) - return -0x7fff; - return i; + if(val > upper) + return upper; + if(val < lower) + return lower; + return val; } static class mixer : public audio_stream @@ -65,34 +77,40 @@ public: enum { MAX_CHANNELS=32, + MAX_FILL_FRAMES=256, }; channel channels[MAX_CHANNELS]; + int buffer[MAX_FILL_FRAMES*2]; - void fill_mono(short *out, unsigned long frames, channel *c, float dv = 0.0f) + void fill_mono(int *out, unsigned long frames, channel *c, float dv = 0.0f) { + float pl = clamp(1.0f - c->pan, 0.0f, 1.0f); + float pr = clamp(1.0f + c->pan, 0.0f, 1.0f); + for(unsigned long i = 0; i < frames; i++) { - float p = (1.0f-(c->pan+1.0f)*0.5f); - int val = (int)(p*c->vol * master_volume * c->data->data[c->tick]); - out[i<<1] += (short)val; - out[(i<<1)+1] += (short)val; + float val = c->vol * master_volume * c->data->data[c->tick]; + + out[i<<1] += (int)(pl*val); + out[(i<<1)+1] += (int)(pr*val); c->tick++; c->vol += dv; if(c->vol < 0.0f) c->vol = 0.0f; } } - void fill_stereo(short *out, unsigned long frames, channel *c, float dv = 0.0f) + void fill_stereo(int *out, unsigned long frames, channel *c, float dv = 0.0f) { + float pl = clamp(1.0f - c->pan, 0.0f, 1.0f); + float pr = clamp(1.0f + c->pan, 0.0f, 1.0f); + for(unsigned long i = 0; i < frames; i++) { - float pl = c->pan<0.0f?-c->pan:1.0f; - float pr = c->pan>0.0f?1.0f-c->pan:1.0f; int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]); int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]); - out[i<<1] += (short)vl; - out[(i<<1)+1] += (short)vr; + out[i<<1] += vl; + out[(i<<1)+1] += vr; c->tick += 2; c->vol += dv; if(c->vol < 0.0f) c->vol = 0.0f; @@ -103,10 +121,12 @@ public: { short *out = (short*)output; + dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer"); + for(unsigned long i = 0; i < frames; i++) { - out[i<<1] = 0; - out[(i<<1)+1] = 0; + buffer[i<<1] = 0; + buffer[(i<<1)+1] = 0; } for(int c = 0; c < MAX_CHANNELS; c++) @@ -133,9 +153,9 @@ public: } if(channels[c].data->channels == 1) - fill_mono(out, to_fill, &channels[c], dv); + fill_mono(buffer, to_fill, &channels[c], dv); else - fill_stereo(out, to_fill, &channels[c], dv); + fill_stereo(buffer, to_fill, &channels[c], dv); if(channels[c].loop >= 0 && channels[c].data->sustain_start >= 0 && @@ -156,6 +176,12 @@ public: filled += to_fill; } } + + for(unsigned long i = 0; i < frames; i++) + { + out[i<<1] = (short)clamp(buffer[i<<1], -0x7fff, 0x7fff); + out[(i<<1)+1] = (short)clamp(buffer[(i<<1)+1], -0x7fff, 0x7fff); + } } int play(sound_data *sound, unsigned loop, float vol, float pan) @@ -262,7 +288,7 @@ int snd_load_wv(const char *filename) char error[100]; - file = fopen(filename, "r"); + file = fopen(filename, "rb"); WavpackContext *context = WavpackOpenFileInput(read_data, error); if (context) @@ -453,7 +479,8 @@ int snd_load_wav(const char *filename) { unsigned char smpl[36]; unsigned char loop[24]; - dbg_msg("sound/wav", "got sustain"); + if(config.debug) + dbg_msg("sound/wav", "got sustain"); file.read(smpl, sizeof(smpl)); unsigned num_loops = (smpl[28] | (smpl[29]<<8) | (smpl[30]<<16) | (smpl[31]<<24)); @@ -482,7 +509,10 @@ int snd_load_wav(const char *filename) } if(id >= 0) - dbg_msg("sound/wav", "loaded %s", filename); + { + if(config.debug) + dbg_msg("sound/wav", "loaded %s", filename); + } else dbg_msg("sound/wav", "failed to load %s", filename); diff --git a/src/engine/compression.cpp b/src/engine/compression.cpp index 5c812d23c..427eceb57 100644 --- a/src/engine/compression.cpp +++ b/src/engine/compression.cpp @@ -32,7 +32,7 @@ const unsigned char *vint_unpack(const unsigned char *src, int *i) int sign = (*src>>6)&1; *i = *src&0x3F; - while(1) + do { if(!(*src&0x80)) break; src++; @@ -49,7 +49,7 @@ const unsigned char *vint_unpack(const unsigned char *src, int *i) if(!(*src&0x80)) break; src++; *i |= (*src&(0x7F))<<(6+7+7+7); - } + } while(0); src++; *i ^= -sign; // if(sign) *i = ~(*i) diff --git a/src/engine/config.cpp b/src/engine/config.cpp index fbfce3e98..508f63686 100644 --- a/src/engine/config.cpp +++ b/src/engine/config.cpp @@ -119,7 +119,7 @@ void config_save(const char *filename) #else const char newline[] = "\n"; #endif - const int newline_len = sizeof(newline); + const int newline_len = sizeof(newline)-1; #define MACRO_CONFIG_INT(name,def,min,max) { char str[256]; sprintf(str, "%s=%i", #name, config.name); file.write(str, strlen(str)); file.write(newline, newline_len); } #define MACRO_CONFIG_STR(name,len,def) { file.write(#name, strlen(#name)); file.write("=", 1); file.write(config.name, strlen(config.name)); file.write(newline, newline_len); } diff --git a/src/engine/config_variables.h b/src/engine/config_variables.h index b8f11c325..ba9852aaa 100644 --- a/src/engine/config_variables.h +++ b/src/engine/config_variables.h @@ -1,12 +1,16 @@ #include "../game/game_variables.h" -MACRO_CONFIG_INT(debug, 0, 0, 1) MACRO_CONFIG_INT(volume, 200, 0, 255) MACRO_CONFIG_INT(cpu_throttle, 0, 0, 1) MACRO_CONFIG_STR(player_name, 32, "nameless tee") MACRO_CONFIG_STR(clan_name, 32, "") MACRO_CONFIG_STR(password, 32, "") + +MACRO_CONFIG_INT(debug, 0, 0, 1) +MACRO_CONFIG_INT(stress, 0, 0, 0) +MACRO_CONFIG_STR(cl_stress_server, 32, "localhost") + MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0) MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0) MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1) @@ -25,3 +29,7 @@ MACRO_CONFIG_STR(sv_name, 128, "unnamed server") MACRO_CONFIG_STR(sv_bindaddr, 128, "") MACRO_CONFIG_INT(sv_port, 8303, 0, 0) MACRO_CONFIG_INT(sv_sendheartbeats, 1, 0, 1) +MACRO_CONFIG_STR(sv_map, 128, "dm1") + +MACRO_CONFIG_INT(sv_max_clients, 8, 1, 8) + diff --git a/src/engine/interface.h b/src/engine/interface.h index 7fa1fedb4..31b985b71 100644 --- a/src/engine/interface.h +++ b/src/engine/interface.h @@ -12,15 +12,11 @@ enum { MAX_CLIENTS=8, SERVER_TICK_SPEED=50, - SERVER_CLIENT_TIMEOUT=5, SNAP_CURRENT=0, SNAP_PREV=1, IMG_RGB=0, IMG_RGBA=1, - /* - IMG_BGR, - IMG_BGRA,*/ CLIENTSTATE_OFFLINE=0, CLIENTSTATE_CONNECTING, @@ -795,4 +791,7 @@ void client_quit(); void client_serverbrowse_refresh(int lan); int client_serverbrowse_getlist(server_info **servers); +int snap_new_id(); +void snap_free_id(int id); + #endif diff --git a/src/engine/network.cpp b/src/engine/network.cpp index d38b825e0..ac28002e4 100644 --- a/src/engine/network.cpp +++ b/src/engine/network.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include "config.h" #include "network.h" #include "ringbuffer.h" @@ -39,6 +41,8 @@ enum NETWORK_PACKETFLAG_VITAL=0x08, NETWORK_PACKETFLAG_RESEND=0x10, NETWORK_PACKETFLAG_CONNLESS=0x20, + + NETWORK_MAX_SEQACK=0x1000, }; static int current_token = 1; @@ -75,19 +79,23 @@ static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet) struct NETCONNECTION { - unsigned seq; - unsigned ack; + unsigned short seq; + unsigned short ack; unsigned state; int token; + int remote_closed; + int connected; int disconnected; ring_buffer buffer; + int64 last_update_time; int64 last_recv_time; int64 last_send_time; + char error_string[256]; NETADDR4 peeraddr; @@ -104,6 +112,10 @@ struct NETSERVER { NETSOCKET socket; NETSLOT slots[NETWORK_MAX_CLIENTS]; + int max_clients; + NETFUNC_NEWCLIENT new_client; + NETFUNC_NEWCLIENT del_client; + void *user_ptr; unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; }; @@ -125,6 +137,7 @@ static void conn_reset(NETCONNECTION *conn) { conn->seq = 0; conn->ack = 0; + conn->remote_closed = 0; //dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_OFFLINE); if(conn->state == NETWORK_CONNSTATE_ONLINE || @@ -136,6 +149,7 @@ static void conn_reset(NETCONNECTION *conn) conn->state = NETWORK_CONNSTATE_OFFLINE; conn->last_send_time = 0; conn->last_recv_time = 0; + conn->last_update_time = 0; conn->token = -1; conn->buffer.reset(); } @@ -176,7 +190,7 @@ static void conn_ack(NETCONNECTION *conn, int ack) break; NETPACKETDATA *resend = (NETPACKETDATA *)i->data(); - if(resend->seq <= ack) + if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2)) conn->buffer.pop_first(); else break; @@ -207,7 +221,9 @@ static void conn_resend(NETCONNECTION *conn) static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data) { if(flags&NETWORK_PACKETFLAG_VITAL) - conn->seq++; + { + conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK; + } NETPACKETDATA p; p.ID[0] = 'T'; @@ -244,6 +260,7 @@ static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr) conn_reset(conn); conn->peeraddr = *addr; conn->token = current_token++; + mem_zero(conn->error_string, sizeof(conn->error_string)); //dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_CONNECT); conn->state = NETWORK_CONNSTATE_CONNECT; conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); @@ -252,10 +269,13 @@ static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr) static void conn_disconnect(NETCONNECTION *conn, const char *reason) { - if(reason) - conn_send(conn, NETWORK_PACKETFLAG_CLOSE, strlen(reason)+1, reason); - else - conn_send(conn, NETWORK_PACKETFLAG_CLOSE, 0, 0); + if(conn->remote_closed == 0) + { + if(reason) + conn_send(conn, NETWORK_PACKETFLAG_CLOSE, strlen(reason)+1, reason); + else + conn_send(conn, NETWORK_PACKETFLAG_CLOSE, 0, 0); + } conn_reset(conn); } @@ -267,12 +287,16 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) if(p->flags&NETWORK_PACKETFLAG_CLOSE) { - conn_reset(conn); + conn->state = NETWORK_CONNSTATE_ERROR; + conn->remote_closed = 1; + + //conn_reset(conn); if(p->data_size) conn_set_error(conn, (char *)p->data); else conn_set_error(conn, "no reason given"); - dbg_msg("conn", "closed reason='%s'", conn_error(conn)); + if(config.debug) + dbg_msg("conn", "closed reason='%s'", conn_error(conn)); return 0; } @@ -288,7 +312,8 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) conn->token = p->token; //dbg_msg("connection", "token set to %d", p->token); conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); - dbg_msg("connection", "got connection, sending connect+accept"); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); } } else if(net_addr4_cmp(&conn->peeraddr, addr) == 0) @@ -310,15 +335,15 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) if(p->flags&NETWORK_PACKETFLAG_VITAL) { - if(p->seq == conn->ack+1) + if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK) { // in sequence - conn->ack++; + conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK; } else { // out of sequence, request resend - //dbg_msg("conn", "asking for resend"); + dbg_msg("conn", "asking for resend %d %d", p->seq, (conn->ack+1)%NETWORK_MAX_SEQACK); conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); return 0; } @@ -361,8 +386,10 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) }*/ else { - conn_reset(conn); + //conn_reset(conn); // strange packet, wrong state + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "strange state and packet"); } } else @@ -379,15 +406,37 @@ static int conn_update(NETCONNECTION *conn) { if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR) return 0; + + // watch out for major hitches + int64 now = time_get(); + int64 delta = now-conn->last_update_time; + if(conn->last_update_time && delta > time_freq()/2) + { + dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); + + conn->last_recv_time += delta; + + ring_buffer::item *i = conn->buffer.first(); + while(i) + { + NETPACKETDATA *resend = (NETPACKETDATA *)i->data(); + resend->first_send_time += delta; + i = i->next; + } + } + + conn->last_update_time = now; // check for timeout if(conn->state != NETWORK_CONNSTATE_OFFLINE && conn->state != NETWORK_CONNSTATE_CONNECT && - (time_get()-conn->last_recv_time) > time_freq()*3) + (now-conn->last_recv_time) > time_freq()*10) { //dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_ERROR); conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "timeout"); + char buf[128]; + sprintf(buf, "timeout %lld %lld %lld %lld", now-conn->last_recv_time, now, conn->last_recv_time, time_freq()*10); + conn_set_error(conn, buf); } // check for large buffer errors @@ -401,11 +450,11 @@ static int conn_update(NETCONNECTION *conn) if(conn->buffer.first()) { NETPACKETDATA *resend = (NETPACKETDATA *)conn->buffer.first()->data(); - if(time_get()-resend->first_send_time > time_freq()*3) + if(now-resend->first_send_time > time_freq()*10) { //dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_ERROR); conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (not acked for 3 seconds)"); + conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); } } @@ -463,9 +512,18 @@ static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet) NETSERVER *net_server_open(NETADDR4 bindaddr, int max_clients, int flags) { + NETSOCKET socket = net_udp4_create(bindaddr); + if(socket == NETSOCKET_INVALID) + return 0; + NETSERVER *server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); mem_zero(server, sizeof(NETSERVER)); - server->socket = net_udp4_create(bindaddr); + server->socket = socket; + server->max_clients = max_clients; + if(server->max_clients > NETWORK_MAX_CLIENTS) + server->max_clients = NETWORK_MAX_CLIENTS; + if(server->max_clients < 1) + server->max_clients = 1; for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) conn_init(&server->slots[i].conn, server->socket); @@ -473,15 +531,24 @@ NETSERVER *net_server_open(NETADDR4 bindaddr, int max_clients, int flags) return server; } +int net_server_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) +{ + s->new_client = new_client; + s->del_client = del_client; + s->user_ptr = user; + return 0; +} + int net_server_close(NETSERVER *s) { // TODO: implement me return 0; } +/* int net_server_newclient(NETSERVER *s) { - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + for(int i = 0; i < s->max_clients; i++) { if(s->slots[i].conn.connected) { @@ -495,7 +562,7 @@ int net_server_newclient(NETSERVER *s) int net_server_delclient(NETSERVER *s) { - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + for(int i = 0; i < s->max_clients; i++) { if(s->slots[i].conn.disconnected) { @@ -505,20 +572,24 @@ int net_server_delclient(NETSERVER *s) } return -1; -} +}*/ int net_server_drop(NETSERVER *s, int client_id, const char *reason) { // TODO: insert lots of checks here dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason); conn_disconnect(&s->slots[client_id].conn, reason); + + if(s->del_client) + s->del_client(client_id, s->user_ptr); + //conn_reset(&s->slots[client_id].conn); return 0; } int net_server_update(NETSERVER *s) { - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + for(int i = 0; i < s->max_clients; i++) { conn_update(&s->slots[i].conn); if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR) @@ -560,7 +631,7 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet) int found = 0; // check if we already got this client - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + for(int i = 0; i < s->max_clients; i++) { if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE && net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) @@ -574,27 +645,42 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet) // client that wants to connect if(!found) { - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + for(int i = 0; i < s->max_clients; i++) { if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) { //dbg_msg("netserver", "connection started %d", i); - conn_feed(&s->slots[i].conn, &data, &addr); found = 1; + conn_feed(&s->slots[i].conn, &data, &addr); + if(s->new_client) + s->new_client(i, s->user_ptr); break; } } } - if(found) + if(!found) { - // TODO: send error + // send connectionless packet + const char errstring[] = "server full"; + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CLOSE; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.token = data.token; + p.data_size = sizeof(errstring); + p.data = (unsigned char *)errstring; + send_packet(s->socket, &addr, &p); } } else { // find matching slot - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + for(int i = 0; i < s->max_clients; i++) { if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) { @@ -648,7 +734,7 @@ int net_server_send(NETSERVER *s, NETPACKET *packet) else { dbg_assert(packet->client_id >= 0, "errornous client id"); - dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id"); + dbg_assert(packet->client_id < s->max_clients, "errornous client id"); int flags = 0; if(packet->flags&PACKETFLAG_VITAL) flags |= NETWORK_PACKETFLAG_VITAL; @@ -664,7 +750,7 @@ void net_server_stats(NETSERVER *s, NETSTATS *stats) int num_stats = sizeof(NETSTATS)/sizeof(int); int *istats = (int *)stats; - for(int c = 0; c < NETWORK_MAX_CLIENTS; c++) + for(int c = 0; c < s->max_clients; c++) { int *sstats = (int *)(&(s->slots[c].conn.stats)); for(int i = 0; i < num_stats; i++) @@ -774,6 +860,7 @@ int net_client_send(NETCLIENT *c, NETPACKET *packet) p.seq = 0; p.ack = 0; p.crc = 0; + p.token = 0; p.data_size = packet->data_size; p.data = (unsigned char *)packet->data; send_packet(c->socket, &packet->address, &p); diff --git a/src/engine/network.h b/src/engine/network.h index 1bb02c1a3..afd0dcd0e 100644 --- a/src/engine/network.h +++ b/src/engine/network.h @@ -35,15 +35,19 @@ enum NETSTATE_ONLINE, }; +typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); +typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); + // server side NETSERVER *net_server_open(NETADDR4 bindaddr, int max_clients, int flags); +int net_server_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); int net_server_recv(NETSERVER *s, NETPACKET *packet); int net_server_send(NETSERVER *s, NETPACKET *packet); int net_server_close(NETSERVER *s); int net_server_update(NETSERVER *s); int net_server_drop(NETSERVER *s, int client_id, const char *reason); -int net_server_newclient(NETSERVER *s); // -1 when no more, else, client id -int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id +//int net_server_newclient(NETSERVER *s); // -1 when no more, else, client id +//int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id void net_server_stats(NETSERVER *s, NETSTATS *stats); // client side @@ -71,13 +75,16 @@ public: int open(NETADDR4 bindaddr, int max, int flags) { ptr = net_server_open(bindaddr, max, flags); return ptr != 0; } int close() { int r = net_server_close(ptr); ptr = 0; return r; } + int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) + { return net_server_set_callbacks(ptr, new_client, del_client, user); } + int recv(NETPACKET *packet) { return net_server_recv(ptr, packet); } int send(NETPACKET *packet) { return net_server_send(ptr, packet); } int update() { return net_server_update(ptr); } int drop(int client_id, const char *reason) { return net_server_drop(ptr, client_id, reason); } - int newclient() { return net_server_newclient(ptr); } - int delclient() { return net_server_delclient(ptr); } + //int newclient() { return net_server_newclient(ptr); } + //int delclient() { return net_server_delclient(ptr); } void stats(NETSTATS *stats) { net_server_stats(ptr, stats); } }; diff --git a/src/engine/packet.h b/src/engine/packet.h index 79c969c2d..1dd3c3ab8 100644 --- a/src/engine/packet.h +++ b/src/engine/packet.h @@ -1,4 +1,5 @@ #include +#include #include #include @@ -273,6 +274,9 @@ public: int get_int() { + if(current >= end) + return 0; + int i; current = vint_unpack(current, &i); // TODO: might be changed into variable width @@ -285,6 +289,9 @@ public: const char *get_string() { + if(current >= end) + return ""; + // TODO: add range check // TODO: add debug marker const char *ptr = (const char *)current; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 3769157ef..984292e99 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -30,6 +30,97 @@ void *snap_new_item(int type, int id, int size) return builder.new_item(type, id, size); } + +struct snap_id +{ + short next; + short state; // 0 = free, 1 = alloced, 2 = timed + int timeout_tick; +}; + +static const int MAX_IDS = 8*1024; // should be lowered +static snap_id snap_ids[8*1024]; +static int snap_first_free_id; +static int snap_first_timed_id; +static int snap_last_timed_id; +static int snap_id_usage; +static int snap_id_inusage; + +static int snap_id_inited = 0; + +void snap_init_id() +{ + for(int i = 0; i < MAX_IDS; i++) + { + 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; +} + +int snap_new_id() +{ + 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_tick < server_tick()) + { + 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--; + } + + int id = snap_first_free_id; + dbg_assert(id != -1, "id error"); + snap_first_free_id = snap_ids[snap_first_free_id].next; + snap_ids[id].state = 1; + snap_id_usage++; + snap_id_inusage++; + return id; +} + +void snap_free_id(int id) +{ + dbg_assert(snap_ids[id].state == 1, "id is not alloced"); + + snap_id_inusage--; + snap_ids[id].state = 2; + snap_ids[id].timeout_tick = server_tick() + server_tickspeed()*5; + snap_ids[id].next = -1; + + if(snap_last_timed_id != -1) + { + snap_ids[snap_last_timed_id].next = id; + snap_last_timed_id = id; + } + else + { + snap_first_timed_id = id; + snap_last_timed_id = id; + } +} + + + + // class client { @@ -132,31 +223,51 @@ int server_send_msg(int client_id) net.send(&packet); return 0; } + +static int new_client_callback(int cid, void *user) +{ + clients[cid].state = client::STATE_CONNECTING; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + clients[cid].snapshots.purge_all(); + clients[cid].last_acked_snapshot = -1; + return 0; +} + +static int del_client_callback(int cid, void *user) +{ + clients[cid].state = client::STATE_EMPTY; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + clients[cid].snapshots.purge_all(); + mods_client_drop(cid); + return 0; +} + // TODO: remove this class class server { public: //socket_udp4 game_socket; - - const char *map_name; int64 lasttick; int64 lastheartbeat; netaddr4 master_server; int biggest_snapshot; - bool run(const char *mapname) + bool run() { biggest_snapshot = 0; net_init(); // For Windows compatibility. - map_name = mapname; + + snap_init_id(); // load map - if(!map_load(mapname)) + if(!map_load(config.sv_map)) { - dbg_msg("server", "failed to load map. mapname='%s'", mapname); + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); return false; } @@ -173,12 +284,14 @@ public: bindaddr.port = config.sv_port; } - if(!net.open(bindaddr, 0, 0)) + if(!net.open(bindaddr, config.sv_max_clients, 0)) { - dbg_msg("network/server", "couldn't open socket"); + dbg_msg("server", "couldn't open socket. port might already be in use"); return false; } - + + net.set_callbacks(new_client_callback, del_client_callback, 0); + dbg_msg("server", "server name is '%s'", config.sv_name); dbg_msg("server", "masterserver is '%s'", config.masterserver); if (net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0) @@ -242,12 +355,16 @@ public: if(reporttime < time_get()) { - dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%", - (simulationtime/reportinterval)/(double)time_freq()*1000, - (snaptime/reportinterval)/(double)time_freq()*1000, - (networktime/reportinterval)/(double)time_freq()*1000, - (totaltime/reportinterval)/(double)time_freq()*1000, - (totaltime)/reportinterval/(double)time_freq()*100.0f); + if(config.debug) + { + dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%% ids=%d/%d", + (simulationtime/reportinterval)/(double)time_freq()*1000, + (snaptime/reportinterval)/(double)time_freq()*1000, + (networktime/reportinterval)/(double)time_freq()*1000, + (totaltime/reportinterval)/(double)time_freq()*1000, + (totaltime)/reportinterval/(double)time_freq()*100.0f, + snap_id_inusage, snap_id_usage); + } simulationtime = 0; snaptime = 0; @@ -313,8 +430,6 @@ public: delta_tick = clients[i].last_acked_snapshot; deltashot = (snapshot *)delta_data; } - else - dbg_msg("server", "no delta, sending full snapshot"); } // create delta @@ -337,6 +452,7 @@ public: const int max_size = MAX_SNAPSHOT_PACKSIZE; int numpackets = (snapshot_size+max_size-1)/max_size; + (void)numpackets; for(int n = 0, left = snapshot_size; left; n++) { int chunk = left < max_size ? left : max_size; @@ -373,14 +489,13 @@ public: void send_map(int cid) { msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL); - msg_pack_string(map_name, 0); + msg_pack_string(config.sv_map, 0); msg_pack_end(); server_send_msg(cid); } void send_heartbeat() { - dbg_msg("server", "sending heartbeat"); NETPACKET packet; packet.client_id = -1; packet.address = master_server; @@ -397,7 +512,7 @@ public: clients[cid].state = client::STATE_EMPTY; mods_client_drop(cid); - dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name); + dbg_msg("server", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name); } void process_client_packet(NETPACKET *packet) @@ -434,7 +549,7 @@ public: { if(clients[cid].state != client::STATE_INGAME) { - dbg_msg("game", "player as entered the game. cid=%x", cid); + dbg_msg("server", "player as entered the game. cid=%x", cid); clients[cid].state = client::STATE_INGAME; mods_client_enter(cid); } @@ -483,7 +598,7 @@ public: packer.reset(); packer.add_raw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); packer.add_string(config.sv_name, 128); - packer.add_string(map_name, 128); + packer.add_string(config.sv_map, 128); packer.add_int(MAX_CLIENTS); // max_players int c = 0; for(int i = 0; i < MAX_CLIENTS; i++) @@ -537,7 +652,8 @@ public: else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) && memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) { - dbg_msg("server", "no firewall/nat problems detected"); + if(config.debug) + dbg_msg("server", "no firewall/nat problems detected"); } else if(packet.data_size == sizeof(SERVERBROWSE_FWERROR) && memcmp(packet.data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) @@ -549,35 +665,6 @@ public: else process_client_packet(&packet); } - - // check for removed clients - while(1) - { - int cid = net.delclient(); - if(cid == -1) - break; - - clients[cid].state = client::STATE_EMPTY; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - clients[cid].snapshots.purge_all(); - - mods_client_drop(cid); - } - - // check for new clients - while(1) - { - int cid = net.newclient(); - if(cid == -1) - break; - - clients[cid].state = client::STATE_CONNECTING; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - clients[cid].snapshots.purge_all(); - clients[cid].last_acked_snapshot = -1; - } } }; @@ -586,50 +673,30 @@ int main(int argc, char **argv) dbg_msg("server", "starting..."); config_reset(); + #ifdef CONF_PLATFORM_MACOSX - config_load("~/.teewars"); + const char *config_filename = "~/.teewars"; #else - config_load("default.cfg"); + const char *config_filename = "default.cfg"; #endif - const char *mapname = "dm1"; - - // parse arguments for(int i = 1; i < argc; i++) { - if(argv[i][0] == '-' && argv[i][1] == 'm' && argv[i][2] == 0 && argc - i > 1) + if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) { - // -m map + config_filename = argv[i+1]; i++; - mapname = argv[i]; - } - else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) - { - // -n server name - i++; - config_set_sv_name(&config, argv[i]); - } - else if(argv[i][0] == '-' && argv[i][1] == 'p' && argv[i][2] == 0) - { - // -p (private server) - config_set_sv_sendheartbeats(&config, 0); - } - else if(argv[i][0] == '-' && argv[i][1] == 'o' && argv[i][2] == 0) - { - // -o port - i++; - config_set_sv_port(&config, atol(argv[i])); } } - if(!mapname) - { - dbg_msg("server", "no map given (-m MAPNAME)"); - return 0; - } + config_load(config_filename); + + // parse arguments + for(int i = 1; i < argc; i++) + config_set(argv[i]); server_init(); server s; - s.run(mapname); + s.run(); return 0; } diff --git a/src/engine/snapshot.cpp b/src/engine/snapshot.cpp index 2e68df722..e8b75d361 100644 --- a/src/engine/snapshot.cpp +++ b/src/engine/snapshot.cpp @@ -42,6 +42,19 @@ int snapshot_crc(snapshot *snap) return crc; } +void snapshot_debug_dump(snapshot *snap) +{ + dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); + for(int i = 0; i < snap->num_items; i++) + { + snapshot::item *item = snap->get_item(i); + int size = snap->get_item_datasize(i); + dbg_msg("snapshot", "\ttype=%d id=%d", item->type(), item->id()); + for(int b = 0; b < size/4; b++) + dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, item->data()[b], item->data()[b]); + } +} + static int diff_item(int *past, int *current, int *out, int size) { /* diff --git a/src/engine/snapshot.h b/src/engine/snapshot.h index b0ffc768c..e3358b1ab 100644 --- a/src/engine/snapshot.h +++ b/src/engine/snapshot.h @@ -42,6 +42,7 @@ struct snapshot void *snapshot_empty_delta(); int snapshot_crc(snapshot *snap); +void snapshot_debug_dump(snapshot *snap); int snapshot_create_delta(snapshot *from, snapshot *to, void *data); int snapshot_unpack_delta(snapshot *from, snapshot *to, void *data, int data_size); diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp index dc09815fd..52a58ad54 100644 --- a/src/game/client/game_client.cpp +++ b/src/game/client/game_client.cpp @@ -61,10 +61,10 @@ void snd_play_random(int setid, float vol, float pan) } // sound volume tweak -static const float stereo_separation = 0.01f; -static const float stereo_separation_deadzone = 512.0f; -static const float volume_distance_falloff = 100.0f; -static const float volume_distance_deadzone = 512.0f; +static const float stereo_separation = 0.001f; +static const float stereo_separation_deadzone = 200.0f; +static const float volume_distance_falloff = 200.0f; +static const float volume_distance_deadzone = 320.0f; static const float volume_gun = 0.5f; static const float volume_tee = 0.5f; static const float volume_hit = 0.5f; @@ -434,16 +434,59 @@ static int killmsg_current = 0; extern unsigned char internal_data[]; + +extern void draw_round_rect(float x, float y, float w, float h, float r); +extern int render_popup(const char *caption, const char *text, const char *button_text); + +static void render_loading(float percent) +{ + gfx_clear(0.65f,0.78f,0.9f); + gfx_mapscreen(0,0,800.0f,600.0f); + + float tw; + + float w = 700; + float h = 200; + float x = 800/2-w/2; + float y = 600/2-h/2; + + gfx_blend_normal(); + + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_quads_setcolor(0,0,0,0.50f); + draw_round_rect(x, y, w, h, 40.0f); + gfx_quads_end(); + + const char *caption = "Loading"; + + tw = gfx_pretty_text_width(48.0f, caption); + ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f); + + gfx_texture_set(-1); + gfx_quads_begin(); + gfx_quads_setcolor(1,1,1,1.0f); + draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); + gfx_quads_end(); + + gfx_swap(); +} + void modc_init() { // load the data container data = load_data_from_memory(internal_data); // TODO: should be removed - music_menu = snd_load_wav("data/audio/menu_music.wav"); + music_menu = snd_load_wav("data/audio/music_menu.wav"); + + float total = data->num_sounds+data->num_images; + float current = 0; // load sounds for(int s = 0; s < data->num_sounds; s++) + { + render_loading(current/total); for(int i = 0; i < data->sounds[s].num_sounds; i++) { int id; @@ -454,10 +497,17 @@ void modc_init() data->sounds[s].sounds[i].id = id; } + + current++; + } // load textures for(int i = 0; i < data->num_images; i++) + { + render_loading(current/total); data->images[i].id = gfx_load_texture(data->images[i].filename); + current++; + } } void modc_entergame() @@ -483,13 +533,15 @@ void modc_shutdown() { } -void modc_newsnapshot() +static bool must_process_events = false; + +static void process_events(int s) { - int num = snap_num_items(SNAP_CURRENT); + int num = snap_num_items(s); for(int i = 0; i < num; i++) { snap_item item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); + const void *data = snap_get_item(s, i, &item); if(item.type == EVENT_DAMAGEINDICATION) { @@ -618,6 +670,26 @@ void modc_newsnapshot() } } } + + must_process_events = false; +} + +void modc_newsnapshot() +{ + if(must_process_events) + process_events(SNAP_PREV); + must_process_events = true; + + if(config.stress) + { + if((client_tick()%250) == 0) + { + msg_pack_start(MSG_SAY, MSGFLAG_VITAL); + msg_pack_string("galenskap!!!!", 512); + msg_pack_end(); + client_send_msg(); + } + } } static void render_projectile(const obj_projectile *prev, const obj_projectile *current, int itemid) @@ -883,7 +955,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos) select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, shift*4); break; } - int h = emote == EMOTE_BLINK ? basesize/3 : basesize; + int h = emote == EMOTE_BLINK ? (int)(basesize/3) : (int)(basesize); gfx_quads_draw(position.x-4+direction.x*4, position.y-8+direction.y*3, basesize, h); gfx_quads_draw(position.x+4+direction.x*4, position.y-8+direction.y*3, -basesize, h); } @@ -956,6 +1028,7 @@ static void render_player(const obj_player *prev, const obj_player *player) { if(player->health < 0) // dont render dead players return; + int skin = gametype == GAMETYPE_TDM ? skinseed + player->team : player->clientid; vec2 direction = get_direction(player->angle); @@ -1248,7 +1321,7 @@ void render_game() int c = input::last_char(); // TODO: bypasses the engine interface int k = input::last_key(); // TODO: bypasses the engine interface - if (c >= 32 && c < 255) + if (!(c >= 0 && c < 32)) { if (chat_input_len < sizeof(chat_input) - 1) { @@ -1291,13 +1364,9 @@ void render_game() { player_input input; mem_zero(&input, sizeof(input)); - - float a = atan((float)mouse_pos.y/(float)mouse_pos.x); - if(mouse_pos.x < 0) - a = a+pi; - input.target_x = (int)mouse_pos.x; //(int)(a*256.0f); - input.target_y = (int)mouse_pos.y; //(int)(a*256.0f); + input.target_x = (int)mouse_pos.x; + input.target_y = (int)mouse_pos.y; input.activeweapon = -1; if(chat_active) @@ -1313,25 +1382,30 @@ void render_game() input.fire = inp_key_pressed(config.key_fire); input.hook = inp_key_pressed(config.key_hook); - input.blink = inp_key_pressed('S'); - + //input.blink = inp_key_pressed('S'); // Weapon switching -#define TEST_WEAPON_KEY(key) if (inp_key_pressed(config.key_weapon ## key)) input.activeweapon = key-1; - if(config.scroll_weapon) - { - int delta = inp_mouse_scroll(); - input.activeweapon = input.activeweapon + delta; - - if(input.activeweapon > 3) - input.activeweapon = 3; - else if(input.activeweapon < 0) - input.activeweapon = 0; - } - - TEST_WEAPON_KEY(1); - TEST_WEAPON_KEY(2); - TEST_WEAPON_KEY(3); - TEST_WEAPON_KEY(4); + if(inp_key_pressed(config.key_weapon1)) input.activeweapon = 0; + if(inp_key_pressed(config.key_weapon2)) input.activeweapon = 1; + if(inp_key_pressed(config.key_weapon3)) input.activeweapon = 2; + if(inp_key_pressed(config.key_weapon4)) input.activeweapon = 3; + } + + // stress testing + if(config.stress) + { + float t = client_localtime(); + mem_zero(&input, sizeof(input)); + input.left = 1; + input.jump = ((int)t)&1; + input.fire = ((int)(t*10))&1; + input.hook = ((int)t)&1; + input.activeweapon = ((int)t)%NUM_WEAPONS; + input.target_x = (int)(sinf(t*3)*100.0f); + input.target_y = (int)(cosf(t*3)*100.0f); + + //input.target_x = (int)((rand()/(float)RAND_MAX)*64-32); + //input.target_y = (int)((rand()/(float)RAND_MAX)*64-32); + } snap_input(&input, sizeof(input)); @@ -1365,6 +1439,10 @@ void render_game() gameobj = (obj_game *)data; } } + + // everything updated, do events + if(must_process_events) + process_events(SNAP_PREV); // pseudo format float zoom = 3.0f; @@ -1413,7 +1491,7 @@ void render_game() { float parallax_amount = 0.55f; select_sprite(cloud_sprites[i]); - draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 1700.0f))+screen_x*parallax_amount, + draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 3000.0f))+screen_x*parallax_amount, cloud_pos[i].y+screen_y*parallax_amount, 300); } gfx_quads_end(); @@ -1707,7 +1785,7 @@ void render_game() // sort players for(int k = 0; k < num_players; k++) // ffs, bubblesort { - for(int i = k; i < num_players-1; i++) + for(int i = 0; i < num_players-k-1; i++) { if(players[i]->score < players[i+1]->score) { diff --git a/src/game/client/menu.cpp b/src/game/client/menu.cpp index e0b171b1b..b5cbc1b63 100644 --- a/src/game/client/menu.cpp +++ b/src/game/client/menu.cpp @@ -414,7 +414,7 @@ int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int if (at_index > len) at_index = len; - if (c >= 32 && c < 128) + if (!(c >= 0 && c < 32)) { if (len < str_size - 1 && at_index < str_size - 1) { @@ -673,7 +673,7 @@ static int do_server_list(float x, float y, int *scroll_index, int *selected_ind } *scroll_index = do_scroll_bar_vert(scroll_index, x + real_width - 16, y, real_height, - min(num_servers - visible_items, 0), *scroll_index); + max(num_servers - visible_items, 0), *scroll_index); return r; } @@ -837,8 +837,11 @@ static int settings_controls_render() static const int MAX_RESOLUTIONS = 128; static int settings_video_render_select_mode() { - video_mode modes[MAX_RESOLUTIONS]; - int num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); + static video_mode modes[MAX_RESOLUTIONS]; + static int num_modes = -1; + + if(num_modes == -1) + num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); static int scroll_index = 0; scroll_index = do_scroll_bar_vert(&scroll_index, 500, row1_y, 40 * 7, num_modes - 7, scroll_index); @@ -1033,10 +1036,10 @@ static int settings_render(bool ingame) extern int gametype; static int ingame_main_render() { - static int menu_resume, menu_active, menu_quit, menu_settings; - char buf[128]; + static int menu_resume, menu_quit, menu_settings; /*if (gametype == GAMETYPE_TDM) { + char buf[128]; // Switch team ui_do_label(100,100,"Switch Team",40); sprintf(buf,"Team: %s",local_player->team ? "A" : "B"); @@ -1255,7 +1258,7 @@ static int kerning_render() } -static int render_popup(const char *caption, const char *text, const char *button_text) +int render_popup(const char *caption, const char *text, const char *button_text) { float tw; @@ -1278,9 +1281,14 @@ static int render_popup(const char *caption, const char *text, const char *butto tw = gfx_pretty_text_width(32.0f, text); ui_do_label(x+w/2-tw/2, y+130, text, 32.0f); - static int back_button = 0; - if(ui_do_button(&back_button, button_text, 0, x+w/2-100, y+220, 200, 48, draw_teewars_button)) - return 1; + if(button_text) + { + static int back_button = 0; + if(ui_do_button(&back_button, button_text, 0, x+w/2-100, y+220, 200, 48, draw_teewars_button)) + return 1; + if(inp_key_down(input::esc) || inp_key_down(input::enter)) + return 1; + } return 0; } diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp index f6dfccd84..54e6f32f8 100644 --- a/src/game/server/game_server.cpp +++ b/src/game/server/game_server.cpp @@ -175,6 +175,11 @@ event_handler::event_handler() void *event_handler::create(int type, int size, int target) { + if(num_events == MAX_EVENTS) + return 0; + if(current_offset+size >= MAX_DATASIZE) + return 0; + void *p = &data[current_offset]; offsets[num_events] = current_offset; types[num_events] = type; @@ -215,8 +220,7 @@ entity::entity(int objtype) flags = FLAG_ALIVE; proximity_radius = 0; - current_id++; - id = current_id; + id = snap_new_id(); next_entity = 0; prev_entity = 0; @@ -226,10 +230,9 @@ entity::entity(int objtype) entity::~entity() { + snap_free_id(id); } -int entity::current_id = 1; - ////////////////////////////////////////////////// // game world ////////////////////////////////////////////////// @@ -538,7 +541,7 @@ void gameobject::tick() void gameobject::snap(int snapping_client) { - obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, id, sizeof(obj_game)); + obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, 0, sizeof(obj_game)); game->paused = world.paused; game->game_over = game_over_tick==-1?0:1; game->sudden_death = sudden_death; @@ -563,7 +566,7 @@ int gameobject::getteam(int notthisid) return numplayers[0] > numplayers[1] ? 1 : 0; } -gameobject gameobj; +gameobject *gameobj = 0; ////////////////////////////////////////////////// // projectile @@ -733,6 +736,15 @@ void player::init() team = 0; extrapowerflags = 0; ninjaactivationtick = 0; + + + latency_accum = 0; + latency_accum_min = 0; + latency_accum_max = 0; + latency_avg = 0; + latency_min = 0; + latency_max = 0; + reset(); } @@ -750,10 +762,25 @@ void player::reset() die_tick = 0; damage_taken = 0; state = STATE_UNKNOWN; + + mem_zero(&input, sizeof(input)); + mem_zero(&previnput, sizeof(previnput)); + last_action = -1; + + emote_stop = 0; + damage_taken_tick = 0; + attack_tick = 0; } void player::destroy() { } + +void player::set_weapon(int w) +{ + last_weapon = active_weapon; + active_weapon = w; +} + void player::respawn() { @@ -788,7 +815,7 @@ void player::try_respawn() defered_pos = pos; - health = data->playerinfo[gameobj.gametype].maxhealth; + health = data->playerinfo[gameobj->gametype].maxhealth; armor = 0; jumped = 0; dead = false; @@ -803,9 +830,11 @@ void player::try_respawn() weapons[WEAPON_HAMMER].got = true; weapons[WEAPON_HAMMER].ammo = -1; weapons[WEAPON_GUN].got = true; - weapons[WEAPON_GUN].ammo = data->weapons[active_weapon].maxammo; + weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo; active_weapon = WEAPON_GUN; + last_weapon = WEAPON_HAMMER; + reload_timer = 0; // Create sound and spawn effects @@ -849,7 +878,7 @@ int player::handle_ninja() if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000)) { // time's up, return - active_weapon = WEAPON_GUN; + active_weapon = last_weapon; return 0; } @@ -935,6 +964,18 @@ int player::handle_ninja() int player::handle_weapons() { + if(config.stress) + { + for(int i = 0; i < NUM_WEAPONS; i++) + { + weapons[i].got = true; + weapons[i].ammo = 10; + } + + if(reload_timer) // twice as fast reload + reload_timer--; + } + // check reload timer if(reload_timer) { @@ -954,8 +995,8 @@ int player::handle_weapons() if (active_weapon != input.activeweapon) create_sound(pos, SOUND_WEAPON_SWITCH); + last_weapon = active_weapon; active_weapon = input.activeweapon; - } if(!previnput.fire && input.fire) @@ -1342,6 +1383,8 @@ void player::tick_defered() void player::die(int killer, int weapon) { + dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d", killer, players[killer].name, client_id, name, weapon); + // send the kill message msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL); msg_pack_int(killer); @@ -1372,7 +1415,7 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon) if(from == client_id) dmg = max(1, dmg/2); - if (gameobj.gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team) + if (gameobj->gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team) return false; damage_taken++; @@ -1505,7 +1548,12 @@ void player::snap(int snaping_client) player->hook_x = (int)hook_pos.x; player->hook_y = (int)hook_pos.y; - float a = atan((float)input.target_y/(float)input.target_x); + float a = 0; + if(input.target_x == 0) + a = atan((float)input.target_y); + else + a = atan((float)input.target_y/(float)input.target_x); + if(input.target_x < 0) a = a+pi; @@ -1517,7 +1565,7 @@ void player::snap(int snaping_client) player->state = state; } -player players[MAX_CLIENTS]; +player *players; ////////////////////////////////////////////////// // powerup @@ -1569,18 +1617,18 @@ void powerup::tick() switch (type) { case POWERUP_HEALTH: - if(pplayer->health < data->playerinfo[gameobj.gametype].maxhealth) + if(pplayer->health < data->playerinfo[gameobj->gametype].maxhealth) { create_sound(pos, SOUND_PICKUP_HEALTH, 0); - pplayer->health = min((int)data->playerinfo[gameobj.gametype].maxhealth, pplayer->health + data->powerupinfo[type].amount); + pplayer->health = min((int)data->playerinfo[gameobj->gametype].maxhealth, pplayer->health + data->powerupinfo[type].amount); respawntime = data->powerupinfo[type].respawntime; } break; case POWERUP_ARMOR: - if(pplayer->armor < data->playerinfo[gameobj.gametype].maxarmor) + if(pplayer->armor < data->playerinfo[gameobj->gametype].maxarmor) { create_sound(pos, SOUND_PICKUP_ARMOR, 0); - pplayer->armor = min((int)data->playerinfo[gameobj.gametype].maxarmor, pplayer->armor + data->powerupinfo[type].amount); + pplayer->armor = min((int)data->playerinfo[gameobj->gametype].maxarmor, pplayer->armor + data->powerupinfo[type].amount); respawntime = data->powerupinfo[type].respawntime; } break; @@ -1607,6 +1655,7 @@ void powerup::tick() // activate ninja on target player pplayer->ninjaactivationtick = server_tick(); pplayer->weapons[WEAPON_NINJA].got = true; + pplayer->last_weapon = pplayer->active_weapon; pplayer->active_weapon = WEAPON_NINJA; respawntime = data->powerupinfo[type].respawntime; create_sound(pos, SOUND_PICKUP_NINJA); @@ -1635,7 +1684,10 @@ void powerup::tick() }; if(respawntime >= 0) + { + dbg_msg("game", "pickup player='%d:%s' item=%d/%d", pplayer->client_id, pplayer->name, type, subtype); spawntick = server_tick() + server_tickspeed() * respawntime; + } } } @@ -1757,9 +1809,12 @@ void create_damageind(vec2 p, float angle, int amount) { float f = mix(s, e, float(i+1)/float(amount+2)); ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind)); - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->angle = (int)(f*256.0f); + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + ev->angle = (int)(f*256.0f); + } } } @@ -1767,8 +1822,11 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) { // create the event ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion)); - ev->x = (int)p.x; - ev->y = (int)p.y; + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } if (!bnodamage) { @@ -1796,24 +1854,33 @@ void create_smoke(vec2 p) { // create the event ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion)); - ev->x = (int)p.x; - ev->y = (int)p.y; + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } } void create_spawn(vec2 p) { // create the event ev_spawn *ev = (ev_spawn *)events.create(EVENT_SPAWN, sizeof(ev_spawn)); - ev->x = (int)p.x; - ev->y = (int)p.y; + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } } void create_death(vec2 p) { // create the event ev_death *ev = (ev_death *)events.create(EVENT_DEATH, sizeof(ev_death)); - ev->x = (int)p.x; - ev->y = (int)p.y; + if(ev) + { + ev->x = (int)p.x; + ev->y = (int)p.y; + } } void create_targetted_sound(vec2 pos, int sound, int target, int loopingflags) @@ -1823,9 +1890,12 @@ void create_targetted_sound(vec2 pos, int sound, int target, int loopingflags) // create a sound ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound), target); - ev->x = (int)pos.x; - ev->y = (int)pos.y; - ev->sound = sound | loopingflags; + if(ev) + { + ev->x = (int)pos.x; + ev->y = (int)pos.y; + ev->sound = sound | loopingflags; + } } void create_sound(vec2 pos, int sound, int loopingflags) @@ -1864,7 +1934,7 @@ void mods_tick() world.tick(); if(world.paused) // make sure that the game object always updates - gameobj.tick(); + gameobj->tick(); if(debug_bots) { @@ -1905,6 +1975,11 @@ void mods_client_input(int client_id, void *input) void send_chat_all(int cid, const char *msg) { + if(cid >= 0 && cid < MAX_CLIENTS) + dbg_msg("chat", "%d:%s: %s", cid, players[cid].name, msg); + else + dbg_msg("chat", "*** %s", msg); + msg_pack_start(MSG_CHAT, MSGFLAG_VITAL); msg_pack_int(cid); msg_pack_string(msg, 512); @@ -1928,10 +2003,13 @@ void mods_client_enter(int client_id) else strcpy(players[client_id].name, "(bot)"); - if (gameobj.gametype == GAMETYPE_TDM) + + dbg_msg("game", "join player='%d:%s'", client_id, players[client_id].name); + + if (gameobj->gametype == GAMETYPE_TDM) { // Check which team the player should be on - players[client_id].team = gameobj.getteam(client_id); + players[client_id].team = gameobj->getteam(client_id); } msg_pack_start(MSG_SETNAME, MSGFLAG_VITAL); @@ -1962,8 +2040,9 @@ void mods_client_drop(int client_id) char buf[512]; sprintf(buf, "%s has left the game", players[client_id].name); send_chat_all(-1, buf); + + dbg_msg("game", "leave player='%d:%s'", client_id, players[client_id].name); - dbg_msg("mods", "client drop %d", client_id); world.remove_entity(&players[client_id]); players[client_id].client_id = -1; } @@ -1990,6 +2069,9 @@ void mods_init() data = load_data_from_memory(internal_data); col_init(32); + players = new player[MAX_CLIENTS]; + gameobj = new gameobject; + int start, num; map_get_type(MAPRES_ITEM, &start, &num); @@ -1999,7 +2081,7 @@ void mods_init() mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0); int type = -1; - int subtype = -1; + int subtype = 0; switch(it->type) { @@ -2043,7 +2125,7 @@ void mods_init() } } - world.insert_entity(&gameobj); + world.insert_entity(gameobj); } void mods_shutdown() {} diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h index 83824f0a5..6361b3eaf 100644 --- a/src/game/server/game_server.h +++ b/src/game/server/game_server.h @@ -35,7 +35,6 @@ private: entity *next_type_entity; int index; - static int current_id; protected: int id; @@ -123,7 +122,7 @@ public: virtual int getteam(int notthisid); }; -extern gameobject gameobj; +extern gameobject *gameobj; // TODO: move to seperate file @@ -214,6 +213,7 @@ public: bool got; } weapons[NUM_WEAPONS]; int active_weapon; + int last_weapon; int reload_timer; int attack_tick; @@ -291,6 +291,8 @@ public: void respawn(); bool is_grounded(); + + void set_weapon(int w); void release_hooked(); void release_hooks(); @@ -307,7 +309,7 @@ public: virtual void snap(int snaping_client); }; -extern player players[MAX_CLIENTS]; +extern player *players; // TODO: move to seperate file class flag : public entity diff --git a/tilesets/grassland_doodads.png b/tilesets/grassland_doodads.png index 8646ef1b3..5fd6db49d 100644 Binary files a/tilesets/grassland_doodads.png and b/tilesets/grassland_doodads.png differ diff --git a/tilesets/grassland_main.png b/tilesets/grassland_main.png index 4ec0a9c81..5a7c25b2e 100644 Binary files a/tilesets/grassland_main.png and b/tilesets/grassland_main.png differ diff --git a/tilesets/unprocessed/grassland_doodads.png b/tilesets/unprocessed/grassland_doodads.png index 12ee5a9b3..e464693bd 100644 Binary files a/tilesets/unprocessed/grassland_doodads.png and b/tilesets/unprocessed/grassland_doodads.png differ diff --git a/tilesets/unprocessed/grassland_main.png b/tilesets/unprocessed/grassland_main.png index f337f576d..e8123cf0f 100644 Binary files a/tilesets/unprocessed/grassland_main.png and b/tilesets/unprocessed/grassland_main.png differ