diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-10-06 18:05:01 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-10-06 18:05:01 +0000 |
| commit | 12472ef7f405f5e8eb620059cbf95926a458538a (patch) | |
| tree | 712cc453e491ff46c96b48785a94093b1d17cb1f /src/engine | |
| parent | d1b55351ccc2252917ad494b74bb6ad562df34ce (diff) | |
| download | zcatch-12472ef7f405f5e8eb620059cbf95926a458538a.tar.gz zcatch-12472ef7f405f5e8eb620059cbf95926a458538a.zip | |
major update. continued on ban support. added demo recording (client and server side). added demo player. added demo menu. demos have some quirks and file size optimizations havn't been done yet. some interface tweaks
Diffstat (limited to 'src/engine')
| -rw-r--r-- | src/engine/client/ec_client.c | 246 | ||||
| -rw-r--r-- | src/engine/e_demorec.c | 354 | ||||
| -rw-r--r-- | src/engine/e_demorec.h | 63 | ||||
| -rw-r--r-- | src/engine/e_engine.c | 3 | ||||
| -rw-r--r-- | src/engine/e_if_client.h | 21 | ||||
| -rw-r--r-- | src/engine/e_if_modc.h | 2 | ||||
| -rw-r--r-- | src/engine/e_if_other.h | 6 | ||||
| -rw-r--r-- | src/engine/e_if_server.h | 2 | ||||
| -rw-r--r-- | src/engine/server/es_server.c | 90 |
9 files changed, 749 insertions, 38 deletions
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c index 8d8bdad7..3e7e8c27 100644 --- a/src/engine/client/ec_client.c +++ b/src/engine/client/ec_client.c @@ -23,6 +23,8 @@ #include <engine/e_huffman.h> +#include <engine/e_demorec.h> + #include <mastersrv/mastersrv.h> #include <versionsrv/versionsrv.h> @@ -70,8 +72,13 @@ static char versionstr[10] = "0"; /* pinging */ static int64 ping_start_time = 0; +/* */ +static char current_map[256] = {0}; +static int current_map_crc = 0; + /* map download */ static char mapdownload_filename[256] = {0}; +static char mapdownload_name[256] = {0}; static IOHANDLE mapdownload_file = 0; static int mapdownload_chunk = 0; static int mapdownload_crc = 0; @@ -85,7 +92,6 @@ static SERVER_INFO current_server_info = {0}; static int current_tick = 0; static float intratick = 0; static float ticktime = 0; - static int prev_tick = 0; /* predicted time */ @@ -231,6 +237,9 @@ static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES]; static int recived_snapshots; static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; +static SNAPSTORAGE_HOLDER demorec_snapshotholders[NUM_SNAPSHOT_TYPES]; +static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][MAX_SNAPSHOT_SIZE]; + /* --- */ void *snap_get_item(int snapid, int index, SNAP_ITEM *item) @@ -310,8 +319,15 @@ int client_send_msg() packet.flags = NETSENDFLAG_VITAL; if(info->flags&MSGFLAG_FLUSH) packet.flags = NETSENDFLAG_FLUSH; + + if(info->flags&MSGFLAG_RECORD) + { + if(demorec_isrecording()) + demorec_record_write("MESG", packet.data_size, packet.data); + } - netclient_send(net, &packet); + if(!(info->flags&MSGFLAG_NOSEND)) + netclient_send(net, &packet); return 0; } @@ -525,6 +541,10 @@ void client_connect(const char *server_address_str) void client_disconnect_with_reason(const char *reason) { + /* stop demo playback */ + demorec_playback_stop(); + + /* */ rcon_authed = 0; netclient_disconnect(net, reason); client_set_state(CLIENTSTATE_OFFLINE); @@ -659,7 +679,7 @@ static void client_render() client_debug_render(); } -static const char *client_load_map(const char *filename, int wanted_crc) +static const char *client_load_map(const char *name, const char *filename, int wanted_crc) { static char errormsg[128]; DATAFILE *df; @@ -676,7 +696,7 @@ static const char *client_load_map(const char *filename, int wanted_crc) /* get the crc of the map */ crc = datafile_crc(filename); - if(crc != wanted_crc) + if(0 && crc != wanted_crc) /* TODO: FIX ME!!! */ { datafile_unload(df); str_format(errormsg, sizeof(errormsg), "map differs from the server. %08x != %08x", crc, wanted_crc); @@ -686,6 +706,10 @@ static const char *client_load_map(const char *filename, int wanted_crc) dbg_msg("client", "loaded map '%s'", filename); recived_snapshots = 0; map_set(df); + + str_copy(current_map, name, sizeof(current_map)); + current_map_crc = crc; + return NULL; } @@ -699,14 +723,14 @@ static const char *client_load_map_search(const char *mapname, int wanted_crc) /* try the normal maps folder */ str_format(buf, sizeof(buf), "maps/%s.map", mapname); - error = client_load_map(buf, wanted_crc); + error = client_load_map(mapname, buf, wanted_crc); if(!error) return error; /* try the downloaded maps */ str_format(buf2, sizeof(buf2), "maps/%s_%8x.map", mapname, wanted_crc); engine_savepath(buf2, buf, sizeof(buf)); - error = client_load_map(buf, wanted_crc); + error = client_load_map(mapname, buf, wanted_crc); return error; } @@ -862,6 +886,7 @@ static void client_process_packet(NETCHUNK *packet) dbg_msg("client/network", "starting to download map to '%s'", mapdownload_filename); mapdownload_chunk = 0; + str_copy(mapdownload_name, map, sizeof(mapdownload_name)); mapdownload_file = io_open(mapdownload_filename, IOFLAG_WRITE); mapdownload_crc = map_crc; mapdownload_totalsize = -1; @@ -904,7 +929,7 @@ static void client_process_packet(NETCHUNK *packet) mapdownload_totalsize = -1; /* load map */ - error = client_load_map(mapdownload_filename, mapdownload_crc); + error = client_load_map(mapdownload_name, mapdownload_filename, mapdownload_crc); if(!error) { dbg_msg("client/network", "loading done"); @@ -1107,6 +1132,28 @@ static void client_process_packet(NETCHUNK *packet) /* add new */ snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3, 1); + /* add snapshot to demo */ + if(demorec_isrecording()) + { + DEMOREC_TICKMARKER marker; + + /* write tick marker */ + marker.tick = game_tick; + swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int)); + demorec_record_write("TICK", sizeof(marker), &marker); + + /* build snap and possibly add some messages */ + modc_recordkeyframe(); + + /* + snapbuild_init(&builder); + mods_snap(-1); + snapshot_size = snapbuild_finish(&builder, data);*/ + + /* write snapshot */ + demorec_record_write("SNAP", snapsize, tmpbuffer3); + } + /* apply snapshot, cycle pointers */ recived_snapshots++; @@ -1147,6 +1194,9 @@ static void client_process_packet(NETCHUNK *packet) else { /* game message */ + if(demorec_isrecording()) + demorec_record_write("MESG", packet->data_size, packet->data); + modc_message(msg); } } @@ -1161,20 +1211,23 @@ static void client_pump_network() netclient_update(net); - /* check for errors */ - if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) + if(client_state() != CLIENTSTATE_DEMOPLAYBACK) { - client_set_state(CLIENTSTATE_OFFLINE); - dbg_msg("client", "offline error='%s'", netclient_error_string(net)); - } + /* check for errors */ + if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) + { + client_set_state(CLIENTSTATE_OFFLINE); + dbg_msg("client", "offline error='%s'", netclient_error_string(net)); + } - /* */ - if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) - { - /* we switched to online */ - dbg_msg("client", "connected, sending info"); - client_set_state(CLIENTSTATE_LOADING); - client_send_info(); + /* */ + if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) + { + /* we switched to online */ + dbg_msg("client", "connected, sending info"); + client_set_state(CLIENTSTATE_LOADING); + client_send_info(); + } } /* process packets */ @@ -1182,11 +1235,89 @@ static void client_pump_network() client_process_packet(&packet); } +static void client_democallback(DEMOREC_CHUNK chunk, void *data) +{ + /* dbg_msg("client/playback", "got %c%c%c%c", chunk.type[0], chunk.type[1], chunk.type[2], chunk.type[3]); */ + + if(mem_comp(chunk.type, "SNAP", 4) == 0) + { + /* handle snapshots */ + SNAPSTORAGE_HOLDER *temp = snapshots[SNAP_PREV]; + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = temp; + + mem_copy(snapshots[SNAP_CURRENT]->snap, data, chunk.size); + mem_copy(snapshots[SNAP_CURRENT]->alt_snap, data, chunk.size); + + modc_newsnapshot(); + modc_predict(); + } + else if(mem_comp(chunk.type, "MESG", 4) == 0) + { + /* handle messages */ + int sys = 0; + int msg = msg_unpack_start(data, chunk.size, &sys); + if(!sys) + modc_message(msg); + } + +} + +const DEMOPLAYBACK_INFO *client_demoplayer_getinfo() +{ + static DEMOPLAYBACK_INFO ret; + const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); + ret.first_tick = info->first_tick; + ret.last_tick = info->last_tick; + ret.current_tick = info->current_tick; + ret.paused = info->paused; + ret.speed = info->speed; + return &ret; +} + +void client_demoplayer_setpos(float percent) +{ + const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); + int point = (int)((info->seekable_points-1)*percent); + demorec_playback_set(point); +} + +void client_demoplayer_setspeed(float speed) +{ + demorec_playback_setspeed(speed); +} + +void client_demoplayer_setpause(int paused) +{ + if(paused) + demorec_playback_pause(); + else + demorec_playback_unpause(); +} + static void client_update() { - /* switch snapshot */ - if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) + if(client_state() == CLIENTSTATE_DEMOPLAYBACK) + { + demorec_playback_update(); + if(demorec_isplaying()) + { + /* update timers */ + const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); + current_tick = info->current_tick; + prev_tick = info->previous_tick; + intratick = info->intratick; + ticktime = info->ticktime; + } + else + { + /* disconnect on error */ + client_disconnect(); + } + } + else if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) { + /* switch snapshot */ int repredict = 0; int64 freq = time_freq(); int64 now = st_get(&game_time, time_get()); @@ -1393,7 +1524,6 @@ static void client_run() int64 frame_start_time = time_get(); frames++; - perf_start(&rootscope); /* */ @@ -1582,6 +1712,67 @@ static void con_addfavorite(void *result, void *user_data) client_serverbrowse_addfavorite(addr); } +void client_demoplayer_play(const char *filename) +{ + int crc; + client_disconnect(); + + /* try to start playback */ + demorec_playback_registercallback(client_democallback); + + if(demorec_playback_load(filename)) + return; + + /* load map */ + crc = (demorec_playback_info()->header.crc[0]<<24)| + (demorec_playback_info()->header.crc[1]<<16)| + (demorec_playback_info()->header.crc[2]<<8)| + (demorec_playback_info()->header.crc[3]); + client_load_map_search(demorec_playback_info()->header.map, crc); + modc_connected(); + + /* setup buffers */ + mem_zero(demorec_snapshotdata, sizeof(demorec_snapshotdata)); + + snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT]; + snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV]; + + snapshots[SNAP_CURRENT]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][0]; + snapshots[SNAP_CURRENT]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][1]; + snapshots[SNAP_CURRENT]->snap_size = 0; + snapshots[SNAP_CURRENT]->tick = -1; + + snapshots[SNAP_PREV]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][0]; + snapshots[SNAP_PREV]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][1]; + snapshots[SNAP_PREV]->snap_size = 0; + snapshots[SNAP_PREV]->tick = -1; + + /* enter demo playback state */ + client_set_state(CLIENTSTATE_DEMOPLAYBACK); + + demorec_playback_play(); + modc_entergame(); +} + +static void con_play(void *result, void *user_data) +{ + client_demoplayer_play(console_arg_string(result, 0)); +} + +static void con_record(void *result, void *user_data) +{ + char filename[512]; + char path[512]; + str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0)); + engine_savepath(filename, path, sizeof(path)); + demorec_record_start(path, modc_net_version(), current_map, current_map_crc, "client"); +} + +static void con_stoprecord(void *result, void *user_data) +{ + demorec_record_stop(); +} + static void client_register_commands() { MACRO_REGISTER_COMMAND("quit", "", con_quit, 0x0); @@ -1591,6 +1782,10 @@ static void client_register_commands() MACRO_REGISTER_COMMAND("screenshot", "", con_screenshot, 0x0); MACRO_REGISTER_COMMAND("rcon", "r", con_rcon, 0x0); + MACRO_REGISTER_COMMAND("play", "r", con_play, 0x0); + MACRO_REGISTER_COMMAND("record", "s", con_record, 0); + MACRO_REGISTER_COMMAND("stoprecord", "", con_stoprecord, 0); + MACRO_REGISTER_COMMAND("add_favorite", "s", con_addfavorite, 0x0); } @@ -1599,6 +1794,13 @@ void client_save_line(const char *line) engine_config_write_line(line); } +const char *client_user_directory() +{ + static char path[1024] = {0}; + fs_storage_path("Teeworlds", path, sizeof(path)); + return path; +} + int main(int argc, char **argv) { /* init the engine */ diff --git a/src/engine/e_demorec.c b/src/engine/e_demorec.c new file mode 100644 index 00000000..67f7f237 --- /dev/null +++ b/src/engine/e_demorec.c @@ -0,0 +1,354 @@ + +#include <base/system.h> +#include "e_demorec.h" +#include "e_memheap.h" +#include "e_if_other.h" + +static IOHANDLE record_file = 0; +static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1}; + +/* Record */ + +int demorec_isrecording() { return record_file != 0; } + +int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type) +{ + DEMOREC_HEADER header; + if(record_file) + return -1; + + record_file = io_open(filename, IOFLAG_WRITE); + + if(!record_file) + { + dbg_msg("demorec/record", "Unable to open '%s' for recording", filename); + return -1; + } + + /* write header */ + mem_zero(&header, sizeof(header)); + mem_copy(header.marker, header_marker, sizeof(header.marker)); + str_copy(header.netversion, netversion, sizeof(header.netversion)); + str_copy(header.map, map, sizeof(header.map)); + str_copy(header.type, type, sizeof(header.type)); + header.crc[0] = (crc>>24)&0xff; + header.crc[1] = (crc>>16)&0xff; + header.crc[2] = (crc>>8)&0xff; + header.crc[3] = (crc)&0xff; + io_write(record_file, &header, sizeof(header)); + + dbg_msg("demorec/record", "Recording to '%s'", filename); + return 0; +} + +void demorec_record_write(const char *type, int size, const void *data) +{ + DEMOREC_CHUNK chunk; + if(!record_file) + return; + + chunk.type[0] = type[0]; + chunk.type[1] = type[1]; + chunk.type[2] = type[2]; + chunk.type[3] = type[3]; + chunk.size = size; + swap_endian(&chunk.size, sizeof(int), 1); + io_write(record_file, &chunk, sizeof(chunk)); + io_write(record_file, data, size); +} + +int demorec_record_stop() +{ + if(!record_file) + return -1; + + dbg_msg("demorec/record", "Stopped recording"); + io_close(record_file); + record_file = 0; + return 0; +} + +/* Playback */ + +typedef struct KEYFRAME +{ + long filepos; + int tick; +} KEYFRAME; + +typedef struct KEYFRAME_SEARCH +{ + KEYFRAME frame; + struct KEYFRAME_SEARCH *next; +} KEYFRAME_SEARCH; + +static IOHANDLE play_file = 0; +static DEMOREC_PLAYCALLBACK play_callback = 0; +static KEYFRAME *keyframes = 0; + +static DEMOREC_PLAYBACKINFO playbackinfo; +const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; } + +int demorec_isplaying() { return play_file != 0; } + +int demorec_playback_registercallback(DEMOREC_PLAYCALLBACK cb) +{ + play_callback = cb; + return 0; +} + +static int read_chunk_header(DEMOREC_CHUNK *chunk) +{ + if(io_read(play_file, chunk, sizeof(*chunk)) != sizeof(*chunk)) + return -1; + swap_endian(&chunk->size, sizeof(int), 1); + return 0; +} + +static void scan_file() +{ + long start_pos; + HEAP *heap = 0; + KEYFRAME_SEARCH *first_key = 0; + KEYFRAME_SEARCH *current_key = 0; + DEMOREC_CHUNK chunk; + int i; + + heap = memheap_create(); + + start_pos = io_tell(play_file); + playbackinfo.seekable_points = 0; + + while(1) + { + long current_pos = io_tell(play_file); + + if(read_chunk_header(&chunk)) + break; + + /* read the chunk */ + + if(mem_comp(chunk.type, "TICK", 4) == 0) + { + KEYFRAME_SEARCH *key; + DEMOREC_TICKMARKER marker; + io_read(play_file, &marker, chunk.size); + swap_endian(&marker.tick, sizeof(int), 1); + + /* save the position */ + key = memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); + key->frame.filepos = current_pos; + key->frame.tick = marker.tick; + key->next = 0; + if(current_key) + current_key->next = key; + if(!first_key) + first_key = key; + current_key = key; + + if(playbackinfo.first_tick == -1) + playbackinfo.first_tick = marker.tick; + playbackinfo.last_tick = marker.tick; + playbackinfo.seekable_points++; + } + else + io_skip(play_file, chunk.size); + } + + /* copy all the frames to an array instead for fast access */ + keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1); + for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++) + keyframes[i] = current_key->frame; + + /* destroy the temporary heap and seek back to the start */ + memheap_destroy(heap); + io_seek(play_file, start_pos, IOSEEK_START); +} + +static void do_tick() +{ + static char buffer[64*1024]; + DEMOREC_CHUNK chunk; + + /* update ticks */ + playbackinfo.previous_tick = playbackinfo.current_tick; + playbackinfo.current_tick = playbackinfo.next_tick; + + while(1) + { + int r = read_chunk_header(&chunk); + if(chunk.size > sizeof(buffer)) + { + dbg_msg("demorec/playback", "chunk is too big %d", chunk.size); + r = 1; + } + + if(r) + { + /* stop on error or eof */ + demorec_playback_stop(); + break; + } + + /* read the chunk */ + io_read(play_file, buffer, chunk.size); + + if(mem_comp(chunk.type, "TICK", 4) == 0) + { + DEMOREC_TICKMARKER marker = *(DEMOREC_TICKMARKER *)buffer; + swap_endian(&marker.tick, sizeof(int), 1); + playbackinfo.next_tick = marker.tick; + break; + } + else if(play_callback) + play_callback(chunk, buffer); + } +} + +void demorec_playback_pause() +{ + playbackinfo.paused = 1; +} + +void demorec_playback_unpause() +{ + if(playbackinfo.paused) + { + /*playbackinfo.start_tick = playbackinfo.current_tick; + playbackinfo.start_time = time_get();*/ + playbackinfo.paused = 0; + } +} + +int demorec_playback_load(const char *filename) +{ + play_file = io_open(filename, IOFLAG_READ); + if(!play_file) + { + dbg_msg("demorec/playback", "could not open '%s'", filename); + return -1; + } + + /* clear the playback info */ + mem_zero(&playbackinfo, sizeof(playbackinfo)); + playbackinfo.first_tick = -1; + playbackinfo.last_tick = -1; + /*playbackinfo.start_tick = -1;*/ + playbackinfo.next_tick = -1; + playbackinfo.current_tick = -1; + playbackinfo.previous_tick = -1; + playbackinfo.speed = 1; + + /* read the header */ + io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header)); + if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0) + { + dbg_msg("demorec/playback", "'%s' is not a demo file", filename); + io_close(play_file); + play_file = 0; + return -1; + } + + /* scan the file for interessting points */ + scan_file(); + + /* ready for playback */ + return 0; +} + +int demorec_playback_play() +{ + /* fill in previous and next tick */ + while(playbackinfo.previous_tick == -1) + do_tick(); + + /* set start info */ + /*playbackinfo.start_tick = playbackinfo.previous_tick; + playbackinfo.start_time = time_get();*/ + playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED; + playbackinfo.last_update = time_get(); + return 0; +} + +int demorec_playback_set(int keyframe) +{ + if(!play_file) + return -1; + if(keyframe < 0 || keyframe >= playbackinfo.seekable_points) + return -1; + + io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START); + + /*playbackinfo.start_tick = -1;*/ + playbackinfo.next_tick = -1; + playbackinfo.current_tick = -1; + playbackinfo.previous_tick = -1; + + demorec_playback_play(); + + return 0; +} + +void demorec_playback_setspeed(float speed) +{ + playbackinfo.speed = speed; +} + +int demorec_playback_update() +{ + int64 now = time_get(); + int64 deltatime = now-playbackinfo.last_update; + playbackinfo.last_update = now; + + if(playbackinfo.paused) + { + + } + else + { + int64 freq = time_freq(); + playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed); + + while(1) + { + int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; + + /* break if we are ready */ + if(curtick_start > playbackinfo.current_time) + break; + + /* do one more tick */ + do_tick(); + } + + /* update intratick */ + { + int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; + int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED; + playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start); + playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq; + } + + if(playbackinfo.current_tick == playbackinfo.previous_tick || + playbackinfo.current_tick == playbackinfo.next_tick) + { + dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d", + playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick); + } + } + + return 0; +} + +int demorec_playback_stop(const char *filename) +{ + if(!play_file) + return -1; + + dbg_msg("demorec/playback", "Stopped playback"); + io_close(play_file); + play_file = 0; + mem_free(keyframes); + keyframes = 0; + return 0; +} diff --git a/src/engine/e_demorec.h b/src/engine/e_demorec.h new file mode 100644 index 00000000..f6ceb382 --- /dev/null +++ b/src/engine/e_demorec.h @@ -0,0 +1,63 @@ + +typedef struct DEMOREC_HEADER +{ + char marker[8]; + char netversion[64]; + char map[64]; + unsigned char crc[4]; + char type[8]; +} DEMOREC_HEADER; + +typedef struct DEMOREC_CHUNK +{ + char type[4]; + int size; +} DEMOREC_CHUNK; + +typedef struct DEMOREC_TICKMARKER +{ + int tick; +} DEMOREC_TICKMARKER; + +typedef struct DEMOREC_PLAYBACKINFO +{ + DEMOREC_HEADER header; + + int paused; + float speed; + + int64 last_update; + int64 current_time; + + int first_tick; + int last_tick; + + int seekable_points; + + int next_tick; + int current_tick; + int previous_tick; + + float intratick; + float ticktime; +} DEMOREC_PLAYBACKINFO; + +int demorec_record_start(const char *filename, const char *netversion, const char *map, int map_crc, const char *type); +int demorec_isrecording(); +void demorec_record_write(const char *type, int size, const void *data); +int demorec_record_stop(); + +typedef void (*DEMOREC_PLAYCALLBACK)(DEMOREC_CHUNK chunk, void *data); + +int demorec_playback_registercallback(DEMOREC_PLAYCALLBACK cb); +int demorec_playback_load(const char *filename); +int demorec_playback_play(); +void demorec_playback_pause(); +void demorec_playback_unpause(); +void demorec_playback_setspeed(float speed); +int demorec_playback_set(int keyframe); +int demorec_playback_update(); +const DEMOREC_PLAYBACKINFO *demorec_playback_info(); +int demorec_isplaying(); +int demorec_playback_stop(); + diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c index c8a1b07b..d739a589 100644 --- a/src/engine/e_engine.c +++ b/src/engine/e_engine.c @@ -63,6 +63,9 @@ void engine_init(const char *appname) str_format(path, sizeof(path), "%s/maps", application_save_path); fs_makedir(path); + + str_format(path, sizeof(path), "%s/demos", application_save_path); + fs_makedir(path); } } diff --git a/src/engine/e_if_client.h b/src/engine/e_if_client.h index 08402ad1..7aef86e9 100644 --- a/src/engine/e_if_client.h +++ b/src/engine/e_if_client.h @@ -17,12 +17,14 @@ enum CLIENTSTATE_CONNECTING - The client is trying to connect to a server. CLIENTSTATE_LOADING - The client has connected to a server and is loading resources. CLIENTSTATE_ONLINE - The client is connected to a server and running the game. + CLIENTSTATE_DEMOPLAYBACK - The client is playing a demo CLIENTSTATE_QUITING - The client is quiting. */ CLIENTSTATE_OFFLINE=0, CLIENTSTATE_CONNECTING, CLIENTSTATE_LOADING, CLIENTSTATE_ONLINE, + CLIENTSTATE_DEMOPLAYBACK, CLIENTSTATE_QUITING, /* Constants: Image Formats @@ -541,6 +543,23 @@ enum void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info); - int client_serverbrowse_refreshingmasters(); + + +typedef struct DEMOPLAYBACK_INFO +{ + int first_tick; + int last_tick; + int current_tick; + int paused; + float speed; +} DEMOPLAYBACK_INFO; + +void client_demoplayer_play(const char *filename); +const DEMOPLAYBACK_INFO *client_demoplayer_getinfo(); +void client_demoplayer_setpos(float percent); +void client_demoplayer_setpause(int paused); +void client_demoplayer_setspeed(float speed); +const char *client_user_directory(); + #endif diff --git a/src/engine/e_if_modc.h b/src/engine/e_if_modc.h index 8839d5f1..14ab9ded 100644 --- a/src/engine/e_if_modc.h +++ b/src/engine/e_if_modc.h @@ -142,4 +142,6 @@ int modc_snap_input(int *data); */ const char *modc_net_version(); + +void modc_recordkeyframe(); #endif diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h index 85148d85..ca09f48b 100644 --- a/src/engine/e_if_other.h +++ b/src/engine/e_if_other.h @@ -16,7 +16,6 @@ enum SNAP_CURRENT=0, SNAP_PREV=1, - MASK_NONE=0, MASK_SET, @@ -306,7 +305,10 @@ void snap_set_staticsize(int type, int size); enum { MSGFLAG_VITAL=1, - MSGFLAG_FLUSH=2 + MSGFLAG_FLUSH=2, + MSGFLAG_NORECORD=4, + MSGFLAG_RECORD=8, + MSGFLAG_NOSEND=16 }; /* message sending */ diff --git a/src/engine/e_if_server.h b/src/engine/e_if_server.h index 98e2b452..b165ae3c 100644 --- a/src/engine/e_if_server.h +++ b/src/engine/e_if_server.h @@ -135,4 +135,6 @@ int server_tick(); */ int server_tickspeed(); +int server_ban_add(NETADDR addr, int type, int seconds); +int server_ban_remove(NETADDR addr); #endif diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c index d761e81e..306227b8 100644 --- a/src/engine/server/es_server.c +++ b/src/engine/server/es_server.c @@ -18,6 +18,7 @@ #include <engine/e_config.h> #include <engine/e_packer.h> #include <engine/e_datafile.h> +#include <engine/e_demorec.h> #include <mastersrv/mastersrv.h> @@ -38,6 +39,8 @@ static int browseinfo_progression = -1; static int64 lastheartbeat; /*static NETADDR4 master_server;*/ +static IOHANDLE demorec_file = 0; + static char current_map[64]; static int current_map_crc; static unsigned char *current_map_data = 0; @@ -66,7 +69,6 @@ static int snap_id_usage; static int snap_id_inusage; static int snap_id_inited = 0; - enum { SRVCLIENT_STATE_EMPTY = 0, @@ -338,19 +340,26 @@ int server_send_msg(int client_id) if(info->flags&MSGFLAG_FLUSH) packet.flags |= NETSENDFLAG_FLUSH; - if(client_id == -1) + /* write message to demo recorder */ + if(!(info->flags&MSGFLAG_NORECORD)) + demorec_record_write("MESG", info->size, info->data); + + if(!(info->flags&MSGFLAG_NOSEND)) { - /* broadcast */ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - packet.client_id = i; - netserver_send(net, &packet); - } + if(client_id == -1) + { + /* broadcast */ + int i; + for(i = 0; i < MAX_CLIENTS; i++) + if(clients[i].state == SRVCLIENT_STATE_INGAME) + { + packet.client_id = i; + netserver_send(net, &packet); + } + } + else + netserver_send(net, &packet); } - else - netserver_send(net, &packet); return 0; } @@ -364,7 +373,29 @@ static void server_do_snap() mods_presnap(); perf_end(); } + + /* create snapshot for demo recording */ + if(demorec_file) + { + char data[MAX_SNAPSHOT_SIZE]; + int snapshot_size; + DEMOREC_TICKMARKER marker; + + /* write tick marker */ + marker.tick = server_tick(); + swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int)); + demorec_record_write("TICK", sizeof(marker), &marker); + + /* build snap and possibly add some messages */ + snapbuild_init(&builder); + mods_snap(-1); + snapshot_size = snapbuild_finish(&builder, data); + + /* write snapshot */ + demorec_record_write("SNAP", snapshot_size, data); + } + /* create snapshots for all clients */ for(i = 0; i < MAX_CLIENTS; i++) { /* client must be ingame to recive snapshots */ @@ -788,6 +819,17 @@ static void server_process_client_packet(NETCHUNK *packet) } } + +int server_ban_add(NETADDR addr, int type, int seconds) +{ + return netserver_ban_add(net, addr, type, seconds); +} + +int server_ban_remove(NETADDR addr) +{ + return netserver_ban_remove(net, addr); +} + static void server_send_serverinfo(NETADDR *addr, int token) { NETCHUNK packet; @@ -1116,6 +1158,15 @@ static void con_kick(void *result, void *user_data) server_kick(console_arg_int(result, 0), "kicked by console"); } +static void con_ban(void *result, void *user_data) +{ + NETADDR addr; + const char *str = console_arg_string(result, 0); + + if(net_addr_from_str(&addr, str) == 0) + server_ban_add(addr, 1, 60); +} + static void con_status(void *result, void *user_data) { int i; @@ -1135,14 +1186,27 @@ static void con_status(void *result, void *user_data) static void con_shutdown(void *result, void *user_data) { run_server = 0; - /*server_kick(console_arg_int(result, 0), "kicked by console");*/ +} + +static void con_record(void *result, void *user_data) +{ + demorec_record_start(console_arg_string(result, 0), mods_net_version(), current_map, current_map_crc, "server"); +} + +static void con_stoprecord(void *result, void *user_data) +{ + demorec_record_stop(); } static void server_register_commands() { MACRO_REGISTER_COMMAND("kick", "i", con_kick, 0); + MACRO_REGISTER_COMMAND("ban", "r", con_ban, 0); MACRO_REGISTER_COMMAND("status", "", con_status, 0); MACRO_REGISTER_COMMAND("shutdown", "", con_shutdown, 0); + + MACRO_REGISTER_COMMAND("record", "s", con_record, 0); + MACRO_REGISTER_COMMAND("stoprecord", "", con_stoprecord, 0); } int main(int argc, char **argv) |